Stamp Chip

A compact inline pill that pairs a short label with a timestamp under a single tone. Use it for event ledgers, audit trails, activity feeds, or anywhere you need to mark when something happened.

defaultDeployedwarningerrorinfoaccent
Customize
Tone
success
Size
md

Installation

npx shadcn@latest add https://craftbits.dev/r/stamp-chip.json

Usage

import { StampChip } from "@craft-bits/core";
 
<StampChip
  label="Deployed"
  timestamp={1717200000000}
  tone="success"
/>

Drop the timestamp to render just a label pill:

<StampChip label="Featured" tone="accent" />

Pass a custom formatter for relative time or any locale-stable string:

import { formatDistanceToNow } from "date-fns";
 
<StampChip
  label="Saved"
  timestamp={savedAt}
  formatTimestamp={(d) => formatDistanceToNow(d, { addSuffix: true })}
/>

Understanding the component

  1. Tone-driven palette. Six semantic combos (default, success, warning, error, info, accent) tint the border toward the tone while the background stays on a muted surface — a row of mixed-tone chips reads as one ledger, not as a rainbow.
  2. Timestamp normalisation. timestamp accepts epoch milliseconds, an ISO-8601 string, or a Date. Invalid input (NaN) falls back to label-only rather than rendering Invalid Date.
  3. Hydration-safe default formatter. The default uses toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }) — pinned locale, deterministic between server and client. Provide formatTimestamp for relative time, ISO output, or a custom date library; just keep the function pure.
  4. Semantic <time dateTime>. When a timestamp renders, it lives inside a <time> element with the full ISO string as dateTime — parseable by screen readers, translation tools, and microdata crawlers.
  5. Mono + tabular nums. The timestamp slot uses font-cb-mono + tabular-nums so digit widths stay constant — a feed of stamps stays vertically aligned as minutes tick.
  6. Stateless presentational <span>. Unlike the source (a toggle button with sounds + sweep animation), the library version has no state, no motion, and no Date.now() call. Toggle behaviour belongs to TogglePill; this is the stamp, not the switch.

Props

PropTypeDefaultDescription
labelReactNoderequiredShort event name.
timestampnumber | string | DateMoment the event occurred. Skipped if undefined or invalid.
tone'default' | 'success' | 'warning' | 'error' | 'info' | 'accent''default'Semantic color tone.
size'sm' | 'md''md'Height + padding preset.
iconReactNodeOptional leading icon.
formatTimestamp(date: Date) => stringlocale-stable hh:mmCustom timestamp formatter.
classNamestringMerged onto the rendered <span>.

Accessibility

  • Renders as a single <span> with the label and a <time dateTime> element. Screen readers announce both; the <time> exposes the precise ISO moment for assistive tech that surfaces it.
  • Icon and separator are aria-hidden="true" so AT reads only the meaningful content.
  • All tone combinations meet WCAG AA against the muted surface in both light and dark themes.
  • No motion is attached, so there is nothing to gate behind prefers-reduced-motion.

Credits

  • Extracted from: AlgoFlashcards (src/platform/ui/StampChip.tsx). The source was a toggle button with sound + sweep animation; the library version reframes the primitive as a stateless timestamp chip and ships toggle behaviour through TogglePill instead.