GlassKit UI
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).

Pinch opens your capture flow

600 × 600 · live

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-field

Install 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