Skip to content
ComponentsDomain cardsTimeline

Timeline

Hero example

⚠ component not resolved: Timeline

When to use

Reach for the Timeline namespace when the surface is a feed of events — an entity's audit log, a Compliance view's "what changed in the last month," an Inbox of agent activity, a deal-room activity stream. Timeline is unique among Matter components: it's a namespace, not a single component. You compose feeds from the atoms (KindDot, KindChip, ActorChip, SourceChip), wire the filter controls (Search, FilterSelect, KindToggleRow) above the feed, and reach into the constants (KIND_CFG, ACTOR_TONES, SOURCE_LABEL) when you need to label something canonically.

Don't reach for Timeline when the feed is a plan still executing — that's PlanCard. Don't use it for filing checklists either — those are FilingStatusCard. Timeline is for what already happened; plan and filing surfaces are for what's happening now.

Do — compose your event-feed surface from Timeline atoms. Wire KindToggleRow to your kind-filter state and Search to your text-search state.
Don't — import only KindDot to render a status dot elsewhere. Use StatusPill — the KindDot tones are scoped to the Timeline event vocabulary.

Anatomy

Three composable layers — Timeline doesn't ship a "Timeline" container component. Authors compose the namespace into their own feed layout.

Layer 1: atoms

AtomPurposeVisual
KindDot8px filled dot keyed off the event kind.Solid circle, color from KIND_CFG[kind].dotColor.
KindChipMono uppercase pill carrying the kind label + dot.Pill with leading dot + text.
ActorChipAvatar + name + tone-colored backdrop.Chip with 16px avatar + name.
SourceChipInline mono tag identifying the event source.Mono chip (API / MCP / DASHBOARD / etc.).

Layer 2: filter controls

ControlPurpose
SearchText-search input above the feed.
FilterSelectDropdown menu of FilterOption[] for actor / source / scope filters.
KindToggleRowRow of toggleable kind-chips for filtering by event kind.

Layer 3: constants

ConstantTypePurpose
KIND_CFGRecord<Kind, { label, dotColor }>Canonical kind metadata. Reach for this when rendering kinds outside the Timeline atoms (legend tables, dashboard summaries).
ACTOR_TONESRecord<ActorTone, string>Canonical actor tone → color map.
SOURCE_LABELRecord<Source, string>Human-readable source labels.

Props

The Timeline namespace has no aggregate props — each atom has its own. See the individual atoms: KindDot, KindChip, ActorChip, SourceChip, Search, FilterSelect, KindToggleRow. Props.json introspection per atom lands in the same generator pass and surfaces under each atom's name (e.g., <PropsTable component="KindDot" /> when those atom-level pages are added in a future tranche).

States

Each atom and control has its own state contract. The two stateful primitives:

ComponentState surface
SearchEmpty / typing / has-value / focused / disabled. Mirrors a controlled <input>.
FilterSelectClosed / open / has-selection / disabled. Mirrors a controlled <select>.
KindToggleRowPer-kind: active / inactive. The row tracks an array of active kinds.

Density

In compact, Search input height drops from 32px to 28px, FilterSelect trigger drops likewise. Chip and dot sizes stay constant — they're at the legibility floor.

Themes

Each atom inherits theme tokens. KindDot colors are tonally adjusted in dark; ActorChip tone families have matching dark-theme entries in ACTOR_TONES.

Tokens consumed

No tokens match. No tokens match. No tokens match.

KindDot colors are declared in KIND_CFG and resolve to the --kind-* token family. ActorChip tones resolve from --actor-*. SourceChip mono tag uses --ink-6 for background and --fg-muted for the foreground.

Accessibility

  • Keyboard interactions. Search is a native <input> — full keyboard support. FilterSelect opens on Enter / Space / ArrowDown, navigates options with arrows, closes on Escape. KindToggleRow chips are <button> elements — Enter / Space toggle them.
  • ARIA roles and properties. KindDot is aria-hidden (decorative). Chips inherit text-content accessible names. Filter controls implement standard listbox semantics.
  • Focus order. Filter region first (Search → FilterSelect → KindToggleRow chips), then feed. Feed items themselves are non-focusable; click affordances live on row wrappers (consumer responsibility).
  • Screen-reader expectations. Each event row reads as "kind, actor, action description, source, timestamp." Filter changes are announced via aria-live="polite" on a status region (consumer wires the region).
  • Reduced motion. KindToggleRow active-state transition collapses to opacity. FilterSelect dropdown skips the expand animation.
  • Forced colors. KindDot fills → ButtonText. ActorChip backgrounds → Highlight. SourceChip → Canvas with ButtonBorder.
  • WIG rules. Mono caps on KindChip and SourceChip use letter-spacing: 0.06em (WIG typography minimum for caps legibility). Filter controls are real <input> / <button> elements (semantic-element rule). Touch targets on filter chips ≥ 32px (compact density floor).

Do / Don't

Do — compose Timeline into your own feed layout. The namespace ships the atoms, not the container — you own the row geometry.
Don't — try to use Timeline atoms outside of an event-feed context. The tone vocabulary is event-feed-specific.
Do — wire KindToggleRow to URL state so filter selections deep-link and survive page reloads (WIG navigation-state rule).
Don't — store filter state only in client memory. Filtered feed views are bookmarkable contexts.
Do — use KIND_CFG and SOURCE_LABEL as the single source of truth for labels. Don't hardcode strings.
Don't — translate kind labels in the consumer. Adding a new kind belongs in constants.ts.

Recipes

The Timeline namespace appears in:

Code example

import * as Timeline from "@matter/components/Timeline";

export function EntityAuditLog({ events }: { events: AuditEvent[] }) {
  const [search, setSearch] = useState("");
  const [activeKinds, setActiveKinds] = useState<Timeline.Kind[]>([]);

  return (
    <div>
      <div className="timeline-filters">
        <Timeline.Search onChange={setSearch} value={search} />
        <Timeline.KindToggleRow active={activeKinds} onChange={setActiveKinds} />
      </div>
      <ul className="timeline-feed">
        {events.map((event) => (
          <li key={event.id}>
            <Timeline.KindDot kind={event.kind} />
            <Timeline.KindChip kind={event.kind} />
            <Timeline.ActorChip actor={event.actor} tone={event.actorTone} />
            <span>{event.summary}</span>
            <Timeline.SourceChip source={event.source} />
            <time>{event.occurred_at_relative}</time>
          </li>
        ))}
      </ul>
    </div>
  );
}

Source

packages/components/src/Timeline

On this page