Components
Slider
A continuous level control (volume, brightness — the quick controls). A native range tinted with accent-color; arrow keys / Neural-Band pinch-twist adjust it. Controlled via value + onChange.
Installation
npx @glasskit-ui/cli add sliderInstall 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/slider.tsximport type { ReactNode } from "react";import { cn, stringLabel } from "../lib/utils";/** * <Slider> — a continuous level control (volume, brightness — the quick * controls). A native range so the fill + thumb need no inline style * (`accent-color` tints them); arrow keys / Neural-Band pinch-twist adjust it. * Controlled via `value` + `onChange`. Omit `onChange` for a read-only * display — the slider then leaves the D-pad focus order (`readOnly` is * meaningless on a range input; an adjustable-looking dead control is worse * than a plain level display). */export function Slider({ value, min = 0, max = 100, label, icon, onChange, className,}: { value: number; min?: number; max?: number; /** a11y / caption label. */ label?: ReactNode; /** Leading glyph — typically a <GlowIcon> (volume / brightness). */ icon?: ReactNode; onChange?: (next: number) => void; className?: string;}) { return ( <div className={cn("gk-slider", className)}> {icon != null ? <span className="gk-slider__icon">{icon}</span> : null} <input type="range" className={cn("gk-slider__input", onChange != null && "focusable")} min={min} max={max} value={value} // readOnly only suppresses React's controlled-input warning (the // attribute itself is meaningless on type="range") — the real // read-only mechanism is leaving the focus order above. readOnly={onChange == null} tabIndex={onChange == null ? -1 : undefined} aria-readonly={onChange == null ? true : undefined} aria-label={stringLabel(label)} onChange={ onChange ? (e) => onChange(Number(e.currentTarget.value)) : undefined } /> {label != null ? ( <span className="gk-slider__label t-caption">{label}</span> ) : null} </div> );}Usage
<Slider value={volume} onChange={setVolume} icon={<GlowIcon size="md"><VolumeIcon /></GlowIcon>} label="Volume"/>Props
Prop
Type
Segmented
Pick one of a few options (a watchOS-style segmented control). Each segment is a D-pad-focusable radio; the selected one lifts with the accent. Keep it to 2–4 options.
Stepper
Adjust a value in discrete steps (glasses have no fine slider). The − and + are D-pad-focusable; bounds disable the ends. Controlled via value + onChange.