Liaan platform · living styleguide

Design system

One page for the whole platform. Edit the tokens behind it (or a shared @liaan/ui component) and the change flows to every app on its next build.

Zone A

Unified elements

Shared across every app via @liaan/design tokens and @liaan/ui components. These are the defaults to reuse.

Neutrals

Page and surface backgrounds, hairlines, nested panels.

--page-bg
#faf9f5
App background (warm paper).
--canvas
#ffffff
Cards, panels, inputs.
--hairline
#e7e4db
Default 1px borders.
--hairline-soft
#efece4
Lighter internal dividers.
--hairline-ink
#d8d3c6
Registry rules (Unaya, additive).
--nested-bg
#f5f2ea
Nested / inset panels.
--nested-border
#e6e1d4
Border of nested panels.
--snippet-bg
#f4f2ec
Quoted / snippet surfaces.

Ink

Text colours, strongest first.

--ink
#1d211e
Primary body + headings.
--ink-2
#45483f
Secondary text.
--muted
#7c7d74
Captions, labels, eyebrows.
--muted-2
#9a9b91
Faintest text / placeholders.

Accent — Liaan teal

The brand action colour and its tints.

--accent
#1a6b62
Primary actions, links, focus.
--accent-hover
#13514a
Hover/active of the accent.
--accent-tint
color-mix(--accent 10%)
Subtle accent wash (tracks --accent).
--accent-tint-2
color-mix(--accent 16%)
Focus ring, selection (tracks --accent).
--accent-disabled
#b8cdc9
Disabled primary button.

Stance — load-bearing

Agree / question / disagree. Kept exact across the platform.

--colour-agree
#2e7d32
Agree stance ink.
--colour-agree-bg
color-mix(--colour-agree 18%)
Agree stance fill (tracks the ink).
--colour-question
#b8860b
Question/doubt stance ink.
--colour-question-bg
color-mix(--colour-question 18%)
Question stance fill (tracks the ink).
--colour-disagree
#b3261e
Disagree stance ink.
--colour-disagree-bg
color-mix(--colour-disagree 18%)
Disagree stance fill (tracks the ink).

Notice / caution

Info banners + demo strips. Minted in R3.2 (was the orphan .demo-banner palette).

--notice-bg
#fbf4d6
Notice / banner surface.
--notice-border
#e6cf86
Notice border.
--notice-ink
#6b5a16
Notice text.

Document status (Unaya · additive)

Lifecycle of a registry template. current/draft now alias the platform palette.

--status-current
var(--accent)
Canonical / in force (= accent).
--status-draft
var(--colour-question)
Being worked (= question stance).
--status-historical
#6b7d77
Superseded but kept.
--status-obsolete
#a8584a
Retired.

Document tier (Unaya · additive)

Derived from the platform palette (no foreign colours).

--tier-canonical
var(--accent)
The circle's authoritative copy.
--tier-country
var(--ink-2)
A country's adopted version.
--tier-raw
var(--muted-2)
Un-promoted raw material.

Diff paint (governance ceremony)

The one diff card — additions and deletions.

--diff-add-bg
color-mix(--colour-agree 14%)
Added-text fill (one green).
--diff-add-ink
#1f5a23
Added-text ink.
--diff-del-bg
color-mix(--colour-disagree 12%)
Deleted-text fill (one red).
--diff-del-ink
#8f2019
Deleted-text ink.

Type scale

Headings + reading set in the serif; UI in the sans. Sizes are the --t-* tokens.

Headings + reading (Source Serif 4).--serif
UI, labels, buttons (Hanken Grotesk).--sans
Masthead wordmark.--t-display-xl · 4rem
Section openers.--t-display-l · 2.5rem
Sub-section titles.--t-display-m · 1.9rem
Card / panel titles.--t-title · 1.35rem
Reading column.--t-body-l · 1.1875rem
Default body.--t-body · 1.0625rem
Secondary / dense UI.--t-small · 0.95rem
Uppercase eyebrows.--t-eyebrow · 0.72rem
Catalogue reference codes.--t-ref · 0.78rem

Spacing & layout

Today only layout/measure tokens exist — there is no --space-* ramp yet (R1.1 flag; R3 adds the two-tier scale).

--space-1
0.5rem
--space-2
1rem
--space-3
1.5rem
--space-4
2rem
--gap-section
6.5rem
--measure
70ch
--shell
1180px
--shell-read
880px

Radii

--radius-pill
999px
--radius-cta
6px
--radius-card
8px

Shadows

--shadow-card
0 1px 2px rgba(0,0,0,.03)
--shadow-soft
0 4px 14px rgba(15,23,42,.06)
--shadow-pane
-2px 0 12px rgba(0,0,0,.05)
--shadow-lift
0 6px 22px rgba(28,40,36,.09)

Primitives

The live @liaan/ui components, in the reference look. Labelled with their export name.

Textvariant (the --t-* scale, pf look)

Eyebrow · kicker

Display — section opener

Title — a card or pane heading

Lede — the serif standfirst that introduces a passage, set a touch larger and in the secondary ink.

Body — the reading set. Members read policy passages and reflections in this serif face at a comfortable measure.

Small — muted meta, captions, and footnotes.

REF · CATALOGUE-0042
Buttonvariant · size · disabled
Badgetone · variant · dot
accentneutralagreequestiondisagree
currentdrafthistoricalobsoletecanonicalcountryraw
approveddraftappliednone
approvedappliedoutline
Cardpadded · interactive · accent · active
Resting card

Canvas + hairline + soft shadow.

Interactive card

Hover lift for clickable surfaces.

Active card

Open / selected: deeper shadow + accent border.

Accent · agree
Accent · question
Accent · disagree
Accent · neutral
RegisterList · RegisterRownavigational register (title · meta · trailing cells · arrow)
Input · TextFieldlabel · hint · error · size · serif
An optional hint sits here.
This field is required.
Textarea · TextareaFieldsans · serif · label
Resizes vertically.
Selectbox · sm · pill
FloatText · FloatTextarea · FloatListbox · FloatListboxMultifloat-label · hint · error · single/multi

The float-label input family the CRM record forms use (e.g. crm AddPersonForm). Controlled by the consumer; the label floats on focus/fill.

We never share it.
This field is required.
RecordListsearchable record table · row select · empty

The generic searchable record table (the CRM/people directory list). Columns, selection, search, and decrypt/empty states are all driven by props.

NameCity
Ada LovelaceLondon
Grace HopperNew York
Katherine JohnsonHampton
InlineNotetone
InfoA neutral contextual message.
SuccessSomething went well.
Modal · Drawer · ConfirmDialog · Toasttriggers
LiaanLoader · DecryptSpinnerasync states
Loading
Decrypting…
AppChromebrand · role-nav · actions slot

The shared top bar every app wears — brand + context tag, the role-aware three-circles nav, and a right-side actions slot. Pass a linkComponent for client-side routing (here a plain anchor).

RoleLensview-as switcher (member · coordinator · admin)
LanguageSwitcherlocale pips
OrgPicker · EventPickershared · data-bound (best seen in-app)

The two remaining shared @liaan/ui composites — the CRM “link a member to organisations / events” pickers. They drive an async loader and a full set of localised labels, so they live on the real crm screens rather than a synthetic demo; listed here so the catalogue is complete. Both are built from the primitives above (Modal, FloatText, Button, Badge) on the shared tokens.

Zone B

Feature-specific

Genuinely bespoke, app-local surfaces — allowed exceptions to reuse-first. R1 is read-only over the apps, so these are catalogued (name + the CSS that styles them, from INVENTORY-style); R3 decides which get promoted into @liaan/ui and which stay local.

Specialised shared — the governance diff

Shared in @liaan/ui but specialised: the one diff card the revision ceremony uses. Rendered live.

removedadded

Members may submit and revise feedback within the consultation window.

The same diff core in the stance tone — the policy-feedback workshop palette (agree-green / disagree-red on the shared stance tokens), adopted by ProposedEditCard in R3.4.

Members may submit and revise feedback within the consultation window.

removedadded

App-local feature surfaces

policy-feedback
Reading + annotation surface
The document reading column with inline highlight/annotation affordances — the reference editorial look.
fp* / law-* (feedback-*, stakeholder-overview.css)
policy-feedback
Companion chat surface
The persistent stakeholder companion / chat dock and message stream.
chat-* (chat-surface.css)
policy-feedback
Admin revision workshop
Stakeholder-overview + revision facilitator console (admin-gated).
sf-* / sg-* (admin.css)
unaya
Registry catalogue
The document registry rows / catalogue entries (quiet-archive register).
.reg-* (registry CSS)
unaya
Document detail
A single document/template view with tier + status vocabulary.
.doc-* (document CSS)
crm
Add-a-person form layout (RA)
The RA "add a person" screen — the float inputs, listboxes, button and toast are all shared @liaan/ui primitives; only the page-head + three fieldset sections are laid out locally (on shared tokens).
.crm-people-* (people.css)
website
Shared-core tokens (aliased)
The public site imports @liaan/design/tokens.css (the hard contract) and aliases its shared-core brand tokens onto the platform semantics — --teal→--accent, --bg→--canvas, --bg-warm→--page-bg, --radius→--radius-card, --font-*→--serif/--sans; --hairline, --space-1..4, --radius-cta, --radius-pill resolve straight from @liaan/design. A platform token edit now propagates to the website.
apps/website/app/styles/tokens.css
website
Marketing palette + scale (local, inherits less)
By owner ruling (Decision 12) the site keeps its own marketing identity local: the navy/gold/forest palette, the dark-photo overlay chrome (--on-dark*, --overlay), the marketing type scale (--fs-*, --lh-*, --tracking-*), the editorial layout frame (--container*, --gap-grid, --header-h, --space-5..8), the refined editorial radii (--radius-sm/-lg) + navy-tinted shadows, and the motion --ease.
apps/website/app/styles/*.css
website
Marketing components + layout
The ported marketing pages keep their own components/layout where the editorial look is genuinely better (site header/footer, hero, donation tiers, editorial grid) rather than force-migrating onto @liaan/ui primitives just for uniformity.
apps/website/app/components/** + styles/{layout,components,editorial}.css