How to add conditional styles in emotion/react? - reactjs

I'm using react and emotion. I need to change the style of an element according to a boolean value. The below code does not work. How can I combine multiple styles correctly?
import { css } from "#emotion/react"
const navSticky = css({
transform: "translateY(-10px)",
})
const navStyle = css({
background: "red",
})
...
<nav css={isSticky ? {...navStyle, ...navSticky} : navStyle}> </nav>

As first variant i suggest use styled component for this. Set background as base property and set transform if isSticky prop has been passed with true value.
As second variant i suggest correct your example. Use JSX Pragma with jsx function from '#emotion/react'. This allow use css prop. Docs: https://emotion.sh/docs/css-prop#jsx-pragma
// first variant
const Nav = styled.nav`
background: red;
transform: ${p => p.isSticky && "translateY(-10px)"};
`
const App = () => <Nav isSticky={true}>Some Elements</Nav>
// second variant
/** #jsx jsx */
import {jsx} from '#emotion/react'
const navSticky = {
transform: 'translateY(-10px)',
}
const navStyle = {
background: 'red',
}
const App = () => (
<nav css={{...navStyle, ...(isSticky && navSticky)}}>Some Elements</nav>
)

Related

React Material UI v5 styled with defaultProps

When using multiple styled components, the top one overrides other default props.
import { styled } from '#mui/material/styles'
import { Badge } from '#mui/material'
const Badge1 = styled(Badge)``
// this works if Badge1 is used directly: <Badge1 />
Badge1.defaultProps = {
max: Infinity
}
const Badge2 = styled(Badge1)`` // styled Badge1
// this overrides defaultProps from Badge1. Prop max: Infinity does no apply here
Badge2.defaultProps = {
variant: 'standard'
}
Badge2 has only variant: 'standard' default prop. It skips max: Infinity
How can I keep all the defaultProps from each level
When you style a component via multiple styled calls using Emotion, Emotion collapses the styling layers into a single wrapper component rather than adding an additional wrapper around the first wrapper. Emotion retains the defaultProps from the previous wrapper, but you are then overwriting that when you set Badge2.defaultProps.
You can retain any previous defaultProps with the following syntax:
Badge2.defaultProps = {
...Badge2.defaultProps,
variant: 'standard'
}
Below is an example demonstrating what happens with default props with each styled wrapping. The fix is demonstrated with StyledAgainWithDefaultRetainExisting.
import styled from "#emotion/styled";
function MyComponent({ className, ...defaults }) {
return <div className={className}>Defaults: {JSON.stringify(defaults)}</div>;
}
MyComponent.defaultProps = {
orig: true
};
const StyledMyComponent = styled(MyComponent)`
background-color: blue;
color: white;
`;
StyledMyComponent.defaultProps = {
styled: true
};
const StyledAgainNoDefaultsAdded = styled(StyledMyComponent)`
background-color: purple;
`;
const StyledAgainWithDefault = styled(StyledMyComponent)`
background-color: green;
`;
StyledAgainWithDefault.defaultProps = {
styledAgain: true
};
const StyledAgainWithDefaultRetainExisting = styled(StyledMyComponent)`
background-color: brown;
`;
StyledAgainWithDefaultRetainExisting.defaultProps = {
...StyledAgainWithDefaultRetainExisting.defaultProps,
styledAgainRetain: true
};
export default function App() {
return (
<div>
<MyComponent />
<StyledMyComponent />
<StyledAgainNoDefaultsAdded />
<StyledAgainWithDefault />
<StyledAgainWithDefaultRetainExisting />
</div>
);
}

React jss overrides parent styles and retains child style

in using the jss lib in react with a custom theme that merges two themes together for use. Previously there was no problems but to type it correctly and create an ease of development, it was modified a bit of how the createUseStyles works. Here is an example of the usage before the change:
import { cn, createUseStyles, useTheme } from "#styling";
const useStyles = createUseStyles({
container: {
width: "500px",
height: "100px",
}
});
const Child = ({extraStyles}:{extraStyles:string[]}) => {
const theme = useTheme();
const styles = useStyles({theme});
return (
<div className={cn(styles.container, ...extraStyles)}>
</div>
)
}
// parent
import { cn, createUseStyles, useTheme } from "#styling";
import Child from "#child";
const useStyles = createUseStyles({
large: {
width: "999px",
height: "999px",
}
});
const Parent = () => {
const theme = useTheme();
const styles = useStyles({theme});
return (
<Child extraStyles={[styles.large]}/>
)
}
where cn just combined the classes together. This would result in a stylesheet where the parents styles would override the childs, allowing for flexibility.
After some necessary modification the createUseStyles is typed and generated within the styling. It looks something like this:
//styling
import {
createUseStyles as createStyles,
useTheme,
ThemeProvider,
Styles,
} from "react-jss";
const createUseStyles = (styles) => {
return (props) => {
const tempTheme = useTheme();
const temp = createStyles(styles);
return temp({ ...props, theme: tempTheme });
};
};
export { ThemeProvider, createUseStyles };
I have excluded the extra typing, but it was essentially to save the need to typescript createUseStyles and the usage of useTheme every single time within a component.
...But in doing so & in usage the exact same example as provided above with this new createUseStyle, the child injects it's style above the parents and doesn't pull in the props styling. Using !important on every single prop style is extremely tedious.
I'm confused why just pulling useTheme outside would break the injection structure (assuming that is the cause of this problem)?
Possible, you can add index for createUseStyles to set order of injection, something like this:
// Child
const useStyles = createUseStyles({
container: {
width: "500px",
height: "100px",
}
}, { index: 2 });
// Parent
const useStyles = createUseStyles({
large: {
width: "999px",
height: "999px",
}
}, { index: 1 });
I don't know how it works, if you need to add into every componente style or in entire stylesheet. The official documentation of React JSS isn't clear but it has in JSS API.

Not able to dynamically style component - TypeScript , Styled-component

I have a struggle here and I fear I do not understand what is happening.
I am following the documentation of styled-components
and everything seems to be like they described it, but it seems that it is not working on my side.
I am trying to render a Card/div or basically to expand it based on props coming from the parent
...
const [open, setOpen] = useState<boolean>(false)
...
return (
<CardWrapper
open={open}
... />
...
)
And here I use my state inside the styled component
import { keyframes, css } from '#emotion/react'
import styled from '#emotion/styled/macro'
const expandAnimation = keyframes`
0% {
height: 180px;
}
100% {
height: 240px;
}
`
const unexpandAnimation = keyframes`
0% {
height: 240px;
}
100% {
height: 180px;
}
`
...
export const CardWrapper = styled.div<{open: boolean}>`
animation: ${(open) => (open
? css`${expandAnimation} 1s ease-in-out ;`
: css`${unexpandAnimation} 1s ease-in-out;`)} ;
`
Gave it a try also with
export const CardWrapper = styled.div<{open: boolean}>`
${({ open }) => open && css`
animation-name: ${expandAnimation }
`}
`
But it seems I am not doing something right. I am pretty new to styled-components, but if someone can tell me what am I doing wrong.
UPDATE
In order for us to display some animation we need to provide a prop of ease and a prop of direction. In my case i use forwards and backawrds
export const CardWrapper = styled.div<{open: boolean}>`
${({ open }) => (open
? css`animation: ${expandAnimation} 0.7s ease forwards`
: css`animation: ${unexpandAnimation}1s ease backwards `)
}
`

Next.js: How to get applied styles from element?

Let's say I have global.css
.test {
background-color: black;
width: 50px;
height: 50px;
}
For some reason, I need to get the styles data from applied element. I tried refs but it always return empty string.
import { useState, useEffect, useRef } from "react";
const IndexPage = () => {
const divEl = useRef<HTMLDivElement | null>(null);
const [divStyle, setDivStyle] = useState({} as CSSStyleDeclaration);
useEffect(() => {
if (divEl.current) {
setDivStyle(divEl.current.style);
}
}, [divEl.current]);
return (
<div>
<div ref={divEl} className="test"></div>
<pre>{JSON.stringify(divStyle, undefined, 2)}</pre>
</div>
);
};
export default IndexPage;
Is it because next.js SSR or should I add something to dependency array?
code sandbox here
You can use computed styles to get what you need, although it won't be a "simple" object with properties. You'll need to query each property individually:
if (divEl.current) {
setDivStyle(getComputedStyle(divEl.current));
}
and then you can do something like:
divStyle.getPropertyValue("background-color")
Here's an example sandbox (forked from yours) that demostrates it.

React Styled-components TypeScript extending styles with props on base style gets TS2769 error

I am trying to extend a base styled to other styled components and I get a typescript error. I have been able to do this in just pure javascript, am not able to do the same in typescript
base file
_Modal.ts
import styled from 'styled-components/macro'
interface I_Modal {
'background-color': string
'min-height': string
'min-width': string
}
const _modal = styled.div<I_Modal>`
background-color: ${props => props['background-color'] ? props['background-color'] : props.theme.colors.baseColor};
min-height: ${props => props['min-height'] ? props['min-height'] : '300px'};
min-width: ${props => props['min-width'] ? props['min-width'] : '200px'}
`
export default _modal
file i am trying to extend the styles to
registerModal.ts
import styled from 'styled-components/macro'
import _modal from './_modal'
export const RegisterModal = styled(_modal)`
background-color: purple;
height: 300px;
width: 200px;
`
everything in VSCode says its good, its just not compiling properly
image of error
Tips for using styled-components in TypeScript
If the parent is not a specific react component, it is recommended to write it simply as follows.
const Button = styled.button<{primary: boolean}>`
color: ${({primary}) => primary ? 'skyblue' };
`
Picking can reduce maintenance costs if the component is complex or references the parent props
interface Props {
primary
secondary
}
function MyComponent(props: Props) {
return (
<div>
<Button secondary={props.secondary} primary={props.primary}>{props.children}</Button>
</div>
)
}
const Button = styled.button<Pick<Props, 'primary' | 'secondary'>>`
${({primary, secondary}) => primary ? css`` : secondary ? css`` : css``}
`
Type a constant
It is convenient to type constants such as color and size so that you can see which px is applied when you use them.
const FONT = {
XXXLARGE: 32,
XXLARGE: 24,
XLARGE: 18,
LARGE: 16,
MEDIUM: 14,
BASE: 12,
SMALL: 11,
XSMALL: 10,
TINY: 8,
} as const
const FONT_WEIGHT = {
NORMAL: 400,
BOLD: 600,
} as const
const BORDER_RADIUS = 4 as 4
export default {
FONT,
FONT_WEIGHT,
BORDER_RADIUS
}
By doing this, you can check which constant has which px with the suggestion function of the editor.
When using css prop
Babel-plugin and css prop enabled by babel macro are extensions of jsx, so if you do nothing, TS will get angry.
You can solve it by writing the following in index.d.ts of type root. (Styled-components / cssprop is an extension of react)
import {} from 'styled-components/cssprop'
When using attrs
This has to be a bit cumbersome to write, so you may not use attrs a lot in the first place.
const StyledImg = styled.img.attrs<{ logoSrc: logoSrc }>(
({logoSrc}) => ({
src: logoSrc,
})
)<{ logoSrc: logoSrc }>

Resources