Timeline

A sequence where the order is the content, and a progress stepper when you need one.

import { ... } from '@corndev/ui'
  1. POST/session
    Sign in, and keep the token.
  2. POST/orders
    Place the order.
  3. GET/orders/{id}

    Poll until it ships.

  1. Account
  2. Billing
  3. Confirm

HTML

No JavaScript required — these classes are the whole component.

<ol class="corn-timeline" data-variant="sequence">
  <li class="corn-timeline-item">
    <span class="corn-timeline-bullet" aria-hidden="true">1</span>
    <div class="corn-timeline-title">Sign in</div>
    <div class="corn-timeline-body">Exchange credentials for a token.</div>
  </li>
  <li class="corn-timeline-item">
    <span class="corn-timeline-bullet" aria-hidden="true">2</span>
    <div class="corn-timeline-title">Place the order</div>
  </li>
</ol>

<!-- Progress: write the state on each item; the current one is aria-current="step". -->
<ol class="corn-timeline" data-variant="progress">
  <li class="corn-timeline-item" data-state="complete"><span class="corn-timeline-bullet" aria-hidden="true"></span><div class="corn-timeline-title">Account</div></li>
  <li class="corn-timeline-item" data-state="active" aria-current="step"><span class="corn-timeline-bullet" aria-hidden="true"></span><div class="corn-timeline-title">Billing</div></li>
  <li class="corn-timeline-item" data-state="pending"><span class="corn-timeline-bullet" aria-hidden="true"></span><div class="corn-timeline-title">Confirm</div></li>
</ol>

Classes

Class
.corn-timeline
.corn-timeline-item
.corn-timeline-bullet
.corn-timeline-title
.corn-timeline-body

Attributes

OnAttributeValues
.corn-timelinedata-variantprogress
.corn-timeline-itemdata-statecomplete|active

CSS variables

VariableDefaultDescription
--corn-timeline-bullet-size22pxBullet diameter
--corn-timeline-line-width2pxConnector and bullet border width

Vanilla JS

Optional. createTimeline only adds and removes the classes and attributes above, so the HTML is equivalent. Reach for it when you need to change options at runtime.

import { createTimeline } from '@corndev/ui';
import '@corndev/ui/theme';

const el = document.querySelector('#target');
const instance = createTimeline(el, { /* options */ });

Options

OptionTypeDefaultDescription
variant'sequence'|'progress''sequence'sequence: every item equal — a documented workflow, where nothing is running. progress: items before active are complete, active is current, the rest are pending.
activenumberundefinedIndex of the current item, for progress. Items before it read as complete; pass the item count to complete them all.
bulletSizenumber|stringundefinedBullet diameter — a number is pixels. Sets --corn-timeline-bullet-size; the stylesheet default is 22px.
lineWidthnumber|stringundefinedConnector width — a number is pixels. Sets --corn-timeline-line-width; the stylesheet default is 2px.

React

Timeline, TimelineItem — from @corndev/ui-react.

The props are the same as the options above, plus className.

<Timeline.Item> props

One step, with title and bullet slots and the body as children.

PropTypeDefaultDescription
titleReact.ReactNodeundefinedHeading beside the bullet.
bulletReact.ReactNodeundefinedInside the bullet: a number, an icon. Decoration — it is hidden from assistive technology, so do not put meaning only here.
childrenReact.ReactNodeundefinedThe body.
classNamestringundefined

React usage

import { Timeline } from '@corndev/ui-react';

<Timeline>
  <Timeline.Item bullet="1" title="Sign in">Exchange credentials for a token.</Timeline.Item>
  <Timeline.Item bullet="2" title="Place the order" />
  <Timeline.Item bullet="3" title="Poll until it ships" />
</Timeline>

A timeline rather than a list of headings because a list does not say that step 2 happens after step 1 rather than instead of it. It renders an <ol> for the same reason: the order is the meaning.

Sequence, the default

A documented workflow has no current step — nothing is running — so every item is equal and the line between them is only structure. That is variant="sequence", and it is the default.

Progress

For a stepper — onboarding, checkout — variant="progress" with active, the index of the current item:

<Timeline variant="progress" active={1}>
  <Timeline.Item title="Account" />
  <Timeline.Item title="Billing" />
  <Timeline.Item title="Confirm" />
</Timeline>

Items before active are complete: a filled bullet and a coloured line. The current one has a ringed bullet, a bold title and aria-current="step", so a screen reader says which step you are on. Pass the number of items to mark every one complete.

Slots

title and bullet take any node. The title wraps, so a row of badges fits in it. The bullet is aria-hidden — it is decoration — so a number or an icon there must not be the only place that information lives.

If you colour a bullet by meaning, do it from your own content: the component never maps colour to status, so a bullet you style stays yours.

Density

Sized for documentation set at 14px inside a card: 22px bullets and a 2px line. bulletSize and lineWidth change them, a number meaning pixels — or set --corn-timeline-bullet-size and --corn-timeline-line-width in CSS.