WrenfieldEstate Law Design system
Appearance
The site →

01 · Foundations

Foundations

Every decision a component is not allowed to make for itself. These are read out of tokens.css at build time and measured again in the browser, so what is printed here is what the page is actually running.

Source src/assets/css/tokens.cssAppearances Light and dark, defined in full in both

How the palette is arranged

Two layers. Underneath, a palette of literal values, declared once for the light appearance and again for the dark. Above it, a set of working names that every selector in wrenfield.css reaches for instead. Nothing in the component sheet names a colour; that is what lets one seed change the firm's whole appearance without touching a rule.

The two layers

A component asks for --accent. Only tokens.css knows that this is --brand, and only the palette knows that --brand is a particular green.

  1. Palette

    Literal values. --ink, --paper, --brand, --rule. Declared twice, once per appearance, so no value is ever derived at runtime and no appearance is an afterthought.

  2. Working names

    Aliases with jobs rather than colours. --bg, --text, --accent, --field. A component only ever names these.

  3. The rest of the sheet

    922 lines that between them mention no colour at all.

Ground and ink

The paper and what is written on it. Every surface in the site is one of these four.

6 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--paper #FBFAF7 / #0B0B0B
--paper-2 #F1EFE9 / #141414
--ink #14162E / #F0EFED
--ink-2 #2C2F4A / #C9C7C3
--muted #4E4C46 / #B8B5AF
--muted-2 #5C5A54 / #A8A5A0

The ratios are measured live against the ground each colour is written on, and they move when you switch appearance. Four bands: 7 and over is AAA, 4.5 is AA, 3 clears large text only and is marked amber, and anything under that is called a failure. That is how a palette change announces that it has broken something.

Brand

One green, and the colour that stays legible on it. The brand switcher reseeds these and nothing else.

3 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--brand #14543C / #86C9AC
--on-brand #FBFAF7 / #0B0B0B
--accent-soft rgba(20,84,60,.10) / rgba(134,201,172,.16)

Reseed the brand in the bar above and these four do not move. The switcher overrides the working names — --accent, --accent-text, --on-accent and the rest — and leaves the palette underneath alone, which is why --brand still reads as the firm's green while every button on the page has gone coral. See Reseeding.

Rules and frames

The site separates with a line far more often than with a box, so the rules carry a lot of the structure.

4 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--rule #D9D5CB / #333333
--rule-2 #E7E4DC / #262626
--card #FFFFFF / #181818
--glass rgba(241,239,233,.72) / rgba(20,20,20,.92)

The panel

The inverted band, dark in both appearances. Its own accent is literal, because the light brand is unreadable here.

7 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--invert-bg #14162E / #1A1A1A
--invert-bg-2 #1B1E38 / #242424
--invert-fg #F2F1FA / #F5F4F2
--invert-fg-2 #C3C6E6 / #CFCCC7
--invert-rule #2F3358 / #3A3A3A
--panel-accent #9FDCC0
--hero-ground #02170E / #000F08

The ratios are measured live against the ground each colour is written on, and they move when you switch appearance. Four bands: 7 and over is AAA, 4.5 is AA, 3 clears large text only and is marked amber, and anything under that is called a failure. That is how a palette change announces that it has broken something.

Working names

Aliases. Every selector in wrenfield.css reaches for one of these, never for a palette value, which is what lets the palette move underneath it.

17 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--bg var(--paper) alias
--raised var(--paper-2) alias
--sunken var(--paper-2) alias
--text var(--ink) alias
--accent var(--brand) alias
--accent-text var(--brand) alias
--on-accent var(--on-brand) alias
--focus var(--brand) alias
--spark var(--brand) alias
--field var(--rule) alias
--error var(--brand) alias
--hero-btn-bg var(--brand) alias
--hero-btn-fg var(--on-brand) alias
--panel var(--invert-bg) alias
--panel-text var(--invert-fg) alias
--panel-muted var(--invert-fg-2) alias
--panel-rule var(--invert-rule) alias

Corners

A law firm's corners, not an app's: tight enough that a card reads as a printed panel with the corner knocked off.

6 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--radius-xs 2px
--radius-sm 3px
--radius-md 4px
--radius-lg 6px
--radius-chip 3px
--radius-pill 999px

Lift

Three shadows and no more. A card at rest, a card under the pointer, and the brand's own glow under a button.

3 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--shadow-card 0 1px 2px rgba(20,24,33,.05),0 2px 6px rgba(20,24,33,.04) / 0 1px 2px rgba(0,0,0,.4),0 2px 6px rgba(0,0,0,.3)
--shadow-card-hover 0 6px 18px -6px rgba(20,24,33,.12),0 2px 6px rgba(20,24,33,.05) / 0 6px 18px -6px rgba(0,0,0,.55),0 2px 6px rgba(0,0,0,.4)
--shadow-brand 0 12px 28px -10px rgba(20,84,60,.35) / 0 12px 28px -10px rgba(134,201,172,.40)

Motion

Two durations and one curve. Everything that moves on the site moves on these.

3 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--dur-fast 140ms
--dur-base 220ms
--ease cubic-bezier(.2,.8,.2,1)

Voices

Three faces with three jobs: the serif thinks, the sans speaks, the mono labels.

3 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

Aa --serif 'Literata',Georgia,serif
Aa --sans 'Public Sans',system-ui,sans-serif
Aa --mono "IBM Plex Mono",ui-monospace,monospace

Measure

How wide a thing is allowed to get, and how tall the bar is. These were split across two files until the catalogue asked which one was true.

5 tokens

Declared value on the left of the slash, dark on the right. Underneath, what the browser resolved it to just now.

tokens.css

--measure 46rem
--wide 70rem
--shell 76rem
--col 34rem
--bar 5.5rem

Reseeding the brand

The control beside the appearance switcher takes one hex and derives the whole accent layer from it in OKLCH, then runs a contrast gate that darkens derived text until it clears AA and reports every adjustment it made. It is the same engine the site ships; the catalogue is simply the right place to stand while using it.

What a seed touches

Pick a seed in the bar and watch this stage. Everything below is painted from a working name, so none of it needed changing for any of this to happen.

.btn .chip .card a .pull

A card, a chip, a quotation

Probate

Eight to eighteen months in California.

A trust that was never funded does none of the work it was written to do.

Turn on Both and reseed again: the light pane and the dark pane each take their own derivation of the same seed, because a seed is only good if it holds in both. The engine returns both maps at once and the catalogue writes the one the root is not currently using into a stylesheet of its own.

The gate, and what it does not check

Two gates run on this page and they do not always agree. That disagreement is the useful part.

brand-engine.js and ds.js

  1. The engine's gate, in the panel

    Runs at the moment a seed is applied. It corrects derived text upward until it clears AA against the surface it was derived for, reports every adjustment, and says how many tokens are still below.

  2. The catalogue's measurement, on this page

    Runs afterwards, on what the browser actually resolved, against the ground each colour is really written on. It has no opinion about how a value was arrived at.

  3. Where they part

    Seed the brand with Coral and the engine reports all pass, while --accent-text measures 4.37 against the paper — enough for a heading, not enough for the links and body-sized accent text this site actually sets. Read the amber marks in Working names before shipping a seed.

Type

Three faces with three jobs. The serif thinks — every heading on the site is set in an italic Literata, which is unusual and is the whole voice. The sans speaks: body copy, navigation, buttons. The mono labels: eyebrows, counts, numerals, anything that is a fact rather than a sentence.

The scale

Set on the page, not described. Headings clamp between a handset and a desktop, so drag the window and watch them move.

h1 h2 h3 .lede p .eyebrow .label .mono

h1 · serif italic
clamp(2.7rem, 7vw, 4.8rem)

A plan your family can actually use

h2 · serif italic
clamp(1.9rem, 4vw, 2.7rem)

What the first conversation covers

h3 · serif italic
1.4rem

Trust administration

.lede · serif 300
24px

Most people arrive having meant to do this for a year.

body · sans
19px / 1.65

The documents that decide who acts for you and who receives what, written to be used rather than filed. Nineteen pixels and a generous line, because the people reading this are frequently reading it in a difficult week.

.eyebrow · mono
16px / .16em

Estate planning

.label · mono
15px / .13em
Selected matters
.mono · mono
inherits its size

310-555-0142

.muted · any voice
--muted

A second rank of text, still above 4.5 against the paper.

Measure

How wide a line of text is allowed to get before it becomes hard to return from. The site keeps two: one for reading, one for a page.

--measure --wide

--measure
--wide
--bar

Prose sits on --measure. A section opener is allowed --wide, because a short line of large type reads as a caption rather than as a sentence.

Corners, lift and motion

The three things that decide whether the site reads as a printed firm or as an app. It is trying quite hard to read as a printed firm.

Corners

Six values, none of them soft. A card should read as a printed panel with the corner knocked off.

--radius-xs … --radius-pill

xs · 2px
sm · 3px
md · 4px
lg · 6px
chip · 3px
pill · 999px

--radius-pill is for the things that are genuinely circles: a bullet, a swatch, an avatar. Pill shaped controls take --radius-chip, so buttons, nav chips and hero tags move together when the corner changes.

Lift

Three shadows. A card at rest, a card under the pointer, and the brand's own glow under the one button on the page that matters.

--shadow-card --shadow-card-hover --shadow-brand

card
card, hovered
brand

Motion

Two durations and one curve, and everything that moves uses them. Hover the squares.

--dur-fast --dur-base --ease

fast · 140ms
base · 220ms

Fast is for a thing answering you — an underline drawing itself, a chevron turning. Base is for a thing arriving — a drawer, a scrim, a panel. Everything that moves is also written to stand still under prefers-reduced-motion.