I hadn't used styled-components until about a year ago when I started at my current job. I can't say its my favorite CSS-in-JS option for various reasons. One of those reasons is the pitfall of relying on props for styling, which can do a lot more damage than devs may realize. Take a look at this example:
const Button = styled.button``const FormInput = styled.input``
The problem here is somewhat subtle. Can you spot it?
When used like this the props only cause the class name to change. The pure HTML/CSS equivalent would be something like <button class="button disabled loading">
(though the output classNames from styled components are even less understandable <button class="sc-aBcDe sc-fGhIj sc-kLmNo">
). This leads to some very interesting gotchas.
Setting pointer-events: none
through usage of isDisabled
seems innocuous but this does not disable the button correctly! Disabled buttons 1) should not receive focus, and 2) should not respond to mouse or keyboard click events; but pointer-events: none
only prevents mouse click events. A keyboard user will still be able to interact with this button, and a screenreader user will have no idea that the button is disabled.
Similarly, the <Button isLoading>
and the <FormInput isValid/>
does not communicate the state of the control to assitive technology. This leaves those users in the dark as to what is happening when they're interacting with your application.
For example, isDisabled
should instead be renamed to disabled
which would then be propagated to the DOM as the disabled
attribute on the button. The corresponding CSS could then be simplified to use the :disabled
CSS pseudoselector.
isLoading
can instead be used to set the aria-busy
role, and isValid
used to set the aria-invalid
role; then, use those roles as your selectors in your css!
const StyledButton = styled.button``const Button = ({ disabled, isLoading, ...props }) => (<StyledButton disabled={disabled} aria-busy={isLoading || null} {...props} />)const StyledFormInput = styled.input``const FormInput = ({ isValid, ...props }) => (<StyledFormInput aria-invalid={!isValid} {...props} />)
The benefits of this new approach are:
This isn't to say that you should never use props in styles (perhaps the topic of another post), but do think through how your user experience should work and ensure the proper semantics are in place before simply reaching through to props in your styled components.