Glyph tileDesign system
VisualProduct

Glyph tile

Product glyph in a bordered tile. Includes static and larger animated variants.

npx context-ds add glyph-tile
In the product

Usage

Use it for

Card and list icons for product pages and APIs. ringed for the large featured glyph.

Avoid

  • Glyph tiles as body illustrations
  • Several looping tiles starting in step (stagger startsAtSeconds)

Dependencies: glyph-engine, glyph-loop, glyph-story

Example

examples/glyph-tile.tsx
import { GlyphTile, StillGlyph } from "@/components/visuals/glyph/glyph-tile";// Small tiles as card and list icons (staggered so they never loop in step), one held still, and the bare StillGlyph.// `ringed` (last) draws the large fine-dot glyph instead of a tile.export function Product() {  return (    <div className="flex flex-wrap items-center justify-center gap-4">      <GlyphTile name="scrape" className="size-10 p-1.5" />      <GlyphTile name="search" startsAtSeconds={0.9} className="size-10 p-1.5" />      <GlyphTile name="parse" startsAtSeconds={1.8} className="size-10 p-1.5" />      <GlyphTile name="crawl" still className="size-10 p-1.5" />      <StillGlyph name="apiKeys" dense className="size-5 text-brand" />      <span aria-hidden className="mx-2 h-10 w-px bg-hairline" />      <GlyphTile name="dataApis" ringed className="w-16" />    </div>  );}

Source

components/visuals/glyph/glyph-tile.tsx
import { GLYPH_GRID, glyphSubject, type GlyphPairName } from "@/components/visuals/glyph/glyph-art";import { GlyphLoop } from "@/components/visuals/glyph/glyph-loop";import { GlyphStory } from "@/components/visuals/glyph/glyph-story";import { SURFACE_COLOR } from "@/components/visuals/glyph/surface-color";import { cn } from "@/lib/utils";type GlyphTileProps = {  name: GlyphPairName;  className?: string;  /** The large, featured glyph: fine brand-blue dots on the page instead of a small bordered tile. */  ringed?: boolean;  /** How far into its loop this tile starts, so neighbors don't change in step. */  startsAtSeconds?: number;  /** Holds the pair's first picture instead of looping between the two. */  still?: boolean;};/** * A page's glyph as the dashboard's cards show it. Small, it is a brand-blue glyph in a plain bordered square that * loops between what the page is about and what it gives you; `ringed`, it is drawn large in the fine dot pattern. */export function GlyphTile({ name, className, ringed = false, startsAtSeconds, still = false }: GlyphTileProps) {  // The large, featured glyph is drawn like the Browse APIs and Upgrade plan cards: fine brand-blue dots on the page.  if (ringed) return <GlyphStory name={name} still={still} className={cn("shrink-0 text-brand", className)} />;  // A small brand-blue glyph in a plain bordered square, like the chart cards' icons.  return (    <span className={cn("flex shrink-0 items-center justify-center rounded-control border border-line bg-surface", className)}>      {still ? (        <StillGlyph name={name} className="w-full text-brand" />      ) : (        <GlyphLoop name={name} surfaceColor={SURFACE_COLOR} startsAtSeconds={startsAtSeconds} className="w-full text-brand" />      )}    </span>  );}// Drawn small, the usual dots leave more gap than ink and a glyph reads faded; dense ones nearly touch, so a 20px icon// keeps its weight and one-dot lines (a key's shaft, a trend line) stay joined up.const DENSE_DOT_RADIUS = 4.7;/** A glyph's first picture as a still SVG, in the current text color. */export function StillGlyph({  name,  className = "w-full text-white",  dense = false,}: {  name: GlyphPairName;  className?: string;  /** Bigger dots for icon sizes, where the default spacing looks thin. */  dense?: boolean;}) {  const { columns, rows, pitch, ringStroke } = GLYPH_GRID;  const dotRadius = dense ? DENSE_DOT_RADIUS : GLYPH_GRID.dotRadius;  return (    <svg aria-hidden="true" viewBox={`0 0 ${columns * pitch} ${rows * pitch}`} className={className}>      {glyphSubject(name).map(({ column, row, filled }) => (        <circle          key={`${column}:${row}`}          cx={(column + 0.5) * pitch}          cy={(row + 0.5) * pitch}          r={filled ? dotRadius : dotRadius - ringStroke / 2}          fill={filled ? "currentColor" : "none"}          stroke={filled ? "none" : "currentColor"}          strokeWidth={ringStroke}        />      ))}    </svg>  );}

Reference: context-dashboard c0ddf7a src/components/dots/glyph-tile.tsx.