GlassKit UI
Components

Pin

A world-anchored waypoint marker (ring + dot, name + distance above) placed at a projected screen point. You project: derive x from the target's relative bearing (lib/geo) — the platform gives heading + GPS, not 3D pose. Never mirrored under RTL.

120 mBlue Bottle
600 × 600 · live

Installation

npx @glasskit-ui/cli add pin

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/pin.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <Pin> — a world-anchored waypoint marker: a ring + dot at a screen point with * the name and distance stacked above. `x`/`y` are 0–100 (% of the lens), * projected by the consumer from the world position. * * WORLD-ANCHORED — placed by an SVG `transform="translate()"` *attribute* (not * inline style), absolute, so it is never mirrored under RTL: a flipped pin sits * over the wrong thing. The overlay is non-interactive (pointer-events: none). * * Projection: the Display gives web apps heading (`useDeviceOrientation`) and * phone-proxied GPS (`useGeolocation`) but no 3D pose, so a stable projection * is bearing-based: `relativeBearing(bearingBetween(me, target), heading)` * (see `lib/geo`) → map ±FOV/2 onto x 0–100, pick a fixed y band. Off-screen * targets are your call — hide the Pin or switch to <DirectionArrow>. */export function Pin({  x,  y,  label,  distance,  className,}: {  /** 0–100, % of the lens width. */  x: number;  /** 0–100, % of the lens height. */  y: number;  label?: ReactNode;  distance?: 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 : "Waypoint"}    >      <g transform={`translate(${cx} ${cy})`}>        {distance != null ? (          <text y={-58} className="gk-pin__dist">            {distance}          </text>        ) : null}        {label != null ? (          <text y={-33} className="gk-pin__label">            {label}          </text>        ) : null}        <circle r={20} className="gk-pin__ring" />        <circle r={8} className="gk-pin__dot" />      </g>    </svg>  );}

Usage

// x/y projected from the world position each frame<Pin x={x} y={y} label="Blue Bottle" distance="120 m" />

Props

Prop

Type