Design Lounge
Install

Nº 154 of 520

Team hover portrait grid

A 3×2 Halden studio grid: six geometric SVG portraits, hover or focus unveils role, one-line bio, and a studio email.

Preview

Open full size
1280 × 800 · 10.1 KB · 191 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 "Team hover portrait grid" (piece team-hover-portrait-grid) 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

174 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 team section for Halden, an Oslo design studio. Six people sit in a 3×2 grid. Each cell is a geometric “portrait” — layered SVG rectangles, circles, and gradients in wine, gold, and cream — not photographs and not initials discs. At rest only the italic Bodoni name sits on the picture. Hover or keyboard focus drops a 78% ink veil over 320ms and reveals role (gold uppercase), a one-line bio, and a mailto: link. No JavaScript. The first frame is the six pictures with names already on them.

Reference behaviour

  1. Initial state: cream page #F3EBE3, 56px nav (italic “Halden” 22px Bodoni, Work / Index / Visit, wine studio@halden.studio). Lead: kicker “THE STUDIO · OSLO”, heading “Six people, one room”, right meta “HOVER A PORTRAIT”. Six portraits fill the remaining height. Names visible; role, bio, and email hidden (max-height:0; opacity:0).
  2. Hover a card: .veil opacity 0 → 1 over 320ms; .more max-height 0 → 140px, opacity 0 → 1, translateY 8px → 0 over 320ms / 180ms. The SVG stays put underneath.
  3. Focus-within a card (tab onto the card, then onto the email): same reveal. A 2px wine outline, 3px offset, sits on the card.
  4. Hover the email: colour --gold. The underline is gold at rest (text-decoration-color: var(--gold), offset 3px).
  5. Leave the card: veil and .more reverse on the same clocks.
  6. Tab order: brand → Work → Index → Visit → studio email → Mira card → Mira mail → Jonas card → Jonas mail → … through Henrik.
  7. Reduced motion: veil and .more durations become 1ms; translate is forced to none. Reveal still happens.

Structure

1280 × 800
┌──────────────────────────────────────────────────────────────────────┐
│ Halden          Work   Index   Visit           studio@halden.studio  │ 56
│ THE STUDIO · OSLO                              HOVER A PORTRAIT      │
│ Six people, one room                                                 │
│ ┌──────────────┬──────────────┬──────────────┐                       │
│ │ Mira         │ Jonas        │ Amara        │                       │
│ │ (wine/gold   │ (diag split) │ (offset sq)  │                       │
│ │  circles)    │              │              │                       │
│ ├──────────────┼──────────────┼──────────────┤  gap 14               │
│ │ Piet         │ Clara        │ Henrik       │                       │
│ │ (ellipse +   │ (5 bands +   │ (gold fall   │                       │
│ │  triangle)   │  circle)     │  + circle)   │                       │
│ └──────────────┴──────────────┴──────────────┘                       │
└──────────────────────────────────────────────────────────────────────┘
  pad 56
  • <nav>: italic .brand, three .links a, .cta mailto.
  • <section aria-label="Studio team">: .lead + .grid.
  • .grid: repeat(3, 1fr) / 1fr 1fr, gap 14px, flex:1.
  • Each person is a <figure class="card" tabindex="0">: aria-hidden SVG, .veil, <figcaption> with h2 + .more (.role, .bio, .mail).

People, exact copy:

NameRoleBioEmailPortrait
Mira SolbergPartner · typeCuts the wordmarks. Taught letterpress at KHIO for six years.mira@halden.studioWine diagonal + gold radial circle + dark circle + 2px cream rule
Jonas KeelPartner · spacePlans the rooms. Last year: a reading hall in Bergen with no hanging signs.jonas@halden.studioThree-stop linear (ink → wine → gold) + dark polygon + cream circle
Amara NdiayeArt directionHolds the still-life days. Shoots on 4×5 when the budget allows.amara@halden.studioThree offset squares (wine, gold, cream) on #2E1A20
Piet RaskPrintRuns the Heidelberg on Thursdays. Keeps a book of every spoilage sheet.piet@halden.studioRadial ellipse + wine triangle on #3A2018
Clara VossWritingWrites the long captions. Edited the Halden index for issues 11–18.clara@halden.studioFive 56px bands (ink, wine, rose, gold, cream) + cream circle
Henrik DahlProductionSchedules the press and the couriers. Answers the studio line after 16:00.henrik@halden.studioGold-to-ink vertical gradient + wine circle + two cream hairlines

SVG viewBox is 0 0 400 280 with preserveAspectRatio="xMidYMid slice" so the composition crops, it does not letterbox.

Tokens

:root {
  --bg: #f3ebe3;          /* page */
  --ink: #1a1412;         /* type */
  --ink-2: #6a5a52;       /* nav links */
  --ink-3: #9a8a80;       /* meta */
  --line: #ddd2c6;
  --wine: #5c2430;        /* kicker, focus, card fallback */
  --gold: #b8954a;        /* role, mail hover, portrait metal */
  --cream: #f7f0e8;       /* name + bio on the veil */
  --veil: rgba(26, 20, 18, .78);

  --serif: "Bodoni Moda", Georgia, serif;
  --sans: "Tenor Sans", Georgia, serif;

  --nav-h: 56px;
  --pad: 56px;
  --gap: 14px;

  --t-fast: 180ms;
  --t-reveal: 320ms;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
}

Tenor Sans is a display sans that sits next to Bodoni; do not substitute Inter.

Typography

RoleFamilySizeWeightLine-heightTrackingCase
BodyTenor Sans16px4001.40sentence
BrandBodoni Moda italic22px5001−0.02emsentence
Nav links / CTATenor Sans13px4001+0.08–0.1emUPPERCASE
KickerTenor Sans12px4001+0.16emUPPERCASE
HeadingBodoni Moda36px5001−0.02emsentence
MetaTenor Sans13px4001+0.08emUPPERCASE
NameBodoni Moda italic22px5001−0.015emsentence
RoleTenor Sans12px4001+0.12emUPPERCASE
Bio / mailTenor Sans13px4001.40sentence

Bodoni optical size: "opsz" 22 on the brand and names, "opsz" 36 on the heading.

Motion

ElementTriggerPropertyFrom → ToDurationEasing
.veilhover / focus-withinopacity0 → 1320ms--ease
.moresamemax-height0 → 140px320ms--ease
.moresameopacity0 → 1180ms--ease
.moresametranslateY8px → 0320ms--ease

No portrait Ken Burns. No stagger on load. Reduced motion: 1ms durations, no translate.

States

  • Card rest: portrait at full chroma, name on a transparent caption, .more collapsed, veil at 0.
  • Card hover / focus-within: veil 78% ink, .more open, wine 2px outline when focused.
  • Mail hover: text --gold.
  • Nav link hover: colour --ink.
  • Nav / brand / CTA focus-visible: 2px wine, 3px offset.
  • Cards have tabindex="0" so the portrait itself is a stop, not only the mail link.

Accessibility

  • Section labelled aria-label="Studio team".
  • Portraits are aria-hidden="true"; the name in h2 is the accessible title.
  • Emails are real <a href="mailto:…">. Do not fake them with click handlers.
  • Keyboard users get the same reveal via :focus-within when they tab to the card or the mail.
  • Contrast on the veil: cream #F7F0E8 on rgba(26,20,18,.78) over dark portraits is ≥ 8:1. Gold role on the veil is ≥ 4.6:1 at 12px uppercase. Wine kicker on cream is ~7:1.
  • Hit targets: each card is a large region; mail line is 13px with underline — keep the padding in .more so the tap row is ≥ 40px tall (10px margin-top + 13px + caption padding).

Responsive rules

  • ≥ 1280: 3×2, pad 56px, heading 36px, gap 14px.
  • 1024–1279: pad 28px, heading 28px. Grid stays 3×2.
  • 768–1023: nav links hide; grid becomes 2×3. Cards keep the same reveal.
  • < 640: 2×3 remains. Heading 26px. Do not drop to a single column in the demo frame; a host page may do so below 480.

Acceptance checklist

  • First frame shows six geometric portraits and six italic names; roles and emails are not visible.
  • Hover or focus on a card reveals role, one-line bio, and mailto over 320ms with a 78% ink veil.
  • Exactly these six people and emails; all addresses end in @halden.studio.
  • Portraits are inline SVG gradient compositions — no raster, no initials discs, no stock faces.
  • Type pairing is Bodoni Moda + Tenor Sans only.
  • Grid is 3×2 at 1280, gap 14px, filling the height under the lead.
  • Focus-visible / focus-within draws a 2px #5C2430 ring, 3px offset.
  • prefers-reduced-motion: reduce reveals instantly; content is the same.
  • No JavaScript, no emoji, no dummy copy.
  • Demo fills 1280×800 and starts with the piece header comment.

Implementation notes

Reveal with max-height, not display. display:none kills the fade and drops the name:

.more { max-height: 0; opacity: 0; overflow: hidden; transform: translateY(8px);
        transition: max-height 320ms var(--ease), opacity 180ms var(--ease), transform 320ms var(--ease); }
.card:hover .more, .card:focus-within .more { max-height: 140px; opacity: 1; transform: none; }

Veil is a sibling, not a filter on the SVG. Filtering the SVG would muddy the gold. Stack: SVG (bottom) → .veil (z 1) → figcaption (z 2).

Slice, don’t letterbox. Each SVG uses preserveAspectRatio="xMidYMid slice" and the card is overflow:hidden so the composition crops as the cell grows.

Common mistakes: photograph avatars; a purple-to-blue portrait gradient; revealing the bio at rest so the hover has nothing to do; using Inter under Bodoni; article + figcaption without a figure (invalid). Cards must be <figure class="card">.

Details

Palette
Type
Bodoni Moda · Tenor Sans
Motion
Subtle motion
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Don't Board Me

A service with a next step. The headline can shout. The booking stays plain.

All sources