Detent web components
By Shane Logsdon

Data & tables

Timeline

Implemented

Milestone & pipeline tracker with spring progress physics

detent-timeline

Port status
Catalog status
implemented
Origin
upstream
Phase
3
Wave
phase-3-data
Group
Data & tables

6.9 KB gzip for the class module

Variants

A local sample release pipeline. Activating a status node selects the step, toggles its detail panel and emits detent-item-select; the progress rail fills to the last completed or in-progress step. The in-progress node carries a decorative breathing aura, and prefers-reduced-motion: reduce removes it. The commit hash button copies the full hash and shows a copied state for two seconds — nothing is fetched.

No step has been selected yet.

Recorded states

  • error recorded state; no setter
  • expanded recorded state; no setter
  • collapsed recorded state; no setter
  • reduced motion recorded state; no setter
  • completed recorded state; no setter
  • pending recorded state; no setter

Usage

Import

import '/components/timeline/define.js';

// or register it yourself
import { DetentTimeline } from '/components/timeline/timeline.js';

Attributes

  • collapsible
  • default-selected-id

Properties

  • items
  • onItemSelect

Slots

No slots are recorded for this component.

Events

  • detent-item-select — {}

Example

Runnable example: site/examples/timeline.ts. The snippet below is generated from the catalogued attributes.

<detent-timeline
  collapsible
  default-selected-id
></detent-timeline>

Property-only values

const element = document.querySelector('detent-timeline');
if (element !== null) {
  // items: TimelineItem[]
  element.items = [];
  // onItemSelect: (item: TimelineItem) => void
  element.onItemSelect = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/InteractiveTimeline.tsx sha256 264bdd3db270…

Native files

The native module and its artifacts:

  • src/components/timeline/timeline.ts
  • src/components/timeline/styles.css
  • src/components/timeline/define.ts
  • docs/components/timeline.md
  • site/examples/timeline.ts
  • tests/browser/timeline.spec.ts
  • evidence/components/timeline.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
collapsiblecollapsiblebooleantruePresence semantics: present means true, absent means false.
default-selected-iddefaultSelectedIdstringitems[0]?.idKebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
itemsTimelineItem[][]Property-only: assign from script; not accepted as an attribute.
onItemSelect(item: TimelineItem) => voidundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-item-selectonItemSelect(item: TimelineItem) => voidComposed, bubbles; fires only for a user action.

Slots

No slots are recorded for this component.

CSS customisation

  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • The status node is the single disclosure control; upstream made the whole card a click div that also contained the copy <button> (nested interactive content). The card keeps a pointer affordance, but only the node owns aria-expanded and the keyboard path.
  • Upstream rendered a title tooltip with <title> - <status>; the native port uses an aria-label with the same information so assistive technology gets it.
  • The progress rail is a solid accent fill instead of the upstream #3B82F6 → transparent gradient, so its contrast is measurable; the animated springGentle height becomes a CSS transition that reduced motion removes.
  • Upstream's hard-coded dark-only palette becomes theme tokens, and its lucide-react status icons (Check, Loader2, Clock, AlertCircle) become aria-hidden local SVG icons. They take the node's token colour (accent, muted or failed) instead of upstream's white, grey and rose, because the native node is not filled and white would vanish on it; each icon measures at least 3:1 against its node fill in light and dark. The in-progress icon spins unless reduced motion is requested.
  • Copy commit hash is best-effort (the clipboard API needs a permission grant), keeps upstream's 2 s feedback and emits no event.
  • commitHash is rendered as text, never parsed as a URL (a javascript: value stays inert text).

What it does

A milestone / pipeline tracker: a continuous progress line, interactive status nodes (completed, in-progress, pending, failed), and collapsible detail cards with a metrics grid, author metadata and a copyable commit hash.

Upstream behaviour
  • Animated continuous progress line with spring physics
  • Interactive status nodes (completed, in-progress, pending, failed) with breathing aura
  • Collapsible details with metrics grid and commit metadata
  • One-click commit hash copying with feedback state
  • Accessible semantic list and keyboard navigation structure
Differences from upstream
  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • The status node is the single disclosure control; upstream made the whole card a click div that also contained the copy <button> (nested interactive content). The card keeps a pointer affordance, but only the node owns aria-expanded and the keyboard path.
  • Upstream rendered a title tooltip with <title> - <status>; the native port uses an aria-label with the same information so assistive technology gets it.
  • The progress rail is a solid accent fill instead of the upstream #3B82F6 → transparent gradient, so its contrast is measurable; the animated springGentle height becomes a CSS transition that reduced motion removes.
  • Upstream's hard-coded dark-only palette becomes theme tokens, and its lucide-react status icons (Check, Loader2, Clock, AlertCircle) become aria-hidden local SVG icons. They take the node's token colour (accent, muted or failed) instead of upstream's white, grey and rose, because the native node is not filled and white would vanish on it; each icon measures at least 3:1 against its node fill in light and dark. The in-progress icon spins unless reduced motion is requested.
  • Copy commit hash is best-effort (the clipboard API needs a permission grant), keeps upstream's 2 s feedback and emits no event.
  • commitHash is rendered as text, never parsed as a URL (a javascript: value stays inert text).
Previous: Area Chart Next: Intro Loader

Docs

Components

Project