Nº 454 of 520
Three passes, one frame
A method section: one portrait frame shown as sketch, light and motion passes, with timed tabs and a drag line that compares each pass with the one before.
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 "Three passes, one frame" (piece stage-passes-compare) 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º 454 · "Three passes, one frame" · www.designlounge.live -->
# Three passes, one frame
> **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 gmxdigital.com: the idea is the "from an image to an experience" block, where one frame steps through base image, atmosphere and film while three numbered tabs underneath keep time. This version is the method section of Siltworks, a fictional visualisation studio, on a warm paper ground instead of a dark one. The same 400×520 frame of a lake pavilion is shown in three passes: 01 Sketch (line drawing on a grid), 02 Light (flat colour, sun, shadows) and 03 Motion (dusk grade, drifting clouds, birds, water shimmer, a lamp glow and a ticking REC timecode). The detail worth copying is the compare line: from pass 02 on, the previous pass sits under the current one, and a draggable divider shows how much the new pass changed. The page opens on pass 02 with the line at 50%, so the point is visible in the first frame.
## Reference behaviour
1. Load: pass 02 "Light" is selected. Tab 01 shows a full grey line (done). Tab 02's 2px green line starts filling over 6000ms.
2. The frame shows pass 01 on the left of the divider and pass 02 on the right. The divider wipes in from 100% to 50% over 700ms (ease-out quart). Chips: "Pass 01" (paper chip) top-left, "Pass 02 · Light" (ink chip) top-right.
3. Left column shows "02 / 03", the italic step title "Light and weather", a 3-line description, and a link "Read the light study ↗". The step block fades up 8px over 420ms on each change.
4. When tab 02's line completes, pass 03 becomes selected and the wipe runs again with pass 02 now underneath. After 03 it wraps to 01.
5. Pass 01 has no previous pass: no divider, no knob, no left chip. The frame shows the sketch alone.
6. Pass 03 adds the moving layer: clouds drift ±30px (26s and 34s alternate), two birds cross the frame every 9s, five water lines shimmer, the window glow breathes between 55% and 100%, the scene pushes in to scale 1.04 over 6s, and "REC 00:SS:FF" counts at 24 frames per second with a blinking red dot.
7. Drag the 44px knob (or click anywhere in the frame) to move the divider. Dragging also turns autoplay off (Pause becomes Play).
8. Hovering the frame pauses the tab timer. Leaving resumes it unless Pause is on.
9. Pause button (40px pill, right of the tabs) toggles autoplay. The current tab's line freezes in place.
10. Keyboard: tabs are a tablist. Left / Right arrows move and select, Home / End jump. The knob is a slider: Left / Right ±5, PageUp / PageDown ±20, Home 0, End 100.
11. A polite live region announces "Pass 3 of 3: The moving frame".
12. Reduced motion: autoplay starts paused, the wipe jumps straight to 50%, no clouds, birds, shimmer, push or blinking; the timecode still updates as text.
## Structure
```
1280 × 800, padding-inline 56px, paper ground --bg
┌──────────────────────────────────────────────────────────────────────────┐
│ —— 03 / Method Sketch, light and motion, in one frame. Skip to ↘ │ head 84px, 1px rule
├──────────────────────────────────────────┬───────────────────────────────┤
│ From a sketch │ ┌───────────────────┐ │
│ to a moving frame. (serif italic green) │ │Pass 01 Pass 02 ·│ │ frame 400×520
│ intro 16px, 430px │ │ sketch │ lit │ │ centred in column
│ ────────────────────────────── │ │ ◉ │ │ knob 44px
│ 02 / 03 │ │ │ │ │
│ Light and weather (34px serif italic) │ │ │ REC ● │ │
│ description, 440px │ └───────────────────┘ │
│ Read the light study ↗ │ │
… 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 gmxdigital.com: the idea is the “from an image to an experience” block, where one frame steps through base image, atmosphere and film while three numbered tabs underneath keep time. This version is the method section of Siltworks, a fictional visualisation studio, on a warm paper ground instead of a dark one. The same 400×520 frame of a lake pavilion is shown in three passes: 01 Sketch (line drawing on a grid), 02 Light (flat colour, sun, shadows) and 03 Motion (dusk grade, drifting clouds, birds, water shimmer, a lamp glow and a ticking REC timecode). The detail worth copying is the compare line: from pass 02 on, the previous pass sits under the current one, and a draggable divider shows how much the new pass changed. The page opens on pass 02 with the line at 50%, so the point is visible in the first frame.
Reference behaviour
- Load: pass 02 “Light” is selected. Tab 01 shows a full grey line (done). Tab 02’s 2px green line starts filling over 6000ms.
- The frame shows pass 01 on the left of the divider and pass 02 on the right. The divider wipes in from 100% to 50% over 700ms (ease-out quart). Chips: “Pass 01” (paper chip) top-left, “Pass 02 · Light” (ink chip) top-right.
- Left column shows “02 / 03”, the italic step title “Light and weather”, a 3-line description, and a link “Read the light study ↗”. The step block fades up 8px over 420ms on each change.
- When tab 02’s line completes, pass 03 becomes selected and the wipe runs again with pass 02 now underneath. After 03 it wraps to 01.
- Pass 01 has no previous pass: no divider, no knob, no left chip. The frame shows the sketch alone.
- Pass 03 adds the moving layer: clouds drift ±30px (26s and 34s alternate), two birds cross the frame every 9s, five water lines shimmer, the window glow breathes between 55% and 100%, the scene pushes in to scale 1.04 over 6s, and “REC 00:SS:FF” counts at 24 frames per second with a blinking red dot.
- Drag the 44px knob (or click anywhere in the frame) to move the divider. Dragging also turns autoplay off (Pause becomes Play).
- Hovering the frame pauses the tab timer. Leaving resumes it unless Pause is on.
- Pause button (40px pill, right of the tabs) toggles autoplay. The current tab’s line freezes in place.
- Keyboard: tabs are a tablist. Left / Right arrows move and select, Home / End jump. The knob is a slider: Left / Right ±5, PageUp / PageDown ±20, Home 0, End 100.
- A polite live region announces “Pass 3 of 3: The moving frame”.
- Reduced motion: autoplay starts paused, the wipe jumps straight to 50%, no clouds, birds, shimmer, push or blinking; the timecode still updates as text.
Structure
1280 × 800, padding-inline 56px, paper ground --bg
┌──────────────────────────────────────────────────────────────────────────┐
│ —— 03 / Method Sketch, light and motion, in one frame. Skip to ↘ │ head 84px, 1px rule
├──────────────────────────────────────────┬───────────────────────────────┤
│ From a sketch │ ┌───────────────────┐ │
│ to a moving frame. (serif italic green) │ │Pass 01 Pass 02 ·│ │ frame 400×520
│ intro 16px, 430px │ │ sketch │ lit │ │ centred in column
│ ────────────────────────────── │ │ ◉ │ │ knob 44px
│ 02 / 03 │ │ │ │ │
│ Light and weather (34px serif italic) │ │ │ REC ● │ │
│ description, 440px │ └───────────────────┘ │
│ Read the light study ↗ │ │
├──────────────────────────────────────────┴───────────────────────────────┤
│ 01 Sketch ───────── 02 Light ━━━━────── 03 Motion ───────── (‖ Pause)│ tabs row, 2px lines
└──────────────────────────────────────────────────────────────────────────┘
body grid: 520px | 1fr, gap 56px
main.wrap: grid rows head / body / tabs.- Head: three-column grid. Label span, middle span, skip link.
- Body left:
sectionlabelled by theh2. The step block is therole="tabpanel",tabindex="0",aria-labelledbythe selected tab. - Body right:
figure.framewith two absolute layers (#underprevious pass,#overcurrent pass, clipped), two chips, a divider holding abutton role="slider", and the REC label. - Tabs row: a
role="tablist"of three buttons (number + name + absolute 2px bar), then the pause button. - The scene is one SVG template, cloned into both layers. The pass is a class on the SVG root:
sk,lt,mo.
Tokens
:root {
--bg: #ece7dc; /* paper ground */
--paper: #f6f2ea; /* frame and chips */
--ink: #1d211c; /* headings, sketch lines */
--ink-2: #474d45; /* body text */
--ink-3: #6a7067; /* counters, idle tabs */
--line: #d3ccbe; /* rules, idle tab lines, sketch grid */
--line-2: #b9b1a1; /* frame edge, done tab lines, button borders */
--accent: #2f6b4f; /* forest green: italic word, active tab, focus */
--accent-2: #e3b45a; /* lamp and sun, used only inside the scene */
--sans: "Hanken Grotesk", system-ui, sans-serif;
--serif: "Literata", Georgia, serif; /* italic only */
--space: 4px 8px 12px 16px 24px 28px 56px;
--frame-w: 400px; --frame-h: 520px;
--shadow-frame: 0 0 0 1px var(--line-2), 0 30px 60px -30px rgba(29,33,28,.35);
--ease: cubic-bezier(.2,.7,.2,1);
--expo: cubic-bezier(.16,1,.3,1);
--dwell: 6000ms; /* per pass */
--wipe: 700ms; /* divider 100% → 50% */
}
Typography
| Role | Family | Size | Weight | Line-height | Tracking | Notes |
|---|---|---|---|---|---|---|
| Section heading | Hanken Grotesk | 60px | 500 | 1.02 | -0.035em | Last words in Literata italic 400, green |
| Step title | Literata italic | 34px | 400 | 1.1 | -0.015em | --ink |
| Body | Hanken Grotesk | 16px | 400 | 1.55 | 0 | --ink-2, max 440px |
| Counter “02 / 03” | Hanken Grotesk | 12px | 600 | 1 | 0.12em | --ink-3 |
| Head row | Hanken Grotesk | 13px | 500–600 | 1 | 0 | Label green with 28px rule |
| Tab number | Hanken Grotesk | 26px | 400 | 1 | -0.02em | Green when selected |
| Tab name | Hanken Grotesk | 14px | 600 | 1 | 0 | |
| Chips, REC | Hanken Grotesk | 12px | 600 | 1 | 0.02–0.08em | REC uses tabular numerals |
Serif italic is used for exactly two things: the end of the heading and the step title. Nothing else.
Motion
| Thing | Trigger | Property | From → to | Duration | Easing | Reduced motion |
|---|---|---|---|---|---|---|
| Divider wipe | pass change (02, 03) | --x via rAF | 100% → 50% | 700ms | ease-out quart | jump to 50% |
| Step block | pass change | opacity, translateY | 0, 8px → 1, 0 | 420ms | --ease | none |
| Tab line | tab selected | scaleX | 0 → 1 | 6000ms | linear (timer) | full, static |
| Scene push | pass 03 | scale | 1 → 1.04 | 6s, once | cubic-bezier(.25,.6,.3,1) | none |
| Clouds | pass 03 | translateX | -24px ↔ 30px | 26s / 34s alternate | linear | off |
| Birds | pass 03 | translate | (-60, 10) → (460, -30) | 9s loop | linear | off |
| Water shimmer | pass 03 | opacity, translateX | .15, -4px ↔ .7, 4px | 2.8s alternate, half offset | ease-in-out | off |
| Lamp glow | pass 03 | opacity | .55 ↔ 1 | 4s alternate | ease-in-out | off |
| REC dot | pass 03 | opacity | 1 / .2 | 1.2s steps | steps(1) | steady |
| Knob | hover / press | scale | 1.06 / .96 | 160ms | --ease | none |
| Sketch fills | pass class change | fill | — | 400ms | --ease | none |
States
- Tab idle:
--ink-3, 2px--lineunderline. - Tab hover:
--ink-2. - Tab selected:
--ink, number green, green bar filling.aria-selected="true",tabindex=0. - Tab done (before the selected one): bar full in
--line-2. - Frame hovered: timer paused.
- Divider hidden on pass 01 (
.has-prevremoved). Left chip hidden too. - Knob focus-visible: 2px green outline, offset 3px. The ring sits outside the 44px circle.
- Pause on: play icon, text “Play”,
aria-pressed="true". - Link hover: underline darkens from
--line-2to--ink. “Skip to projects” arrow moves 2px down-right.
Accessibility
- Tablist “Passes” with three tabs. Roving tabindex. The step block is the tabpanel.
- The knob is
role="slider"witharia-valuemin=0,aria-valuemax=100,aria-valuenow, andaria-valuetext="50 percent previous pass". - The figure has
aria-label="Pavilion at Lake Orta, shown at the current pass". The scene SVGs arearia-hidden; the step text describes each pass. - The live region names the pass on change. The REC timecode is not announced.
- The pause control is in the tab order right after the tabs. Autoplay stops for good once the reader drags the divider.
- Contrast:
#1d211con#ece7dcis about 14:1;#474d45about 7.6:1; green#2f6b4fabout 5.3:1;#6a7067counters about 4.6:1. - Targets: knob 44px, tabs 48px tall, pause 40px tall.
Responsive rules
- ≥1280: as drawn.
- 1024: body grid becomes
1fr 400px, gap 32px, heading 48px. - 768: same as 1024; if the left column gets tight, the description wraps to 4 lines. The step block keeps
min-height: 228pxso the tabs never jump. - <760: padding 20px, head drops its middle text, body stacks (text then frame). Frame is
width: min(100%, 400px)withaspect-ratio: 400/520. Tabs become three columns with the number above the name; Pause moves to its own row, left aligned. - No horizontal overflow at 375px.
Acceptance checklist
Always
- One frame, one camera. Every pass is the same composition; only the rendering changes.
- Three tabs with a timer line each. Completed tabs show a full neutral line.
- From the second pass on, the previous pass sits under the current one, split by a draggable divider. The first pass has no divider.
- The divider wipes from 100% to 50% on every pass change.
- Dragging, clicking in the frame, or using the slider keys moves the divider and turns autoplay off.
- Hovering the frame pauses the timer. A visible Pause / Play button exists.
- The current pass chip is top-right; the previous pass chip is top-left, on the side where that pass shows.
- Tablist and slider keyboard support as listed.
- Reduced motion: paused autoplay, no loops, divider at 50% with no wipe.
- No horizontal overflow at 375px.
This demo
- Opens on “02 / 03 Light and weather” with the divider at 50%.
- Passes are 01 Sketch, 02 Light, 03 Motion; titles “Massing and line”, “Light and weather”, “The moving frame”.
- Heading “From a sketch to a moving frame.” with “moving frame.” in Literata italic
#2f6b4f. - Pass 03 shows “REC 00:SS:FF” counting at 24fps with a red dot.
- Frame is 400×520 on
#f6f2eawith a 1px#b9b1a1edge.
Implementation notes
- One scene, three looks. Draw the scene once with colours as SVG
fillattributes, give every surface a classf, and let a class on the root restyle it. CSS beats presentation attributes, so the sketch pass is just an override:
.sk .f { fill: var(--paper); stroke: var(--ink); stroke-width: 1.1; }
.sk .shade, .sk .anim, .sk .grade, .sk .glow { display: none; }
.sk .cons, .sk .grid { display: block; } /* construction lines, pencil grid */
.lt .anim, .lt .grade, .lt .glow { display: none; }
.mo .grade { display: block; } /* dusk gradient over everything */
- The compare. Both layers are
position:absolute; inset:0. Only the top layer is clipped, with a custom property the divider also reads. A common mistake is leaving the layer wrappers unpositioned: a zero-height box with aclip-pathhides its whole content.
#under, #over { position: absolute; inset: 0; }
.over { clip-path: inset(0 0 0 var(--x, 0%)); }
.divider { position: absolute; top: 0; bottom: 0; left: var(--x, 0%); }
const setX = v => { x = Math.max(0, Math.min(100, v));
frame.style.setProperty('--x', x + '%');
knob.setAttribute('aria-valuenow', Math.round(x)); };
knob.addEventListener('pointerdown', e => { knob.setPointerCapture(e.pointerId); stopAutoplay(); });
knob.addEventListener('pointermove', e => { if (e.buttons) {
const r = frame.getBoundingClientRect(); setX((e.clientX - r.left) / r.width * 100); } });
- Cloned SVGs need unique gradient ids. When you inject the template twice, rename every
idandurl(#…)per copy (the demo appends a counter), or the second copy silently paints with the first copy’s gradients.
Common mistakes:
- Letting the motion pass differ only by tiny animation. Add the dusk grade and the lamp glow so pass 03 reads as a different time of day even in a still.
- Running the wipe with CSS transitions on
clip-pathwhile also dragging. Drive one custom property from one place. - Opening on pass 01, which has nothing to compare.
- Putting labels on the wrong side of the divider.
Details
- Palette
- Type
- Hanken Grotesk · Literata
- Motion
- Subtle motion
- Build
- One session
- Tags
Studied
StripeLet one big moving thing carry the colour. Keep the words light and the cards real.
All sources