With the release of Free Style 1.0, I figure it's about time to write about Free Style - how it works, why you'd want to use it and little introduction to CSS-in-JS. This has been a long time coming, with my first commit to Free Style over a year ago, and the first commit to Free Style in its current form 10 months ago. This is not a blog post designed to sway decisions - as always, you should use your own fair judgement.
The idea of CSS-in-JS is well covered in this presentation by React engineer, Christopher Chedeau, and by many others, so I'll be brief. As React popularized the declarative DOM, it also enabled a generation of CSS-in-JS approaches that attempt to solve the many pitfalls of CSS. These pitfalls are well known and documented, and including "features" such as the global namespace, constant sharing and many approaches to component isolation (BEM, SMACSS). Writing CSS in a way that avoids the pitfalls can be regarded an art.
How and Why Does Free Style Work?
Free Style works with hashes. If there's one word you should love at the end of this section, it's hashing. With that said, the essence of free-style is less than 500 lines of code (in TypeScript), so I definitely suggest you check it out.
Free Style is built on top of a core
Cache class. This class implements a way to append children using an ID (which is a hash), keeps track of how many times a child was added and removed, and can also attach simple change listeners (for when a child is added or removed). Three classes extend the
Cache implementation to replicate the structure of CSS -
FreeStyle. The only other important class is
Selector, which implements a
Cacheable interface (
Cache fulfills the same interface).
Using these four classes, we can replicate CSS in a way that automatically de-dupes styles. First, we create a
FreeStyle instance (it can be imagined as a
.css file). This class holds
Style children. When you use
registerStyle, it'll stringifying each object to CSS and hash the contents, while also propagating any rules upward (E.g. when
@media nested inside a style). The result is a single style registering (potentially) a number of
Rule instances, all of which have their hashes of their own contents. Throughout each instance creation,
registerStyle collects a separate hash that is returned as the CSS class name to use. Finally, when the final class name hash is known, the class name is interpolated with all selectors and returned for you to use.
The result of this means that duplicate hashes are automatically merged. A duplicate hash means a duplicate rule, style or selector. The benefit of separating
Style means that two identical media queries can be merged together (less CSS output) and so can identical styles within each context (E.g. identical styles inside and outside the media query can not be merged, but duplicates both inside or outside can be). The
Selector class exists because now that duplicates are merged, multiple selectors can exist for the same
The other interesting methods are
registerKeyframes. Both work similar to
registerStyle, but are much be simpler.
registerRule works by recursively registering rules, which are automatically being hashed based on the rule and their contents.
registerKeyframes works by creating rules and styles that get added to a
Rule instance and returns a selector of the complete hashed contents (hence keyframes are automatically hashed/namespaced).
Update: One useful fact that may not be immediately obvious. By using hashes as the class name, it means output is always consistent across front-end and back-end (E.g. in isomorphic applications).
Free Style Output Targets
Now that you understand how Free Style works, the output targets should make a lot more sense. By default, Free Style exposes a feature-rich implementation ready for third-parties to build on top. To use it today, you must create instances of
create()), merge any other instances and use
getStyles to get the CSS output. There's an
inject() method, which will take the result of
getStyles and wrap it in
<style /> in the
Currently, there are two other implementations of output targets. The first is
easy-style, a simple wrapper around the complex functionality in Free Style that abstracts away multiple
FreeStyle instances. It exposes three core methods -
keyframe - avoiding the concept of multiple instances, which makes it suitable for most web-apps. There's also
react-free-style, which extends the core
FreeStyle class with the ability to wrap React components and use React's
context for collecting all the styles used in the application. This is an interesting feature with interesting repercussions, such as only styles for the components on screen will be output in CSS (useful for minimizing the transfer size of isomorphic applications).
Other CSS-in-JS Solutions
A number of other CSS-in-JS solutions also exist, including
react-style are more familiar. Where
react-style differ to
free-style is the approaches they take. Both went for namespacing in CSS by generating unique names instead of hashes. They also both went with
StyleSheet instances that you create once, while
free-style makes you register each style individually (and for a good reason, it allows linters to detect when a style is no longer used as it'll appear as dead code). They may also restrict some subset of CSS that you can actually use for different reasons. As far as I can tell, neither go too much further into CSS tooling concepts such as style de-duping and minifying, which
free-style gives you for free with hashes.