What it governs
Every color that renders: surfaces, text, borders, icons, states, and status. Color in Titan is not a palette you pick from. It is a set of named intents that resolve to different values depending on mode. Color is the largest foundation — 97 of the 244 tokens ontheme.tokens are color, and
every one is token-bound rather than an untokenised literal.
Vocabulary
The colour vocabulary is fully specified. Four axes apply.Sourced from Invoca’s design-token naming specification, confirmed by its owner as the
baseline the design language is being built on. This is the vocabulary of record — not
inferred from token names.
Two terms overlap and the spec does not separate them.
neutral and disabled both
appear as purposes, and background-neutral and background-disabled resolve to the same
value — see TITAN-GAP-04.The model
Titan’s colors are authored in three tiers as DTCG JSON, and flattened at build time.background-selected, not the alias chain behind it.
--titan-color-* and exists to be aliased, never
referenced directly. The component tier is where Titan’s stated model and its source
disagree — see TITAN-DIV-01.
The same name resolves to a different value depending on the active color scheme. That
resolution is the entire mechanism — there is nothing to configure and nothing to opt into.
Every color token also ships a
…Channel companion — background-selectedChannel is
"212 224 254", the space-separated RGB triplet. They exist so transparency can be
applied to a color internally. Do not use them directly — deriving a color by applying
alpha to a channel is the opacity anti-pattern TITAN-COLOR-06 prohibits.The naming pattern
Four families, and two modifiers that appear throughout. Learning the modifiers is most of learning the system.Choosing a token
This is the section to read. The left column is what you are trying to express — in your words, not the system’s.Surfaces
Text
Borders
Border widths are separate tokens:
border-width-md and border-width-emphasis are both 2px, but they are not the same
token. Source records border-width-emphasis as the emphasized width for buttons, filter
outlines, and slider thumbs; border-width-md records nothing. They also differ
structurally — border-width-md aliases the primitive scale, while border-width-emphasis
is a literal 2px declared at the semantic tier.Because the values are identical, the distinction is invisible on screen. See
TITAN-GAP-04.Status
Four statuses. A fifth invented locally is not a status — it is an unmet need, and it belongs in a token proposal. Each status has two complete treatments. Pick one and use it consistently.Token reference
The primitive tier
Tier 1 exists and is published — 106 raw ramp values as--titan-color-* custom properties.
Semantic tokens alias into it; component code never touches it.
Every semantic color, with its alias chain
Interaction states
The state tokens, grouped by the state they express. Each is a normal semantic color token — there is no separate state mechanism. Hover is not available on touch. Never let hover color be the only signal that something is interactive — see TITAN-COLOR-09.Surface layering
Proposed, pending sign-off — see Open issues for what that leaves unsettled.
Titan does not specify what happens below the raised level. A card inside a card has no
defined surface, and inventing one locally is how a layering model stops being one.
Constraints
Accessibility
Full measured tables live in Accessibility. The headlines:text-success(2.41:1) andtext-warning(2.74:1) fail AA on all light surfaces. Use the-altvariants.text-infoandtext-errorpass on white but fail on the page canvas.- There is no semantic focus token. Three component-scoped rings ship —
form-field-focus-ring,form-field-focus-ring-error,data-grid-focus-ring— each authored independently against a primitive. Nothing supplies a focus ring for the general case, so every other component decides for itself. See TITAN-DIV-02. - Disabled controls are exempt from contrast minimums, which is exactly why disabled state must never be the sole signal.
- Do not rely on hover color alone to indicate interactivity; it does not exist on touch.
Known issues
Color: open issues
Divergences, open decisions, and undocumented gaps for Color.
Related
34 core components consume this foundation — derived from token usage incomponents/core/src/components, not assumed:
Accordion · AddList · Badge · Button · ButtonGroup · Card · Checkbox · Chip · CircularProgress · DataGrid · DatePicker · Dropzone · Footer · Form · FormInput · Header · List · LoaderOverlay · Menu · Modal · Progress · Radio · SearchableSelect · Select · Slider · Tabs · TextField · TextFieldWithSearchIcon · TextLink · Tile · Toast · Toggle · Tooltip · Typography
Why it works this way
Semantic tokens are named for intent, not appearance.background-selected rather than
background-light-blue. Appearance names become lies the moment a second mode exists — and
they were already lying to the person reading the code, who wants to know why the surface
is that color.
Two treatments per status, not one. A single status color cannot serve both a 12px
badge and a paragraph of error text: what is legible as a small saturated mark is unreadable
as a text color, and what carries text contrast is too dark to read as “success” at a
glance. The plain/-alt split is that problem solved — provided the pairs stay paired.
Four statuses, not six. Systems that add “neutral,” “pending,” and “in progress” as
distinct status colors find that no user can distinguish them without reading the label — at
which point the label is doing the work and the color is decorative. Four is the number that
survives being squinted at.