The site's colour roles: cotton and ink for reading, four dyes and ink for structure, small marks for state, all resolved from Loom Base's Selvedge tokens.

Foundation · stable · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)

Reference

Source reference; not an executed check.

--cotton
Page ground · color.background.canvas
--surface
Raised panel, input, frame · color.background.surface
--surface-2
Quiet band, table stripe · color.background.sunken
--surface-3
Track, empty bar · color.background.subtle
--ink
Text, rules, primary button · color.text.default
--ink-2
Secondary text on the ground · color.text.subtle
--line
Hairline inside data (tables, lists) · color.border.default
--line-strong
Control outline at rest · color.border.strong
--focus
Focus ring on the ground · color.focus.ring
--link
Links in running text · color.text.link
--indigo
The hero · Product · Run · tightly woven · color.field.indigo.background
--madder
Shape · Roles · the problem and cost · loose threads · color.field.madder.background
--saffron
Govern · Proof · the call to action · taking shape · color.field.saffron.background
--teal
See · the UX Ops lead · the Academy · articles · holding together · color.field.teal.background
--field-ink
Company · showcase panels · color.field.ink.background
--on-dye
Text on indigo, madder, teal and ink · color.field.on-dark.text
--on-dye-2
Secondary text on those fields · color.field.on-dark.text-subtle
--on-saffron
Text on saffron; fixed dark in both modes · color.field.on-light.text
--on-saffron-2
Secondary text on saffron · color.field.on-light.text-subtle
--indigo-ink
Indigo text on the ground · color.palette.dye-indigo.800 · dark color.palette.dye-indigo.300
--madder-ink
Madder text on the ground (trouble) · color.palette.dye-madder.600 · dark color.palette.dye-madder.300
--teal-ink
Teal text on the ground (holding) · color.palette.dye-teal.600 · dark color.palette.dye-teal.200
--saffron-ink
Saffron as text; never the dye itself on cotton · color.palette.dye-saffron.700 · dark color.palette.dye-saffron.300
--indigo-2
See thread on dark · color.thread.see
--madder-2
Shape thread on dark · color.thread.shape
--saffron-2
Govern thread, light · color.palette.dye-saffron.300
--teal-2
Run thread on dark · color.thread.run
--indigo-soft
Indigo note ground · color.palette.dye-indigo.50 · dark color.palette.dye-indigo.900
--madder-soft
Error and loose-thread ground · color.palette.dye-madder.50 · dark color.palette.dye-madder.900
--saffron-soft
Notice ground · color.palette.dye-saffron.50 · dark color.palette.dye-saffron.900
--teal-soft
Success ground · color.palette.dye-teal.50 · dark color.palette.dye-teal.900
--band-woven
Tightly woven (90-100) · color.health.woven.thread
--band-holding
Holding together (75-89) · color.health.holding.thread
--band-shape
Taking shape (60-74) · color.health.shape.thread
--band-loose
Loose threads (0-59) · color.health.loose.thread
--ai
Marks agent output only · color.ai.border
--ai-ink
Agent output label · color.ai.text
--term-bg
Code panel · color.palette.greige.900 · dark color.palette.greige.950
--term-fg
Code text · color.palette.greige.50
--term-muted
Code comment · color.palette.greige.300
--term-accent
Code accent · color.palette.dye-indigo.200
--term-add
Added value in a diff · color.palette.dye-teal.200
--term-del
Removed value in a diff · color.palette.dye-madder.200
--sample-1
Sample client ink · color.palette.aizome.900
--sample-2
Sample client action · color.palette.aizome.500
--sample-3
Sample client accent · color.palette.kaki.300
--sample-4
Sample client ground · color.palette.washi.25
--sample-on
Sample client text on action · color.palette.white

Do and don't

2 positive / 2 negative authored documentation examples (2 each required); 1 reference (1 required); 0 failed. References and failures do not count; this is not behavior acceptance.

Do: Reading roles in light theme

Authored by colour.ts

Rendered consumer example; not an executed correctness check. text Isolated document pinned light; the reader's contrast preference still applies. The outer theme toolbar does not control this document.

Open example: Colour · Do: Reading roles in light theme Use the full-page example to follow page links.

Inspect the actual paragraph foreground and document ground in a pinned light root. The reader's higher-contrast preference still takes precedence; this is not a source-value list.

Do: Reading roles in dark theme

Authored by colour.ts

Rendered consumer example; not an executed correctness check. text Isolated document pinned dark; the reader's contrast preference still applies. The outer theme toolbar does not control this document.

Open example: Colour · Do: Reading roles in dark theme Use the full-page example to follow page links.

The same native consumer resolves its reading roles at a dark document root; computed foreground and ground, rather than different list text, demonstrate the context. Higher contrast remains authoritative.

Don't: A band word contradicts its dye

Authored by colour.ts

Rendered misuse illustration; not an executed correctness check. band-word Isolated document follows the browser's colour and contrast preferences. The outer theme toolbar does not control this document.

Open example: Colour · Don't: A band word contradicts its dye Use the full-page example to follow page links.

This actual native output visibly combines the wrong word and dye. It is a rendered misuse illustration, not a renderer refusal or an accepted health judgement.

Don't: An error turns the whole section red

Authored by colour.ts

Rule illustration; not an executed check. colour / colour.structure

<section class="field-madder"><h2>Three checks failed</h2></section>
The field is being used only as an error state, without section identity.
Health and error meaning belongs in small marks with words; the full field would confuse state with the site's structural colour.

Rules

RuleWhyCheck
Large fields carry section identity; state is shown only by small marks with their word.Colour as structure, marks as state: a reader never has to decode a colour alone (D-SITE-004).Automated: npm run site:brand and scripts/site/system/check-pages.ts
Element CSS uses only var(--…) roles.A raw value is a decision nobody reviewed; the brand gate keeps it at zero.Automated: scripts/site/check-brand.ts (0 findings)
Tokens, icons and illustrations use the same Base steps (indigo 800, greige 900 and 25).Two indigos side by side looked like a mistake; one source of truth ends it (D-SITE-014).Automated: scripts/site/assets/check.ts (palette) and the brand gate
Light, dark and high contrast are token modes; every text pair is checked in each (4.5:1, and 7:1 in high contrast).Readers who ask for more contrast get it from the same roles, so no element needs its own fix.Automated: tests/unit/site-contrast-modes.test.ts

API

No props.

Contract details

The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests

Identity

AspectValue
Idcolour
LayerFoundations
Categoryfoundation
OwnerSite system lead
Statusstable
Version1.0.0
Sourceapps/showcase/site/system/foundations/colour.ts (private repository; the contract and the render function are this one module)
VersionDateChange
1.0.02026-10-26Colour as one module: its markup moved out of the page builder (scripts/site) into the system.

Use when

  • Any colour in any element. Why: One set of roles keeps the site recognisable and every pair checked for contrast.

Do not use when

  • Sample client artwork. Why: Demos use the sample-* roles so a client's look never borrows the brand's.

Anatomy

PartWhat it is
rolesThe roles in apps/showcase/site/brand/brand.ts (COLOUR)
sourceLoom Base primitives and semantics (packages/base/tokens)
outputapps/showcase/site/brand/tokens.css (generated)

Made of: nothing (a base element).

Used in: library-overview.

Variants

VariantWhenWhy
light and darkEvery page, following the reader's setting.The same roles resolve to light or dark values, so no element chooses its own.

States

StateRendered or not applicable
defaultThe specimen.
hoverNot applicable: A foundation is not interactive.
focus-visibleNot applicable: Not focusable.
activeNot applicable: Not interactive.
disabledNot applicable: Nothing to disable.
loadingNot applicable: Values are built into the stylesheet.
emptyNot applicable: The build fails if a value is missing.
errorNot applicable: Unresolved values fail the brand gate, not the reader.
currentNot applicable: No current item.
visitedNot applicable: Not a link.
expandedNot applicable: Does not open.

Tokens

PartToken
--cottonPage ground
--surfaceRaised panel, input, frame
--surface-2Quiet band, table stripe
--surface-3Track, empty bar
--inkText, rules, primary button
--ink-2Secondary text on the ground
--lineHairline inside data (tables, lists)
--line-strongControl outline at rest
--focusFocus ring on the ground
--linkLinks in running text
--indigoThe hero · Product · Run · tightly woven
--madderShape · Roles · the problem and cost · loose threads

Layout

AspectValue
breakpointsRequired review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; columns stack under 768 px; the wrap keeps a 16 px gutter on phones.
wrappingText wraps; nothing scrolls sideways.
textExpansionJapanese runs 20 to 40% longer; headings break between phrases (jaBreaks) and blocks grow downwards.

Spacing and rhythm

AspectValue
internalNot applicable: a foundation has no box of its own.
externalNot applicable.
rhythmApplies to every element that reads it.

Motion

AspectValue
NoneA foundation does not move.

Runtime and reuse

  1. Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.

Accessibility passport

AspectValue
roleNot rendered on the site; its specimen in the Library is a list
nameThe foundation's name
keyboardNot interactive.
focusNot focusable.
screenReaderThe specimen is a plain list of names and values.
contrastEvery text and ground pair in the palette is checked (check-brand).
targetNot a target.

Content

SlotLengthVoice
health words beside colour marks (BandWord)One of the four band names, exactly; BandWord owns the three-word budgetUse the rubric's name beside the matching mark. A section dye is identity, not a health judgement.
Library role and reference rows (fixed specimen)The complete COLOUR role and reference; no editable content propDisplay the native role and light/dark reference as stored in brand.ts. Do not rename a token as a marketing claim.

Good: “Holding together (teal health mark with its BandWord name)” · “Taking shape (saffron health mark with dark text, BRAND.md)”

Bad: “Good (replaces the governed band name)” · “Loose threads beside the woven band's indigo mark (BandWord's rejected mismatch)”

Japanese: Use the existing band.* Japanese content tokens for health words, preserving the score and dye meaning. Token identifiers and references remain English code. Check Japanese words in every supported colour context; this fixed specimen adds no translation prop and records no completed native or visual review.

Icons and illustrations

None: this element uses no icon or illustration of its own.

Decisions and exceptions

Usage

709 owned-class occurrences in built pages plus metadata-token references in built CSS (measured by npm run site:system).

No application mechanism is declared.

Current check result: not assessed by this report. Declarations and source-file presence do not establish executed enforcement or accepted coverage.

Tests

AspectValue
unittests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns
visualNot yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts
a11ytests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark
parityscripts/site/system/check.ts: Library examples are rendered by the same render function the site imports

Figma

not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.

Open in Storybook

Start with a conversation about your system.

We read your system, show you what we found, and agree with your team what to fix first.