GlassKit UI
Copy for LLM

Conventions

The cross-cutting rules every GlassKit component follows, covering input and focus, the accessibility contract, the performance budget, and the prop vocabulary. The contract that holds across all 44 components.

These are the rules of the system, true of every component rather than any one of them. Read this once and the per-component pages stop surprising you. For "which of these similar components do I use?", that guidance now lives on the components themselves (a "When to use" note on each).

Accessibility

The conventions, so your additions match:

  • The Screen cue is the narration line (role="status"): Screen renders its cue slot as a polite live region. One cue per screen; two live regions talking over each other is noise. Set cueLive for an active state.
  • Transients announce themselves: AsyncView and NotificationCard are role="status"; Timer is role="timer". Don't wrap them in another live region.
  • Rapidly changing values stay silent: Readout, StatGrid, Meter, and Compass would announce on every tick. They expose labels (aria-label/visible text) but are deliberately not live. Narrate milestones through the Screen cue instead ("Halfway there").
  • World-anchored SVGs (Pin, Compass) are role="img" with meaningful labels. They describe a point in the world, not a control.

Performance

  • The perf budget is the platform's: under 500 KB gzipped JS and 10 requests (CI enforces the bundle budget on every PR).
  • Long lists are cheap by default: rows use content-visibility: auto, so offscreen rows skip layout and paint while keeping honest scroll metrics and focus-engine rects. Past a few hundred rows, paginate per screen anyway: a wearer never scans 300 rows on a lens.
  • Component source is capped at 12 KB (build:registry fails over it). A vendored component should read in one sitting.
  • Sensor hooks guard setState (a 60 Hz orientation stream doesn't re-render on every fire). Keep that property when composing them.

API conventions

Every component follows these:

  • Auto-wiring: sensor-driven components self-connect when their data prop is omitted. <Compass /> follows live head orientation, <Compass heading={290} /> is controlled. The prop always wins. Same shape for DirectionArrow (target), Clock and Timer (self-ticking), Deck (Neural Band swipe).
  • Focus: interactive elements carry the focusable class; useDpad() (called once at the root) does spatial navigation and Enter-to-click. A focused Slider owns ArrowLeft/Right for value adjust, while vertical arrows still navigate away. data-autofocus picks where a screen's ring starts; <FocusScope> contains it to a modal subtree (Confirm and PermissionPrompt do this for you); Navigator restores the ring to the row that opened a screen when you come back (focus memory).
  • One task per view: most components are sized to be a screen's main event, not dashboard tiles. If a screen needs three of them, it's probably three screens (see Navigator).
  • World-anchored never mirrors: DirectionArrow, Compass, and Pin keep physical positioning under RTL; everything else uses logical CSS and flips for free.
  • One word per meaning: variant is the visual look (Badge: default | accent | outline; Button: primary | secondary | ghost), status is semantic state ("on" | "live" | "off" on StatusDot), and tone is reserved for the gradient palette (Avatar, Icon plates, Launcher tiles). A prop name tells you what kind of choice you're making.
  • Platform honesty: components that look like device capture (ComposeFlow's field activation) are presentation. Web apps on the Display get no camera, microphone, or gaze API. Their JSDoc says exactly what you own.

On this page