GlassKit UI
Components

ErrorState

A recoverable error screen: optional glyph + title + message + a retry action. No red — the lens has one accent, so the failure reads from the words. Pairs with AsyncView's error slot.

No signal

Move to an open area and try again.

600 × 600 · live

Installation

npx @glasskit-ui/cli add error-state

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/button.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <Button> — a D-pad-focusable action. Renders a real <button> carrying * the `focusable` class, so `useDpad()` includes it in spatial navigation * and activates it on Enter/Space (the hook calls `.click()`, which fires * `onClick`). Edge + focus ring come from the additive `.focusable` recipe; * `primary` brightens the edge — no fills (apple-feel §9). */export function Button({  children,  variant = "secondary",  icon,  disabled,  onClick,  type = "button",  initialFocus = false,  className,}: {  children: ReactNode;  variant?: "primary" | "secondary";  /** Optional leading glyph — typically a <GlowIcon>. */  icon?: ReactNode;  disabled?: boolean;  onClick?: () => void;  type?: "button" | "submit" | "reset";  /** Seed the D-pad ring here when the screen mounts (`data-autofocus`). */  initialFocus?: boolean;  className?: string;}) {  return (    <button      type={type}      disabled={disabled}      onClick={onClick}      data-autofocus={initialFocus || undefined}      className={cn(        "focusable gk-btn t-body",        variant === "primary" && "gk-btn--primary",        className,      )}    >      {icon}      {children}    </button>  );}
// components/glasskit/error-state.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";import { Button } from "./button";/** * <ErrorState> — a recoverable error screen: optional glyph + title + message + * a retry action. No red (the lens has one accent); the failure reads from the * words and a dimmed treatment. Drop it into a <Screen> stage, or use it as the * `error` slot of <AsyncView>. */export function ErrorState({  title = "Something went wrong",  message,  icon,  onRetry,  retryLabel = "Retry",  className,}: {  title?: ReactNode;  message?: ReactNode;  /** Optional leading glyph — typically a <GlowIcon>. */  icon?: ReactNode;  onRetry?: () => void;  retryLabel?: ReactNode;  className?: string;}) {  return (    <div className={cn("gk-errorstate", className)}>      {icon}      <p className="gk-errorstate__title t-title">{title}</p>      {message != null ? (        <p className="gk-errorstate__message t-body">{message}</p>      ) : null}      {onRetry ? (        <Button variant="primary" onClick={onRetry}>          {retryLabel}        </Button>      ) : null}    </div>  );}

Usage

<ErrorState  title="No signal"  message="Move to an open area and try again."  onRetry={refetch}/>

Props

Prop

Type