GlassKit UI
Components

Callout

A world-object annotation: an anchor + a vertical leader up to an emitted label (no box — just a leader line + emitted type). Project x from relative bearing like Pin (lib/geo). World-anchored, never mirrored.

Muni · 3 minPowell St
600 × 600 · live

Installation

npx @glasskit-ui/cli add callout

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/callout.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <Callout> — a world-object annotation: a small anchor at a screen point with a * vertical leader line up to an emitted label (no box — additive translates the * card to line + type). `x`/`y` are 0–100 (% of the lens). * * WORLD-ANCHORED — placed by an SVG `transform="translate()"` *attribute*, so it * never mirrors under RTL. The leader is vertical (no inline-direction), keeping * the annotation tied to its real-world point in any writing direction. * * Projection: same recipe as <Pin> — the platform exposes heading + GPS but no * 3D pose, so derive x from the target's relative bearing (`lib/geo`: * `relativeBearing(bearingBetween(me, target), heading)` mapped across the * FOV) and keep y a fixed band. Hide it once the target leaves the FOV. */export function Callout({  x,  y,  label,  detail,  className,}: {  /** 0–100, % of the lens width. */  x: number;  /** 0–100, % of the lens height. */  y: number;  label: ReactNode;  detail?: ReactNode;  className?: string;}) {  const cx = Math.round((x / 100) * 600);  const cy = Math.round((y / 100) * 600);  return (    <svg      viewBox="0 0 600 600"      className={cn("gk-worldlayer", className)}      role="img"      aria-label={typeof label === "string" ? label : "Annotation"}    >      <g transform={`translate(${cx} ${cy})`}>        <circle r={6} className="gk-callout__anchor" />        <line x1={0} y1={0} x2={0} y2={-46} className="gk-callout__leader" />        {detail != null ? (          <text y={-82} className="gk-callout__detail">            {detail}          </text>        ) : null}        <text y={-58} className="gk-callout__label">          {label}        </text>      </g>    </svg>  );}

Usage

<Callout x={x} y={y} label="Powell St" detail="Muni · 3 min" />

Props

Prop

Type