glasses-ui API reference
The typed 600×600 + D-pad + sensor primitives Meta doesn't ship. Public API of @glasskit/glasses-ui.
@glasskit/glasses-ui is the typed component library every glasses
app imports. It's workspace-only, not published to npm; the source
lives at packages/glasses-ui/src/.
Its job is to make the platform's awkward bits (additive display, no pointer, D-pad-only focus, W3C sensors) feel like normal React. You get one container component, one focus hook, four sensor hooks, two pure utilities, and a stylesheet.
import {
GlassViewport,
useDpad,
useDeviceOrientation,
useDeviceMotion,
useGeolocation,
useNeuralBand,
} from "@glasskit/glasses-ui";
import "@glasskit/glasses-ui/styles.css";Stylesheet is required
It defines the additive-display palette, the .screen /
.focusable / .launcher-* semantic classes, and the focus ring.
Import once at your app entry.
<GlassViewport>
The 600×600 root container. Wrap your whole glasses app in it.
<GlassViewport>
<App />
</GlassViewport>Props
Prop
Type
Design rules baked in
The Meta Ray-Ban Display is additive: pure black emits no light, so black is transparent (the wearer sees the real world) and everything you draw is light layered on top. The viewport enforces:
- Pure black background (
#000): don't fill it - Few elements, high contrast, generous spacing
- Light is the ink: green and white render best
This is the design system, not a config option. Bright marks on pure black. Anything else (gradients, gray-on-gray cards) looks muddy through the optics.
useDpad()
Wires the D-pad (Neural Band arrows + Enter) to focus navigation. Call it once, near the app root.
function App() {
useDpad();
return (
<GlassViewport>
<button className="focusable">One</button>
<button className="focusable">Two</button>
</GlassViewport>
);
}How it works
- Anything with
className="focusable"participates - Arrow keys (or the Neural Band's swipes, which arrive as arrow
keys at the platform level) move focus spatially, picking the
nearest
focusablein the pressed direction using a scored algorithm that balances "distance along the travel direction" against "drift perpendicular to it" - Enter or Space on the focused element fires its
onClick - The first focusable on mount auto-receives focus
- Disabled elements (
disabledoraria-disabled="true") are skipped
Adding focusable
Any element. Buttons most commonly, but cards, links, list items work too:
<button className="focusable" onClick={onPick}>…</button>
<div className="focusable" role="button" tabIndex={0} onClick={onPick}>…</div>The class only registers the element with useDpad. Styling for
the focus ring comes from styles.css (it draws a 2px primary-color
outline on :focus-visible for any .focusable).
scoreRect + Dir type: exported for testing
The pure scoring function is exported separately so you can unit-test custom focus layouts:
import { scoreRect, type Dir } from "@glasskit/glasses-ui";
scoreRect(currentRect, candidateRect, "right");
// → null (candidate isn't to the right)
// → number (lower = better fit)The library's own packages/glasses-ui/src/dpad.test.ts is the
reference for how to test this. You probably never call scoreRect
yourself outside tests.
useDeviceOrientation()
Compass heading + tilt, in degrees. Wraps W3C deviceorientation
events.
const { alpha, beta, gamma } = useDeviceOrientation();Returns
Prop
Type
Returns null for each axis before the first event fires (sensor
needs a moment to start producing values).
The hook is setState-guarded so it only re-renders when a value
actually changes. That's critical at the 600×600 surface's tight perf
budget, since orientation events fire at ~60 Hz.
Local testing
Drive with Chrome DevTools → ⋮ → More tools → Sensors. Set
"Orientation" to a custom value and your alpha updates.
useDeviceMotion()
Acceleration including gravity, in m/s² per axis. Wraps W3C
devicemotion.
const { x, y, z } = useDeviceMotion();Returns
Prop
Type
The demos use it for effort detection:
const mag = motion.x != null && motion.y != null && motion.z != null
? Math.abs(Math.hypot(motion.x, motion.y, motion.z) - 9.8)
: null;
const effort = mag == null ? "unknown" : mag < 1 ? "low" : mag < 4 ? "moderate" : "high";(Subtract 9.8 to remove gravity; the remainder is "how much the user is moving.")
Same setState-guard pattern as orientation: re-renders only
when a value changes.
No built-in DevTools simulation
Either test on-device (recommended for any motion-driven demo)
or write a small window.setInterval shim that dispatches
synthetic devicemotion events during dev.
useGeolocation()
Live GPS. Wraps navigator.geolocation.watchPosition.
const { position, error } = useGeolocation();Returns
Prop
Type
Hide UI that needs coordinates behind if (!position) return ....
accuracy is the radius in meters, useful for showing "±15 m"
status indicators.
The hook configures enableHighAccuracy: true + a 5-second
maximumAge (cached fixes from the last 5 seconds are acceptable;
older requires a fresh fix). It cleans up the watch on unmount and
guards against the OS firing callbacks after the component leaves.
Local testing
Chrome DevTools → Sensors → Geolocation. Pick a city preset or paste lat/lon.
useNeuralBand()
The wristband's richer gestures: pinch, double-pinch, swipe.
const gesture = useNeuralBand();
useEffect(() => {
if (gesture === "pinch") advance();
}, [gesture]);Returns the gesture string for one render, then clears to
null on the next microtask. That way useEffect(..., [gesture])
fires for every event, even two consecutive identical pinches.
Why this is a separate hook from useDpad
The Neural Band's swipes + click arrive as arrow keys + Enter at
the platform level, which is what useDpad consumes. That covers
~80% of input. This hook is for the rest: pinch and swipe
events that the platform exposes as a neuralband CustomEvent on
window instead of as keys.
Simulating in dev
The platform fires the CustomEvent on real glasses. In the browser you fire it yourself:
window.dispatchEvent(
new CustomEvent("neuralband", { detail: { gesture: "pinch" } }),
);Useful for testing recipe-advance / presentation-advance flows without the hardware.
What gestures are supported
Whatever Meta's platform fires. The hook is a pass-through, not
an opinionated whitelist. "pinch" is the one shipped in every
demo today. "double_pinch", "swipe_left", "swipe_right", etc.
may be added by Meta over time without library changes; you only
need to extend your switch.
orientationEqual / motionEqual
Pure equality helpers exported for the same reason scoreRect is:
unit-testability of the sensor hooks. They check whether two
orientation (or motion) snapshots are field-by-field identical, so
the hooks can skip setState when nothing changed.
import { orientationEqual, motionEqual } from "@glasskit/glasses-ui";You won't call these directly unless you're writing custom sensor plumbing.
The stylesheet (styles.css)
Imported once at your app entry:
import "@glasskit/glasses-ui/styles.css";Provides:
- The additive-display @theme tokens: bg / accent / ink / dim colors tuned for the optics
.glass-viewport+.glass-viewport--frame: the 600×600 container styling.screen: flex column, padding, the standard demo layout.focusable: base styles + the focus ring on:focus-visible.launcher,.launcher-grid,.launcher-card,.launcher-card__label,.launcher-card__tagline: the demo launcher grid.app-head,.row,.readout,.label,.dim,.cue-script,.cue-line,.cue-bar: the semantic typography + layout classes the demos use
You can extend, override, or replace these in your fork's own CSS.
They're plain Tailwind v4 @layer base rules with the additive
palette. See Theming.
When to extend the library
The library stays small on purpose. If your demo needs a new
primitive (e.g. a useCompass() that's specifically heading-only,
or a <RingProgress> component), add it INSIDE your fork's
app/src/lib/ first. Only promote to @glasskit/glasses-ui if
it's truly platform-shaped (sensors, focus, viewport conventions)
and would benefit every GlassKit developer.
If you do promote something, run
pnpm --filter @glasskit/glasses-ui test to make sure the existing
test suite still passes. The dpad spatial-scoring algorithm is the
trickiest piece and has good coverage.
The 6 SDK + AI showcase demos
SDK + AI integration showcases. Each demo wires one sensor primitive to one AI call so you have a working starting point, not a finished product.
Cross-device pairing
How the glasses inherit the Clerk session from the companion: same-network device pairing with on-glasses approval, end to end.