The Basics
Written using JSX
Immutable styles are represented using a Tree – the same data structure as HTML – and are written using JSX (like React). Here is an immutable style in its simplest form:
The example above is the equivalent of a CSS ruleset. It consists of a selector – in this case the h1
tag, and contains three CSS declarations: font-family
, font-weight
and font-size
. In immutable styles this is known as an immutable ruleset.
An immutable ruleset can contain other immutable rulesets:
The example above equates to a CSS ruleset that consists of two selectors. The flex-basis
declaration will apply to elements matching the second selector – the dt
tag only when the ancestor element matches the first selector – the dl
tag. This is the equivalent of a CSS descendant selector.
An immutable ruleset can contain both CSS declarations and other immutable rulesets:
The example above includes CSS declarations for both the first selector – the dl
tag – and the second selector – the dt
tag. As with CSS there is no limit to the number of descendants in a given selector. In the example above the second ruleset could contain a thrid ruleset, which in turn could contain a forth ruleset, and so on:
JSX Attributes
So far we've only seen type selectors – selectors that match elements by their HTML tag name. However it is common for CSS selectors to match elements based on a specific class name, pseudo class/element, and or screen-size.
In immutable styles these are achieved using JSX attributes. Similar to props in React – immutable attributes are defined on the opening JSX tag.
className
The example above is the equivalent of a CSS ruleset whose selector matches HTML elements of type div
and have the class side-bar
.
pseudo
The example above is the equivalent of a CSS selector using the pseudo-class keyword that matches HTML elements of type a
in a specific state – in this case hover
.
Likewise the pseudo
attribute is also used for pseudo-elements :
Note on Pseudo AttributesThe
pseudo
JSX attribute supports both CSS2 (:before
) and CSS3 (::before
) syntax.
minWidth
The example above is the equivalent of a CSS ruleset defined within a media-query
. In this case the selector targets the HTML element body
on screen-sizes wider than 900px.
maxWidth
The example above is the equivalent of a CSS media-query targeting screens less than 350px wide.
Note on Media Query UnitsThe unit for media queries is predefined by immutable styles. All media queries default to pixels, where
maxWidth="350"
equates to 350px.🔮 Supporting units other than pixels is something that could be added in future.
Any combination of JSX attributes can be used together:
The example above features the className
, minWidth
and pseudo
JSX attributes. On screens wider than 350px HTML elements of type div
with the class side-bar
will have an ivory background color.
On screens wider than 350px HTML elements of type span
– with the class icon
– whose ancestor is a div
with the class side-bar
will be the color magenta. On hover
the same icon color will change to darkmagenta.
Note on JSX AttributesIt should be noted that unlike props in React, only JSX attributes predefined by immutable styles are allowed.
No Overrides Allowed
Recommended ReadingIf you haven't already, I strongly recommend reading The Case for Immutable Styles and the post CSS Overrides: Friend or Foe? before reading this section.
Until now we have only observed the similarities between immutable styles and CSS – the only difference so far being immutable styles are written using JSX.
There are however some major differences between immutable styles and CSS – most of which fall under the umbrella goal of preventing CSS overrides. Just like elms compiler prevents runtime errors in JavaScript, the immutable styles compiler prevents runtime overrides in CSS.
Runtime Override | ˈrʌntʌɪm | əʊvəˈrʌɪd |A runtime override happens when two or more rulesets containing conflicting declarations target the same element.
An Example
Imagine a designer has provided a styleguide that shows all primary buttons should look like this:
In conventional CSS the ruleset would be:
The equivalent immutable ruleset would be almost identical:
Whilst semantically similar, the behaviour of each ruleset is different. The CSS ruleset is vulnerable to both intentional and unintentional overrides:
The example above modifies the background and text color of primary buttons inside elements with the class promo
. Whilst being perfectly valid CSS this invalidates the design principle – in this case – that all primary buttons should have a cornflowerblue
background and ivory
color.
It therefore cannot be guaranteed that all primary buttons will look the same – which in effect makes the designers styleguide more a suggestion than a gospel truth. Not only does this lead to an inconsistent UI and an unhappy designer, it also doubles the number of primary buttons developers need to maintain.
Attempting the same in immutable styes yields a different result. Overriding an immutable ruleset is not allowed:
Modifying the background and text color of primary buttons inside elements with the class promo
throws a compile time error:
The compiler makes overriding styles impossible. Overrides are caught and prevented ahead of time. Compile time errors – such as the case above – are treated as failed builds, for which no CSS is generated. CSS is only generated for successful builds – those without compile time errors.
The immutable ruleset for primary buttons is therefore a gospel truth. It can be guaranteed that all primary buttons will always have a cornflowerblue
background and ivory
text color.
Whether attempted overrides happen in the same file or in another file – among equal selectors or nested selectors – or even among different screen-sizes, the immutable styles compiler catches them all. The complex task of detecting and preventing overrides is offloaded to a compiler – and the burden of keeping track of overrides is no longer a developer concern.
Last updated