Design Lounge
Install

Nº 151 of 520

Studio hours and booking

Ceramics-studio contact: a two-line giant email, a seven-day hours table, a Lisbon timezone note, and a Book 30 min dialog with name, email, note and a focus trap.

Preview

Open full size
1280 × 800 · 8.9 KB · 144 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 "Studio hours and booking" (piece contact-booking-hours) 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

170 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 visit page for Clay & Co, a fictional ceramics studio in Alcântara, Lisbon. Left: a kicker, a giant two-line italic email, a short hint, and a “Book 30 min” button. Right: an “Hours this week” table for Monday–Sunday and a timezone note. The button opens a native <dialog> with three fields (name, email, note). showModal() supplies the focus trap, Esc-to-close, and inert background. The studio feeling is clay dust and a fired-brick accent, not a marketing form with a map.

Reference behaviour

  1. Initial state: clay ground #ebd8cc. 64px nav: italic 28px “Clay & Co”, links Work / Studio / Visit (aria-current on Visit). Main is a two-column split with a 1px hairline between.
  2. Email hello@ / clayandco.studio is 64px italic Cormorant, a mailto: link. Hover or focus draws a 2px accent underline from left over 320ms expo-out (background-size 0 → 100%).
  3. Primary button “Book 30 min” is 48px tall, brick #a34b38, cream type, 4px radius. Hover darkens to #8d3e2e. Active scales to 0.98.
  4. Hours table: caption “Hours this week” as italic 28px serif. Header row DAY / OPEN in 11px uppercase. Seven body rows, 11px vertical padding, 1px hairline between. Mon–Thu 10:00 – 18:00, Fri 10:00 – 16:00, Sat and Sun Closed in italic --ink-3. Tabular nums, times right-aligned.
  5. Timezone note under the table: 16px clock SVG + “Times in Lisbon, Europe/Lisbon (WEST, UTC+1). Kiln days may close the floor at 15:00; we will write if your slot moves.”
  6. Clicking Book 30 min: form.reset(), remove .ok, dialog.showModal(), focus the name input. The panel is 420px, dust #f4ece6, 8px radius, scales from 0.96 and fades in over 280ms. Backdrop rgba(46,32,24,.42).
  7. Fields: Name (text, required, autocomplete=name), Email (email, required), Note (textarea, 3 rows, placeholder “What you would like to see”). Labels 11px uppercase above 1px bottom-border inputs. Focus turns the border --accent.
  8. Submit: prevent default, add .ok to the form (hides fields and actions, shows “Request sent. We will write within a working day.” in --ok). After 1200ms the dialog closes. Cancel, ×, Esc, and a click on the dialog element (the scrim) all close. On close, focus returns to Book 30 min.
  9. Tab inside the open dialog cycles name → email → note → Cancel → Send → × → name. Page content behind is inert.
  10. Reduced motion: dialog and underline transitions drop to 1ms.

Structure

1280 × 800
┌────────────────────────────────────────────────────────────┐
│ Clay & Co                    Work  Studio  Visit           │ 64
├─────────────────────────────┬──────────────────────────────┤
│ STUDIO · APPOINTMENTS       │ Hours this week              │
│ hello@                      │ DAY              OPEN        │
│ clayandco.studio            │ Monday     10:00 – 18:00     │
│ A ceramics studio in        │ …                            │
│ Alcântara. Write anytime…   │ Saturday          Closed     │
│ [ Book 30 min ]             │ Sunday            Closed     │
│                             │ ⏱ Times in Lisbon, WEST…    │
└─────────────────────────────┴──────────────────────────────┘
          dialog 420, centred, when open
  • <nav aria-label="Primary"> + <main> CSS grid 1.15fr .85fr, padding 0 56px.
  • .left: kicker, <a class="mail"> with a <br>, hint <p>, <button class="book" id="open">.
  • .right aria-labelledby="hours-h": <h2 id="hours-h">, <table> with thead + seven rows, .tz note.
  • <dialog id="dlg" aria-labelledby="dlg-h"> wrapping <form class="dlg" method="dialog" id="form">: header (h2 + ×), helper p, .fields (three labels), .done status, .acts (Cancel + Send).
  • Click-on-scrim works because padding lives on .dlg, not on dialog. e.target === dlg closes.

Tokens

:root {
  --clay: #ebd8cc;             /* page */
  --dust: #f4ece6;             /* dialog surface */
  --ink: #2e2018;
  --ink-2: #6d5648;
  --ink-3: #9a8170;
  --line: rgba(46, 32, 24, .14);
  --accent: #a34b38;           /* button, underline, focus */
  --accent-ink: #f7efe9;
  --ok: #3f6b4e;               /* sent status */
  --serif: "Cormorant Garamond", Georgia, serif;
  --sans: "Work Sans", system-ui, sans-serif;
  --gutter: 56px;
  --r: 4px;
  --t-micro: 160ms;
  --t-dlg: 280ms;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --expo: cubic-bezier(.16, 1, .3, 1);
}

Typography

RoleFamilySizeWeightLine-heightTrackingCase
EmailCormorant Garamond64pxitalic 5000.92−0.03emlowercase
Logo / hoursCormorant Garamond28pxitalic 5001−0.02emTitle
Dialog titleCormorant Garamond28pxitalic 5001−0.02emTitle
Body / hintWork Sans14–15px4001.50sentence
Table bodyWork Sans14px500/4001.450Title / nums
Kicker / labelsWork Sans11px5001+0.1–.16emUPPERCASE
ButtonWork Sans14px50010Title

Motion

ElementTriggerPropertyFrom → ToDurationEasing
Email rulehover/focusbackground-size0 2px → 100% 2px320ms--expo
Book buttonhoverbackgroundaccent → #8d3e2e160ms--ease
Book buttonactivetransform1 → 0.98160ms--ease
Dialog panelopenscale, opacity0.96 + 0 → 1 + 1280ms--expo / --ease
Backdropopenopacity0 → 1280ms--ease

Reduced motion: those transitions become 1ms. Do not disable showModal().

States

  • Nav current: aria-current="page", colour --ink. Hover same.
  • Email hover/focus: accent underline draws.
  • Book hover: #8d3e2e. Active: scale 0.98.
  • Closed days: italic --ink-3 in the hours cell.
  • Input focus: bottom border --accent.
  • Form .ok: .fields and .acts display: none; .done shows.
  • Focus-visible: 2px accent outline, 3px offset.

Accessibility

  • Use native <dialog> + showModal() for the trap. Do not hand-roll Tab cycling unless the host stack has no dialog primitive.
  • Dialog labelled by aria-labelledby="dlg-h". Close button has aria-label="Close". Success copy has role="status".
  • Esc, scrim click, Cancel and × all close. Focus returns to #open.
  • Hours are a real <table> with a <thead>, not a definition list pretending to be a grid.
  • Email is a real mailto: link. Button is 48px tall. Dialog actions are 40px tall.
  • Contrast: ink on clay exceeds 9:1. Accent-ink on accent exceeds 4.5:1. --ink-3 is captions and closed hours, not body.

Responsive rules

  • ≥ 1280: two columns as specified, email 64px on two lines.
  • 1024–1279: email 52px. Right padding 40px.
  • 768–1023: stack. Hours move under the email. Dialog still 420px or min(420px, 100vw − 32px).
  • < 640: email 40px. Nav keeps the mark and Visit only. Book button full width. Table can scroll-x if needed; prefer wrapping the day name.

Acceptance checklist

  • Email is two lines, 64px italic Cormorant, mailto:hello@clayandco.studio.
  • Hours table lists seven days with Fri closing at 16:00 and weekend Closed in italic.
  • Timezone note names Lisbon / Europe/Lisbon / WEST UTC+1.
  • Book 30 min opens a 420px dialog with name, email, note.
  • Focus is trapped inside the open dialog; Tab never reaches the page behind.
  • Esc, ×, Cancel, and scrim click close the dialog; focus returns to Book 30 min.
  • Submit shows the status line, then closes after 1200ms.
  • Focus rings are 2px #a34b38 with a 3px offset.
  • Reduced motion keeps the dialog usable with 1ms transitions.
  • Palette is clay #ebd8cc and brick #a34b38, not orange brutalist and not a map split.
  • Only Cormorant Garamond and Work Sans load.

Implementation notes

Let the platform trap focus. showModal() is the whole overlay implementation:

open.addEventListener('click', () => {
  form.classList.remove('ok');
  form.reset();
  dlg.showModal();
  form.querySelector('input').focus();
});
dlg.addEventListener('click', (e) => { if (e.target === dlg) dlg.close(); });
dlg.addEventListener('close', () => open.focus());

Underline as a background gradient, so it can draw without wrapping a span:

.mail {
  background: linear-gradient(var(--accent), var(--accent)) 0 100% / 0 2px no-repeat;
  transition: background-size 320ms cubic-bezier(.16, 1, .3, 1);
}
.mail:hover, .mail:focus-visible { background-size: 100% 2px; }

Padding on the inner form, not the dialog, so a click on the dialog box itself is a scrim click. Putting padding on dialog makes e.target === dlg fail.

Return focus after the 1200ms auto-close. dlg.close() fires close, which already points at #open. Do not call open.focus() from the timeout as well or it races if the user closed early.

Common mistakes: a custom focus trap that forgets Shift+Tab. Using alert. Putting Saturday hours as 10–18. One-line email at 72px that overflows the left column at 1280. Forgetting autocomplete on name and email.

Details

Palette
Type
Cormorant Garamond · Work 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