Timeline
Free · MITHeadless, accessible, and styled entirely through the attribute surface below — bring your own CSS system, or start from the reference theme.
Install
Without a flag you get no styling at all — plain HTML carrying the attribute surface below. --theme copies the same theme in two dialects;--bridge lumos re-points either one onto Lumos for Astro's tokens (it needs a theme to re-point, and Lumos itself, already in your project).
Unstyled — behavior only, you write the CSS
npx astrocraft-ui add display/timelinePlain-CSS theme — no build step
npx astrocraft-ui add display/timeline --theme cssTailwind theme — needs Tailwind v4
npx astrocraft-ui add display/timeline --theme tailwindPlain-CSS theme, re-pointed onto Lumos tokens
npx astrocraft-ui add display/timeline --theme css --bridge lumosTailwind theme, re-pointed onto Lumos tokens
npx astrocraft-ui add display/timeline --theme tailwind --bridge lumosLive demo
Timeline
A real <ol>, because the order is the meaning. The dot and the connector exist only so a theme has something to draw the rail on — delete every rule and you still have a numbered list of events.
- 0.1.0 — the port261 class strings became attributes.
- 0.2.0 — form wiringFormField and the grouping set.
- 0.3.0 — overlaysPopover, menus, Toast, Tour.
- 0.4.0 — navigationSidebar, Tree, Stepper, and this.
Attribute surface
With no class-merging machinery, these attributes are the whole extension point. State is a data-attribute or a native attribute; visibility is the hiddenattribute. Generated from source by pnpm slots.
| Component | Slots | Variants | Runtime state | Native state |
|---|---|---|---|---|
| `Timeline.astro` | `timeline` | `data-orientation`: `vertical` · `horizontal` | — | — |
| `TimelineConnector.astro` | `timeline-connector` | — | — | — |
| `TimelineDot.astro` | `timeline-dot` | — | — | — |
| `TimelineItem.astro` | `timeline-item` | — | — | — |
Source
What the command copies — 5 files, alias-free so the copy lands standing alone. This is the whole component; there is nothing else.
---
// src/components/ui/display/timeline/Timeline.astro — headless primitive (see ../../README.md).
// An ordered run of events, on a real <ol> — the order IS the meaning, so it is carried by the
// element rather than by a stylesheet. Zero-JS.
//
// It earns its files on the last rule of the filter in ../README.md: structure a selector depends
// on. A theme cannot draw the rail between two events without an element to draw it on, and it
// cannot place the marker without one to place — hence TimelineDot and TimelineConnector, which
// render nothing on their own and exist purely as hooks.
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"ol"> & { orientation?: "vertical" | "horizontal" };
const { orientation = "vertical", class: className, ...rest } = Astro.props;
---
<ol class={className} data-slot="timeline" data-orientation={orientation} {...rest}>
<slot />
</ol>
---
// src/components/ui/display/timeline/TimelineConnector.astro — Timeline compound part (see ../../README.md).
// The rail segment joining this event to the next. Always decorative — it is a line, and a line that
// announced itself would interrupt every event. Render it in every item but the last, or let a theme
// hide the last one with `[data-slot="timeline-item"]:last-child [data-slot="timeline-connector"]`.
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"span">;
const { class: className, ...rest } = Astro.props;
---
<span aria-hidden="true" class={className} data-slot="timeline-connector" {...rest}></span>
---
// src/components/ui/display/timeline/TimelineDot.astro — Timeline compound part (see ../../README.md).
// The marker on the rail. Decorative by default (`aria-hidden`) because the event's own text is what
// carries the meaning; pass an icon or a number through the slot. If you put real text in here,
// override `aria-hidden` through {...rest} so it is announced.
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"span">;
const { class: className, ...rest } = Astro.props;
---
<span aria-hidden="true" class={className} data-slot="timeline-dot" {...rest}><slot /></span>
---
// src/components/ui/display/timeline/TimelineItem.astro — Timeline compound part (see ../../README.md).
// One event. Compose TimelineDot + TimelineConnector + your own content inside it.
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"li">;
const { class: className, ...rest } = Astro.props;
---
<li class={className} data-slot="timeline-item" {...rest}>
<slot />
</li>
import Timeline from "./Timeline.astro";
import TimelineConnector from "./TimelineConnector.astro";
import TimelineDot from "./TimelineDot.astro";
import TimelineItem from "./TimelineItem.astro";
export { Timeline, TimelineConnector, TimelineDot, TimelineItem };
export default Timeline;
What you get
The component source, copied into your project by npx astrocraft-ui add display/timeline — no package dependency, no CSS to fight. Whatever the paid blocks compose, this is it.