Nº 327 of 520
Outline to fill topic list
A long list of topics in huge condensed outline type. The one at the centre of the screen fills solid, steps right with a mint bar, and swaps the backdrop.
Preview
Use it
Free to use · credit appreciated1 Once, in your project
npx skills add SusanAcharya/Design-Lounge 2 Then ask
Using the Design Lounge skill, build "Outline to fill topic list" (piece outline-fill-topic-list) in my stack. Match its brief; restyle it onto my product's theme.
No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.
<!-- Design Lounge Nº 327 · "Outline to fill topic list" · designlounge.vercel.app -->
# Outline to fill topic list
> **Build brief for a coding agent.** Rebuild this piece in the reader's stack. If they haven't said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don't "improve" them.
## What it is
Studied from hauntedbouldercity.com: the "You'll learn about..." list, where every topic is a giant outlined condensed word and only the one in the middle of the screen fills in, steps to the right behind a coloured bar, and changes the photo behind it. This piece is the "what you'll hear" section of a fictional cemetery night walk, Hollinsmoor After Dark. Eight topics, each a number, an 88px name and one sentence. A sticky backdrop on the right shows a large monoline drawing for the current topic, a soft colour wash that shifts per topic, and a big counter "01/08". The detail worth copying is that a plain features list becomes a reading pace: the page shows one topic at full voice and keeps the others as quiet outlines, so eight items never feel like a wall.
## Reference behaviour
1. First frame: the kicker "04 / On the walk · 90 minutes · 1.2 km · lanterns provided", the heading "You'll hear" (solid) / "about..." (outlined), then topic 01 "The iron gate" already filled, stepped 160px right, with a 2px mint bar on its left and mint number. Topic 02 shows below as an outline. The backdrop shows the gate drawing at 20% opacity and the counter reads 01/08 "The iron gate".
2. As you scroll, the topic whose vertical centre is closest to the viewport centre becomes current. Only one is current.
3. Becoming current: name fill goes from transparent to bone (450ms), outline from 50% to full bone, description opacity 0.55 → 1, the block slides right 0 → 160px (600ms expo-out), the mint bar grows from the top (scaleY 0 → 1, 500ms).
4. Losing current reverses all of it at the same speeds.
5. The backdrop crossfades: old drawing fades to 0 and shrinks to 0.94, new one fades to 0.2 and scales to 1 (700ms opacity, 1000ms scale). The radial colour wash interpolates to the topic's colour over 800ms. The counter and caption change.
6. A fixed rail of eight short ticks sits at the left edge, vertically centred. The current tick is mint and 40% taller. Clicking a tick or a topic name scrolls that topic to the centre (smooth, or instant under reduced motion).
7. Tabbing onto a topic name also centres it, so keyboard users get the same filled state.
8. After topic 08 the backdrop releases and a booking strip ("Find a night") scrolls up.
## Structure
```
1280 × 800, section scrolls (≈ 2400px), backdrop sticky 100vh
┌──────────────────────────────────────────────────────────────────────────┐
│ 04 / ON THE WALK 90 MINUTES · 1.2 KM · LANTERNS PROVIDED │ header 56px 48px
│ YOU'LL HEAR │ h2 112px
│ ABOUT... (outlined) │
│ ╎ │
│ ╎ ┃ 01 / ┌────────────────┐ │ backdrop drawing
│ ╎ rail ┃ THE IRON GATE (filled, +160px) │ monoline SVG │ │ min(42vw,520px) square
│ ╎ 8 ticks┃ one sentence, max 440px │ 20% bone │ │ right 6%
│ ╎ └────────────────┘ │
│ 02 / │
│ THE BELL GRAVE (outline 1px, 50% bone) 01/08 │ counter 64px
│ sentence at 55% THE IRON GATE │ right 48 bottom 36
└──────────────────────────────────────────────────────────────────────────┘
list: padding 64px 48px 30vh, item max-width 760, item padding 22px 0 22px 22px
```
… 167 more lines. Copy or download for the full brief. Loading the source…Brief
207 linesBuild brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them.
What it is
Studied from hauntedbouldercity.com: the “You’ll learn about…” list, where every topic is a giant outlined condensed word and only the one in the middle of the screen fills in, steps to the right behind a coloured bar, and changes the photo behind it. This piece is the “what you’ll hear” section of a fictional cemetery night walk, Hollinsmoor After Dark. Eight topics, each a number, an 88px name and one sentence. A sticky backdrop on the right shows a large monoline drawing for the current topic, a soft colour wash that shifts per topic, and a big counter “01/08”. The detail worth copying is that a plain features list becomes a reading pace: the page shows one topic at full voice and keeps the others as quiet outlines, so eight items never feel like a wall.
Reference behaviour
- First frame: the kicker “04 / On the walk · 90 minutes · 1.2 km · lanterns provided”, the heading “You’ll hear” (solid) / “about…” (outlined), then topic 01 “The iron gate” already filled, stepped 160px right, with a 2px mint bar on its left and mint number. Topic 02 shows below as an outline. The backdrop shows the gate drawing at 20% opacity and the counter reads 01/08 “The iron gate”.
- As you scroll, the topic whose vertical centre is closest to the viewport centre becomes current. Only one is current.
- Becoming current: name fill goes from transparent to bone (450ms), outline from 50% to full bone, description opacity 0.55 → 1, the block slides right 0 → 160px (600ms expo-out), the mint bar grows from the top (scaleY 0 → 1, 500ms).
- Losing current reverses all of it at the same speeds.
- The backdrop crossfades: old drawing fades to 0 and shrinks to 0.94, new one fades to 0.2 and scales to 1 (700ms opacity, 1000ms scale). The radial colour wash interpolates to the topic’s colour over 800ms. The counter and caption change.
- A fixed rail of eight short ticks sits at the left edge, vertically centred. The current tick is mint and 40% taller. Clicking a tick or a topic name scrolls that topic to the centre (smooth, or instant under reduced motion).
- Tabbing onto a topic name also centres it, so keyboard users get the same filled state.
- After topic 08 the backdrop releases and a booking strip (“Find a night”) scrolls up.
Structure
1280 × 800, section scrolls (≈ 2400px), backdrop sticky 100vh
┌──────────────────────────────────────────────────────────────────────────┐
│ 04 / ON THE WALK 90 MINUTES · 1.2 KM · LANTERNS PROVIDED │ header 56px 48px
│ YOU'LL HEAR │ h2 112px
│ ABOUT... (outlined) │
│ ╎ │
│ ╎ ┃ 01 / ┌────────────────┐ │ backdrop drawing
│ ╎ rail ┃ THE IRON GATE (filled, +160px) │ monoline SVG │ │ min(42vw,520px) square
│ ╎ 8 ticks┃ one sentence, max 440px │ 20% bone │ │ right 6%
│ ╎ └────────────────┘ │
│ 02 / │
│ THE BELL GRAVE (outline 1px, 50% bone) 01/08 │ counter 64px
│ sentence at 55% THE IRON GATE │ right 48 bottom 36
└──────────────────────────────────────────────────────────────────────────┘
list: padding 64px 48px 30vh, item max-width 760, item padding 22px 0 22px 22px
section.sec aria-labelledbytheh2.div.back(aria-hidden,position: sticky; top: 0; height: 100vh; margin-bottom: -100vh) so it sits behind the list without taking space:.wash,.glyphs(eight inline SVGs on a 200×200 viewBox, stroke 1.2, round caps),.grain,.hud(counter and caption).headerwith a kickerpand theh2.ol.list > li.item× 8, each withid,data-w(wash colour) anddata-c(caption). Inside:p.nnumber,h3 > a href="#tN",psentence.ol.rail aria-label="Topics", fixed, generated from the items:awith anaria-labellike “03 The stone choir” andaria-current.section.endwith a sentence and the booking link.- A visually hidden polite live region.
Tokens
@property --w { syntax: "<color>"; inherits: true; initial-value: #3a2226; }
:root {
--ash: #140d0e; /* page */
--ash-2: #1d1415; /* bottom of page gradient */
--bone: #eae0cf; /* filled names, heading */
--bone-2: #bfb5a6; /* sentences */
--bone-3: #8f8579; /* numbers, kicker, caption */
--stroke: rgba(234,224,207,.5); /* outline names */
--line: rgba(234,224,207,.14); /* rail ticks, end rule */
--mint: #7fd1c3; /* spectral accent: bar, current number, rail, button */
--mint-2: #b5e6dc; /* button hover */
--focus: #7fd1c3;
--display: "Big Shoulders Display", Impact, "Arial Narrow", sans-serif;
--mono: "Spline Sans Mono", ui-monospace, monospace;
--h2: clamp(56px, 8vw, 112px);
--h3: clamp(48px, 6.4vw, 88px);
--step: 160px; /* current indent; 48px ≤900, 14px ≤560 */
--gutter: 48px;
--ease: cubic-bezier(.2,.7,.2,1);
--expo: cubic-bezier(.16,1,.3,1);
--t-fill: 450ms; --t-step: 600ms; --t-bar: 500ms;
--t-glyph: 700ms; --t-glyph-scale: 1000ms; --t-wash: 800ms;
}
Wash colours, topics 01–08: #3a2226, #1f3134, #3a2c1f, #2a2238, #3a3020, #22301f, #1a2a36, #34212a. Each is a radial gradient (60% × 70% at 72% 50%) fading to transparent at 70% over the page gradient.
Typography
| Role | Family | Size / LH | Weight | Tracking | Case / colour |
|---|---|---|---|---|---|
| Kicker | Spline Sans Mono | 11px | 400 / 500 | 0.16em | uppercase, --bone-3; “04” mint |
| Section h2 | Big Shoulders Display | --h2 / 0.88 | 900 | 0.005em | uppercase; line 2 outlined 1.5px bone |
| Topic number | Spline Sans Mono | 11px | 400 | 0.16em | “01 /”, --bone-3, current mint |
| Topic name h3 | Big Shoulders Display | --h3 / 0.9 | 700 | 0.01em | uppercase; outline 1px --stroke, current solid bone |
| Sentence | Spline Sans Mono | 14.5px / 1.6 | 400 | 0 | --bone-2, max 440px |
| Counter | Big Shoulders Display | 64px | 900 | 0 | tabular, “/08” 28px 700 --bone-3 |
| Caption | Spline Sans Mono | 11px | 400 | 0.16em | uppercase, --bone-3 |
| Button | Spline Sans Mono | 14px | 500 | 0 | --ash on --mint |
Outlined type uses color: transparent plus -webkit-text-stroke. Keep outline width 1px for names and 1.5px for the bigger heading so both read the same weight.
Motion
| Thing | Trigger | Property | From → to | Duration / easing | Reduced motion |
|---|---|---|---|---|---|
| Name fill | becomes current | color, stroke colour | transparent / 50% → bone / bone | 450ms --ease | instant |
| Item step | becomes current | translateX | 0 → 160px | 600ms --expo | no step; fill only |
| Mint bar | becomes current | scaleY from top | 0 → 1 | 500ms --expo | instant |
| Sentence | becomes current | opacity | 0.55 → 1 | 450ms --ease | instant |
| Drawing out | topic change | opacity, scale | 0.2 → 0, 1 → 0.94 | 700ms / 1000ms | instant |
| Drawing in | topic change | opacity, scale | 0 → 0.2, 0.94 → 1 | 700ms / 1000ms | instant |
| Wash | topic change | --w colour | old → new | 800ms --ease | instant |
| Rail tick | topic change | background, scaleY | line → mint, 1 → 1.4 | 300ms | instant |
Nothing animates on its own. Every change follows scroll position, so the list never moves while the reader is still.
States
- Item resting: outlined name, muted number, sentence 55%, no bar, no indent.
- Item current (
.on): filled name, mint number, sentence 100%, mint bar, stepped right. - Name link hover (any item): outline goes to full bone, fill stays transparent unless current.
- Focus-visible: 2px mint outline, 6px offset on names, 4px elsewhere. Focusing a name centres it, which also makes it current.
- Rail tick hover:
--bone-2. Current: mint, 1.4× tall,aria-current="true". - Button hover:
--mint-2.
Accessibility
- The list is an
olof real headings with links, so it reads as eight topics in order with or without the visual effect. - Outlined text alone fails contrast as body text, so it’s only used for 48px+ names; the sentence under each is always solid
--bone-2(≈ 9:1 on--ash, ≈ 5:1 at 55% opacity on--ash). The 55% state is decorative emphasis, not hidden content. - The backdrop, drawings, wash and counter are
aria-hidden. A polite live region says “Topic 3: The stone choir” on change. - Rail links have names (“03 The stone choir”) and
aria-current. Ticks are 14×22px visually; in a touch product, pad them to 44px. - Tab order: names in order, then rail, then the booking link.
Responsive rules
- ≥1280: as drawn. Step 160px.
- 1024: same; names shrink with
6.4vw. - ≤900: step 48px, drawing 70vw pushed off the right edge at 70% opacity so it stays a backdrop.
- ≤560: page padding 22px, list left padding 30px (room for the rail at 6px), step 14px, the second kicker item and the counter hide, sentence 13.5px.
- Keep 30vh of bottom padding on the list at every size so the last topic can reach the centre.
Acceptance checklist
Always
- Exactly one topic is current: the one whose centre is nearest the viewport centre.
- Current = solid fill, accent bar, accent number, full-opacity sentence, stepped right. Others = outline only.
- The backdrop is sticky behind the list (negative margin), not a fixed layer covering the footer.
- Drawing, wash colour, counter and caption all change with the current topic.
- Rail ticks and topic names scroll their topic to centre.
- Focusing a topic name centres and fills it.
- Sentences are solid text, never outline.
- Reduced motion: no step, no fades; states switch instantly.
- No horizontal overflow at 375px.
This demo
- Eight topics from “The iron gate” to “The last tram”.
- Names 88px Big Shoulders Display 700, outline
rgba(234,224,207,.5). - Accent
#7fd1c3; page#140d0e. - Counter shows “01/08” in the first frame.
- Booking link reads “Find a night”.
Implementation notes
- Pick the item nearest the centre, in one rAF pass. IntersectionObserver with a thin band also works, but nearest-centre never leaves a gap between items.
function update() {
ticking = false;
const mid = innerHeight / 2; let best = 0, bd = Infinity;
items.forEach((el, i) => {
const r = el.getBoundingClientRect();
const d = Math.abs(r.top + r.height / 2 - mid);
if (d < bd) { bd = d; best = i; }
});
set(best); /* toggles .on, swaps drawing, sets --w, counter, rail, live region */
}
addEventListener("scroll", () => { if (!ticking) { ticking = true; requestAnimationFrame(update); } }, { passive: true });
- Sticky backdrop that takes no space.
.back { position: sticky; top: 0; height: 100vh; margin-bottom: -100vh;
overflow: hidden; pointer-events: none; }
- Animate a gradient colour. Gradients can’t transition, but a registered custom property inside one can:
@property --w { syntax: "<color>"; inherits: true; initial-value: #3a2226; }
.wash { background: radial-gradient(60% 70% at 72% 50%, var(--w), transparent 70%),
linear-gradient(180deg, var(--ash), var(--ash-2));
transition: --w 800ms var(--ease); }
Common mistakes:
- Filling the item when it enters the viewport instead of when it reaches the centre; then three are filled at once.
- Animating
font-weightorfont-sizefor emphasis. That reflows the whole list. Fill and translate only. - Outline text for sentences. Thin strokes at 14px are unreadable.
- A fixed backdrop that floats over the booking strip. Use sticky inside the section.
- Forgetting bottom padding, so the last topic can never become current.
- Photo backdrops with no dark wash; the outlines vanish over bright spots.
Details
- Palette
- Type
- Big Shoulders Display · Spline Sans Mono
- Motion
- Rich motion
- Build
- One session
- Tags
Studied
Haunted Boulder CityPin the screen and let scroll turn the pages. Only the line you are on gets to shout.
All sources