01 · Overview
A magazine that serves dinner
For this concept, I imagined Ember & Oak as a wood-fired restaurant that has been cooking over oak in Portland since 2016 — the kind of place where the room already has a voice, and most websites would keep talking over it. So the page stops performing. It sets the restaurant the way a food magazine would — a masthead, a standfirst, ruled columns, plates that run to the edge of the paper, and a menu you read rather than scroll past.
Everything on it is typographic. There is no hero video, no parallax showreel, no scroll-jacking. The only motion in the entire build is an eight-pixel rise on section entry and a slow drift on the masthead photograph, and both stop dead if the visitor asks for reduced motion.
02 · The idea
Seasonal, unhurried, on paper
The concept was fixed by one line of copy from the restaurant itself: seasonal menus written by the Willamette Valley, cooked over oak, served unhurried. Unhurried became the design constraint. Nothing on the page may hurry the reader — not the transitions, not the type, not the calls to action.
That produced three rules the whole build obeys. Type leads and images support, never the reverse. Every animation is slower than the visitor expects. And the reservation form sits at the end of the story, not in a sticky bar that follows you down the page.
Type leads
Fraunces at variable optical size for display, Libre Franklin for text. The photography is set into the columns like plates in a magazine.
Slower than expected
A single easing curve — cubic-bezier(0.33, 1, 0.68, 1) — and reveals that take most of a second to land.
No pressure
One reservation form, at the foot of the page, after the menu, the chef, the room and the reviews have made the case.
03 · Goals
What the page had to do
Make the menu readable
Starters, mains and desserts as three real sections with dot leaders and prices — the printed card, on screen, at any width.
Sell the room without a slideshow
Twelve photographs placed into the grid at the moment the copy earns them, rather than a carousel nobody advances.
Take a booking in one screen
Six fields, a live region for the confirmation, and focus moved to the message so a screen-reader user hears it.
Stay under a hard weight budget
No framework, no carousel library, no icon font. The whole interaction layer had to fit in a few kilobytes of hand-written JavaScript.
04 · Build order
How it was built, in order
The page was written the way it reads: structure first, then the type system, then colour, then the two pieces of motion. Nothing was styled before it was marked up, and nothing moved before it was legible standing still.
Semantic skeleton
Every beat is a
<section>with anaria-labelledbypointing at its own heading —#story,#menu,#chef,#gallery,#private,#press,#reservations. The document outline was correct before a single rule was written.The token block
Twenty-five custom properties on
:root: seven colours, five ink alphas, two families, one easing curve, one masthead height. Contrast ratios are recorded next to the values that needed checking.The column grid
CSS Grid with a named measure column, so long-form copy is capped at a comfortable line length while plates and the menu span wider tracks.
Fifteen breakpoints of proof
Fifteen media queries, each written against a real reflow problem rather than a device name.
Under six kilobytes
Mobile navigation, scroll reveals, masthead shadow, hero drift and the reservation form — 164 lines, one IIFE, one
matchMediaguard at the top.
05 · Architecture
Three files, nothing else
There is no bundler, no transpiler and no node_modules. The site is three files served as written; the browser is the only runtime it has ever needed.
├── index.html 32.6 KB — 7 sections, 12 photographs, 0 inline SVG
├── styles.css 30.2 KB — 25 custom properties, 15 media queries, 2 reduced-motion blocks
└── script.js 5.8 KB — 164 lines: nav · reveals · masthead · parallax · form
The stack
- Semantic HTML
- CSS Grid
- Custom properties
- IntersectionObserver
- matchMedia
- Fraunces
- Libre Franklin
- Vanilla JS
Accessibility surface
- aria-expanded
- aria-controls
- aria-labelledby
- aria-live
- aria-hidden
- Escape to close
- Focus moved on submit
06 · What fought back
Problems, and what beat them
A printed menu uses dot leaders to carry the eye from dish to price. On the web that is usually faked with a row of full stops, which screen readers then read aloud, one by one.
The leader is drawn with a repeating background gradient on a flex spacer. It is decoration, not content, so assistive technology never encounters it — and it stretches perfectly at any width.
Cream on cream is the whole palette, and low-contrast palettes are where accessibility quietly fails — particularly on form borders, which are non-text and still have a 3:1 requirement.
Two accents instead of one. #C4552D paints rules and fills; a darkened #A8431F is reserved for accent text at 5.54:1 on cream. Field borders got their own token at 3.6:1, and the ratio is written in the stylesheet beside it.
Reveal-on-scroll animations hide content by default. If the script fails — or never runs — the page is blank paper.
The hidden state is scoped behind a class the script adds itself. No script, no class, no hidden content: the page renders in its settled end state and reads perfectly.
A parallax masthead is the easiest way to make a restaurant site feel cheap, and the easiest way to make it stutter on a mid-range phone.
One transform, driven by scrollY, at a coefficient low enough to register as depth rather than movement — and skipped entirely under prefers-reduced-motion.
07 · Worth keeping
The parts worth keeping
The Autumn Menu
Three courses set as a printed card, dot leaders and all, that reflows to a single readable column on a phone without losing the price alignment.
From the Dining Room
A photo grid that breaks its own rhythm — different spans, different heights — so it reads as a spread rather than a gallery widget.
Notices & Reviews
Press quotes set as pull quotes with the publication in small caps, borrowing the one typographic device restaurants actually earn.
A booking, not a funnel
Six fields, inline validation, a polite live region for the confirmation and focus moved to it — no modal, no upsell, no countdown.
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 ↗




