Skip to content

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

.css .iss

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.

S

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.

.html .iss

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>
  • ];

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>
  • );