ADR-066: Career Arrangement Timeline

The About page shows the career as a DAW-style arrangement view (lanes of clips on a year ruler, one playhead at now) above an accessible tracklist of the details. Data lives in the bio entry; the component is static Astro with CSS scroll-driven reveals inside the ADR-048 motion budget.

Last updated

Status

Accepted (2026-10-20, requested by the owner: “a visual timeline for the About page”)

Context

The bio says “fifteen years… twice as the first marketing hire” but the site never says where. The career (Scandit, Hustle, Cloud City Development, Take 3 Presents, Pezza Creative with its Saviynt and Knowde contracts, Coalesce) overlaps: a founder project ran beside two day jobs, and contracts ran inside the consultancy. A single vertical list hides that shape.

The Pezza design system has a producer register: Space Mono metadata, the accent as a playhead, motion as a crossfader. A DAW arrangement view (tracks as lanes, clips sized by length, a playhead at the current position) shows overlap at a glance and reads as the brand rather than a generic timeline.

Decision Drivers

  • Show overlap: parallel roles must read as parallel
  • Zero JS: constitution rules 2 and 7; ADR-048 allows scroll reveals in CSS
  • Accessible: the content must not depend on the visual
  • Brand rules: accent only as the one playhead; dates and stats in Space Mono
  • Content-driven: entries are data, not markup

Considered Options

Option 1: Vertical list with a spine

Pros: simple, works at every width. Cons: overlapping roles read as sequential.

Option 2: Arrangement view above a tracklist (chosen)

Pros: overlap is visible; the tracklist carries the full content and works on phones; the arrangement is a set of jump links into it. Cons: two renderings of the same data; the arrangement is hidden below the md breakpoint, where it would be too dense.

Option 3: Horizontal scroller

Pros: one rendering. Cons: horizontal scrolling hides most of the career and needs JS or awkward scroll-snap to feel right.

Decision

Option 2.

Implementation Details

  • bio schema gains experience[]: id, company, role, optional progression, lane (employer | independent | founder), start and optional end as YYYY-MM (no end means present), summary, stats[], optional href.
  • src/utils/timeline.ts is pure: month offsets, lane rows by greedy interval packing (a clip that starts the month another ends shares its row), year ticks, and the date range label. “Present” resolves to the build date.
  • src/components/structural/CareerTimeline.astro renders:
    • the arrangement as <nav aria-label="Career at a glance">, a CSS grid with one column per month; each clip is a link to its tracklist entry; the year ruler is aria-hidden; one 2px playhead in --color-ring marks now
    • the tracklist as an <ol> in chronological order, each entry with an id, its dates, lane and stats in MetaLabel, and an ink spine
  • Motion (ADR-048 technique 3): each clip is revealed by an ink cover sliding off with transform: scaleX, and the tracklist spine draws down with scaleY, both on animation-timeline: view(). Everything sits inside @media (prefers-reduced-motion: no-preference) and @supports (animation-timeline: view()); without either, the resting state is fully visible. No loops, no JS.

Consequences

Positive

  • The career’s shape, including overlap, is visible in one glance
  • No JavaScript and no new motion tokens

Negative

  • The arrangement is desktop and tablet only; phones get the tracklist
  • “Present” is the build date, so the playhead moves only on a rebuild

Neutral

  • Narrow clips (under a year) show the company name truncated; the tracklist carries the full text

Validation

  • src/utils/__tests__/timeline.test.ts covers offsets, row packing, ticks and labels
  • src/components/structural/__tests__/CareerTimeline.test.ts covers the markup contract: order, anchors, the playhead, no script
  • e2e/about.spec.ts covers the section, the jump links and heading order
  • The decision is wrong if the arrangement adds a Lighthouse regression on /about/, or if visitors on wide screens do not reach the tracklist

Amendment (2026-10-22): names beside the bars, a playing playhead, xl and up

The owner asked for a way to show the arrangement “so we don’t have the …s”. Names inside the bars cut short roles to an ellipsis (Hustle, at eight months, read “H…”; Coalesce and Cloud City Development were cut too), which the Neutral consequence above had accepted.

  • Names sit in a strip above each bar, and may use the free lane after it. labelRooms in src/utils/timeline.ts gives each name the months from its clip’s start to the next clip in its row; the current role’s name is anchored at now and runs back; where a finished clip is followed by the current one, the free gap is split at its midpoint. The bars keep their durations and carry the year range when wide enough.
  • The playhead plays the arrangement. It sweeps from the first year to now over cover 5%–45% of the arrangement’s view timeline, and each bar and name is uncovered across the stretch where the playhead crosses it. Same technique, same gates (reduced motion, @supports), same resting state: everything visible, playhead at now. Compositor-only (transform, opacity).
  • The arrangement shows from xl (1280px), not md. The names need room: the 13 months of lane after Hustle are under 68px below about 1120px. Below xl the tracklist carries the content, as it already did on phones.

This supersedes the Neutral consequence on truncation and the md breakpoint. Validation adds unit tests for labelRooms and e2e tests that no name is cut at 1280px and that the arrangement is hidden at 1024px.

References