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, and actionText all ship on primary.
  • link, linkHover, and inputSelection ship on complementary.
  • linkHoverBg and inputSelectionBg ship on triadic-left.
  • inputCaret ships on triadic-right.
  • dangerText ships on danger.

Two defaults are worth keeping explicit when you build a theme editor:

  • headings does not share the same lightness stop as body text. It ships at muted, while text and headingsHover ship at text.
  • actionBackground is intentionally quiet. It ships at raised3, the same lightness stop as layer3, while actionText ships at ink. 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. actionBackground and actionText ship as a raised3 plus ink pair, and inputSelection plus inputSelectionBg ship 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 headings at muted versus text and headingsHover at text

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 one Action surface option.
  • 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_MAPPINGS unchanged 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 SemanticColorGroupName type and grouped metadata export in src/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.

Where does this show?