Creating Accessible Colours Based on Props with Styled Components
Hey! Just so you know, this article is over 2 years old. Some of the information in it might be outdated, so take it with a grain of salt. I'm not saying it's not worth a read, but don't take everything in it as gospel. If you're curious about something, it never hurts to double-check with a more up-to-date source!
So I've seen some shade thrown on CSS-in-JS, more specifically the unnecessary complications JS provides when you can achieve the same outcomes with standard CSS or SCSS.
Variables? We've got custom CSS properties
Responsive backgrounds? Breakpoints in CSS or with dynamic images you can use an HTML Picture or srcset attributes.
Fixed navigations on scroll? Position sticky
Now while I can see the argument for using CSS as it comes, there are some use cases where using CSS-in-JS is a necessary evil.
One of the most common uses I have for CSS-in-JS is the creation of an accessible colour palette that is dynamically produced based on the colours a layout is given.
Let's create an example to illustrate just what I'm talking about.
The Dynamic Hero#
A hero component, if you've read any of my previous posts is one of my favourite components. It's a great way to show off imagery, create space and focus on the subject of the page.
Our hero component will be kept quite simple, it'll include -
- Background Colour
Now all of these are quite easily defined in our application using some basic HTML and we'll sprinkle in some minimal CSS for the colours and spacing -
One requirement of this component is the need for it to have a dynamic background colour that will be defined by a content editing tool, such as Gutenberg. The content creator will build a page or post with a hero component, but the colours used for the background aren't absolute.
As we want a dynamic background colour, we'll write this as a 'styled-component'. Styled components is an npm package which allows us to pass props (bits of data, e.g. background colour value) into a JS file which will render CSS for use on the fly.
Essentially allowing us to say
and generating a hero with a CSS class which could use the prop from background to style the background colour of the component.
Now one could argue that you can mitigate the use of CSS-in-JS in this example with the introduction of CSS trump classes.
We could use common colours and the brands colour palette to generate an arsenal of CSS classes such as -
Where we can apply any of these trump classes to set the background colour of a component. No specific, completely reusable classes. Seems delightful right?
Well not only does this restrict a colour palette (which you could again argue is good) but it does answer the question of what text colour the component has.
We can mitigate this again by adding in another set of classes!
The Issues with Class Libraries and Chaining#
But now we're creating the need to create a component by chaining multiple non-specific classes until we get a result we are happy with.
It reminds of me of CSS utility libraries like tailwind and bootstrap where developers can in theory build components with a list of classes but it requires the knowledge of these classes + the control to add classes to components + the import of a large library of classes. Something that doesn't excite me.
Let's look at how we can do this with the use of Styled Components
Hero Component Colours Based on Props#
So styled-components deserves a post of its own, but to briefly introduce it, styled-components is an NPM package that we can use in our React environment to create components which isolate CSS and access props from a component before rendering CSS.
In our hero example, we will create the Hero element as a React Styled components, component.
This styled component has previously been defined as a section element so we'll use the same convention when defining it with styled-components.
Once we've created the component we will use it with a similar syntax to our previous example, with a nested title and subtitle.
In our definition of our styled component, we have imported the CSS applied to the component within its definition. One thing to note though is that where we set a background-colour and colour property, we aren't using absolute value.
Instead, we use string interpolation to access the props of the component, more specifically we are accessing the background property and using it to set the background-colour property. For anyone using styled-components, this is a pretty simple use case and should be familiar.
The next concept is a little more complex. WIthin our component, we are setting a dynamic colour, but this isn't a prop from our component.
Instead, we are again accessing the background prop, and passing it to a defined function which uses a formula I found on Stack Overflow to determine whether a dark or light text colour would be most appropriate to use on our previously defined background colour rule.
So when we have a light background, as determined in our function, we will return a dark text colour. VIce versa, we set a light text colour on a dark background.
As you can see in our example, we have four Hero components, each with their own different background property.
Behind the scenes, styled-components is generating unique classes and applying them to each section with the CSS rules it determines when creating the Hero components at run time.
As we are passing in four unique values, Styled components recognise this and split the CSS for the Hero component accordingly. Leaving us with four unique class names that are applied to our hero components.
Now, this is exciting as it keeps our CSS lean and only loads the CSS we need for the page, but it also allowed us to create isolated styled rules based on the data we fed into the components.
Now a content creator can build their posts and pages without trialling whether or not a text colour needs to be light or dark. They can simply set a background colour and the component will handle the rest.
This reduces cognitive overload and improves UX for the editor, and creates a legible text format for the end-user of the site. As someone with a vision disability, the legibility and contrast of text on its background is essential for me to be able to read the text and understand the content.
Love or hate CSS-in-JS?
Share your thoughts or let me know what you thought of this article on Twitter @whatjackhasmade