Skip to content

SECTION

Timeline

A work history: dates on the left, a spine with a node for each role, and the story on the right.

Two entries

2024 β€” NOW 2 YRS
Independent DESIGN ENGINEER

Tools for people who make things. One product shipped, two more in the drawer.

GOTYPESCRIPTOPENGL
2019 β€” 2024 5 YRS
A product company STAFF ENGINEER, DESIGN PLATFORM

Owned the token layer four product teams drew against, and the migration when it changed.

Nobody had to open a file the day it changed. That was the whole design goal, and it is the only part worth writing down.

REACTCSS
HTML
<div class="rule-stack" style="width:100%">
  <div class="rule-row timeline-entry">
    <div class="timeline-when">
      <span class="timeline-span">2024 β€” NOW</span>
      <span class="timeline-length">2 YRS</span>
    </div>
    <div class="timeline-body">
      <div style="display:flex; flex-direction:column; gap:8px">
        <span class="timeline-org">Independent</span>
        <span class="timeline-role">DESIGN ENGINEER</span>
      </div>
      <p class="timeline-text">Tools for people who make things. One product shipped, two more in the drawer.</p>
      <div class="chip-row">
        <span class="chip">GO</span><span class="chip">TYPESCRIPT</span><span class="chip">OPENGL</span>
      </div>
    </div>
  </div>
  <div class="rule-row timeline-entry timeline-entry-past">
    <div class="timeline-when">
      <span class="timeline-span">2019 β€” 2024</span>
      <span class="timeline-length">5 YRS</span>
    </div>
    <div class="timeline-body">
      <div style="display:flex; flex-direction:column; gap:8px">
        <span class="timeline-org">A product company</span>
        <span class="timeline-role">STAFF ENGINEER, DESIGN PLATFORM</span>
      </div>
      <!-- A story of more than one paragraph: the class goes on a WRAPPER and
           the paragraphs inside it need no class of their own. This is the
           shape rendered Markdown produces. -->
      <div class="timeline-text">
        <p>Owned the token layer four product teams drew against, and the migration when it changed.</p>
        <p>Nobody had to open a file the day it changed. That was the whole design goal, and it is the only part worth writing down.</p>
      </div>
      <div class="chip-row"><span class="chip">REACT</span><span class="chip">CSS</span></div>
    </div>
  </div>
</div>

The tenure band

Blocks scaled to time spent, set with --tenure-cols. Try dragging it sideways on a narrow screen.

2013β€”15Agency, learning
2015β€”17First product job
2017β€”20Design platform
2020β€”NOWIndependent
HTML
<div class="tenure-scroll" style="width:100%">
  <div class="tenure-band" style="--tenure-cols:2fr 2fr 3fr 4fr">
    <div class="tenure-block"><span class="tenure-years">2013β€”15</span><span class="tenure-what">Agency, learning</span></div>
    <div class="tenure-block"><span class="tenure-years">2015β€”17</span><span class="tenure-what">First product job</span></div>
    <div class="tenure-block"><span class="tenure-years">2017β€”20</span><span class="tenure-what">Design platform</span></div>
    <div class="tenure-block tenure-block-current"><span class="tenure-years">2020β€”NOW</span><span class="tenure-what">Independent</span></div>
  </div>
</div>

Reference

Read from sections/timeline.css when this page was built, so it cannot fall behind the stylesheet.

CLASSES.timeline-entry.timeline-when.timeline-span.timeline-length.timeline-body.timeline-entry-past.timeline-org.timeline-role.timeline-text.tenure-band.tenure-scroll.tenure-block.tenure-block-current.tenure-years.tenure-what
READS--ds-accent--ds-font-body--ds-font-display--ds-gap-loose--ds-gap-tight--ds-ground--ds-hairline--ds-ink--ds-ink-faint--ds-ink-soft--ds-measure-wide--ds-pad-card--ds-raised--ds-rule--ds-rule-strong--ds-sunk--ds-tenure-h--ds-tenure-min-w--ds-text-caption--ds-text-chip--ds-text-h3--ds-text-label--ds-text-small--ds-text-ui--ds-timeline-body-basis--ds-timeline-gap--ds-timeline-node--ds-track-key--ds-track-snug--ds-track-subtle--ds-weight-display

Notes

THE SPINE IS A BORDER, NOT A COLUMN

It was a third column holding a node and a stretched connector, and that had two faults. The connector had to be told to reach the row's height, which is not known until the body beside it has wrapped; and three columns could not hold at 320px β€” the body collapsed to 35 pixels and its chips spilled off the page. A border is exactly the row's height by construction, and two columns collapse where three could not.

The entry is flex-wrap rather than a grid, so the body drops under the date at whatever width its own content needs. The node is a square: the only round things in this system are avatars and control dots, and a round node here reads as a bullet.

A finished role takes .timeline-entry-past β€” on the entry, not the body, because the node it hollows out is a pseudo-element of the body and a modifier cannot reach a sibling's pseudo-element from inside it.

A STORY OF MORE THAN ONE PARAGRAPH

.timeline-text is a paragraph and also a box of paragraphs. Put it on a <p> for a one-line role, or on a wrapper holding several β€” the second entry above does that β€” and the paragraphs inside need no class of their own. Both shapes land in the same place, because the wrapper's gap is the same step .timeline-body puts between its own children.

It had to be both. Rendered Markdown arrives as a fragment of <p> nobody can put a class on, so a caller with a two-paragraph story has no choice but to wrap it β€” and wrapped, this drew a measured zero pixels between paragraphs. The class set no display and no gap, and the preflight has already zeroed every paragraph margin, so the two sat flush and read as one block. Nothing failed; it reached a live page.

THE ONE THING ALLOWED TO SCROLL SIDEWAYS

The tenure band's whole point is that the blocks are proportional to time. Squeezing them onto a phone destroys the only information it carries, so it scrolls inside .tenure-scroll instead. The scroll is on the wrapper, so the page itself never moves.