Skip to main content
astrocraft-ui/ components · 101

Timeline

Free · MIT

Headless, 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

Install command
npx astrocraft-ui add display/timeline

Plain-CSS theme — no build step

Install command
npx astrocraft-ui add display/timeline --theme css

Tailwind theme — needs Tailwind v4

Install command
npx astrocraft-ui add display/timeline --theme tailwind

Plain-CSS theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/timeline --theme css --bridge lumos

Tailwind theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/timeline --theme tailwind --bridge lumos

Live 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.

  1. 0.1.0 — the port261 class strings became attributes.
  2. 0.2.0 — form wiringFormField and the grouping set.
  3. 0.3.0 — overlaysPopover, menus, Toast, Tour.
  4. 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.

ComponentSlotsVariantsRuntime stateNative 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
---
// 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
---
// 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
---
// 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
---
// 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>
src/components/ui/display/timeline/index.ts
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.