Design Lounge
Install

Nº 130 of 520

Motion designer showreel index

A dark motion-design index: 118px italic name, four case rows that height-morph from 88px to 328px on click, each with a CSS/SVG still.

Preview

Open full size
1280 × 800 · 10.3 KB · 158 lines

Use it

Free to use · credit appreciated

1 Once, in your project

npx skills add SusanAcharya/Design-Lounge

2 Then ask

Using the Design Lounge skill, build "Motion designer showreel index" (piece portfolio-motion-showreel) 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.

Brief

175 lines

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

The showreel home of a fictional motion director, Asha V. The page is near-black with one acid pink. A 118px italic Syne wordmark sits above a vertical stack of four cases. Only one case is open at a time. Opening a case is a height morph from 88px to 328px over 420ms (cubic-bezier(.32, .72, 0, 1)), shared-element-ish: the header row does not jump, the body fades in 80ms late, and a 320×200 motion still plays on the right. The stills are CSS and SVG, not video. The first case starts open so the first frame already has motion.

Reference behaviour

  1. Initial state: case 01 “Pulse” is aria-expanded="true", height 328px, panel fill --panel. Its still (three expanding rings around a 10px acid dot) is playing. Cases 02–04 are 88px, transparent, headers only.
  2. Click any closed header: that case expands to 328px; the previously open case contracts to 88px on the same 420ms clock. Only one case may be open.
  3. Clicking the already-open case does not collapse it to an empty reel. The open case stays open (always one expanded).
  4. The plus icon in the header rotates 45° to a multiply over the same 420ms when that case is open.
  5. The open case’s index number is --acid; closed numbers are --ink-2.
  6. Body copy and still are opacity: 0 and translateY(8px) when closed. When open they go to 1 / 0 over 240ms with an 80ms delay, so type does not appear while the box is still short.
  7. Keyboard: each header is a <button>. Tab moves 01→02→03→04. Enter / Space toggles via the button. Focus ring is 2px acid, 3px offset.
  8. Reduced motion: height change is 1ms; all still animations stop and freeze on a readable frame (rings visible at scale 2, type unblurred, streaks static, orbit stopped).

Structure

1280 × 800
┌────────────────────────────────────────────────────────────────────────┐
│ Asha V.                                      SHOWREEL  ABOUT  BOOK     │ 52
├────────────────────────────────────────────────────────────────────────┤
│ Asha V.   (118px italic 800, V. in acid)     Motion direction for…     │ ~160
├────────────────────────────────────────────────────────────────────────┤
│ 01  Pulse           Brand film · 0:32     2026    +                    │ 88
│     copy 42ch                                      ┌─────────────────┐ │
│     [Direction][Edit][Sound]                       │ pulse still 200 │ │ 328 open
│                                                    └─────────────────┘ │
│ 02  Afterimage      Type titles · 0:18    2025    +                    │ 88
│ 03  Night Bus       Main titles · 0:41    2025    +                    │ 88
│ 04  Kernel          Product loop · 0:08   2023    +                    │ 88
└────────────────────────────────────────────────────────────────────────┘
  pad 48px. Header row: 72 / 1fr / 180 / 120 / 40. Body: 72 / 1fr / 320.
  • <nav aria-label="Primary"> — 52px, wordmark 16px Syne 700, three uppercase links. Current route has a 2px acid underline (box-shadow: inset 0 -2px 0).
  • <header class="mast"> — flex, name left, 14px lede (28ch) right, aligned to the baseline of the wordmark.
  • <section class="reel" aria-label="Showreel"> — four <article class="case">.
    • <button class="head"> 88px: number, title, meta, year, plus.
    • .body grid: spacer + copy/tags + .still.

Tokens

:root {
  --bg: #0b0b10;          /* page */
  --panel: #12121a;       /* open case fill */
  --ink: #f3f0ea;         /* primary text */
  --ink-2: #8b8896;       /* meta */
  --line: #23232e;        /* 1px rules */
  --acid: #ff4ec0;        /* the one accent — not amber, not lime */
  --acid-ink: #1a0012;    /* unused on text; reserved if a chip fills */

  --display: "Syne", system-ui, sans-serif;
  --sans: "Manrope", system-ui, sans-serif;

  --pad: 48px;
  --nav: 52px;
  --closed: 88px;
  --open: 328px;

  --t: 180ms;
  --t-morph: 420ms;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ios: cubic-bezier(.32, .72, 0, 1);
}

Typography

RoleFamilySizeWeightLine-heightTrackingCase
WordmarkSyne italic118px8000.80−0.05emtitle
Case titleSyne28px7001−0.03emtitle
Index numberSyne18px70010tabular
NavManrope12px5001+0.08emUPPERCASE
Body / ledeManrope14px4001.450sentence
Meta / yearManrope12–13px400–6001+0.04emsentence
TagsManrope11px4001+0.08emUPPERCASE

Motion

ElementTriggerPropertyFrom → ToDurationEasingNotes
.caseclickheight88px ↔ 328px420ms--iosboth cases on the same clock
.chev svgexpandedrotate0 → 45°420ms--ios
.bodyexpandedopacity, y0, 8px → 1, 0240ms--easedelay 80ms
Pulse ringsloopscale, opacity0.4, 0.9 → 5.2, 02400ms--ease3 rings, 600ms stagger
Afterimage typeloopx, blur−12px, 0 → 12px, 1.2px2800ms--iosping-pong via 0/50/100
Night Bus streakslooptranslateX0 → 220%1500–2400mslinear4 bars, varied width
Kernel orbitlooprotate0 → 360°8000mslineardots on a 120px ring

Reduced motion: height 1ms; still keyframes animation: none and a frozen fallback frame.

States

  • Closed case: height 88px, transparent, number --ink-2, plus at 0°.
  • Open case: height 328px, --panel fill, number --acid, plus at 45°, body visible.
  • Header hover: no colour shift; the cursor is pointer. Do not invert the row.
  • Nav current: acid 2px inset underline. Nav hover: --ink.
  • Focus-visible: 2px --acid, 3px offset.
  • Tags: 1px --line border, 4×8 padding, no fill.

Accessibility

  • Each case header is a <button> with aria-expanded and aria-controls pointing at the body id.
  • The article also carries aria-expanded for CSS hooks; keep both in sync in JS.
  • Stills are aria-hidden="true"; the paragraph describes the piece.
  • Do not trap focus inside an open case. Tab continues to the next header.
  • Contrast: --ink on --bg > 14:1; --ink-2 #8B8896 on #0B0B10 ≈ 5.6:1; acid #FF4EC0 on #0B0B10 ≈ 6.4:1.
  • Hit target: the entire 88px header row is the button.

Responsive rules

  • ≥ 1280: as specified.
  • 900–1279: wordmark 88px if needed to stay one line; still stays 320px.
  • < 900: --open: 420px so the still can stack. Header columns collapse to 48px 1fr 40px; hide meta and year. Body becomes one column; still full width, 200px tall.
  • < 640: wordmark 72px; side padding 20px.

Acceptance checklist

  • Wordmark is 118px Syne italic 800; the “V.” is #FF4EC0.
  • Exactly four cases; only one aria-expanded="true" at a time.
  • Closed height 88px, open height 328px, morph 420ms cubic-bezier(.32,.72,0,1).
  • Case 01 is open on first paint (hero state).
  • Open case fill is #12121A; closed cases stay transparent.
  • Body fades in over 240ms after an 80ms delay; it is not visible while closed.
  • Each open case shows a 320×200 still with a distinct CSS/SVG loop.
  • Accent is #FF4EC0 only — no amber, no lime, no second accent.
  • Plus rotates 45° on the open row.
  • Keyboard: Tab + Enter/Space opens a case; focus ring is visible.
  • prefers-reduced-motion: reduce freezes stills and makes the height change instant.
  • No <video>, no raster, no emoji, no placeholder copy.

Implementation notes

Animate height between two known values, not auto. Known values let both the opening and closing cases share one easing curve:

.case { height: 88px; overflow: hidden; transition: height 420ms cubic-bezier(.32,.72,0,1); }
.case[aria-expanded="true"] { height: 328px; background: var(--panel); }

Keep one case open. A fully collapsed reel reads as an empty dark slab in the screenshot.

function open(el) {
  cases.forEach((c) => {
    const on = c === el;
    c.setAttribute('aria-expanded', String(on));
    c.querySelector('.head').setAttribute('aria-expanded', String(on));
  });
}

Delay the body, otherwise 14px copy flashes in a 88px slot during the first 100ms:

.body { opacity: 0; transform: translateY(8px); pointer-events: none;
        transition: opacity 240ms var(--ease) 80ms, transform 240ms var(--ease) 80ms; }
.case[aria-expanded="true"] .body { opacity: 1; transform: none; pointer-events: auto; }

Common mistakes: using max-height: 999px (the ease feels late); allowing all four closed; putting video posters in the still; using amber #E0A34B or Kiln lime #C8F65A; making the name roman instead of italic.

Details

Palette
Type
Syne · Manrope
Motion
Rich motion
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Lama Lama

A tiny nav bar can carry the voice. Change its one line for each section.

All sources