Scott's Digital Garden.

CSS Variables Fallback

I used CSS variables to make a theme switch, light and dark, I’ve done this in the past using styled-components but not attempted it via the CSS variables route.

With styled-components you defined your colour schemes or themes in a theme object then use the styled-components ThemProvider to switch between the two high up in the component tree.

This time round I used a React hook to switch the theme and used CSS variables for a light and dark theme.

I got a bit carried away and used variables for lot’s of other things besides a simple colour, I used it for gradients and a box shadow, here’s the light side:

1body[data-theme='light'] {
2 --colour-background: #f7fafc;
3 --colour-on-background: #1a202c;
4 --box-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px
5 rgba(0, 0, 0, 0.05);
6 --box-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px
7 10px -5px rgba(0, 0, 0, 0.04);
8 --title-gradient: linear-gradient(#9966cc, #663399);
9 --qrt-turn-gradient: linear-gradient(0.25turn, #9966cc, #663399);

This is mixed in with a design system approach I’ve adapted from how Tailwind is set out.

So that CSS will be in a CSS-in-JS style and in this instance is living with the global styles of the project. They are defined in a JS theme object, and exported, like this:

1export const GlobalStyle = createGlobalStyle`
2 body[data-theme="light"] {
3 --colour-background: ${({ theme }) => theme.colours.grey[100]};
4 --colour-on-background: ${({ theme }) => theme.colours.grey[900]};
5 }
6 ...

I was using the CSS gradient like this, thinking that :

1background: var(--title-gradient);
2-webkit-background-clip: text;
3background-clip: text;
4-webkit-text-fill-color: transparent;

I was quite happy and feeling kinda smug with myself that I could abstract away those gradients and box shadows. Then things started to get a bit janky on the first render and I couldn’t work out what I had done wrong.

I was getting an awful flash of unstyled content, well I say unstyled content, it wasn’t actually unstyled as the text was transparent which was part of the styles, it’s just the gradient wasn’t being applied.

So, go back to how it was originally, ok that worked:

1background: linear-gradient(#9966cc, #663399);
2-webkit-background-clip: text;
3background-clip: text;
4-webkit-text-fill-color: transparent;

I found that if I didn’t use the variable then it rendered fine, but I needed to use the variable or no theme!

So I was searching for a fallback solution but couldn’t find anything and it wasn’t until I found this article that I realised that I was adding the fallback in the wrong place.

1background: linear-gradient(
2 var(
3 --title-gradient-from,
4 $ {({theme}) => theme.colours.primary[200]}
5 ),
6 var(
7 --title-gradient-to,
8 $ {({theme}) => theme.colours.primary[500]}
9 )
11-webkit-background-clip: text;
12background-clip: text;
13-webkit-text-fill-color: transparent;

This drawback here is that I have had to put in the default colour scheme fallback, which means if the user has the dark theme set then they’re going to have the light theme flash first.

Back to Top