Contents Pill
The contents for case studies and blog posts, on every screen size — the articles have no sidebar. A floating card docked at the bottom centre of the screen (28rem wide, full width minus 32px on phones) with two rows: where you are — back link and title — above what you are reading, which expands on tap to the full section list, opening upward. A progress line fills along the card's bottom edge. Long reads only: a page gets it when its body is 1,000+ words (a 5-minute-plus read) with 3+ sections (showContentsFor in lib/portableText). It shows only while the article body is on screen, uses useScrollSpy (from TableOfContents), sits on the nav-pill-surface glass, and on phones hides the back-to-top button while it is up.
Live
Narrow the window below 1024px and scroll — the card appears at the top of the screen while the demo sections below are on screen. On desktop the same items render as the sticky Table of Contents instead.
What the product is
Placeholder section so the pill has something to track.
Impact
Placeholder section so the pill has something to track.
The problem I was there to solve
Placeholder section so the pill has something to track.
What I'd change now
Placeholder section so the pill has something to track.
const toc = sectionsToToc(sections)
<ContentsPill items={toc} targetId="case-study-body" title={caseStudy.title} />
<div id="case-study-body">…</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
items* | { id: string; label: string }[] | - | The same section list TableOfContents renders on desktop. Ids must match the section elements on the page. |
targetId* | string | - | Id of the element being read. The pill is shown only while that element is on screen, and the progress line spans its height. |
title | string | - | Name of the thing being read, shown beside the back link on the top row. Truncates rather than wrapping. |
backHref | string | "/work" | Where the back link goes. |
backLabel | string | "All work" | Text of the back link. Name the destination rather than the action — the arrow already says “back”. |
label | string | "Contents" | Accessible name for the disclosure and its list. |