Preferences are a contract, not a settings page
Every Aura surface honors three attributes on its root — data-theme, data-density, data-motion. The CSS keys off the attributes, so obedience is structural: a surface cannot half-honor the contract.
:root { --canvas: #FAFAFA; --ink: #141414; /* the light baseline */ }
:root[data-theme="dark"] { --canvas: #0C0C0C; --ink: #F4F4F4; /* … the full sheet retunes */ }
:root[data-density="compact"] { --density-row: 30px; --density-pad: 14px; /* gaps tighten in step */ }
[data-motion="reduced"] *, [data-motion="reduced"] *::before { animation: none !important; }Two hands hold the pen
The owner sets preferences by hand, or leaves them and the team tunes them from observed behavior — the owner’s hand always writes over the team’s. In the app, the contract is served with the first byte (the attributes render on <html> server-side), so there is no flash and no shift.
import { usePreferences } from "../lib/prefs/client";
const { prefs } = usePreferences();
// prefs.theme · prefs.density · prefs.motion · prefs.suggestionStyle · …Try it, live
These segments are not a sandbox — they drive the real contract of this documentation. Flip theme and every seat’s light retunes; flip density and the row specimen on the Rhythm page tightens; flip motion and expressive specimens hold still while their labels keep telling the truth. (Theme follows your system until you touch the toggle in the top bar.)
The open-set terms are the accent (the one color that means do), the background (any canvas — the whole surface ladder re-derives from it), the contrast dial (how much depth the surfaces carry), and the text size (the scale rides one multiplier inside a safe band). None of those are enumerable attributes: they ride style variables on <html>, inlined server-side with the same first-byte guarantee. The enumerable terms — theme, density, motion, sidebar, and its translucent material — stay plain data-* attributes. The live labs live on Foundations → Color & light.