Components
Launcher
The app grid: the entry screen for a multi-app surface. Cards are D-pad-focusable; keep it to ~4 apps so the whole grid is one glance.
Installation
npx @glasskit-ui/cli add launcherInstall the SDK (it provides GlassViewport, useDpad and the stylesheet), then copy these files into your project:
npm install @glasskit-ui/react// components/lib/utils.tsexport type ClassValue = string | number | null | undefined | false;/** * Join truthy class names. Dependency-free on purpose: the lens components * style via bespoke semantic classes (no conflicting Tailwind utilities to * de-dupe), so this needs no clsx/tailwind-merge and resolves from anywhere * the registry is vendored. */export function cn(...inputs: ClassValue[]): string { return inputs.filter(Boolean).join(" ");}/** * Accessible name from a free-form `label` prop: the label itself when it's a * plain string, otherwise undefined (a ReactNode can't become an aria-label). */export function stringLabel(label: unknown): string | undefined { return typeof label === "string" ? label : undefined;}// components/glasskit/launcher.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** Tasteful gradient tones for the app plates (see styles.css `.gk-grad-*`). */export type LauncherTone = | "blue" | "green" | "peach" | "violet" | "cyan" | "amber";const TONES: LauncherTone[] = [ "blue", "green", "peach", "violet", "cyan", "amber",];export type LauncherApp = { id: string; label: ReactNode; tagline?: ReactNode; /** Optional glyph — a stroke SVG (rendered white on the gradient plate). */ icon?: ReactNode; /** Gradient tone for the icon plate; defaults to a cycled palette color. */ tone?: LauncherTone; onSelect?: () => void;};/** * <Launcher> — the app grid: a home screen of gradient app-icon plates + labels. * Cards are D-pad-focusable (useDpad walks them, Enter activates); focus lifts * the plate. Two columns; keep it to ~6 apps so the grid is one glance. RTL-safe. */export function Launcher({ apps, className,}: { apps: LauncherApp[]; className?: string;}) { return ( <div className={cn("gk-launcher", className)}> {apps.map((a, i) => ( <button key={a.id} type="button" onClick={a.onSelect} className="focusable gk-launcher-card" > {a.icon != null ? ( <span className={cn( "gk-launcher-card__icon gk-plate", `gk-grad-${a.tone ?? TONES[i % TONES.length]}`, )} > {a.icon} </span> ) : null} <span className="gk-launcher-card__label t-body">{a.label}</span> {a.tagline != null ? ( <span className="gk-launcher-card__tagline t-caption"> {a.tagline} </span> ) : null} </button> ))} </div> );}Usage
<Launcher apps={[ { id: "nav", label: "Navigate", tagline: "320 m", icon: <GlowIcon active><NavIcon /></GlowIcon>, onSelect: openNav }, { id: "msg", label: "Messages", tagline: "2 new", icon: <GlowIcon><MessageIcon /></GlowIcon>, onSelect: openMessages },]} />Props
Prop
Type
Deck
A horizontal paged flow (wizard / onboarding). Controlled via index; shows one page with step dots beneath. Pages advance on pinch / D-pad — never scroll.
Navigator
A screen stack with system-back integration. Every push adds a real history entry, so the Display's back gesture pops it via popstate. The stack rides in history.state — a mid-flow reload restores the screen; opt-in paths mirror pushes into the URL. Pop restores focus to the row that pushed.