01 · Overview
Care that feels like calm
Most clinic websites are anxious. Red alert bars, phone numbers in three sizes, a portal login shouting from the corner. Norra's promise is the opposite — forty-minute appointments, the same doctor every visit, a waiting room you'll barely see — and a page that contradicts that promise undoes it before anyone books.
So the entire build is an exercise in lowering the temperature: eucalyptus and pine instead of medical blue, twenty-pixel radii on everything, an eighty-millisecond stagger between siblings so content arrives rather than snaps, and a stated floor of four hundred milliseconds — nothing on this page is allowed to be faster than that.
02 · The idea
Three steps, no phone trees
The clinic's differentiator is procedural, not clinical: booking is three steps and you keep your doctor. The page had to make that legible in one screen, which meant the structure had to carry it — not a paragraph claiming it.
Pick your track
General practice, paediatrics, dental, cardiology checkups, vaccinations, telehealth — six services, six plain cards, no jargon.
Meet your doctor
Named physicians with real specialities, so the promise of continuity has faces attached to it.
Leave with a plan
The booking form is the last thing on the page, and it asks six questions rather than twenty.
03 · Goals
What the page had to do
Read at a glance and in depth
Six sections — services, how it works, team, stories, FAQ, appointment — each usable on its own if that is all somebody reads.
Answer the front-desk questions
The FAQ is titled after the receptionist I imagined fielding them, and behaves like a conversation: one answer open at a time.
Prove the numbers
The concept imagines a practice with the numbers to back up its promise: 24,000 patients, 98% of them assigned to a named doctor, a six-minute average wait — counted up once, on entry, never on a loop.
Pass contrast without a plugin
A pale palette on a near-white ground is where accessibility quietly fails, so every borderline value carries its measured ratio in a comment.
04 · Build order
How it was built
The palette, contrast-first
Twenty-two custom properties. Eucalyptus for surfaces, pine for anything that has to be read, sand for the calm blocks — and a dedicated field-border token proven against the 3:1 non-text requirement.
Twenty-six inline SVGs
Every icon, the hand-drawn scribble under the headline and the brand mark are markup. No icon font, no sprite sheet, no extra request.
The stagger
An 80-millisecond delay between siblings, applied in CSS from an index rather than scheduled in JavaScript — the reveal costs nothing per frame.
The accordion
Height animated properly, using
transitionendto settle back toautoso a long answer never clips at a narrow width.Forms with a voice
Inline validation, a polite live region for the confirmation, and focus moved to it — so the outcome is announced, not just displayed.
05 · Architecture
Three files
├── index.html 37.1 KB — 6 sections, 8 photographs, 26 inline SVG
├── styles.css 30.1 KB — 22 custom properties, 5 media queries, ratios in comments
└── script.js 9.8 KB — 284 lines: header · nav · reveals · counters · accordion · forms
The stack
- Semantic HTML
- CSS Grid
- Inline SVG
- IntersectionObserver
- transitionend
- Epilogue
- Karla
Accessibility
- aria-expanded
- aria-controls
- aria-live
- WCAG 1.4.11
- Focus management
- Reduced motion
06 · What fought back
The things people ask
This page borrows its own project's structure: the problems are set as questions and answers, because that is how the clinic answers questions.
How do you animate height without it clipping? height: auto cannot be transitioned, and a fixed pixel height clips the moment the answer wraps to another line at a narrow width.
Animate to the measured scrollHeight, then listen for transitionend and set height: auto. The transition is smooth and the settled state reflows freely.
Why is there a separate token just for field borders? Form borders are non-text content and still require 3:1. On a near-white ground the calm grey everybody reaches for lands around 1.8:1 — invisible to a lot of people, and a straight WCAG failure.
A dedicated --field-border at 3.13:1, chosen deliberately and annotated in the stylesheet with the criterion it satisfies, so nobody softens it later by accident.
Doesn't an 80ms stagger make the page feel slow? Staggered reveals are usually a vanity effect, and on a clinic site they risk delaying the one thing somebody came for.
The stagger applies only within a row of siblings, never between sections, and the total for a six-card row is under half a second. Nothing is ever hidden waiting for its turn.
What happens to the count-ups for a screen reader? A number animating from 0 to 24,000 is announced continuously by some assistive technology, which is both useless and hostile.
Each counter runs once from an observer that unobserves itself, is not inside a live region, and prints its final value immediately under prefers-reduced-motion.
07 · Worth keeping
The parts worth keeping
The hand-drawn underline
An inline SVG scribble under "like calm" — the one imperfect line on an otherwise very tidy page, and the reason it reads as human.
Three steps, no phone trees
The booking explainer laid out as three numbered cards with real copy, not icons with one-word labels.
Freya's FAQ
Named after the receptionist I imagined fielding these questions, which changes the register of every answer beneath it.
Ratios in the source
Contrast measurements written beside the values they justify. The next person to touch the palette inherits the reasoning, not just the hex.
08 · Run it live
Run it here
The real build, embedded and running. It loads on demand — nothing is fetched until you ask for it.
Skip the live preview
The real build, running in the page. The live preview is an embedded page; press Escape to leave it.
Open full screen ↗




