Any way to avoid passing all parameters in styled components mixin? - reactjs

I use styled components and have a mixin with many default parameters similar to this
`export const myMixins = {
font: (
fontFamily = 'Arial',
weight = 600,
size = '10px',
style = 'normal') => `
font-family: ${ fontFamily };
font-size: ${ size };
font-style: ${ style };
font-weight: ${ weight };
`
};`
I used the default params to avoid passing all of them when it is not needed. But when I try to run this, it only works if I pass all of them, like this:
${ myMixins.font('Roboto', 'bold', '9px', 'normal') };
I also tried assigning null to params, but nothing changed. When I try to run it my IDE stills displays placeholders for all parameters and it won't work otherwise. I also tried to use keywords like $size but could not figure out the proper syntax nor did I find it anywhere on the web. I would like to be able to pass only the specific parameter that I want to change. Any help is much appreciated.

I believe something like this would resolve your issue:
export const myMixins = {
font: (
fontFamily?: string,
weight?: number,
size?: string,
style?: string
) => `
font-family: ${fontFamily || 'Arial'};
font-size: ${size || 600};
font-style: ${style || '10px'};
font-weight: ${weight || 'normal'};
`,
}

Related

Chakra-ui heading size by heading level in theme

I'm trying to define the font size for h elements in a Chakra-UI theme, and for some reason, no matter what I do it's not overriding what I'm assuming is the base theme responsive definitions.
This is in NextJS, pulling content from DatoCMS headless CMS, so the actual instantiation of the h elements looks like this:
...
renderRule(isHeading, ({ node, children, key }) => {
return (
<Heading key={key} as={`h${node.level}`} pt={6} pb={4}>
{children}
</Heading>
);
}),
...
If I change it there to, say, <Heading key={key} as={`h${node.level}\`} pt={6} pb={4} size=('5xl')>, it works fine. My theme also works fine in general (e.g., naming brand color variables, setting the font weight on Heading elements), but for some reason, I can't get it to set the size of the headings.
The closest I've gotten is setting the size in the global object:
const styles = {
global: (props) => ({
h1: { fontSize: [null, null, '5xl'] }
})
}
Doing the above at least makes it to the browser, but it gets overridden. In dev tools, the computed css shows
#media screen and (min-width: 48em)
.css-fcs9e9 {
font-size: var(--chakra-fontSizes-4xl);
line-height: 1.2;
}
.css-fcs9e9 {
font-family: var(--chakra-fonts-heading);
font-weight: var(--chakra-fontWeights-light);
// font-size: var(--chakra-fontSizes-3xl); shown as overridden
line-height: 1.33;
padding-top: var(--chakra-space-6);
padding-bottom: var(--chakra-space-4);
}
#media screen and (min-width: 48em)
h1 {
// font-size: var(--chakra-fontSizes-5xl); shown as overridden
}
I've also tried
setting a single value in textStyles:
const textStyles = {
h1: {
fontSize: '5xl'
}
}
setting an array in textStyles:
const textStyles = {
h1: {
fontSize: [ null, null, '5xl' ]
}
}
setting the baseStyle at the component level:
const Heading = defineStyleConfig({
baseStyle: {
fontWeight: 'light', // this works
textStyles: { h1: [ null, null, '5xl' ] } // this doesn't work (also for singular `textStyle`
}
})
but none of those three even make it to the browser.
What's the right way to set the text size at the theme level?

How to avoid long conditional rendering with styled components?

I'm working on a project using a lot of different variables. Depending on props, they will render a certain color (for example). The problem is that with the amount of variables my code ends up really long. Is there a solution to clean up this code?
This is an example:
Variables:
const Variables = {
Colors: {
Primary50: "rgb(244, 246, 247)",
Primary100: "rgb(209, 218, 225)",
...rest of colors
},
...rest of variables
}
And the styled component:
const Component = styled.div<{ $color: number }>`
background-color: ${({ $color }) =>
$color === 50
? Variables.Colors.Primary50
: $color === 100
? Variables.Colors.Primary100
: ...rest};
`
I tried something like this, but it is not working:
const Component = styled.div<{ $number: number }>`
background-color: ${Variables.Colors.Primary[$number]};
`
Thanks for your answers!
Better use switch instead of ternary operator for your instance. The second aproach Primary[$number] is wrong, it look like Variables.Colors.Primary.100 you need to enclose Primary50 entirely in square brackets.
const Variables = {
Colors: {
Primary50: 'rgb(244, 246, 247)',
Primary100: 'rgb(209, 218, 225)',
},
};
const Component = styled.div<{ $color: number }>`
background-color: ${props => {
switch (props.$color) {
case 50:
return Variables.Colors.Primary50;
case 100:
return Variables.Colors.Primary100;
default:
return 'white';
}
}};
`;
const Component2 = styled.div<{ $number: 50 | 100 }>`
background-color: ${props => Variables.Colors[`Primary${props.$number}`]};
`;
function App() {
return (
<div className="App">
<Component $color={100}>First example</Component>
<Component2 $number={50}>Second example</Component2>
</div>
);
}
export default App;

how to use vanilla-extract for `style` attribute?

Have a following sample of code which I want migrate to vanilla-extract, my main question is how to generate styles for style attribute in this case?
// in ColorToken.tsx
function ColorToken(props: { name: string; value?: string }) {
return (
<div
style={{
backgroundColor: `var(${props.value})`,
border: 'solid 1px var(--color-border-neutral)',
borderRadius: '100%',
width: '70px',
height: '70px',
}}
>
{props.name}
</div>
);
}
I tried two approaches:
First
// ColorToken.css.ts
import { style } from '#vanilla-extract/css';
export function colorSelector(bgColor: string) {
return style({
backgroundColor: bgColor,
border: 'solid 1px var(--color-border-neutral)',
borderRadius: '100%',
width: '70px',
height: '70px',
});
}
// ColorToken.tsx
import * as selectors from './Colors.css';
function ColorToken(props: { name: string; value?: string }) {
const color: string = // dynamically piking color based on props.
return (
<div className={selectors.colorSelector(color)}>
Error / issue:
error - ./pages/styles/tokens/Colors.css.ts
Error: Invalid exports.
You can only export plain objects, arrays, strings, numbers and null/undefined.
at Array.map (<anonymous>)
Second
// ColorToken.css.ts
export const colorSelector = {
border: 'solid 1px var(--color-border-neutral)',
borderRadius: '100%',
width: '70px',
height: '70px',
};
// ColorToken.tsx
import { style } from '#vanilla-extract/css';
import * as selectors from './Colors.css';
function ColorToken(props: { name: string; value?: string }) {
const color: string = // dynamically piking color based on props.
return (
<div className={style({ ...selectors.colorSelector, backgroundColor: color })}>
Note: here I'm using style function because I think VE might apply some extra things (e.g add vendor prefixes, optimizations etc).
Error / issue:
Unhandled Runtime Error
Error: Styles were unable to be assigned to a file. This is generally caused by one of the following:
- You may have created styles outside of a '.css.ts' context
- You may have incorrect configuration. See https://vanilla-extract.style/documentation/setup
Note: using VE via NextJS setup.
The key to keep in mind here is that vanilla works at build time. It outputs a plain CSS stylesheet, so trying to substitute values in at runtime isn't what it's built for.
If you have a clearly defined set of background colours that you want to use, you could use a recipe, or you could start to build up some atomic CSS with sprinkles.
If it needs to be truly dynamic, impossible to know at build time, you can take advantage of CSS variables using vanilla's dynamic package.

The component styled.p with the id of "sc-iseJRi" has been created dynamically

Good evening everyone, I need some help.
I can't solve a warning:
Keyframes.js:20 The component styled.p with the id of "sc-iseJRi" has
been created dynamically. You may see this warning because you've
called styled inside another component. To resolve this only create
new StyledComponents outside of any render method and function
component.
In this link ( https://pastebin.com/a0kMztfD ) is an example of how I use the styled-component.
In a checkboxes file I have all the functions I use for the styled-component rules, which I then call in the App.js file to assign them to a const variable to use in the return()
How could I solve this problem? It doesn't create any errors for me but of course it creates a thousand warnings.
I also put the code in addition to the link put previously:
In cards.js:
export function getCard(Card) {
let fillMode = (Card.style === null) ? 'both' : Card.style.fillMode
let duration = (Card.style === null) ? '1s' : Card.style.duration
const tmp = keyframes`
from,to {
width: ${Card.width};
height: ${Card.height};
background-color: ${Card.colorCard};
background: linear-gradient(${Card.colorCard2}, ${Card.colorCard});
box-shadow: 0 16px 16px -8px rgba(0,0,0,0.4);
border-radius: 6px;
overflow: hidden;
position: relative;
margin: ${Card.marginCard};
}
`;
const CardFinal = styled.div`
animation: ${duration} ${tmp} ${fillMode};
`;
return CardFinal
}
In App.js:
Const CardContainer = getCard(card1)
return (
<CardContainer></CardContainer>
);
The problem is that you're creating a styled.div inside your getCard function.
The way you get rid of this warning is to move the creation of CardFinal outside of getCard and use getCard function to return whatever css you want to generate and pass them as props later on. Here's how you can pass props with styled-components.
This is how it would look like for your code
const CardFinal = styled.div`
${getAnimation}
`;
export function getCardProps(Card) {
const fillMode = Card.style === null ? "both" : Card.style.fillMode;
const duration = Card.style === null ? "1s" : Card.style.duration;
const tmp = keyframes`
from,to {
width: ${Card.width};
height: ${Card.height};
background-color: ${Card.colorCard};
background: linear-gradient(${Card.colorCard2}, ${Card.colorCard});
box-shadow: 0 16px 16px -8px rgba(0,0,0,0.4);
border-radius: 6px;
overflow: hidden;
position: relative;
margin: ${Card.marginCard};
}
`;
return { fillMode, duration, tmp };
}
const getAnimation = ({ duration, tmp, fillMode }) => {
return css`
animation: ${duration} ${tmp} ${fillMode};
`;
};
Now you'll just use the getCardProps function to the props that CardFinal expects from the getCardProps.
export default function App() {
const cardProps = getCardProps({
style: null,
weight: "100px",
height: "100px",
colorCard: "grey",
marginCard: "10px"
});
return (
<CardFinal {...cardProps}>
YO
</CardFinal>
);
}
Here's a codesandbox link of where you can try & play around to see how it works.
You can also try to un-comment a // const WarningDiv, that basically replicates the warnings you've been encountering with just a basic function that returns an empty styled.div

Which is a better to write a react component with styled-components?

Given some buttons to be shared across a website which one of these two methods is best to use:
const ButtonA = styled.button`
color: 'red'
`
const ButtonB = styled.button`
color: 'blue'
`
to be used like this
const Home = () => {
return <ButtonA>Button A</ButtonA><ButtonB>Button B</ButtonB>;
}
or
const Button = styled.button`
${({variant }) => variant === 'A' && `
color: red;
`
${({variant }) => variant === 'B' && `
color: blue;
`
`
to be used like this:
const Home = () => {
return <Button variant="A">Button A</Button><Button variant="B">Button B</Button>;
}
Please give reasons, advantages and disadvantages for using one method over the other.
As always depends on the case.
It's usually better to pass variant prop as in your second example.
The problem with the first solution is that you have to repeat the same code. Better to do it like this:
const Button = styled.button`
// basic styles here
`
const ButtonWithDifferentStyles = styled(Button)`
// additional styles here
`

Resources