Components
TextField
A text-entry surface. No keyboard (or microphone API) on the lens — a focusable field showing the value + a mic-style affordance; onActivate opens your own capture flow (picker UI, phone relay).
Platform wishlist — built and waiting on a microphone / system dictation API. The UI ships today; the day Meta exposes the API, it plugs in. See the wishlist →
Installation
npx @glasskit-ui/cli add text-fieldInstall 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/text-field.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <TextField> — a text entry surface. There is no keyboard on the lens, so this * is a D-pad-focusable field that shows the current value (or a placeholder) * and a trailing affordance (a mic glyph). Pure display + `onActivate` — you * own the capture flow it opens. * * Platform note (2026-06): web apps get no microphone (no getUserMedia) and * no system text-input API, so on-device capture means your own picker UI * (e.g. a <List> of choices) or text relayed from the phone. The mic glyph is * a familiar affordance, not a promise of dictation. */export function TextField({ label, value, placeholder = "Pinch to enter text", icon, onActivate, className,}: { label?: ReactNode; value?: ReactNode; placeholder?: ReactNode; /** Trailing glyph — typically a mic <GlowIcon>. */ icon?: ReactNode; onActivate?: () => void; className?: string;}) { const filled = value != null && value !== ""; return ( <button type="button" onClick={onActivate} className={cn("focusable gk-textfield", className)} > <span className="gk-textfield__main"> {label != null ? ( <span className="gk-textfield__label t-caption">{label}</span> ) : null} <span className={cn( "gk-textfield__value t-body", !filled && "gk-textfield__value--empty", )} > {filled ? value : placeholder} </span> </span> {icon != null ? <span className="gk-textfield__icon">{icon}</span> : null} </button> );}Usage
<TextField label="Reply" value={draft} onActivate={startDictation} icon={<GlowIcon size="md"><MicIcon /></GlowIcon>}/>Props
Prop
Type