Contextual accent
A surface declares its accent; everything inside follows without being told.
The five accents
Each accent has a solid form and a 13% tonal form. Selection, focus rings, active states and accent buttons all derive from whichever one the surface has declared.
How a surface sets one
One attribute on the surface. Nothing below it takes an accent prop — that is the whole point, and it is why the same Button markup is correct in the builder and in the CMS.
MODE_ACCENT in ModeSwitcher.tsx is the map for the builder's three modes, and BuilderShell reads the same map — so a mode's surface can never disagree with its own tab.
data-accent differs.The inheritance trap
Custom properties inherit as computed values, so overriding the source token is not enough. --color-accent-primary is declared at :root by reading its semantic source, which means the substitution happens once, at the root. A descendant that overrides only that semantic source inherits the already-computed violet and never re-derives it.
This is why the [data-accent] block in semantic.css restates every derived token rather than just the source — and it has to. If you add a token that should track the accent, add it to that block too, then verify by measuring getComputedStyle inside each scope. Do not verify by eye.
Known gaps
--color-primary-hover is bound flat to the base accent in every [data-accent] scope, so hover and default measure the same colour: its seven consumers have no hover lift on an accent surface. Only the unscoped :root violet gets the lighter tint. This is pre-existing and has live consumers, so fixing it is a visible change and belongs with a density and interaction pass rather than a sweep.