Design Lounge
Install

Nº 304 of 520

Magazine cover card

A 3:4 magazine cover card with masthead, cover lines, an SVG illustration and barcode. Hover lifts it and curls the corner; three issues swap from a stack.

Preview

Open full size
1280 × 800 · 12.5 KB · 198 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 "Magazine cover card" (piece card-magazine-cover) 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

212 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

A back-issue shelf for a fictional coast-and-craft quarterly called Saltgrass. The left column has a big condensed heading and an issue picker with three rows. The right column has one magazine cover at 3:4, drawn completely in HTML, CSS and inline SVG: a full-bleed masthead, an issue strip, a flat poster illustration, a main cover line, three side lines and a printed barcode. Two other issues peek out behind it as rotated coloured sheets. The detail worth copying is the corner: the cover has a real cut corner made with clip-path, and a folded flap that grows from 18px to 68px on hover or focus while the whole card lifts 10px. Everything on the cover is sized in container query units, so the cover scales as one printed object.

Reference behaviour

  1. First frame: issue No. 41, “The Heat Issue”, June 2026, tomato red. The picker row for No. 41 is checked. A blue and a saffron sheet peek out behind the cover, rotated +3.5deg and -2.5deg.
  2. The cover’s bottom-right corner already shows an 18px curl at rest, so the viewer knows it’s paper.
  3. Hovering or keyboard-focusing the cover lifts it translateY(-10px) rotate(-0.6deg), deepens its drop shadow, and grows the curl to 68px over 320ms with expo-out. Leaving reverses it.
  4. Clicking a picker row switches the issue. The current cover content fades out and shifts left 4% over 180ms, then the new issue’s content fades in from 5% right and 1.02 scale over 420ms. The cover colour changes at the swap point.
  5. The two sheets behind always show the two issues that are not on top.
  6. In the picker, Up/Left and Down/Right arrows move to the previous/next issue, wrapping, and move focus with it (roving tabindex). Only the checked row is in the tab order.
  7. The checked row gets a darker tint and its swatch tilts -4deg and rises 4px.
  8. A visually hidden live region announces “Showing number 42, Night Swimming”.
  9. Issue No. 42 uses the mirrored layout: cover lines right-aligned bottom-right, barcode bottom-left, no side lines.
  10. Clicking the cover does nothing (it is a product link in a real shop; here default is prevented).

Structure

1280 × 800 stage, padding 40px 48px, content max 1040px, 2 columns gap 80px
┌───────────────────────────────────┬─────────────────────────────┐
│ SALTGRASS · COAST & CRAFT…  12px  │   ▞ sheet (other issue)     │
│ BACK                       88px   │  ┌──────── 440 × 587 ─────┐ │
│ ISSUES                            │  │ No.41   JUNE 2026   £9 │ │
│ lede 16px, 40ch                   │  │ SALTGRASS  (22.4cqw)   │ │
│ ┌──┬──────────────────┬───────┐   │  │  ( sun art )  ICE HOUSES│ │
│ │sw│ THE HEAT ISSUE   │ No.41 │   │  │  ~~~~~~~~~~~  SHADE    │ │
│ ├──┼──────────────────┼───────┤   │  │               COLD SOUP│ │
│ │sw│ NIGHT SWIMMING   │ No.42 │   │  │ SUMMER SPECIAL         │ │
│ ├──┼──────────────────┼───────┤   │  │ THE LONG HEAT  ┌─────┐ │ │
│ │sw│ HARVEST          │ No.43 │   │  │ italic sub     │|||||│◤│ │
│ └──┴──────────────────┴───────┘   │  └────────────────┴─────┘ │ │
│ hint 12px                         │   curl bottom-right         │
└───────────────────────────────────┴─────────────────────────────┘
  • Left column: section with p.eyebrow, h1, p.lede, a div[role=radiogroup] of three button[role=radio], and a hint.
  • Right column: .stack (relative, max-width 440px) holding two absolutely positioned .sheet divs (aria-hidden), then a.issue containing .cover and a sibling .fold span.
  • .cover: aspect-ratio: 3/4, container-type: inline-size, overflow: hidden, clipped corner.
  • Inside .cover > .content: .top strip (issue no, month, price), p.mast (aria-hidden, the link has the accessible name), svg.art (viewBox 0 0 400 300), .lines with kicker, h2.main, p.sub, .side with three p, .bar barcode block.
  • Z-order inside the cover: masthead 1, art 2 (so the sun or moon overlaps the masthead’s lower edge), text 3.

Tokens

:root {
  --stage: #d8d1c3;      /* warm concrete */
  --stage-2: #cbc3b2;
  --ink: #1c1a17;
  --ink-2: #4a453d;
  --line: rgba(28,26,23,.18);
  --paper: #fbf8f1;      /* barcode label, flap tip */
  --fold-1: #cfc5b1;     /* flap near the crease */
  --fold-2: #f3eee3;     /* flap mid */

  /* per issue, set on .cover */
  --bg: #e84f2f; --fg: #141210; --mast: #f6ebd9; --accent: #f6ebd9; --kick: #141210;   /* No.41 */
  /* No.42: bg #1e3bb3, fg #f2e9d8, mast #f2e9d8, accent #f2c14e */
  /* No.43: bg #e8a722, fg #1f2a1e, mast #1f2a1e, accent #6e2810 */

  --display: "Anton", Impact, sans-serif;
  --sans: "Rethink Sans", system-ui, sans-serif;

  --ease: cubic-bezier(.2,.7,.2,1);
  --expo: cubic-bezier(.16,1,.3,1);
  --t-lift: 320ms;
  --t-out: 180ms;
  --t-in: 420ms;
  --curl-rest: 18px;
  --curl-hover: 68px;

  --space: 4px 8px 16px 24px 32px 48px 80px;
}

The stage has a 5px dot grain: radial-gradient(rgba(28,26,23,.05) 1px, transparent 1.2px) at 5px 5px.

Typography

RoleFamilySizeWeightLine-heightTrackingCase
EyebrowRethink Sans12px6001.16emupper
Page headingAnton88px400.9-.005emupper
LedeRethink Sans16px4001.550sentence
Picker titleAnton20px4001.1.01emupper
Picker metaRethink Sans13px / 12px400 / 600—0 / .12em—
Cover stripRethink Sans2.7cqw6001.14emupper
MastheadAnton22.4cqw400.84-.01emupper
KickerRethink Sans2.8cqw6001.16emupper, --kick (falls back to --accent)
Main cover lineAnton9.6cqw400.90upper
Sub lineRethink Sans italic3.2cqw4001.30sentence
Side line headAnton4.6cqw40010upper, --accent
Side line bodyRethink Sans3cqw6001.20sentence
Barcode digitsRethink Sans1.9cqw6001.12em—

At 440px wide, 22.4cqw is ~98px: the masthead spans edge to edge with 4cqw side margins. Do not set the masthead in a fixed px size.

Motion

ThingTriggerPropertyFrom → toDurationEasing
Card lifthover / focus-visible on .issuetransformnone → translateY(-10px) rotate(-.6deg)320msexpo
Shadowsamefilter drop-shadow0 10px 14px .22 → 0 26px 30px .30320msexpo
Curlsame--c (registered <length>)18px → 68px320msexpo
Issue outpicker changeopacity, transform1 → 0, translateX(-4%) scale(.98)180msstandard
Issue inafter outopacity, transform0, translateX(5%) scale(1.02) → 1, none420msexpo
Picker swatchcheckedtransformnone → translateY(-4px) rotate(-4deg)320msexpo

Reduced motion: all durations become 1ms, the lift transform is removed, and the issue swaps instantly. The curl still appears on hover, without animating, so the paper cue survives.

States

  • Cover rest: curl 18px, shadow small.
  • Cover hover / focus-visible: lifted, curl 68px, and on focus an inset 3px --ink ring inside the cover (the outline would be clipped by clip-path).
  • Picker row hover: background rgba(28,26,23,.05).
  • Picker row checked: background rgba(28,26,23,.08), number goes from --ink-2 to --ink, swatch tilts.
  • Picker row focus-visible: 2px --ink outline, offset 2px.
  • During a swap, further clicks are ignored until the out phase finishes (a busy flag), so content can’t double-render.

Accessibility

  • The picker is role="radiogroup" labelled “Choose an issue”; each row is role="radio" with aria-checked. Roving tabindex: the checked row is tabindex=0, the rest -1.
  • Arrow keys move and select; focus follows.
  • The cover link’s aria-label is “Saltgrass number 41, The Heat Issue, June 2026”. The masthead and the art are aria-hidden; the cover lines are real text and h2 for the main line.
  • A visually hidden p[aria-live=polite] announces the new issue. Don’t put aria-live on the cover itself; it would read every cover line.
  • Contrast: #141210 on #e84f2f is 4.97:1; #f2e9d8 on #1e3bb3 is 7.5:1; #1f2a1e on #e8a722 is 7.1:1; No.43 side heads #6e2810 on #e8a722 are 5.1:1. The cream #f6ebd9 on red is only 3.4:1, so No.41 uses cream only for the masthead and the 4.6cqw side heads (large display text), and sets the small kicker in ink via --kick.
  • Picker rows are at least 72px tall.

Responsive rules

  • ≥1280: as drawn, cover 440px wide.
  • 1024: same two columns; the cover shrinks with its column, everything inside scales by cqw.
  • ≤860: one column, heading 60px, padding 32px 20px, cover max-width 300px below the picker.
  • 375: no horizontal scroll. The rotated sheets reach about 26px past the cover; the 300px cover leaves 37px on each side, so they fit.

Acceptance checklist

Always

  • The cover is 3:4 and all cover text is sized in container query units.
  • The corner is cut with clip-path on the cover, and the flap is a separate sibling so the clip doesn’t hide it.
  • The curl is visible at rest (small) and grows on hover and keyboard focus.
  • Hover lifts the card 10px with a deeper shadow over 320ms expo-out.
  • The issue switcher is a radiogroup with roving tabindex and arrow keys that wrap.
  • Swapping issues uses a short out (180ms) and a longer in (420ms); clicks during the swap are ignored.
  • The sheets behind the cover show the other issues’ colours.
  • A live region announces the new issue; the cover link has a full accessible name.
  • Reduced motion removes the lift and makes swaps instant.
  • No horizontal scroll at 375px.

This demo

  • Masthead “SALTGRASS”; issues No. 41 “The Heat Issue” (June 2026, #e84f2f), No. 42 “Night Swimming” (August 2026, #1e3bb3), No. 43 “Harvest” (October 2026, #e8a722).
  • No. 41 main line “The long heat”; side lines Ice houses / Shade / Cold soup.
  • No. 42 mirrors the layout: lines right, barcode left.
  • Barcode label #fbf8f1 reads “9 772031 0451 41” for No. 41.
  • Stage #d8d1c3 with a 5px dot grain.

Implementation notes

1. The curl. Register --c so it can transition, cut the cover’s corner with it, and draw the flap as the top-left half of a --c square. The cover sits inside a wrapper that carries filter: drop-shadow, because box-shadow is clipped by clip-path.

@property --c { syntax: "<length>"; inherits: true; initial-value: 18px; }
.issue { transition: transform 320ms var(--expo), filter 320ms var(--expo), --c 320ms var(--expo);
         filter: drop-shadow(0 10px 14px rgba(40,30,15,.22)); }
.issue:hover, .issue:focus-visible { --c: 68px; transform: translateY(-10px) rotate(-.6deg); }
.cover { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%); }
.fold {
  position: absolute; right: 0; bottom: 0; width: var(--c); height: var(--c);
  background: linear-gradient(to top left, transparent 50%, var(--fold-1) 50%, var(--fold-2) 78%, var(--paper));
  filter: drop-shadow(-3px -3px 4px rgba(0,0,0,.22));
}

Common mistake: putting .fold inside .cover. The clip removes it.

2. Scaling the cover as one object. container-type: inline-size on .cover, then every size inside is cqw: masthead 22.4cqw, main line 9.6cqw, margins 5.5cqw. The art SVG is width: 100% at top: 16cqw, so its 400×300 viewBox spans 16cqw to 91cqw; cover lines start below 92cqw. Keep the right 30cqw of the art empty between art y 70 and 200 so the side lines sit on flat colour.

3. The barcode. Generate 28 slots from a seed string built from the issue number; even slots are bars 1–3 units wide, separated by 0.6. Every ninth bar is 40 tall instead of 36 (guard bars). Render it as an SVG with preserveAspectRatio="none" at 20cqw × 9cqw on a paper label.

function barcode(no){
  let x = 0, s = "";
  const seed = String(977203141 + no * 7919);
  for (let i = 0; i < 28; i++) {
    const w = 1 + (+seed[i % seed.length] + i) % 3;
    if (i % 2 === 0) s += `<rect x="${x}" width="${w}" height="${i % 9 ? 36 : 40}"/>`;
    x += w + .6;
  }
  return `<svg viewBox="0 0 ${x.toFixed(1)} 40" preserveAspectRatio="none">${s}</svg>`;
}

Other mistakes to avoid:

  • Using a photo. The illustration is flat SVG shapes in two or three of the issue’s colours.
  • Animating clip-path directly with keyframes instead of the registered property; the polygon interpolates fine, but then the flap and the cut drift apart.
  • Putting a focus outline on the link. The clip hides part of it; use the inset ring on .cover.
  • Letting the side lines fall over dark art. Move the art, not the text.

Details

Palette
Type
Anton · Rethink Sans
Motion
Subtle motion
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Bermawy

Let one toy be the real control. Keep the rest plain and on paper.

All sources