Investigate baked in styles to start as atomic declarations
See original GitHub issueSummary
- Refactor baked in styles to start in atomic form
- Create a tiny runtime to ensure the last atomic definition of a particular group wins
- Class names will be hashed and made as short as possible
These changes would
- Enable overrides to work without needing to battle specificity wars
- Get us a step closer to extracted atomic CSS
- Make writing the CSS extraction plugin easier because we don’t need to re-write class names again
- Ensure overrides work across different major versions of the library
Caveats of this change are
- Bundle size will go up slightly (which is offset when we end up extracting to CSS)
- Source maps wouldn’t work well anymore
Background
For background, currently baked in styles looks like this:
const MyComponent = styled.div`
font-size: 12px;
color: blue;
`;
Which would generate this:
.cc-abc111 {
font-size: 12px;
color: blue;
}
<div class="cc-abc111" />
By itself - this works fantastically. But what happens if you want to override something? Well…
const WrappedMyComponent = styled(MyComponent)`
font-size: 15px;
`;
Which would then generate this:
.cc-abc222 {
font-size: 15px;
}
<div class="cc-abc111 cc-abc222" />
And then we are at the bane of specificity wars to see where the override may, or may not, be applied. This is a problem - and it’s a problem for any API not just the styled API.
And what’s worse is we can’t fundamentally fix it with the current CSS output - we can work around it but there will be small edge cases - such as what happens when you render both the original, and the overridden component - which styles win? It will depend who renders first.
What can we do?
Styles start in atomic form
Instead of creating single declarations we start with atomic declarations. Looking back at the example above, let’s rewrite it with this in mind.
const MyComponent = styled.div`
font-size: 12px;
color: blue;
`;
Which would generate this:
.cc-font-size-12 {
font-size: 12px;
}
.cc-color-blue {
color: blue;
}
<div class="cc-font-size-12 cc-color-blue" />
And what does it look like if we override?
const WrappedMyComponent = styled(MyComponent)`
font-size: 15px;
`;
Which would then generate this:
.cc-font-size-15 {
font-size: 15px;
}
<div class="cc-font-size-12 cc-color-blue cc-font-size-15" />
Ok so then we have three class names now assigned to this component. But hopefully you see something we can do with this information…
Runtime to handle atomic declarations
We have a runtime that will iterate through classes and ensure only the last atomic declaration wins.
atomic('cc-font-size-12 cc-color-blue cc-font-size-15') // cc-color-blue cc-font-size-15
The cc-font-size-12 class name is removed. Override is applied. No wars to fight. Consistency.
Formula summarized
To handle all of this:
- Top level declarations
- Nested declarations
- Media queries
- Pseudo selectors
- Dynamic property values
Class names will be generated in the form of:
{atrules}{selectors}{propertyname}{propertyvalue}
That is then hashed. The atomic declaration runtime will compare {atrules}{selectors}{propertyname} and ensure only the last one defined wins.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:9 (3 by maintainers)

Top Related StackOverflow Question
Thanks so much for chiming in dude! I love Github for this reason ❤️
Yeah I think you’re right - (1) seems way more ideal. (2) would be slippery slope to a world of duplicated declarations, and pain.
I’ll spike something out with this in mind.
A few months ago … I was researching this topic. I think this is the right approach to go. By hashing these attributes, it is deterministic!
BTW … this is how react-native-web Stylesheet works and here are some resources that I found useful.
The last rules win is a good approach so that I don’t have to worry about CSS cascade or inheritance.
Supporting atomic CSS for all types of selectors is literally impossible without knowing enough information about the template/component. This is were CSS-Blocks excel because in their case, they support only a subset and obtains info about the component using static analysis. Wondering if https://github.com/linkedin/opticss might help here.
In my case, all I want to support is a small subset of simple selectors and some combinator selectors like
+,~etc…,. 😇All I can say is I am excited about this feature!