Semantic Color Groups And Theme Mapping
semanticMappings is Espalier's low-level color routing table. It is precise, flexible, and a poor place to start if you are building an app theme or a theme editor. Application authors usually need a smaller mental model: a handful of color roles that move together and describe how a UI is composed.
Espalier currently computes nineteen semantic color tokens. Those tokens cluster into six higher-level groups:
| Group | Tokens | What they usually influence | Default mapping | Safe to expose directly? |
|---|---|---|---|---|
| Foundation surfaces | background, layer1, layer2 |
page canvas, cards, field shells, list rows, panel bases | primary on surface through raised2 |
Yes, as one surface choice |
| Structure and chrome | layer3, layer4, border, shadow |
raised layers, headers, hover rows, dividers, outlines, shadows | primary on raised3, raised4, border, and shadow |
Usually only in advanced mode |
| Content ink and hierarchy | text, headings, headingsHover |
body copy, labels, section titles, nav text | primary; headings sits at muted, while text and headingsHover use text |
Usually no separate hue picker |
| Action surfaces | actionBackground, actionText |
filled buttons, progress fills, selected dates, active switches | primary; actionBackground uses raised3, while actionText uses ink |
Optional advanced control |
| Inline emphasis and feedback | link, linkHover, linkHoverBg, inputCaret, inputSelection, inputSelectionBg |
links, hovered-link backgrounds, selection backgrounds, caret color, inline emphasis | complementary for emphasis ink, triadic-left for backgrounds, triadic-right for caret |
Yes, as one accent choice |
| Utility and status | dangerText |
errors, destructive messaging, validation copy | danger at text |
Keep reserved as status language |
A shared group is a coupling rule, not a promise that every token inside it uses the same lightness stop. The content-ink group, for example, deliberately keeps headings softer than body text while still treating the whole set as one semantic family.
Why These Groups Exist
The flat token table is still the source of truth for the engine, but the groups above reflect how the library is actually composed:
- Foundation surfaces carry most of the page's visual mass and should stay calm.
- Structure and chrome create depth, separation, and affordance around that foundation.
- Content ink is not a decorative family. Its job is legibility and hierarchy.
- Action surfaces are the strongest filled controls, so they need a stable pairing between background and text.
- Inline emphasis and feedback cover the small, high-attention moments: links, selection, caret, and hover highlights.
- Utility and status colors must preserve meaning instead of chasing brand harmony.
That grouping lets application authors reason in a few coherent roles instead of nineteen independent decisions.
Recommended Default Mapping Model
Espalier's current shipped defaults are intentionally conservative, and the grouped model should describe those defaults before it tries to expand them:
| Role | Recommended family | Why |
|---|---|---|
| Foundation surfaces | primary |
Large surfaces should feel anchored to the brand without becoming noisy. |
| Structure and chrome | primary |
Depth should read as tonal variation first, hue variation second. |
| Content ink and hierarchy | inherit the foundation family | Text should stay coupled to the reading surface and let APCA drive contrast. Within that group, the default keeps headings at muted while text and headingsHover use text. |
| Action surfaces | primary by default |
Filled controls are intentionally quiet in the shipped defaults: actionBackground shares the raised3 stop used by layer3, while actionText uses ink for contrast. A louder CTA treatment should be an explicit source-family or lightness decision. |
| Inline emphasis and feedback | complementary for emphasis, triadic-left for highlight backgrounds, triadic-right for caret ink |
Small interactive moments can carry more hue contrast than large surfaces. |
| Utility and status | fixed semantic hues such as danger |
Error language should remain semantically stable across brands. |
That maps cleanly onto today's flat defaults in src/shared/theme.ts:
background,layer1,layer2,layer3,layer4,border,shadow,text,headings,headingsHover,actionBackground, andactionTextall ship onprimary.link,linkHover, andinputSelectionship oncomplementary.linkHoverBgandinputSelectionBgship ontriadic-left.inputCaretships ontriadic-right.dangerTextships ondanger.
Two defaults are worth keeping explicit when you build a theme editor:
headingsdoes not share the same lightness stop as body text. It ships atmuted, whiletextandheadingsHovership attext.actionBackgroundis intentionally quiet. It ships atraised3, the same lightness stop aslayer3, whileactionTextships atink. A bolder action family requires an intentional mapping change rather than assuming the default is already louder.
For most products, this is the right default balance. If you want a bolder brand, the first safe expansion is usually to move action surfaces to the same accent family as links. That should be an intentional choice, not the default baseline.
Where 60-30-10 Helps
The classic 60-30-10 rule is useful as an onboarding heuristic for the first three visible families in a theme:
- the dominant surface family
- the supporting structure or chrome family
- the accent family
In Espalier terms, that usually means foundation surfaces dominate, structure and chrome support, and inline emphasis carries the smallest but sharpest color hits.
Where 60-30-10 Breaks Down
60-30-10 is not a complete theming system, because Espalier also has tokens whose job is semantic meaning or contrast safety rather than area:
- text tokens are governed by readability, not percentage
- action text must stay paired with its action surface
- selection and caret colors are feedback states, not layout paint
- danger language should remain semantically reserved
- dark themes compress surface ranges and make small chroma differences feel larger
Treat 60-30-10 as a conversation aid, not as a system invariant.
Rules That Matter More Than Ratios
- Keep large surfaces lower-chroma than accents, especially in dark mode.
- Move coupled tokens together.
actionBackgroundandactionTextship as araised3plusinkpair, andinputSelectionplusinputSelectionBgship as emphasis ink plus emphasis background. Do not remap one side without the other. - Keep content ink derived from the same family as the reading surface unless you are intentionally building an editorial theme.
- Reserve semantic status hues for status meaning. Do not spend your error color on generic emphasis.
- In dark themes, prefer brighter accents over broader accents. The accent can be lighter, but it should usually occupy less area than it would in light mode.
What Should Be Configurable
The grouped model is most useful when it narrows the choices exposed to application authors.
Safe product-level choices:
- pick the foundation surface family
- pick the accent or inline-emphasis family
- optionally pick a separate action-surface family in advanced mode
- optionally tune supporting chrome separately in advanced mode
- tune chroma intensity at the family or theme level
Choices that should usually stay internal to Espalier:
- the exact lightness stop for each semantic token
- APCA targets and the background pairings used to enforce them
- the split between emphasis ink, emphasis backgrounds, and caret colors
- the exact border and shadow token behavior
- the internal lightness split inside the content-ink group, such as
headingsatmutedversustextandheadingsHoverattext
If a product editor starts by exposing all nineteen tokens, it is handing users the engine room instead of the dashboard.
Light And Dark Scheme Guidance
The same group model should exist in both schemes, but the balance shifts:
- Light schemes can tolerate a wider spread between
surface,raised*, and text stops. - Dark schemes should keep surface families more neutral and more compressed.
- Accent families usually need less physical area in dark mode because they already read louder.
- If a dark theme feels dull, raise accent lightness before raising accent coverage on large surfaces.
The important adaptation is not a different taxonomy. It is a different restraint level.
Notes For Taproot TR00030
Taproot should consume this model as grouped configuration, then compile it back down to flat Espalier semanticMappings.
- Simple mode should expose grouped choices such as
Surface family,Accent family, and at most oneAction surfaceoption. - Advanced mode may expose
Structure and chrome, but content ink, APCA pairings, and feedback-state splits should remain derived. - The preview should visibly include a page surface, a raised container, a filled button, a text link, selected text or caret state, and a danger message so every group is represented.
- Existing sites should preserve
DEFAULT_SEMANTIC_MAPPINGSunchanged unless a user explicitly opts into a different grouped choice. - Taproot should avoid a raw nineteen-token editor by default. That is a debugging surface, not a friendly authoring surface.
Roadmap Note: Potential API Follow-Up
This is roadmap guidance, not current API surface. Espalier can ship the grouping model as docs today, but a future API cleanup would make it easier for tools to consume without drift:
- add a canonical
SemanticColorGroupNametype and grouped metadata export insrc/shared/theme.ts - expose a helper that resolves group-level choices into flat
semanticMappings - keep the flat mapping table as the engine contract, but give tooling a higher-level source of truth for group names, descriptions, and defaults
Until that exists, this guide is the canonical grouping model for Espalier-aware tools.