A library for styling web interfaces with a focus on predictability and robustness
Immutable Styles removes CSS side effects allowing unexpected UI bugs to be caught ahead of time
No Side Effects
Side effects in CSS describe styles that are neither expected or desired. They are usually a concequence of: selectors clashing with other selectors, selectors targeting unwanted elements, or elements inheriting undesirable styles.
Immutability removes these side effects which puts an end to unexpected UI bugs during development, or worse reported by users.
Instant Feedback Loop
A friendly compiler with beatiful error messages catches conflicting styles, whilst enforcing design patterns aimed at making CSS more resiliant.
Code changes and large scale refactoring is a breeze since the compiler catches any unexpected outcomes. Whether overrides occur within the same or another file, among equal or nested selectors, or even among different screen-sizes – the compiler catches them all before they reach a browser.
Deterministic
Every unique UI variation and state is handled explicitly allowing you to see the full picture, rather than having to piece together often disjointed class combinations, whilst reasoning with specificity wars or cascading conflicts.
This makes code and all its outcomes easier to understand and its behaviour predictable.
Composition vs Overrides
CSS styles are shared using composition rather than via overrides, which makes reuse more robust whilst encouraging better designed code. Small modular styles can be composed to achieve complex UIs.
The laborious task of orchestrating overrides and reasoning with their side effects; determining what override is intentional or not is offloaded to a compiler.
Simple Example
Styles are expressed using the same data structure as the DOM using JSX (like React).
A unified data structure shared across markup and presentation helps reduce context switching between CSS selectors and their target HTML.
-
import { createStyle }
from
'immutable-styles';
-
export default (
-
<fieldset>
-
border: 2px solid #9E9E9D;
-
padding: var(--size-m);
-
<legend>
-
font-family: 'Muli', sans-serif;
-
font-weight: 700;
-
font-size: var(--size-scale);
-
color: #F7F4F2;
-
</legend>
-
</fieldset>
- );
Mobile Friendly
CSS features such as media queries are exposed using a familiar and intuitive API.
This example implements a responsive grid. On small screens the columns sit above one another, whereas on large screens they sit beside one another.
Since Immutable Styles is just JavaScript common values can be
shared using variables such as the
to_m
and
from_m
breakpoints.
-
import { createStyle }
from
'immutable-styles';
-
const to_m =
999;
-
const from_m =
1000;
-
export default [
-
<section
className="row">
-
display: flex;
-
<div
className="col">
-
flex: 1;
-
</div>
-
</section>,
-
<section
className="row"
maxWidth={
to_m
}>
-
flex-direction: column;
-
<div
className="col"
pseudo=":first-of-type">
-
margin-bottom: var(--size-l);
-
</div>
-
</section>,
-
<section
className="row"
maxWidth={
from_m
}>
-
<div
className="col"
pseudo=":first-of-type">
-
margin-right: var(--size-l);
-
</div>
-
</section>
- ];
Composition
Common styles can be reused via the
createMixin
helper function
to create
Immutable Mixins.
The mixin in this example holds commons styles shared among all
buttons. Each button variant can use the mixin like a template
and subsequently add its own styles such as
color
,
background
, and
border-color
.
Immutable Mixins offer the same guarantee and protection against CSS side effects.
- import {
- createStyle,
- createMixin
-
} from 'immutable-styles';
-
const mixins = {
-
button:
createMixin
(
-
<button>
-
font-family: 'Muli', sans-serif;
-
font-weight: 700;
-
font-size: var(--size-scale);
-
border-style: solid;
-
border-width: 3px;
-
padding: var(--size-xs) var(--size-m);
-
width: 100%;
-
</button>
- )
- };
-
export default [
-
<mixins.button
className="primary-btn">
-
color
: #242322;
-
background: #F7F4F2;
-
border-color: #F7F4F2;
-
</mixins.button>,
-
<mixins.button
className="secondary-btn">
-
color: #F7F4F2;
-
background
: #242322;
-
border-color: #F7F4F2;
-
</mixins.button>,
-
<mixins.button
className="tertery-btn">
-
color: #242322;
-
background: #9E9E9D;
-
border-color
:
#9E9E9D;
-
</mixins.button>
-
];
Interactions
User interactions are expressed in the same way as normal noninteractive styles.
The tooltip in this example reveals additional information when hovering over the help icon.
All CSS pseudo classes such as
:hover
and pseudo elements
such as ::before
are
supported by Immutable Styles.
-
import { createStyle }
from
'immutable-styles';
-
export default [
-
<div
className="tooltip">
-
align-items: center;
-
display: flex;
-
position: relative;
-
<svg>
-
color: #9E9E9D;
-
</svg>
-
</div>,
-
<div
className="tooltip" pseudo="
:hover
">
-
<p
className="tooltip-content">
-
display: flex;
-
</p>
-
</div>
-
];
Tooltip content
UI States
Immutable Styles is symbiotic with other libraries and existing best practices.
This example uses the
darken
utility function from
polished
and a
BEM
modifier class for the forms disabled state (click Submit).
Since states are handled explicitly it becomes easy to identify every state elements have, and what styles each state has.
-
import { createStyle }
from
'immutable-styles';
-
import { darken }
from
'polished';
-
const fieldsetBackground =
'#1F271F';
-
export default (
-
<form
className="form--disabled">
-
opacity: 0.75;
-
pointer-events: none;
-
<fieldset>
-
background:
repeating-linear-gradient(
-
-55deg,
-
{ fieldsetBackground },
-
{ fieldsetBackground } 15px,
-
darken
(0.1,
fieldsetBackground) } 15px,
-
darken
(0.1,
fieldsetBackground) } 30px,
-
);
-
</fieldset>
-
</form>
- );