Copy for LLM
Launcher
The app grid: the entry screen for a multi-app surface. Two columns of D-pad-focusable cards on gradient icon plates. Keep it to about six 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.tsimport { clsx, type ClassValue } from "clsx";import { twMerge } from "tailwind-merge";export type { ClassValue };/** * Merge class names the shadcn way: clsx joins conditionals, tailwind-merge * de-dupes conflicting Tailwind utilities so a consumer's `className` override * wins (e.g. passing `px-2` beats the component's `px-6`). Lens components are * Tailwind utilities + `--gk-*` tokens, so this de-dupe matters. */export function cn(...inputs: ClassValue[]): string { return twMerge(clsx(inputs));}/** * 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("grid w-full grid-cols-2 gap-4", className)}> {apps.map((a, i) => ( <button key={a.id} type="button" onClick={a.onSelect} className="focusable gk-launcher-card press-scale flex flex-col items-center gap-[9px] rounded-[22px] border-transparent px-[10px] py-[14px] text-center" > {a.icon != null ? ( <span className={cn( "gk-launcher-card__icon gk-plate mb-0.5 size-[94px] [&_.gk-icon]:size-[46px]", `gk-grad-${a.tone ?? TONES[i % TONES.length]}`, )} > {a.icon} </span> ) : null} <span className="gk-launcher-card__label t-body font-semibold"> {a.label} </span> {a.tagline != null ? ( <span className="t-caption text-foreground-faint">{a.tagline}</span> ) : null} </button> ))} </div> );}Usage
<Launcher apps={[ { id: "nav", label: "Navigate", tagline: "320 m", icon: <Icon active><NavIcon /></Icon>, onSelect: openNav }, { id: "msg", label: "Messages", tagline: "2 new", icon: <Icon><MessageIcon /></Icon>, onSelect: openMessages },]} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
apps | { id, label, tagline?, icon?, tone?, onSelect? }[] | — | The apps to show as focusable cards. Each card may set a gradient tone; it defaults to a cycled palette color. |
How to use it
Launcher is the app's front door: a grid of destinations, each on a tasteful
gradient icon plate. A tile opens one of the other models (a Navigator stack, a
Tabs shell, a Deck flow). Keep it to about six apps so the grid is one glance,
two columns, no scrolling.
A grid of entries
Each entry is a focusable plate with an icon, a label, and an onSelect
handler. The gradient tone defaults to a cycled palette color, so a launcher
looks deliberate without you choosing every color.
import { Launcher } from "@/components/ui/launcher";
<Launcher
apps={[
{ icon: <IconRun />, label: "Workout", onSelect: () => open("workout") },
{ icon: <IconMap />, label: "Navigate", tone: "cyan", onSelect: () => open("nav") },
{ icon: <IconMusic />, label: "Music", onSelect: () => open("music") },
{ icon: <IconBell />, label: "Alerts", onSelect: () => open("alerts") },
]}
/>;The grid is RTL-safe and D-pad navigable: the focus ring walks the plates and
Enter fires onSelect.
Launcher and the back gesture
A Launcher is usually the root, so the back gesture from it falls through to the system (the app switcher), the same as any root screen. When a tile opens a Navigator, pushes from there put history entries on the stack and back returns through them until it reaches the Launcher again.
Deck
A horizontal paged flow (wizard, onboarding). Shows one page with step dots beneath. Uncontrolled with Neural Band swipe to advance, or controlled via index and onIndexChange. Pages advance on pinch or D-pad, never scroll.
Screen
The on-lens layout shell: a status region, a centered stage for the one task, and a cue region, with safe margins that keep the surface mostly black.