Preferences
The round button in the bottom-left corner — the corner that says you. It's a solid silver-blue circle — flat fill, soft shadow, a little lift when you hover. It opens the device-preferences popover — appearance · reduced-motion · contrast. (Language used to live here too, but it's now top-level on every layout — if you can't read the page, you shouldn't have to dig for the fix.) These are device preferences: cookie-backed, functional (no consent banner needed), scoped to .halocompliance.com so they follow you across subdomains and paint right on the very first load. Each one defaults to your OS and only stores what you change.
BetaPreferencesCorner is Beta — the preference set (appearance · reduced-motion · contrast) and the cookie contract are settled; the prop surface may still move.
Live — this is the real PreferencesCorner, the same component this site mounts at its own bottom-left. Click the round silver-blue button: the popover pops open, and every change is a real device preference — flip Appearance and the whole site repaints. Every row carries an icon, so each control stays recognizable even when the language is set to one you can't read.
Appearance (light · dark · auto), reduced-motion, contrast — all device preferences — cookie-backed, not tied to your account, so they work whether you're signed in or not. (Language moved out of here to the top level of every layout — if you can't read the page, the fix can't be buried. See /app-bar#language.) The popover is a bounded quick-access surface — a glanceable few, on purpose. If device prefs ever outgrow it, the extras graduate to a real Settings page; it never turns into a scrolling menu.
It doesn’t change — same button, same popover, in every shell and every auth state. It's device-scoped and cookie-backed, so it works before you sign in and follows you across subdomains. The only place it hides is embedded / white-label surfaces. (Language is the one preference that decides whether you can read anything at all, so it lives at the top level of every layout, not here — see /app-bar#language.)
A functional cookie scoped to .halocompliance.com — cross-subdomain, no consent banner needed (it isn't tracking, it's remembering). Defaults come from your OS; we only store what you change. Reset to system clears every override at once — handy as an accessibility escape hatch. Account-level preferences (server-side, cross-device) live in the Account menu, not here.
Silver-blue, not gold — the quieter color for the corner that works in the background. Device here (this machine); account up in the top-right (the person). It pairs with Comms at bottom-right — together they're the steady pair at the bottom of every surface, the layer that follows you across halocompliance.com.
API
The pattern’s component is PreferencesCorner, root-exported from @halo-compliance/ui. Two props — everything else (the cookies, OS resolution, the <html> data attributes, self-isolation) is the component’s job.
- cookieDomain?
- Domain for the three functional cookies (halo-appearance · halo-motion · halo-contrast). Pass .halocompliance.com in production so the choices cross subdomains; omit in dev — the browser scopes to the current host.
- onError?
- Telemetry reporter — fires once if the internal error boundary catches. The degraded fallback (an inert button holding the corner so the layout doesn’t shift) renders regardless; this only surfaces the failure.
- AppearancePref · MotionPref · ContrastPref
- Exported value types — each is ‘system’ plus two explicit overrides. ‘system’ resolves against the OS media query at runtime, and only the resolved value is written to <html> (data-theme · data-motion · data-contrast).
- <SiteLayout> · <Portal>
- Both layouts mount it automatically and pass cookieDomain through — you only mount it yourself on a surface that doesn’t use a standard layout.
Usage
import { PreferencesCorner } from '@halo-compliance/ui'
// Usually you don't mount it yourself — SiteLayout and Portal already
// do, passing cookieDomain through:
<SiteLayout cookieDomain=".halocompliance.com" /* … */ />
// Standalone — only on a surface that doesn't use a standard layout:
<PreferencesCorner
cookieDomain=".halocompliance.com" // production; omit in dev
onError={(error, info) => report(error, info)}
/>
// It resolves 'system' against the OS and writes only the resolved
// overrides to <html>:
// data-theme="dark" · data-motion="reduced" · data-contrast="more"
// tokens.css picks up from there — no JS theming in app code.Related
- /shell — the layout that mounts it, outside the content error boundary.
- /app-bar#language — where Language went: first-class top-level chrome, never a popover row.
- /error-handling — the self-isolation doctrine behind the degraded corner fallback.