Replace intent with the shared semantic colour contract (#309)
* Replace the intent colour contract with shared semantic roles
The public contract exposed an asymmetric `color.intent` tree: neutral,
accent, and danger received a full interactive ramp while info, success,
and warning received a feedback-only kit. That described how components
happened to use colour rather than what each colour styles and means.
Replace it with a property-first contract shared by all six roles. Every
role now gets subtle and solid background ramps with rest/hover/pressed,
a resting and a stronger interactive foreground, a guaranteed on-solid
foreground, and one state-free border — 60 leaves keyed off a single
canonical `SEMANTIC_ROLES` list that generation, mapping, validation,
diagnostics, and tooling all read.
`FAMILY_REQUIREMENTS` collapses to one uniform capability set, so all six
families are now gated on on-solid contrast where only neutral, accent,
and danger were before. This moves no existing colour: not one family
needed adapting, and steps 3-8, 11, and 12 generate identically for
info, success, and warning. The golden diff is additive only.
The validation matrix is now uniform per role: 90 hard checks (8
functional text, 60 role foregrounds, 18 on-solid, 2 focus, 2 control)
and 12 advisory semantic-border checks per mode.
`rest` is an explicit leaf because a path cannot be both a string leaf
and the parent of `hover` and `pressed`. Solid pressed deliberately
reuses solid hover; components carry the press through depth, finish, or
transform.
Refs #298, #299, #305
* Migrate components to the shared semantic colour contract
Move every consumer off `color.intent` onto the new background,
foreground, and border paths. Each old path has an exact new equivalent
resolving to an identical value, so this is visually a no-op: 218 of 222
visual captures are pixel-identical, and the four that differ are Token
Board captures whose height changed because the contract grew.
Button and IconButton keep their subtle, solid, ghost, hover, pressed,
disabled, pending, focus, material, and forced-colours behaviour. Button
derives its ramp type from the contract now rather than mirroring a
hand-flattened shape. Link uses the resting foreground at rest and the
stronger hover foreground for both hover and press, since the shared
contract carries no separate pressed content colour.
Fields and Combobox controls use the state-free accent and danger borders
while the functional focus ring stays independent on `border.focus`.
Selection stays a component pattern: selected options compose the accent
subtle ramp with a weight change and a check icon rather than a public
selected colour token. Disabled and pending controls preserve their
resting colours and suppress interaction states.
Add the coverage the specification asks for and the suites lacked:
Button's subtle ramp and its disabled suppression, a Field browser test
for the required indicator and error tone, and Combobox's
selected-plus-keyboard-focus composition.
Note that the advisory semantic borders sit at 1.62-2.19:1, below the 3:1
non-text gate, so they must never be a required state's only cue. The
three pre-existing violations of that rule are unchanged here and remain
tracked in #247.
Refs #298, #300, #301, #302, #303
* Document the shared semantic colour roles
Teach one shared role model instead of action and feedback intent groups:
the six roles and their meanings, the background and foreground state
grammar, the state-free borders, and the contrast guarantees. Publish the
old-to-new migration table, which is now the only place the old paths
appear.
Replace the intent-colours example with a role-colours example that shows
subtle and solid swatches per role, so the page demonstrates that every
role carries the full capability set.
Migrate the raw CSS consumers in the docs app, and fix a mis-cased
`--luke-color-intent-neutral-surface-subtleHover` reference that never
resolved against a real generated variable.
Correct the generation document: the loading skeleton maps to neutral
step 8, as implemented and tested, not step 7.
Refs #298, #304
Fix the theming and styling docs that no longer match the code (#276)
* Fix the theming and styling docs that no longer match the code
Every entry point into the theme and styling layers misled a reader,
because the docs were written before the colour engine was rewritten.
AGENTS.md tells agents to read these docs before changing code, so a wrong
doc generates wrong code.
foundation.ts and define-theme.ts both said the background input is not
consumed and that the canvas still comes from neutral. buildTheme now
takes background directly as the canvas anchor for every family's ramp and
for the elevation surfaces, so anyone tracing the canvas was sent to the
wrong module. The same overclaim had also leaked into the public JSDoc in
theme/index.tsx, which promised a single-value-accent throw and listed an
incomplete set of gates for ThemeContrastError. Internal stage numbers are
gone repo-wide, per the documentation guide.
The authoring page told theme authors that non-text pairs must reach 3:1.
That is not what the compiler does. It now separates what is guaranteed
from what is only measured: text and on-solid pairs are hard-gated at
4.5:1, border.focus and border.control at 3:1, the five intent border
pairs are measured but cannot fail the build, and decorative borders are
never checked at all. An advisory border must never be the only cue for a
state, so the page says so. color.background and ThemeGenerationError are
documented for the first time.
The page no longer promises a "no accessible lightness" error for a
single-value accent. A sweep of the on-solid gate at the accent adaptation
targets found no failing hue or chroma, so the search loop and its throw
are unreachable under the current constants and the error cannot fire.
Authors are pointed at the reachable path instead. Whether to keep that
search as defence-in-depth or delete it is an accent-policy decision,
left alone here.
The styling guide told authors to call recipeInLayer, which no longer
exists, and never documented the recipe engine they actually have to use.
It now covers single-part and slotted recipes, how variant types are
derived rather than hand-maintained, and the shared input-state selectors.
The testing guide's visual test example used a Button tone that does not
exist, so the canonical template did not compile.
Docs and comments only: no non-comment source line changed.
* Make theming and styling copy easier to read
Replace intent with the shared semantic colour contract (#309)
* Replace the intent colour contract with shared semantic roles
The public contract exposed an asymmetric `color.intent` tree: neutral,
accent, and danger received a full interactive ramp while info, success,
and warning received a feedback-only kit. That described how components
happened to use colour rather than what each colour styles and means.
Replace it with a property-first contract shared by all six roles. Every
role now gets subtle and solid background ramps with rest/hover/pressed,
a resting and a stronger interactive foreground, a guaranteed on-solid
foreground, and one state-free border — 60 leaves keyed off a single
canonical `SEMANTIC_ROLES` list that generation, mapping, validation,
diagnostics, and tooling all read.
`FAMILY_REQUIREMENTS` collapses to one uniform capability set, so all six
families are now gated on on-solid contrast where only neutral, accent,
and danger were before. This moves no existing colour: not one family
needed adapting, and steps 3-8, 11, and 12 generate identically for
info, success, and warning. The golden diff is additive only.
The validation matrix is now uniform per role: 90 hard checks (8
functional text, 60 role foregrounds, 18 on-solid, 2 focus, 2 control)
and 12 advisory semantic-border checks per mode.
`rest` is an explicit leaf because a path cannot be both a string leaf
and the parent of `hover` and `pressed`. Solid pressed deliberately
reuses solid hover; components carry the press through depth, finish, or
transform.
Refs #298, #299, #305
* Migrate components to the shared semantic colour contract
Move every consumer off `color.intent` onto the new background,
foreground, and border paths. Each old path has an exact new equivalent
resolving to an identical value, so this is visually a no-op: 218 of 222
visual captures are pixel-identical, and the four that differ are Token
Board captures whose height changed because the contract grew.
Button and IconButton keep their subtle, solid, ghost, hover, pressed,
disabled, pending, focus, material, and forced-colours behaviour. Button
derives its ramp type from the contract now rather than mirroring a
hand-flattened shape. Link uses the resting foreground at rest and the
stronger hover foreground for both hover and press, since the shared
contract carries no separate pressed content colour.
Fields and Combobox controls use the state-free accent and danger borders
while the functional focus ring stays independent on `border.focus`.
Selection stays a component pattern: selected options compose the accent
subtle ramp with a weight change and a check icon rather than a public
selected colour token. Disabled and pending controls preserve their
resting colours and suppress interaction states.
Add the coverage the specification asks for and the suites lacked:
Button's subtle ramp and its disabled suppression, a Field browser test
for the required indicator and error tone, and Combobox's
selected-plus-keyboard-focus composition.
Note that the advisory semantic borders sit at 1.62-2.19:1, below the 3:1
non-text gate, so they must never be a required state's only cue. The
three pre-existing violations of that rule are unchanged here and remain
tracked in #247.
Refs #298, #300, #301, #302, #303
* Document the shared semantic colour roles
Teach one shared role model instead of action and feedback intent groups:
the six roles and their meanings, the background and foreground state
grammar, the state-free borders, and the contrast guarantees. Publish the
old-to-new migration table, which is now the only place the old paths
appear.
Replace the intent-colours example with a role-colours example that shows
subtle and solid swatches per role, so the page demonstrates that every
role carries the full capability set.
Migrate the raw CSS consumers in the docs app, and fix a mis-cased
`--luke-color-intent-neutral-surface-subtleHover` reference that never
resolved against a real generated variable.
Correct the generation document: the loading skeleton maps to neutral
step 8, as implemented and tested, not step 7.
Refs #298, #304