Differences between Next.js and React app using styled components - next.js

I am migrating my app from SPA React to Next.js
I have found this weird bug wherein the SPA I have max-width: 100% while in Next.js width: fit-content
I cannot understand where the different behavior is coming from, I know I can fix it easily but I would like to understand the causes.
code is
const RadioButtonWrapper = styled.div`
color: ${({ theme, dark }) => theme.global.colors[dark ? 'white' : 'black']};
svg {
transform: scale(1.5);
fill: ${({ theme, dark }) => theme.global.colors[dark ? 'white' : 'black']};
}
[class^="StyledRadioButton__StyledRadioButtonContainer"] {
margin-bottom: 6px;
}
[class^="StyledBox__StyledBoxGap"] {
height: 0;
}
[class^="StyledRadioButton__StyledRadioButtonBox"] {
height: 18px;
width: 18px;
transition: opacity 200ms ease-in-out;
border-width: 1px;
border-color: ${({ theme, dark }) => theme.global.colors[dark ? 'white' : 'black']};
margin-bottom: 1px;
}
&:hover {
[class^="StyledRadioButton__StyledRadioButtonBox"] {
border-color: ${({ theme, dark }) => theme.global.colors[dark ? 'white' : 'black']} !important;
}
}
label {
[class^="StyledRadioButton"] {
flex-shrink: 0;
}
}
${({ rightAlign }) => rightAlign
&& ` label {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
}`}
`;
const RadioButtonGroup = ({ dark, rightAlign, ...props }) => (
<RadioButtonWrapper dark={dark} rightAlign={rightAlign}>
<GrommetRadioButtonGroup {...props} />
</RadioButtonWrapper>
);
SPA
Next.js

The difference has to do with what sort of polyfills the two compilers are using to make sure that your code can run in most browsers.
Check out the Browser Compatibility for the two properties. max-width has been part of the CSS spec for a very long time and is supported by every browser. Whereas fit-content is not supported at all in Internet Explorer and needs a prefix for Firefox.
The “max-width: 100%” version is more versatile, so your SPA ships that version.
Next.JS has more advanced polyfill techniques. They only ship the backwards compatible version to older browsers which need it. When you run your code in a modern browser you see “width: fit-content”. If you were to run the code in IE you would see “max-width: 100%” instead.

Related

REACT Cant convert my styling to inline styles

I need to take some button styling and apply it to some inline styling.
I thought this would be a simple copy and paste but when I try that I have a whole host of errors.
I've tried to convert the styles to inline format but I keep chasing errors I don't know how to resolve.
Thanks for the help.
The following is the button I am trying to apply styling to.
<BtnWrap>
{navToPage && navToPage.startsWith("/") ? (
//if it is linked to another page use router link
<LinkR to={navToPage} style={{}}>
{navToPage}
</LinkR>
) : (
//else use the smart link component
<Button
to={navToPage}
smooth={true}
duration={500}
spy={true}
exact="true"
offset={-80}
primary={primary ? 1 : 0}
dark={dark ? 1 : 0}
dark2={dark2 ? 1 : 0}
>
{buttonLabel}
</Button>
)}
</BtnWrap>
This is the button styling I am trying to convert to inline styling.
export const Button = styled(Link)`
border-radius: 50px;
background: ${({ primary }) => (primary ? "#ca1f27" : "#010606")};
white-space: nowrap;
padding: ${({ big }) => (big ? "14px 48px" : "12px 30px")};
color: ${({ dark }) => (dark ? "#010606" : "#fff")};
font-size: ${({ fontBig }) => (fontBig ? "20px" : "16px")};
outline: none;
border: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.2s ease-in-out;
&:hover {
transition: all 0.2s ease-in-out;
background: ${({ primary }) => (primary ? "#fff" : "#ca1f27")};
}
`;
Styles in React.js are a kind of object that is passed inside the style property like this
style={{ marginTop: 10, backgroundColor: 'red' }}
I leave some references that can help you:
https://www.w3schools.com/react/react_css.asp
https://www.pluralsight.com/guides/inline-styling-with-react

Styled-components - Over 200 classes were generated, but cant get :hover to work

I came across the error in styled-components :
Over 200 classes were generated for component......
and did the suggested fix from console, and that did the trick, but when I have a container component "Card" that when hovered should change text color of another component "Number" (which has that suggested fix applied, then I cant change the color (i assume because style overrides the hover change, because it works fine with opacity)
the mentioned components are in src/ProgressPieCard (first 2 components)
anyone got any got suggestions, thanks in advance :)
( sorry styling/position is a bit off )
CodeSandBox
const Number = styled.p.attrs<ColorProps>((props) => ({
style: {
color: props.color,
},
}))`
position: absolute;
span {
font-size: 1.5rem;
}
`;
const Card = styled.div.attrs<ColorProps>((props) => ({
style: {
background: props.color,
},
}))`
position: relative;
&:hover {
${Number} {
opacity: 0.5;
// color: red; <-- this dont work
}
}
`;
Values ​​from props were pass as inline styles. They have higher priority. I suggest passing values ​​from props differently. The example below will now work as you wanted.
const Number = styled.p<ColorProps>`
position: absolute;
color: ${p => p.color};
span {
font-size: 1.5rem;
}
`;
const Card = styled.div<ColorProps>`
position: relative;
background: ${p => p.color};
&:hover ${Number} {
opacity: 0.5;
color: red; <-- this WILL work :)
}
`;

How to remove ant-click-animating of button Ant design

After I click on the button, it has the animation around the button. So I want to turn it off, I try to set CSS for element and Pseudo-classes but it's not working.
.ant-switch,
.ant-switch:focus,
.ant-switch:active {
border-color: white !important;
box-shadow: none !important;
outline: unset;
}
My code:
import React from 'react';
import { Switch } from 'antd';
import styled from 'styled-components';
const RSwitch = styled(Switch)`
background-color: ${props => props.backgroundcolor};
.ant-switch-handle::before {
background-color: #9b9b9b;
right: 0;
}
&[aria-checked='true'] {
.ant-switch-handle {
::before {
background-color: ${props => props.color};
}
}
}
`;
export default function SwitchComponent({
onChange,
checked,
color = '#00afdb',
backgroundColor = ''
}) {
return (
<RSwitch
onChange={onChange}
checked={checked}
size="small"
color={color}
backgroundcolor={backgroundColor}
/>
);
}
ant switch picture
HTML of switch
I fixed it with this solution
create a button.less file in your ant overrides and put this inside
[ant-click-animating-without-extra-node='true']::after{display:none;}
and it will work like a charm.
I realize it is just a div tag so I don't display it. If everybody has a better solution, please let me know.
.ant-click-animating-node {
display: none;
}
.ant-btn {
&::after {
all: unset;
}
}

CSS Ripple effect with pseudo-element causing reflow

I'm trying to create the material ripple effect with styled-components (which is unable to import the material web-components mixins). I want to stick with using the after element for the foreground effect, to keep the accesibility tree intact.
However, most notably on mobile, the ripple transition is causing reflow in the button's content. It would seem to happen because of the display change (from none to block), but I have tried some alternatives which don't share this artifact, and this side-effect is still present.
Here's my code (I'm using some props to set the ripple, but you can hard-set them if you want to reproduce): [Here was an outdated version of the code]
Thanks for the attention.
Edit: The bug only happens when I add a hover effect to the button, very weird. Below follows the link and a code sample (you will have to set a react repository in order to reproduce it, unfortunately)
https://github.com/Eduardogbg/ripple-hover-reflow-bug
import React, { useRef, useReducer } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components'
const ButtonBase = styled.button`
cursor: pointer;
width: 250px;
height: 6vh;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
outline: none;
position: relative;
overflow: hidden;
border-width: 0;
background-color: cyan;
:hover {
filter: brightness(1.06);
}
::after {
content: '';
pointer-events: none;
width: ${({ ripple }) => ripple.size}px;
height: ${({ ripple }) => ripple.size}px;
display: none;
position: absolute;
left: ${({ ripple }) => ripple.x}px;
top: ${({ ripple }) => ripple.y}px;
border-radius: 50%;
background-color: ${({ ripple }) => ripple.color};
opacity: 0;
animation: ripple ${({ ripple }) => ripple.duration}ms;
}
:focus:not(:active)::after {
display: block;
}
#keyframes ripple {
from {
opacity: 0.75;
transform: scale(0);
}
to {
opacity: 0;
transform: scale(2);
}
}
`
const rippleReducer = ref => (ripple, event) => {
const { x, y, width, height } = ref.current.getBoundingClientRect()
const size = Math.max(width, height)
return {
...ripple,
size,
x: event.pageX - x - size / 2,
y: event.pageY - y - size / 2
}
}
const DEFAULT_RIPPLE = {
size: 0,
x: 0,
y: 0,
color: 'white',
duration: 850
}
const Button = props => {
const ref = useRef(null)
const [ripple, dispatch] = useReducer(
rippleReducer(ref),
{ ...DEFAULT_RIPPLE, ...props.ripple }
)
return (
<ButtonBase
ref={ref}
className={props.className}
ripple={ripple}
onClick={event => {
event.persist()
dispatch(event)
}}
>
{props.children}
</ButtonBase>
)
}
ReactDOM.render(
<div style={{
backgroundColor: 'red',
width: '500px', height: '500px',
display: 'grid',
placeItems: 'center'
}}>
<Button>
<span style={{ fontSize: '30px' }}>
abacabadabaca
</span>
</Button>
</div>,
document.getElementById('root')
);
The problem seems to be related to this chromium bug that was supposedly solved a few years ago: Image moves on hover when changing filter in chrome
Setting transform: translate3d(0,0,0); looks like a fix, though my eye isn't pixel-perfect.

React css transition does not work correctly

I've written a React app, using CSS transitions. But those transitions does not work correctly in some of the components. In my app, only the components who are moving upwards works well, those who are moving downwards moves instantly without animation. (I want them both moves with animation.)
Here is the CSS I used there:
div.canvas {
position: absolute;
top: 90px;
left: 60px;
width: 640px;
height: 480px;
border: 1px solid #999;
background: white;
}
div.canvas-rect {
position: relative;
margin-left: 20px;
margin-top: 10px;
height: 20px;
background: green;
transition: all 1s linear;
-moz-transition: all 1s linear; /* Firefox 4 */
-webkit-transition: all 1s linear; /* Safari 和 Chrome */
-o-transition: all 1s linear; /* Opera */
}
UPDATED:
I also built a codepen.io project to show the problem. It has the complete code of this demo project.
I've tried to add a log entry to componentDidUpdate, componentDidMount and componentWillUnmount methods to show whether these component are re-created or updated, it shows that they are all updated (not re-created, or removed) every second.
Well, after I started a bounty because I also have this problem I finally found what seems to be the problem.
When you are using absolute position (or relative, as in your case), if you re-render the whole list every time, React will re-order the elements in the DOM (as you said, the elements are not being recreated, just updated). But this creates the problem with the transitions... apparently, if you move an element while the transition is running then you end up cutting the animation.
So, for cases in which you want to use position absolute, the key concept is to render the containers of your elements once (in this case, just divs) and only change the inner contents based on the new order. If you need to add more elements, just add them at the end.
I modified your codepen so that it reflects what I am saying. My example is very dumb because I just created 4 ad-hoc divs, but it illustrates the idea: create as many containers as you need, but DO NOT use a map that recreates them every time, or your transitions will be cut.
https://codepen.io/damianmr/pen/boEmmy?editors=0110
const ArrList = ({
arr
}) => {
return (
<div style={{position: 'relative'}}>
<div className={`element element-${arr[0]} index-${arr[0]}`}>{arr[0]}</div>
<div className={`element element-${arr[1]} index-${arr[1]}`}>{arr[1]}</div>
<div className={`element element-${arr[2]} index-${arr[2]}`}>{arr[2]}</div>
<div className={`element element-${arr[3]} index-${arr[3]}`}>{arr[3]}</div>
</div>
);
}
So, the problem is basically how you create a static list of containers and how you iterate through that list so that the first container renders the first element of your data, the second container the second element, etc.
Hope that it helps, this problem was driving me crazy too! :)
I know this wasn't the case, but since I got here also looking for React css transition does not work correctly, I just wanted to share:
If you create an element using arrow functions inside render, it won't get properly animated, since a new componente is always being created.
You should create a function outside and invoke it in 'render'.
You can trick React by using index as key. If you think about el, and index as starting position (index) and end position (el), the element has moved to the old end position by the end of the transition, and by when it's there, it's taken over by the new start position and (index) is switched to match the new setup. This is because when you set key in an element in react, the virtual DOM will always interpret it as it is the same element. And for the sake of it, you're right in setting index as the "id" in general.
I made a working example only by switching index/el (and setting element position to absolute).
const {combineReducers, createStore} = Redux;
const { Provider, connect } = ReactRedux;
const ArrList = ({
arr
}) => (
<div>{
arr.map((el, index)=>
<div
key={""+index}
className={`element element-${el}` + ` index-${el}`}
>
{el}
</div>) }
</div>
)
const mapStateToArrList = (state) => {
return {
arr: state.appReducer.arr
}
};
const App = connect(mapStateToArrList, null)(ArrList);
const initialState = {
arr: [1, 2, 3, 4]
}
const appReducer = (state = initialState, action) => {
switch(action.type) {
case "tick":
return {
...state,
arr: _.shuffle(state.arr)
}
default:
return state
}
}
const reducer = combineReducers({
appReducer
})
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const dispatcher = () => {
store.dispatch({
type: "tick"
})
setTimeout(dispatcher, 1000)
}
dispatcher()
.element {
position: absolute;
height: 20px;
background: green;
margin-left: 20px;
margin-top: 20px;
text-align: right;
color: white;
line-height: 20px;
transition: all 1s ease-in;
-moz-transition: all 1s ease-in; /* Firefox 4 */
-webkit-transition: all 1s ease-in; /* Safari 和 Chrome */
-o-transition: all 1s ease-in; /* Opera */
}
.element-1 {
width: 20px;
}
.element-2 {
width: 40px;
}
.element-3 {
width: 60px;
}
.element-4 {
width: 80px;
}
.index-1 {
top: 20px;
}
.index-2 {
top: 40px;
}
.index-3 {
top: 60px;
}
.index-4 {
top: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.js"></script>
<div id="root"></div>
If you are removing the element from the virtual DOM, then the react will update its contents, so you won't see the animations. What you can do is either use react-transition-group OR tell your app to wait x ms before updating the dom once the event is called OR use visibility to toggle between hidden and showing instead of removing it completely from the DOM.
You did recreate DOM elements each time.
You should define collect key value.
I changed your key value '' + el to '' + index.
<div key={'' + index} className={'element element-' + el + ' index-' + index} >
Just change css properties only :)
I''ll just leave that here in case helps somebody but to me it was solved by moving from
export default function Contact(props) {...}
To:
const Contact = (props) => {...}
export default Contact

Resources