How to Use CSS Variables with Styled Components
No matter how you see it, becoming better at CSS makes you a more effective front-end developer. Adding to its arsenal, CSS has introduced CSS variables, also known as Custom Properties.
Now, styled-components have been a wonderful feature for front-end development, with features like passing in props and leveraging them to manipulate styles; it is no wonder that more and more people prefer it for front-end landscaping. Ideally, styled-components are a blessing, but when you add more prop-based styles into the mix, it can get difficult to understand in the future.
This is where CSS variables come in. CSS variables and styled-components are bound to ensure the next developer understands our purpose. In this article, we will see how to use CSS variables with styled-components to improve your workflows and avoid a tangled code.
Before we start, here are a few things you need to know about CSS variables.
- Variable’s value can be called with the var keyword. Think of it as a getter function.
- Custom properties start with two dashes, differentiating them from traditional CSS.
- The type of value they hold is not limited to colors and pixels only. They can have any value.
- The CSS variables can be attached to any selector, not just the root HTML tag!
- If the CSS variable is not defined, you can allot a default value.
To better understand the pairing of CSS variables with styled-components, let’s look at a simple Button component.
const Button = styled.button` height: 32px; padding: 0 32px; border-radius: 16px; border: none; color: white; font-size: 13px; font-weight: 600; text-shadow: 1px 1px 0px #3a00df; background: linear-gradient(170deg, #359eff 5%, #3a00df 95%); `;
render();
Looking at the code, you can tell that the button is small and not up to industry standards. It would help if it were between 44px to 48px tall. CSS variables provide a great answer to this problem. Instead of specifying how each component responds at different breakpoints, we pass a reactive variable that tracks it for us.
const GlobalStyles = createGlobalStyle` html --min-tap-target-height: 48px;
@media (min-width: $ props.theme.bp.desktop>) --min-tap-target-height: 32px; > > `;
With this, the responsive components get much simpler.
const Button = styled.button height: var(—min-tap-target-height); ;
const TextInput = styled.input` height: var(--min-tap-target-height); `;
Now regardless of the viewport size, ‘height’ will always point to the same variable. The difference being that ‘the variable’ will change its value as the window size changes. Looking at the code, you can tell that we are reusing the button without modifying it. You can get target specific variables that the button will use without actually needing to pass styles around. Thus, allowing you to make use of the button without any modification. A truly reusable component.
There is a drawback when using CSS variables with styled-components. With styled-components, you can put your variables anywhere you want, including media queries, but CSS variables cannot be used near these media queries. Though there is a rumor around methods that may let this happen, it is unfortunately still a rumor.
Despite the drawback, combining the two opens up a wide range of opportunities for a developer. It also promotes a more organized environment that increases efficiency and workflow. Whether you plan to use CSS variables with styled-components or not, it is still good to know how to use them.
Other useful articles:
© , Learn CSS 101 — All Rights Reserved — Terms of Use — Privacy Policy
Using CSS Variables to Tame Styled Component Props
When I started writing React components about 3 years ago our code base was full of SCSS. This quickly became unmanageable, not because of SCSS, but as a result of all the styles being overwritten in new components as I was converting an Angular library. At the time, afaik(new), there were only 2 ways to keep your styles isolated and scoped in React: Style objects in JS and CSS Modules. The ecosystem has evolved a lot in 3 years. Today we have a lot of options for scoped styles in React. I have leaned towards styled-components for a majority of projects to date. There are a lot of things I love about this library. From the SCSS inspired syntax to the ability to create and inherit styles, this library is a solid solution for scoping styles to components and implementing it «feels» good to me. One of my favorite features of styled-components is the ability to pass in props and leverage these props to manipulate styles. It is a lot like using the classnames package without having to declare individual classes for every prop-related style update. The following is an example of a button.
Button>Click Me!/Button> const Button = styled.button` background-color: gray; `;
In this example the default Button color is gray . Traditionally, if we wanted to update the background color for a Primary variant of Button we would need to add a className to the Button in order to manipulate the style or pass the background-color override in as a style update on the Button in our jsx.
Button className="primary">Click Me!/Button> const Button = styled.button` background-color: gray; &.primary < background-color: blue; >`;
As you can see, styled-components provides a way for us to add variant classes without the need for the classnames package, but there is a better way to handle this built into the styled API. We can manipulate variants based on props that are passed to our Button ! Take a look at the following:
Button variant="primary">Click Me!/Button> const Button = styled.button` background-color: props.variant === "primary" ? "blue": "gray">; `;
Now, with a little ternary action, we can actually toggle the color of our button based on the value passed into the variant prop. Cool, right? This is one of the features that makes styled-components feel so React-like. Normally, this approach is fine, but when we start adding more props-based styles into the mix, our Button can get busy and the variants can be hard to grok when we come back to it for future iterations. Like such:
Button variant="primary" shape="rounded" weight="bold" size="large" >Click Me!/Button> const Button = styled.button` background-color: props.variant === "primary" ? "blue": "gray">; color: props.variant === "primary" ? "white": "charcoal">; border-radius: props.shape === "rounded" ? "8px": "0">; font-weight: props.weight === "bold" ? "700": "400">; font-size: props.size === "large" ? "18px": "12px">; padding: props.size === "large" ? "1rem": "0.5rem">; `;
As you can see, all the variations on our button quickly get lost in the stack of ternary operations inside of our styles block. Not only this, but without adding in a type-checking library it is hard to follow which props we are actually expecting in our styled.button . If we wanted to update only the font-weight of a bold button, we would have to sift through this mess of ternaries. If we wanted to add a 3rd option for font-weight it would get even messier. Enter CSS Variables. Fortunately, CSS variables are supported by styled components and can easily be inserted into our styles block to ensure our intentions are clear to the next developer that inherits our button code (or our future selves). Take a look at the styled.button when we apply CSS Variables to every prop option.
Button variant="primary" shape="rounded" weight="bold" size="large" >Click Me!/Button> const Button = styled.button` --props-variant-default-background-color: gray; --props-variant-default-color: charcoal; --props-variant-primary-background-color: blue; --props-variant-primary-color: white; --props-variant-primary: blue; --props-shape-default: 0; --props-shape-rounded: 8px; --props-weight-default: 400; --props-weight-bold: 700; --props-size-default-size: 12px; --props-size-large-size: 18px; --props-size-default-padding: 0.5rem; --props-size-large-padding: 1rem; background-color: props.variant === "primary" ? "var(--props-variant-primary-background-color)" : "var(--props-variant-default-background-color)">; color: props.variant === "primary" ? "var(--props-variant-primary-color)" : "var(--props-variant-default-color)">; border-radius: props.shape === "rounded" ? "var(--props-shape-rounded)" : "var(--props-shape-default)">; font-weight: props.weight === "bold" ? "var(--props-weight-bold)" : "var(--props-weight-default)">; font-size: props.size === "large" ? "var(--props-size-large-size)" : "var(--props-size-default-size)">; padding: props.size === "large" ? "var(--props-size-large-padding)" : "var(--props-size-default-padding)">; `;
Alright, I know, this approach is more verbose for sure. It will take you more lines of code than the original implementation. However, your future self will thank you, because there is no need for guessing or fishing through ternaries or switch statements. It is very obvious where I go to update the font size of a large variant to 20px . It is also clear what props we are expecting. We can also use CSS Variables to toggle properties inside of media queries:
const Button = styled.button` --props-size-default: 12px; --props-size-large: 18px; @media screen and (min-width: 860px) < --props-size-default: 14px; --props-size-large: 20px; >font-size: props.size === "large" ? "var(--props-size-large)" : "var(--props-size-default)">; `;
That is all! Have fun adding CSS Variables to your Styled Components! Let me know any other ways you have incorporated CSS Variables into your React workflow.