Design Lounge
Install

Nº 288 of 520

Laptop browser mockup

A CSS laptop whose lid swings open on load to reveal a desktop and a tabbed browser window; tabs switch real pages, finish toggles silver or space.

Preview

Open full size
1280 × 800 · 19.3 KB · 244 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 "Laptop browser mockup" (piece mockup-laptop-browser) 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

189 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 front-view laptop mockup, the fictional Torva Book 14, for presenting a website in a case study. It is drawn in CSS: an aluminium lid with a notch, a black bezel, a dark hinge strip and a thin base with a thumb scoop. On load the lid swings up from flat to upright in 1.5s, then the screen boots: a desktop wallpaper and menu bar fade in, and a browser window rises into place. The browser has three working tabs that swap three small pages of a type studio called Brask and update the address bar. The stage is a pale sage wall meeting a desk surface exactly at the base of the laptop. The detail worth copying is the two-faced lid: the screen face and an aluminium back face share one 3D plane with backface-visibility: hidden, so mid-swing you see the right side at every angle.

Reference behaviour

  1. Load: the lid starts at rotateX(-90deg) (lying flat toward the viewer, back face up) and opens to 0deg over 1500ms with expo-out easing.
  2. At 1000ms the screen gets class on: the wallpaper and menu bar fade in over 500ms; the browser window fades in and rises from translateY(1.2cqw) scale(.97) over 450ms/600ms with a 150ms delay.
  3. First frame after the animation: Index tab selected, address bar reads brask.studio/work.
  4. Clicking a tab (or ArrowLeft/ArrowRight/Home/End with a tab focused) selects it, swaps the page with a 280ms fade and 0.8cqw rise, and sets the path: /work, /pricing, /changelog.
  5. Hovering an unselected tab tints it rgba(255,255,255,.4) and reveals its close glyph at 60% opacity.
  6. Finish toggle (Silver, Space) recolours the lid rim, the lid back, and the base gradient. The bezel stays black.
  7. “Open lid again” resets the screen to black, restarts the lid animation and the boot sequence.
  8. A polite live region announces “Space finish” or “Lid opening”.

Structure

1280 × 800, wall #cfd5d0
┌──────────────────────────────────────────────────────────────────────┐
│ Torva Book 14                         [● SILVER|● SPACE] [↻ OPEN LID]│ 24px top
│ Browser mockup · brask.studio…                                        │
│                  ┌───────────── lid 90% of rig ─────────────┐         │
│                  │ Torva File Edit View History  ▂notch▂  Sun 4 Oct 9:41
│                  │   ┌─● ● ● [Brask, Index ×][Pricing][Changelog] + ┐│
│                  │   │ ‹ › 🔒 brask.studio/work                  ··· ││
│                  │   │  Brask              Work Type Studio Contact ││
│                  │   │  Type and tools for slow, careful reading.   ││
│                  │   │  [ Aa ]  [ ◯ ]  [ ||||| ]                    ││
│                  │   └──────────────────────────────────────────────┘│
│                  └──────────────────────────────────────────────────┘│
│                     ▀▀▀▀▀▀▀▀▀▀▀▀ hinge 84% ▀▀▀▀▀▀▀▀▀▀▀▀              │
│              ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ base 100% ▔▔▔ scoop ▔▔▔▔▔▔▔▔▔▔▔         │
│ ════════════════ desk surface from the base line down ══════════════ │
└──────────────────────────────────────────────────────────────────────┘
  • header.top: caption block and a controls row (div role="group" aria-label="Finish" with two aria-pressed buttons, and a replay button).
  • main.stage centres .rig: width min(900px, 100vw − 48px, (100vh − 190px) × 1.42), container-type: inline-size, perspective: 2400px, perspective-origin: 50% -40% (eye above the laptop).
  • .rig::before is the desk: from 100% − 1.2cqw downwards, extended left and right by 100vw, clipped by .stage { overflow: hidden }.
  • .lid (90% width, aspect 1.545, origin bottom centre) contains .back (rotated 180° on X) and .face > .bezel > .notch + .screen.
  • .screen holds .desk-ui (wallpaper + menu bar) and .win (browser chrome + .pages).
  • Tabs are role="tablist" with button role="tab"; each page is a section role="tabpanel".
  • Below the lid: .hinge (84% width), .base (full rig width), .shadow (blurred ellipse).

Tokens

:root {
  --wall: #cfd5d0;
  --desk: #b4bcb5;  --desk-2: #a6afa8;
  --ink: #131714;   --ink-2: #3f4842;  --ink-3: #5c665f;
  --line: #a9b2ab;
  --accent: #2346d8;   /* cobalt: in-page accent and focus ring */
  --paper: #f3f1ec;    /* browser page */
  --sans: "Bricolage Grotesque", system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, monospace;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --open: 1500ms;
  /* aluminium ramp — Silver */
  --al-1: #f2f3f4; --al-2: #d6d8db; --al-3: #a9adb2; --al-4: #7d8187;
}
[data-finish="space"] { --al-1: #9a9ea4; --al-2: #5f6368; --al-3: #45484c; --al-4: #2a2c2f; }

Geometry in cqw of the rig: lid radius 2.4cqw top / 0.9cqw bottom, rim 0.32cqw, bezel padding 1.25cqw sides and top / 1.6cqw bottom, notch 10% × 2.5cqw with 0.9cqw bottom radii, hinge 0.75cqw tall, base 1.55cqw tall with a 13% × 55% scoop. Browser: tab strip 3.4cqw, tabs 2.6cqw tall, address field 2.4cqw.

Typography

RoleFamilySizeWeightLine-heightTrackingCase
Caption titleBricolage Grotesque22px8001.1−0.01em—
Caption sub, controlsJetBrains Mono12px4001.40.02–0.06emcontrols uppercase
Menu barBricolage Grotesque1cqw500 / 800 for app name10—
Tab labelBricolage Grotesque1cqw50010—
AddressJetBrains Mono1.05cqw40010lowercase
Page headlineBricolage Grotesque4.6cqw8000.95−0.035emsentence
Page bodyBricolage Grotesque1.25cqw4001.450sentence
Changelog datesJetBrains Mono1cqw40010—

Motion

ThingTriggerPropertyFrom → toDuration / easingReduced motion
Lidload, replayrotateX−90° → 0°1500ms expo outlid starts open
Desktop1000ms after startopacity0 → 1500ms standardinstant
Windowsameopacity, translateY, scale0, 1.2cqw, .97 → 1, 0, 1450ms / 600ms expo out, 150ms delayinstant
Page swaptab selectopacity, translateY0, .8cqw → 1, 0280ms / 400ms expo outinstant
Tab hoverpointerbackgroundtransparent → white 40%160mssame
Finishtogglecoloursswapinstantsame

States

  • Tab selected: --paper background joining the address bar, dark text, close glyph at 60%. Unselected: #5d5a53 text, close hidden. Focus-visible: 2px cobalt outline, offset −2px so it stays inside the strip.
  • Finish button pressed: --ink fill, wall-coloured text. Hover (unpressed): 6% ink tint.
  • Replay hover: 6% ink tint.
  • Booting: screen is black until the lid has mostly opened.
  • Pricing tier “Studio” is the highlighted card: dark fill, cobalt button.

Accessibility

  • Tabs follow the ARIA tabs pattern: roving tabindex, ArrowLeft/ArrowRight wrap, Home/End jump, aria-selected, aria-controls, panels aria-labelledby their tab.
  • Finish buttons use aria-pressed inside a labelled group.
  • The rig has role="group", aria-roledescription="device mockup", and a label that names the finish.
  • The lid back, menu bar and decorative browser icons are aria-hidden.
  • Contrast: #131714 on #cfd5d0 ≈ 13:1; #5c665f captions ≈ 4.6:1; page body #56534c on #f3f1ec ≈ 7:1. In-screen text is miniature by nature; it is a picture of a website, and the panels still carry real headings for screen readers.

Responsive rules

  • ≥1280: laptop up to 900px wide, limited by height: (100vh − 190px) × 1.42.
  • 1024 / 768: same composition, laptop narrows; controls stay top right and wrap under the caption when needed.
  • <640: caption title 18px, controls wrap to their own row, laptop 100vw − 48px (327px at 375). Everything inside scales with cqw.
  • The desk band is anchored to the laptop base, not to the viewport, so the laptop always sits on the desk.
  • Never let the desk create horizontal scroll: it extends 100vw each side and the stage clips it.

Acceptance checklist

Always

  • The device is CSS only; the browser contents are live HTML.
  • The lid has a screen face and an aluminium back face, both backface-visibility: hidden, rotating together around the bottom edge.
  • The screen boots only after the lid is mostly open.
  • Tabs implement the ARIA tabs pattern and update the address bar.
  • A replay control re-runs the opening.
  • Every in-device size uses container units of the rig.
  • Reduced motion: lid starts open, no fades, tabs still swap.
  • No horizontal scroll at 375px.

This demo

  • Device name “Torva Book 14”, finishes Silver and Space.
  • Tabs “Brask, Index”, “Pricing, Brask”, “Changelog” with paths /work, /pricing, /changelog on brask.studio.
  • Menu bar “Torva File Edit View History” and “Sun 4 Oct 9:41”.
  • Index headline “Type and tools for slow, careful reading.” with three cards (Aa, ring, barcode stripes).
  • Pricing €40 / €180 / €600; Changelog v3.2, v3.1, v3.0.
  • Lid opens over 1500ms; boot at 1000ms.

Implementation notes

Two-faced lid. The back face is rotated 180° on X so that when the lid lies flat toward the viewer, the viewer (eye above) sees aluminium; as it rises, the screen face takes over.

.rig { perspective: 2400px; perspective-origin: 50% -40%; container-type: inline-size; }
.lid { width: 90%; margin: 0 auto; aspect-ratio: 1.545; transform-origin: 50% 100%; transform-style: preserve-3d; }
.lid.anim { animation: open var(--open) var(--ease-out) both; }
@keyframes open { from { transform: rotateX(-90deg); } to { transform: rotateX(0); } }
.face, .back { position: absolute; inset: 0; backface-visibility: hidden; border-radius: 2.4cqw 2.4cqw .9cqw .9cqw; }
.back { transform: rotateX(180deg); background: radial-gradient(60% 80% at 50% 40%, var(--al-1), var(--al-2) 70%, var(--al-3)); }

Replay without a stuck animation. Remove the class, force a reflow, add it again; restart the boot timer.

function open() {
  clearTimeout(boot); screen.classList.remove('on');
  lid.classList.remove('anim'); void lid.offsetWidth;
  if (reduce) { screen.classList.add('on'); return; }
  lid.classList.add('anim');
  boot = setTimeout(() => screen.classList.add('on'), 1000);
}

Desk that meets the base. Draw the desk from the rig, not the body, so it always lines up:

.rig::before { content: ""; position: absolute; left: -100vw; right: -100vw; top: calc(100% - 1.2cqw);
  height: 100vh; background: linear-gradient(var(--desk), var(--desk-2)); z-index: -2; }
.stage { overflow: hidden; }

Common mistakes:

  • Rotating the lid with rotateX(90deg): that folds it away from the viewer, through the back of the base.
  • Forgetting perspective-origin above centre; a dead-centre eye shows the lid as a line until the last frames.
  • Using an iframe for the browser page; it adds a document, breaks cqw scaling and blocks the boot fade.
  • Drawing real browser branding or real traffic-light colours. The window dots here are neutral grey.
  • Body-level overflow: hidden alone; scrollWidth still counts the desk. Clip on the stage.

Details

Palette
Type
Bricolage Grotesque · JetBrains Mono
Motion
Rich motion
Build
Multi-step
Designed bySusan AcharyaKathmandu

Studied

Icon Museum

Let the icons be the only colour. Give each one a shelf, a lift on hover, and a quiet label.

All sources