Set the timeline’s value to the current item’s value: earlier items become complete, later ones upcoming, and the connectors fill up to the current step. Status is also given as visually hidden text, never by color alone. Timeline is display-only; links and buttons inside items keep their own behavior.
Duplicate non-empty values dispatch one tp-diagnostic warning; the first matching item is current. Timeline is display-only: it adds no focus stop, keyboard handling or selection, and dispatches no change events.
The host has the list role (set as an attribute unless you supply one), so aria-label and aria-labelledby name it directly. The timeline sets role="listitem" on each item, and aria-current="step" on the first current item. The dot and connectors are hidden from assistive technology.
Horizontal
orientation="horizontal" lays items along the inline axis, sharing the width equally. responsive switches from vertical to horizontal above a 40rem container.
Step tracker
Default density
Compact density
Restart from the initial state
Show the copyable source
With align="alternate", horizontal items take turns above and below the axis, and every marker stays on the axis however tall its content is. --tp-timeline-item-min-size keeps items readable; when they no longer fit, the timeline overflows, so place it in a horizontal Scroll area.
Roadmap
Default density
Compact density
Restart from the initial state
Show the copyable source
Q1 2026
Shared workspaces
Invite people to a workspace instead of to each project.
Q2 2026
Offline mode
Edit without a connection; changes sync when you are back.
Q3 2026
Public API
Read and write projects, tasks and comments from your own tools.
Q4 2026
Audit log
Every permission change and export, searchable for a year.
Q1 2027
Data residency
Choose to store a workspace in the EU or the US.
Q2 2027
Automations
Rules that move, assign and notify without a script.
Q1 2026
Shared workspaces
Invite people to a workspace instead of to each project.
Q2 2026
Offline mode
Edit without a connection; changes sync when you are back.
Q3 2026
Public API
Read and write projects, tasks and comments from your own tools.
Q4 2026
Audit log
Every permission change and export, searchable for a year.
Q1 2027
Data residency
Choose to store a workspace in the EU or the US.
Q2 2027
Automations
Rules that move, assign and notify without a script.
<tp-scroll-area orientation="horizontal" label="Product roadmap" style="max-inline-size:44rem;margin-inline:auto">
<tp-timeline orientation="horizontal" align="alternate" aria-label="Product roadmap" style="--tp-timeline-item-min-size:12rem;padding-block-end:var(--tp-space-4)">
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q1 2026</div>
<strong>Shared workspaces</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Invite people to a workspace instead of to each project.</p>
</tp-timeline-item>
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q2 2026</div>
<strong>Offline mode</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Edit without a connection; changes sync when you are back.</p>
</tp-timeline-item>
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q3 2026</div>
<strong>Public API</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Read and write projects, tasks and comments from your own tools.</p>
</tp-timeline-item>
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q4 2026</div>
<strong>Audit log</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Every permission change and export, searchable for a year.</p>
</tp-timeline-item>
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q1 2027</div>
<strong>Data residency</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Choose to store a workspace in the EU or the US.</p>
</tp-timeline-item>
<tp-timeline-item>
<div style="color:var(--tp-muted-foreground)">Q2 2027</div>
<strong>Automations</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Rules that move, assign and notify without a script.</p>
</tp-timeline-item>
</tp-timeline>
</tp-scroll-area>
Markers
The marker slot replaces the dot with a number, an Icon or an Avatar. Hide a visible step number from assistive technology, since the list already conveys position. The marker box is at least one line tall and centers its content, so a dot, an icon or a number sits on the first line of the item’s content. Larger markers, such as an Avatar, align with the start of the content. When the first line of content is inset, add the inset to --tp-timeline-marker-offset.
Numbered steps
Default density
Compact density
Restart from the initial state
Show the copyable source
1Create your account
Signed in as maya@northwind.io
2Verify your domain
Add a TXT record to northwind.io.
3Invite teammates
Bring in the people you work with.
1Create your account
Signed in as maya@northwind.io
2Verify your domain
Add a TXT record to northwind.io.
3Invite teammates
Bring in the people you work with.
<tp-timeline value="domain" aria-label="Workspace setup" style="max-inline-size:24rem;margin-inline:auto">
<tp-timeline-item value="account">
<span slot="marker" aria-hidden="true">1</span>
<strong>Create your account</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Signed in as maya@northwind.io</p>
</tp-timeline-item>
<tp-timeline-item value="domain">
<span slot="marker" aria-hidden="true">2</span>
<strong>Verify your domain</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Add a TXT record to northwind.io.</p>
</tp-timeline-item>
<tp-timeline-item value="invite">
<span slot="marker" aria-hidden="true">3</span>
<strong>Invite teammates</strong>
<p style="margin:var(--tp-space-1) 0 0;color:var(--tp-muted-foreground)">Bring in the people you work with.</p>
</tp-timeline-item>
</tp-timeline>
Expandable items
Item content can be any component. Here each phase is a Collapsible, so expanding one is the Collapsible’s own disclosure and the connectors stretch while it opens. The Collapsible pads its trigger by --tp-space-2-5, so --tp-timeline-marker-offset adds the same inset to keep each marker beside the trigger’s first line.
Expandable phases
Default density
Compact density
Restart from the initial state
Show the copyable source
ResearchJan 6 – Jan 24
Twelve customer interviews and a survey of 480 admins.
DesignJan 27 – Feb 21
Prototype tested with eight teams; navigation simplified to four sections.
BuildFeb 24 – Apr 4
Settings, billing and member pages are done. Notifications are in review, and the import flow starts next week.
In progressBetaApr 7 – Apr 25
Opt-in for workspaces on the Business plan.
LaunchApr 28
Rollout to all workspaces over one week.
ResearchJan 6 – Jan 24
Twelve customer interviews and a survey of 480 admins.
DesignJan 27 – Feb 21
Prototype tested with eight teams; navigation simplified to four sections.
BuildFeb 24 – Apr 4
Settings, billing and member pages are done. Notifications are in review, and the import flow starts next week.
In progressBetaApr 7 – Apr 25
Opt-in for workspaces on the Business plan.
LaunchApr 28
Rollout to all workspaces over one week.
<tp-timeline id="timeline-phases" value="build" aria-label="Admin console redesign" style="max-inline-size:32rem;margin-inline:auto;--tp-timeline-gap:0;--tp-timeline-marker-offset:var(--tp-space-2-5)">
<tp-timeline-item value="research">
<tp-icon slot="marker" data-icon="check"></tp-icon>
<tp-collapsible indicator-position="trailing" content-alignment="label">
<span slot="label"><strong>Research</strong> <span style="color:var(--tp-muted-foreground)">Jan 6 – Jan 24</span></span>
<p style="margin:0">Twelve customer interviews and a survey of 480 admins.</p>
</tp-collapsible>
</tp-timeline-item>
<tp-timeline-item value="design">
<tp-icon slot="marker" data-icon="check"></tp-icon>
<tp-collapsible indicator-position="trailing" content-alignment="label">
<span slot="label"><strong>Design</strong> <span style="color:var(--tp-muted-foreground)">Jan 27 – Feb 21</span></span>
<p style="margin:0">Prototype tested with eight teams; navigation simplified to four sections.</p>
</tp-collapsible>
</tp-timeline-item>
<tp-timeline-item value="build">
<tp-collapsible indicator-position="trailing" content-alignment="label" default-open>
<span slot="label"><strong>Build</strong> <span style="color:var(--tp-muted-foreground)">Feb 24 – Apr 4</span></span>
<p style="margin:0 0 var(--tp-space-2)">Settings, billing and member pages are done. Notifications are in review, and the import flow starts next week.</p>
<tp-badge variant="secondary">In progress</tp-badge>
</tp-collapsible>
</tp-timeline-item>
<tp-timeline-item value="beta">
<tp-collapsible indicator-position="trailing" content-alignment="label">
<span slot="label"><strong>Beta</strong> <span style="color:var(--tp-muted-foreground)">Apr 7 – Apr 25</span></span>
<p style="margin:0">Opt-in for workspaces on the Business plan.</p>
</tp-collapsible>
</tp-timeline-item>
<tp-timeline-item value="launch">
<tp-collapsible indicator-position="trailing" content-alignment="label">
<span slot="label"><strong>Launch</strong> <span style="color:var(--tp-muted-foreground)">Apr 28</span></span>
<p style="margin:0">Rollout to all workspaces over one week.</p>
</tp-collapsible>
</tp-timeline-item>
</tp-timeline>
<script type="module">
const check = { viewBox: '0 0 24 24', paths: [{ d: 'M5 12l4 4L19 6', strokeWidth: 2.5 }] };
for (const icon of document.querySelectorAll('#timeline-phases tp-icon[data-icon="check"]')) icon.icon = check;
</script>
Sides
align puts content on the end side (default), the start side, or alternating sides. An item’s own align overrides its position, so a conversation keeps each author on one side. For per-item sides, use align="alternate" on the timeline so that both sides get equal space.
Sides are logical. In a vertical timeline, start and end are the inline start and end and mirror in right-to-left text. In a horizontal timeline, start is above the axis and end below, and items run in the inline direction, so a right-to-left timeline runs right to left. A vertical alternating timeline puts every item on the end side below 40rem.
Conversation
Default density
Compact density
Restart from the initial state
Show the copyable source
The Stripe webhook PR is ready to merge.Great, I'll review the auth middleware today.Run the token migration against staging first.
The Stripe webhook PR is ready to merge.Great, I'll review the auth middleware today.Run the token migration against staging first.
AttributePropertyTypeDefaultDescriptionalignalignTimelineAlign'end'Side of the axis holding item content.valuevaluestring | nullnullValue of the current item; earlier items are complete and later ones upcoming.
Properties
PropertyTypeDefaultDescriptionmessagesTimelineMessages{}Localized status text announced for items with a status.itemsread-onlyreadonly TpTimelineItem[]—Member items in order.timelineMessagesread-onlyRequired<TimelineMessages>—The status texts in use: messages over the English defaults.
Slots
SlotDescriptiondefaultThe Timeline items, in order.
CSS parts
PartDescription::part(timeline)The root grid whose tracks every item shares.
CSS custom properties
PropertyDefaultDescription--tp-timeline-gap—The space between items along the axis.--tp-timeline-rail-gap—The space between the axis and the content on either side.--tp-timeline-item-min-size—The narrowest a horizontal item gets before the timeline overflows.--tp-timeline-dot-size—The size of the default dot.--tp-timeline-marker-offset—Extra space above a vertical marker, to line it up with content whose first line is inset.
<tp-timeline-item>
Attributes
AttributePropertyTypeDefaultDescriptionvaluevaluestring''Identity matched by the Timeline value.statusstatus'' | Exclude<TimelineStatus, 'none'>''Sets this item's status directly, for a skipped step or statuses given item by item; empty follows the Timeline value.alignalign'' | TimelineSide''Keeps this item on one side of the axis, as for one author in a conversation; empty follows the Timeline align.
Properties
PropertyTypeDefaultDescriptionindexread-onlynumber—Position among the Timeline's items; -1 outside a Timeline.resolvedStatusread-onlyTimelineStatus—Own status when set, otherwise the status derived from the Timeline value.sideread-onlyTimelineSide—Side of the axis holding the content.
Slots
SlotDescriptiondefaultThe content.markerReplaces the dot with an Icon, a number or an Avatar.oppositeContent across the axis, such as a date or a version.
CSS parts
PartDescription::part(timeline-item)An item: one row of a vertical timeline or one column of a horizontal one.::part(timeline-item-connector-fill)The fill drawn over a connector when the earlier item is complete.::part(timeline-item-content)The content region around the default slot.::part(timeline-item-dot)The default marker, drawn for the item status.::part(timeline-item-marker)The box on the axis around the dot or the marker slot.::part(timeline-item-opposite)The region across the axis around the opposite slot.