chore(01-design-tokens-rams-foundation-01): commit design baseline — Rams docs, mockups, agent skills

- Root Rams design docs (DESIGN.md, IDEAS.md) and GSD install-fix notes (GSD_FIX.md, gsd-fix.sh)
- Four Telemetry Rams mockup screens + telemetry_rams/DESIGN.md (canonical token frontmatter)
- .agents/ project skills (design-taste-frontend) + skills-lock.json
- .gitignore: node_modules/dist/env secrets/OS junk; .planning stays local-only; Zone.Identifier scrubbed (9 files deleted)
This commit is contained in:
araharah-executor
2026-10-22 07:33:04 +08:00
parent d4ee39d08b
commit 506ff68976
54 changed files with 39105 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,141 @@
# Colour
The direction sheet deals a palette with named roles. This file is how to apply it so the page looks
decided rather than tinted.
## Contents
1. What the study measured
2. The roles
3. Proportion
4. Surfaces without lines or shadows
5. Colour fields
6. Dark pages
7. Colour and photographs
8. Contrast
9. Banned families, and what "warm" means here
## 1. What the study measured
- 41% of the studied pages are dark, 59% light. A quarter sit on pure white; 12% on pure black.
- Schemes, as the panel classed them: monochrome plus one accent 23%, light neutral 22%, dark neutral
14%, several flat colours 13%, strictly two-tone 12%, colour carried by photographs 9%, one saturated
field 6%.
- Buttons are neutral (black, white, grey) on 52% of sites. A coloured button is a choice, not a
requirement.
- Gradient fills appear on 22% of the top-scored sites and 52% of the weak ones; gradient text on 2%
against 13%.
- One site in 246 has a terracotta primary button and none has a violet one. Those are colours of
generated pages, not of the gallery. Warm off-white grounds do occur (32 sites), nearly always with
a dark or green ink rather than an orange accent.
- Top-scored sites change the page's field colour between sections more often (a median of two section
grounds against one).
## 2. The roles
```css
:root {
--bg: …; /* the ground: most of the surface */
--surface: …; /* one tone step from the ground: filled panels, form fields, tiles */
--ink: …; /* main text and the strongest marks */
--muted: …; /* secondary text; still passes contrast on --bg */
--accent: …; /* the one accent */
--accent-ink: …; /* text on the accent */
/* --field-1 … --field-n and their inks, when the palette has section fields */
}
```
Use the tokens for every colour in the build. A raw hex in a component is a second palette starting.
Do not add greys between the roles: if you need a quieter text, it is `--muted`; if you need a quieter
surface, it is `--surface`.
## 3. Proportion
Decide the shares before colouring anything and check them in the screenshot:
- **Ground:** 70–90% of the page.
- **Ink:** text and perhaps one inverted section (ink as ground, ground as ink).
- **Accent:** under 5% of any screen. It marks the action and at most one other recurring thing. The
moment it appears in five places it stops meaning "here".
Palettes with fields (flat-loud, pastel) work differently: each section is one field at full strength
and the proportion rule applies inside each section (field, its ink, nothing else).
The accent is not for headings, icons, bullets, borders or underlines unless the direction sheet says
so. A headline is ink.
## 4. Surfaces without lines or shadows
With rules and shadows banned, a surface is told apart from the ground by tone alone:
```css
.panel { background: var(--surface); } /* 2–6 lightness points from --bg */
.field { background: var(--surface); border: 0; } /* inputs: filled, with a clear focus state */
.field:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
```
- One step is enough. If a panel does not read, increase padding or separate with space before
reaching for a second tone.
- Do not nest a panel inside a panel.
- Images do not need a frame. If a light photograph melts into a light ground, change the crop or set
it on `--surface`, do not outline it.
- A thick bar (4px or more) in ink or accent is a graphic element, allowed where the direction uses
it. A 1px line is not.
## 5. Colour fields
A section that changes the whole ground colour is the strongest colour move available, and it replaces
dividers, cards and background decoration at once.
- Fields meet edge to edge with a hard line. Nothing fades.
- Every field has its own assigned ink from the palette. Never put `--muted` on a coloured field; use
the field's ink at full strength, or at 70% opacity for secondary text if it still passes contrast.
- Neighbouring fields should differ in lightness, not only in hue, or the page vibrates.
- Order them like chapters: open strong, rest, surprise, close dark or very light.
- The page's meta theme colour and the navigation follow the first field.
## 6. Dark pages
- Put `color-scheme: dark` on the root so form controls and scrollbars follow.
- The ground is a deep tone with a slight tint from the palette, and the ink is a soft light, not
`#fff` on `#000`, unless the palette deals exactly that for a stark two-tone direction.
- Secondary text is where dark pages fail: check `--muted` against the ground (4.5:1).
- Depth on dark comes from a lighter surface step, never from glows or coloured light.
- Use lighter font weights than you would on a light page.
## 7. Colour and photographs
- When photographs carry the colour, the interface stays neutral and takes its ink from the darkest
tone in the imagery.
- Do not wash a photograph with the brand colour. Either leave it alone, or commit to a real treatment
for every image on the page (all greyscale, or all untouched).
- Text over a photograph needs a calm region. If there is none, move the text off the image. A neutral
scrim (black or the ink colour, 30–55% opacity, flat or fading over the text area only) is the one
gradient the inspector allows.
- A flat field sampled from a photograph (its sky, its shadow) makes a section sit with that image.
## 8. Contrast
- Body text 4.5:1 or better; display type and large numerals 3:1 or better; the dealt palettes meet
7:1 for ink on ground.
- `inspect.mjs` computes contrast for text on flat grounds and fails anything under 2.6:1. It cannot
see text over photographs: check those in the screenshots yourself.
- Do not lower opacity on text to make it "soft" unless you have checked the resulting ratio.
- Focus states and hover states increase contrast, never reduce it.
## 9. Banned families, and what "warm" means here
The inspector fails these:
- **Cream with terracotta.** A yellow-cream ground (roughly hue 22–62, light and noticeably
saturated) with a rust, clay or burnt-orange accent is the house look of AI assistants and of the
pages they generate. Either half alone is suspect; together they are unmistakable.
- **Violet and indigo.** The first wave of generated pages was purple; it still reads that way.
If the brief wants warmth, get it from a low-chroma stone, bone or putty ground (saturation under
about 15%), from a deep green, brown or oxblood ink, from photographs, or from a full warm field
(mustard, tomato red, pink) used flat and with conviction. Warmth is not beige.
If the brand itself owns one of the banned colours, it is the brand's and you keep it: use
`--keep palette`, pass `--allow palette-purple` (or the relevant rule) to the inspector, and note it in
the report.
@@ -0,0 +1,177 @@
# Words
Copy is the first design decision. A headline that needs four lines cannot be rescued by typography,
and a page of slogans cannot be rescued by layout. Write the page as plain text before any markup.
## Contents
1. Write the argument first
2. The two-line headline (with the character budget)
3. Headline shapes that work
4. Supporting line, body, buttons
5. Proof without inventing it
6. Things that read as generated
7. Micro-typography
## 1. Write the argument first
Answer in one line each, in a scratch note:
- Who is reading, and what do they already know?
- What is this, said the way its maker would say it to a friend?
- What single thing should the reader believe after one screen?
- What would make them believe it (a picture of the thing, a number, a name, a demonstration)?
- What do they do next?
Then list the page as claims, one per section, each with its proof:
```
1. It is a three-legged oak stool, made to order. → the stool, large
2. Each seat is cut from one board, so the grain is yours. → three close-ups
3. It takes six weeks, and you can follow it. → the stages, photographed
4. It costs 240 and is delivered by the maker. → price, button
```
A section with no claim is filler; delete it. This is also how you escape the stock order of
hero, logos, features, testimonial, pricing, call to action: the order of the argument is the order of
the page.
## 2. The two-line headline
A headline is a title. It is held in one glance. On the studied gallery sites the hero headline has a
median of 5 words and 70% sit on one or two lines at desktop width. Every headline on your page, at
every viewport, fits in **two lines or fewer**. `inspect.mjs` measures this at 1440, 768 and 390 pixels
and fails the page otherwise.
The constraint is arithmetic, so do it before you fall in love with a sentence:
```
characters per line ≈ line width in px ÷ (font size in px × k)
k ≈ 0.40 condensed faces
k ≈ 0.50 most grotesks and serifs at display size, tight tracking
k ≈ 0.58 wide / extended faces, all-caps settings
```
| Where | Line width | Size | Per line | Two-line budget |
| --- | --- | --- | --- | --- |
| Phone 390, 20px gutters | 350px | 44px | ~16 | **~32 characters** |
| Phone 390, 20px gutters | 350px | 34px | ~21 | ~42 characters |
| Tablet 768 | 690px | 64px | ~22 | ~44 characters |
| Desktop, 12-col measure | 1100px | 104px | ~21 | ~42 characters |
| Desktop, half width | 620px | 72px | ~17 | ~34 characters |
So a hero headline of **about 32 characters or fewer** (four to six words) survives everywhere at a
confident size. Up to about 42 works if the phone size comes down to the mid-30s. Past that you are
writing a sentence; move the rest into the supporting line.
Controlling the break:
- Give the headline a `max-width` in `ch` so it breaks where you intend, and add
`text-wrap: balance` so two lines come out even.
- Where the break matters (it usually does), author it: `Stools for<br> small kitchens`. Check the
phone screenshot; if an authored line wraps there, hide the `<br>` under a breakpoint or shorten.
- Never fix a long headline only by shrinking it. A timid 28px headline of nine words is worse than
five words at 64px.
- Avoid a single orphan word on line two. Rebalance by cutting a word, not by nudging the width.
Section headings follow the same rule and are shorter: two to five words.
**A statement is not a headline.** An intro sentence or a quotation set large (the manifesto line
after the hero, a testimonial) is a paragraph: mark it up as `<p>` or `<blockquote>`, keep it to about
25 words, and set it at statement size, which the inspector takes as 36px or less on desktop, 32px on
a tablet and 24px on a phone. It may then run to about three lines (four on a phone). Set larger than
that, or marked up as a heading, or placed as the main line of the first screen, it is a headline and
gets two lines. In blind judging, a sentence set big over three or more lines was read as a long
headline every time, whatever its markup; if the words matter that much, cut them to a two-line
headline instead.
## 3. Headline shapes that work
All seen repeatedly on the gallery sites. Pick the plainest one that is true.
- **The name.** The brand or the person, set very large, with a one-line description elsewhere.
Strongest when the name is short and the subject is visual (studios, restaurants, hotels, people).
- **What it is.** "A vinyl shelf in your pocket." "Furniture for small rooms." Category, stated with
one specific word.
- **The claim.** "Your bank won't do this." One sentence a competitor could not sign.
- **The instruction.** "Understand your money." Verb first, short.
- **The place or the moment.** "Antarctica." "Lisbon, since 1984." For hospitality, events, travel.
Shapes to avoid, because they are the average of every landing page:
- The mad-lib: "The AI-powered platform for modern teams".
- The tricolon: "Fast. Simple. Secure." or "Plan. Build. Ship."
- The correction: "It's not just a tool, it's a workflow". Also "More than a …".
- "X, reimagined / redefined / made simple". "Welcome to …". "Meet …" for a product nobody has met.
- A question the reader did not ask.
- A headline that needs its colon: "Ledgerly: invoicing for freelancers". Drop the name, it is in the
navigation.
- One word of the headline styled differently (italic, serif, coloured, underlined, in a pill).
Sentence case. No full stop unless the direction's voice uses them everywhere.
## 4. Supporting line, body, buttons
**Supporting line.** One sentence, at most about 22 words, that says concretely what the thing is and
who it is for. It is where the detail cut from the headline goes. It never repeats the headline in
other words.
**Body.** Short paragraphs of plain statements. Prefer the specific noun and the real number: "ships
in a week" over "fast delivery"; "38 seats" over "intimate". Read every sentence and ask whether a
different company could print it unchanged; if so, sharpen it or cut it.
**Buttons.** Name the action or the thing: "Book a table", "See the plans", "Download for Mac".
One to three words. The primary action keeps one name everywhere on the page. One primary button per
screen; a second action is a plain text link. No arrow glyph on every link and button; at most one
arrow, on the primary action, if the direction uses arrows at all.
**Navigation.** The wordmark, up to five plain links, one action. Link labels are single nouns.
## 5. Proof without inventing it
Never invent customers, quotes from named people, press logos, user counts, ratings or benchmarks.
Invented proof is both dishonest and a recognised tell.
When the brief supplies no proof, use what is true by construction:
- Show the thing itself, large. A real demonstration outranks any quote.
- State how it works, specifically.
- Use facts from the brief (price, place, year, materials, what is included).
- If a testimonial or logo section is explicitly requested and no real ones exist, build the section
with plainly fictional sample content and say so in your final report so it is replaced before
launch. Do not put real company names or real people in it.
**Sample details are different from proof, and they are expected.** Where the brief gives the shape of
the content but not every detail (six rooms "from €140", "a short menu", twelve speakers), write the
full set of plausible details: each room with a name, a bed, a view and its own rate; each dish with
its price; each speaker with a talk title. A design can only be judged at its real density, and a page
that shows one rate where six rooms exist looks unfinished. List every sample detail in the report so
the client confirms or replaces it.
## 6. Things that read as generated
`lint.mjs` flags most of these.
- Verbs and adjectives of the genre: unlock, unleash, supercharge, elevate, empower, streamline,
seamless, effortless, robust, cutting-edge, next-generation, world-class, all-in-one, blazing fast.
- Labels pretending to be editorial: "Field notes", "The details", "Our philosophy", "Quietly trusted
by". A section heading can simply be "Pricing".
- Furniture text: locale or time strips, "Est. 2024", coordinates, version stamps, "Scroll", "(Scroll
to explore)", fake photo credits, status lines such as "Available for work" with a dot, strings of
words separated by middle dots or slashes.
- The em dash as a habit. One or two on a page read as writing; ten read as a model. Use a full stop.
- Explainer captions that narrate the design: "A clean, minimal layout for…".
- Step labels that duplicate numbers: "Step 1", "Phase 02".
- Three parallel items everywhere: three features, three stats, three steps. Use the number the
content has.
## 7. Micro-typography
- Curly quotes and apostrophes (“ ” ’), a real ellipsis (…), an en dash for ranges (9–5).
- Non-breaking space between a number and its unit, and before the last short word of a headline
when it would otherwise orphan (`&nbsp;`).
- `font-variant-numeric: tabular-nums` where numbers line up in columns (prices, tables).
- `text-wrap: balance` on headings, `text-wrap: pretty` on paragraphs.
- No all-caps for anything longer than three words; if you set caps, add 0.02–0.06em tracking.
- No text under 13px. No centred paragraphs longer than three lines. No justified text.
@@ -0,0 +1,182 @@
# Critique — judging the render
`inspect.mjs` passing means nothing banned or broken was measured. It says nothing about whether the
page is good. This file is how you find out. Use it on the screenshots, never on the code: you wrote
the code, so you will read your intentions into it. The pictures only show what is there.
## Contents
1. How to look (fourteen passes, a few minutes)
2. The score
3. Caps
4. Writing the critique
5. What to fix first
6. When to stop
## 1. How to look
Open `desktop-fold.jpg`, then the `desktop-page-*.jpg` sheets, then `mobile-fold.jpg` and the mobile
sheets. Make each pass on its own; do not blend them.
1. **One second, then five.** Glance and look away. What did you see? There should be exactly one
answer: a word, an image, an object. If the answer is "a website", there is no focal point. Then
give the first screen five seconds as a stranger: can you say what this is, who it is for and
what to do next? If the title only makes sense after scrolling, it is not a title.
2. **Squint.** Blur your attention until text becomes grey bars. The page should reduce to two or
three large masses with clear size differences. Many small similar masses means everything is
medium and nothing leads.
3. **Cover the logo.** Could this first screen belong to a competitor, or to a different industry?
If swapping the name and the noun in the headline would produce an equally valid page, the page has
no idea. Go back to the direction sheet; the idea is written there. Then scroll: does the idea reach
the pricing, the FAQ and the footer, or does the page turn into stock blocks after the first screen?
4. **Edges.** Follow the left edge of every text block down the page. Count the distinct x positions.
A composed page has two or three, reused on purpose. Six means things were placed one at a time.
5. **Space and frame.** Are related things close and unrelated things far? Is the gap between sections
clearly larger than any gap inside a section? Is any region empty by accident (a hole) rather than
by decision (a rest)? A hole usually sits beside a short column or under a removed decoration.
Then look at the frame: are the left and right margins of the page the same? Does any section keep
its text in one half and leave the other half empty? Is there a stretch of nothing taller than half
a screen? Readers name these first ("it is pushed to the left", "random empty space"), before any
detail; in the study of 100 Sites of the Day about 1 section in 100 left half the width empty.
6. **Type.** Is the jump from body text to headline decisive (three times or more)? Do you see more
than five sizes? Any headline with one orphan word on its last line? Any paragraph wider than about
75 characters or centred over more than three lines?
7. **Colour.** Name the proportions: which colour holds most of the surface, which supports, where is
the accent? An accent that appears in ten places means nothing. One that appears on the single
action and one detail means "here".
8. **Images.** Count the real pictures: are there at least four, and is there a moving one? A page of
drawings only, or of type only, looks unfinished next to one with real photographs and footage.
Is every image doing a job (showing the thing, setting the place, proving the claim)?
Do the crops share ratios? Is anything a grey box, a stretched photo, a fake screen made of bars?
Does the photography share one light and one grade, or is it a stock mix?
9. **Sameness.** Write one word for each section's layout (split, list, full-bleed, grid, statement,
index …). The same word twice in a row, or three times on the page, is the template showing.
10. **Phone.** Is the mobile screen a composition of its own, with its own crop and its own line
breaks, or the desktop page squeezed? Is the first screen still one clear thing?
11. **Fullness.** Go through the desktop sheets screen by screen. Is each screen a composed poster, or
is it a narrow column of small text with dead ground either side? Is any statement held on screen
for three screens? An austere page is not a restrained one: if the page could be mistaken for an
unfinished wireframe (grey shapes, grey text, one small column), it is underbuilt. A single motif or
drawing blown up to fill a whole screen with nothing to read is a hole too, however bold it looks.
12. **Substance.** Does every section the brief asked for deliver its information completely and at a
readable size (the full menu, each room, every speaker, the price and what it includes)? Would the
visitor leave knowing what they came to find out? Is the primary action a real button on the first
screen?
13. **Collisions.** Is anything lying on top of a headline, a statement or a button at any scroll
position? Does any element run off the edge of the phone screen? A row that scrolls sideways on
the phone reads as cut off in a screenshot and to most visitors; wrap it unless the cut is
plainly deliberate (a partial card showing at the edge).
14. **The competitor and the live thing.** Put the nearest competitor's name on the page: does the
idea still work? Then name the one live thing (what the visitor can do or what differs each visit).
If either answer is weak, that is the first problem of the round, ahead of any detail.
A note from testing: in blind comparisons a richer page full of template devices beat a cleaner page
that was thin, every time. The house rules remove the devices; passes 11–13 are where you make sure the
page did not lose its substance with them.
## 2. The score
Score like an Awwwards juror: four criteria, 1–10 each, weighted **design 40%, usability 30%,
creativity 20%, content 10%**.
**Design (40%)** — composition, typography, colour, imagery, craft.
- 9–10: every screen holds as a poster. Nothing could be removed or moved.
- 8: confident and coherent; one or two soft spots a designer would notice.
- 7: clean and professional, a clear system, little tension.
- 6: tidy but anonymous. Reads as a kit.
- 5 or less: weak hierarchy, accidental spacing, default type, mismatched images.
**Usability (30%)** — orientation in three seconds, readable text, obvious actions, honest mobile.
- 9–10: you know what it is, who it is for and what to do without scrolling; flawless on a phone.
- 8: clear and comfortable; minor friction (a long section, a small target).
- 7: works, with one real problem (weak contrast somewhere, a cramped phone layout).
- 6 or less: you have to hunt, or something breaks.
**Creativity (20%)** — an idea you can state in a sentence, carried through.
- 9–10: a concept specific to this subject that you have not seen applied this way.
- 8: a clear idea with one memorable move.
- 7: a tasteful execution of a known approach.
- 6 or less: the category default.
**Content (10%)** — words and pictures that are specific, true-sounding and edited.
- 9–10: every line could only be about this subject; imagery is the subject.
- 7–8: clear and concrete, some generic lines.
- 6 or less: slogans, filler, stock.
`overall = 0.4·design + 0.3·usability + 0.2·creativity + 0.1·content`
For calibration: real Sites of the Day land around 7.5–8.0 overall from the jury. An honest 8.5 is
rare. If your first score for your own work is 9, score it again as the juror who did not build it.
## 3. Caps
These override the arithmetic.
- Any `FAIL` still reported by `inspect.mjs`: overall cannot exceed **6.0**.
- A headline of three or more lines at any viewport, an eyebrow label, monospace labels, 01/02/03
numbering, a decorative gradient, a shadow on a resting element, or a decorative hairline visible in
a screenshot: overall cannot exceed **6.5**, whatever else is true.
- Pass 3 fails (the page could belong to anyone): creativity cannot exceed **6**.
- The idea passes for the nearest competitor (pass 14): creativity cannot exceed **7**.
- Fewer than four real pictures (photographs, archival images, footage; drawings do not count):
design cannot exceed **6.5**.
- A page that barely moves (the inspector's `still-page`), or motion that is one identical fade on
every block: design cannot exceed **7**.
- No live thing (pass 14): creativity cannot exceed **7.5**.
- Any placeholder (grey box, lorem, "Image here"): content cannot exceed **5**.
- Two or more desktop screens that are mostly empty ground, or a page whose running text is small and
grey: design cannot exceed **6.5**.
- A section the brief asked for that is missing or reduced to a few words: content cannot exceed **6**.
- No real button for the primary action on the first screen: usability cannot exceed **7**.
- The five-second pass fails (no clear title saying what this is on the first screen): usability
cannot exceed **6**.
- Unequal page margins, a section huddled on one side with the other half empty, or a blank stretch
taller than half a screen: design cannot exceed **6.5**.
- A section of running text with no heading naming it: usability cannot exceed **7**.
- Anything covering a headline at any scroll position: usability cannot exceed **6**.
- A blank band where a reveal did not fire, or content clipped off-screen: usability cannot exceed **5**.
## 4. Writing the critique
Write it down; do not just think it. Use exactly this shape so rounds can be compared:
```
ROUND n
Seen in one second: …
Idea (one sentence): …
Live thing: …
Layout words by section: hero=… / …=… / …
Scores: design _ · usability _ · creativity _ · content _ → overall _._
Caps applied: …
Top three problems (most visible first), each with the screenshot it shows in and the fix:
1. …
2. …
3. …
```
Name problems the way a designer would to a colleague: "the supporting paragraph starts 40px left of
the headline's edge", "the testimonial section is the third split layout in a row", "the phone hero
crops the bottle out of frame". "Could be more polished" is not a finding.
## 5. What to fix first
Fix in this order; later items cannot rescue earlier ones.
1. **The idea.** If pass 3 fails, change the hero concept, not the details.
2. **Hierarchy.** One dominant element per screen. Make the big thing bigger and the small things
fewer before touching anything else.
3. **Composition.** Shared edges, section spacing, variety of layout between sections, holes.
4. **Typography.** Scale contrast, line breaks, measure, leading.
5. **Imagery.** Replace weak pictures, unify crops and grade.
6. **Colour.** Proportion and accent discipline.
7. **Detail.** States, focus, small alignments, mobile refinements.
8. **Motion.** Last.
When removing something leaves a gap, do not refill it with an ornament. Close the gap, enlarge what
remains, or let a picture or a colour field take the space.
## 6. When to stop
Run at most three rounds: critique → fix the top three → re-inspect → critique. Stop early when a round
finds no problem a stranger would notice. If round three still scores under 7.5, say so plainly in
your report, with the remaining problems listed; do not round up.
@@ -0,0 +1,791 @@
# Imagery
Pictures are where generated pages most obviously give up: a grey box, an icon in a square, a fake
dashboard, an abstract blob. This file is how to decide what the pictures are, where to find real ones,
how to crop and place them, and what to do when there are none.
## Contents
1. What the study measured
2. Decide what imagery exists
3. Real pictures from the open collections
4. The dealt photographs
5. Cropping and placing
6. Treatment
7. Showing software
8. Drawing the subject
9. Type and colour alone
10. Markup and credits
11. Video
12. Where pictures and footage go
## 1. What the study measured
- On 60% of the studied sites one piece of media covers at least half of the first screen; on half of
them it covers nearly all of it. Top-scored sites: 68% and 56%. Weak sites: 48% and 39%.
- Only 15% of heroes have no media at all, and those are deliberate typographic statements.
- Media has square corners on 73% of sites. Where corners are rounded the usual radius is about 12px,
applied to every image alike.
- Top-scored pages carry far more imagery (a median of 37 images against 5 on weak pages): the strong
sites show, the weak ones tell.
- What the imagery is, by the panel's classification: a mix of kinds 32%, photography 14%, generative
or WebGL scenes 13%, real product interface 12%, 3D renders 11%, illustration 9%, video 6%, type only
3%.
- Among the 54 sites the panel scored under 6.8, legibility (text lost over a busy image, or too
little contrast) was named as a weakness in 13.
- In the October 2026 study of 91 Awwwards Sites of the Day (section 12), the median page carries 7
pictures (top sites 8, the lowest 2) and footage appears on 42% of sites (half of the top ones, one
of the 16 lowest).
- Pictures come in whole screens: 42% of scrolled screens hold none and 19% are 90% picture or more.
- 94% of pictures have square corners, and on a typical site two ratios hold three-quarters of them.
## 2. Decide what imagery exists
In order of preference:
1. **The project's own images.** Product shots, the place, the people, the work. Even ordinary
photographs of the real thing beat beautiful photographs of something else. Ask what exists; look in
the repository.
2. **Images you can generate** with an image tool in this session. Write prompts as a photographer's
brief: subject, angle, light, background, crop ratio, and "no text". Generate the hero first and
make the rest match its light.
3. **Real pictures of the subject's world from the open collections** (section 3). Archives,
museums and photographers have published millions of pictures anyone may use with a credit: the
client's city and street in old postcards, maps of its neighbourhood, its materials, its trade in
engravings, museum objects of its kind. Whenever an honest one exists, it beats a drawing of the
same thing: it is real, it has detail no drawing in a page's budget reaches, and it carries history.
Honest means nobody could take it for the client's own product, room, dish, person or team.
4. **Pictures you draw in code from the subject itself** (section 8), for what no honest picture
exists of: the product's own objects as flat shapes, the place's tiles and windows, a map of its
streets, the document a tool produces, the interface of a piece of software. In testing, pages that
drew their subject with care beat pages that borrowed unrelated photographs every time, and beat
pages that showed nothing.
5. **The photographs dealt on the direction sheet.** Real photographs of places, light, texture and
weather. Use them only where an honestly generic picture fits (the open sea for a house on the sea,
fog for a quiet retreat). They cannot stand in for a specific product, dish, room, person or
screen: a foggy motorway on a restaurant page reads as stock at once, and jurors notice.
6. **Type and colour alone,** done on purpose (section 9).
Tell `draw.mjs` the truth with `--have`. If you have no product photographs and no image tool, do not
pass `product-photo`; the draw will avoid the directions that depend on showing the object. Pictures
found in the open collections are not the project's own: do not pass `photography` for them.
## 3. Real pictures from the open collections
`scripts/assets.mjs` searches four open sources without keys and prints, for each find, a direct image
URL, the page it comes from, its size, its creator, its licence and a credit line:
- **Openverse**: one index over Flickr, Wikimedia Commons, museums and libraries.
- **Wikimedia Commons**: archival photographs, old maps, scans of prints and books, places as they
look now.
- **The Art Institute of Chicago** and **The Metropolitan Museum of Art**: public-domain paintings,
prints, drawings, photographs, textiles and objects, in large scans.
```bash
node <skill>/scripts/assets.mjs "ghent postcard"
node <skill>/scripts/assets.mjs "fig" --source museums --orientation portrait
node <skill>/scripts/assets.mjs "basalt texture" --min-width 2400 --source openverse,commons
node <skill>/scripts/assets.mjs --get commons:36649959,met:707892 # download, record the credits
node <skill>/scripts/assets.mjs --credits # footer line and report list
```
By default it keeps only licences that allow commercial use (CC0, public domain, CC BY, CC BY-SA) and
pictures at least 1000 pixels wide, and checks that every image it prints actually loads.
`--license pd` keeps only pictures that need no credit; `--orientation`, `--min-width` and `--limit`
narrow the list; `--source` picks the sources; `--json` gives the raw results. Each result has a ref
(`commons:36649959`) that `--get` takes.
**What to search for.** The inventory (SKILL.md step 1), not the category: "bakery" finds other
people's bakeries. The subject's world is specific:
- **Its place:** the city, the street, the harbour, the valley, in old postcards and photographs
("ghent postcard", "rotterdam harbour 1920") and as it looks today ("gent korenmarkt").
- **Its maps:** the street plan, the chart of its bay, the geology of its valley ("ghent old map",
"nautical chart lisbon").
- **Its materials,** close enough to read as a surface: the stone of its quay, the grain of its oak,
its glaze, its tiles ("azulejo tiles", "basalt cobblestones", "oak grain").
- **Its trade's history:** the old tools, the craft in engravings and trade catalogues, patent
drawings ("bread oven engraving", "potter's wheel", "bicycle patent drawing").
- **Museum objects of its kind,** with `--source museums`: a coffee set from about 1810 for a studio
that makes coffee sets, a woodblock print of a wave for a surf school, a porcelain plate painted
with figs for a fig orchard.
- **Scientific imagery:** botanical and zoological plates ("botanical engraving fig"), star charts,
microscope photographs, satellite views of its coast, geological sections.
Search in the place's own words (azulejo, not "blue tile"; Korenmarkt, not "market square"), add a
medium (postcard, engraving, old map, lithograph, aerial photograph) or a decade, and try two or three
queries per idea. The museum sources match best on one or two words ("fig", "harbour", "teapot").
**What makes a good find.**
- It belongs to this subject's world and would not fit the nearest competitor's page.
- It still reads at the crop the slot needs. Look at it before deciding: `--get` downloads it, then
open the file. A title is not a picture.
- It is large enough: twice the width it is shown at for anything full-bleed (the script prints the
largest size available). A 1024-pixel Flickr photograph is a half-width picture at most.
- It belongs to one family with the others on the page: all engravings, or all photographs from one
era, or all close surfaces in one light. A 1900 postcard beside a modern stock photograph beside a
botanical plate reads as a scrapbook.
- No watermark, no burned-in text or logo, no frame or scanner edge you cannot crop away, and no
recognisable living person who could be taken for the client's staff or customers.
**Use it creatively.** Found pictures are material, not decoration:
- **One archival photograph as the hero,** full-bleed: the street as it was a century ago, behind the
name of the business that stands in it today, with the year in the caption.
- **A museum engraving enlarged until its line becomes texture,** cropped tight and returning at
different scales as the page's one motif (the whole fig plate in the hero, a single leaf of it
beside the menu).
- **An engraving or a map on white paper set with `mix-blend-mode: multiply` on the page's colour
field,** so the ink sits on the ground (section 6).
- **The real map of the street as the contact section,** the door marked, the north and the scale
taken from the original.
- **A material close-up as a whole field:** the quay's stone behind the opening hours.
- **A run of old postcards or plates as the gallery,** one size, one treatment.
Treatments stay within section 6: untouched, greyscale for the whole set, or multiply on a field.
**Stay honest.**
- **A found picture is never the client's own.** It may show the client's city, its materials, its
history, the kind of thing it makes; it may never stand in for the client's product, room, dish,
person, team or customers. If a visitor could take it for the client's own (a bakery counter, a
plate of pasta, a smiling barista, a guest room), do not use it.
- **Caption from the source** when a caption helps: what it is and when, as the archive describes it
("Gand, unloading a steamer, 1905"). Never write a caption that ties it to the client.
- **Record and credit every picture.** `--get` writes the source, creator, licence and page of each
download to `assets/credits.json`. CC BY and CC BY-SA require a credit with the title, the creator,
a link to the source and a link to the licence; `--credits` prints them as one line of HTML for
the footer (section 10). Public-domain pictures need no credit; give one anyway.
- **Respect the licence.** CC BY-SA: an altered version (recoloured, combined with other pictures)
must be shared under the same licence. Non-commercial (NC) and no-derivatives (ND) licences are out
for a client's site, which is why the script leaves them out unless asked. Read the `note` lines:
some pictures carry personality rights or a trademark, are public domain only in the United States,
or come with an archive's statement instead of a licence.
- **Download, do not hotlink.** Save what you use into the project: `--get` puts it in `assets/` at
1920 pixels wide (pass `--width 1280` for a picture shown at half the screen, `--width 3840` for a
very large hero where the source has it). The page then works offline, does not break when an
archive moves a file, and does not load from someone else's server. Every licence the script
allows by default permits copying.
- **List them in the report:** file, title, creator, licence, source page, and where the credit sits.
`--credits` prints that list too.
If two or three honest queries per idea find nothing that fits, draw (section 8). A drawing made for
the subject is better than a found picture that is merely nearby. If the script cannot reach any
source (no network), draw, and say so in the report. For moving pictures, the same script searches
real footage with `--video` (section 11).
## 4. The dealt photographs
- The sheet lists eight from one set so they share a subject and a light. Use three to six.
- Each line says where the calm area is. Put text there or keep text off the image.
- More from the same or another set: `node scripts/draw.mjs --photos list`, `--photos <set>`, or
`--photos "fog coast"` to search descriptions.
- URLs have the form `https://picsum.photos/id/<id>/<width>/<height>`. Request the exact pixel size you
render at twice the CSS size for sharpness, capped around 2400 wide. Add `?grayscale` for
black-and-white.
- These are photographs from Unsplash served by Lorem Picsum under the Unsplash licence. List the ids
you used in your report; they are meant to be replaced by the project's own pictures.
## 5. Cropping and placing
- **Choose a small set of ratios and stick to them:** for example 4:5 for portraits, 16:10 for
landscapes and 1:1 for details. Random ratios look like a stock dump.
- **Go large.** A photograph at full width, or at half the screen and full height, has presence. Six
small ones in a grid have none. If the image is good, give it the room; if it is not, cut it.
- **Crop with intent.** Use `object-fit: cover` with an `object-position` chosen per image so the
subject sits where the composition needs it (off-centre, on a third, against the text side).
- **Let images touch edges.** Full-bleed, or bleeding off one side, reads as designed. An image
floating with equal margins on all sides reads as placed by default.
- **Overlap sparingly and on purpose:** a headline whose last line runs over the top edge of the
picture, a small image overlapping a large one by a fixed 40–80px. One overlap per screen at most.
- **Square corners** unless the direction or the one-motif shape says otherwise. If rounded, one radius
for every image on the page.
- **No frames:** no borders, no shadows, no device mock-ups, no browser chrome around pictures.
- **Captions** are one short line in the small size and soft ink under the image, left-aligned to its
edge. Most images need none.
- Where each picture goes on the page, how large, and what the phone does with it: section 12.
## 6. Treatment
One treatment for the whole page:
- **Untouched.** The default. Pick images whose light already agrees.
- **Greyscale.** `filter: grayscale(1) contrast(1.05)`, or `?grayscale` on the URL. Unifies a mixed
set instantly and suits editorial and typographic directions. Apply to every image or none.
- **Multiply on a field.** On a light flat ground, `mix-blend-mode: multiply` makes a white studio
background vanish so a product sits directly on the colour.
- **Darkened for a dark page.** `filter: brightness(0.85) contrast(1.05)` so the image edges sink into
the ground.
Not treatments: colour washes over photographs, duotone overlays, blur, grain overlays, vignettes,
tilted "polaroid" frames with shadows.
For text on a photograph, a neutral scrim covering only the text area: a solid block at 40–55% black,
or a fade from 55% black to transparent over the lower third. Never a coloured overlay.
## 7. Showing software
- Build a real fragment of the interface in HTML at its true size, with believable content, instead of
a shrunken screenshot or a decorative mock-up. Crop to the part that makes the point: one control,
one row, one panel.
- Believable means real labels, real numbers that add up, one item hovered or selected, proper
alignment. Grey bars standing in for text are worse than no image.
- Put the fragment directly on the page or on one flat stage colour. No laptop, no phone frame unless
the product is a phone app and the frame is a plain rounded rectangle.
- Inside a fragment the product's own interface conventions apply (its small radii, its 1px control
borders, a shadow on its open menu). Put the attribute `data-ui` on the fragment's root element: the
inspector and the linter then leave what is inside alone, and hold the page around it to the house
rules. `data-ui` is for cropped fragments only; wrapping a heading or a whole section in it fails.
Write the fragment's CSS under a `[data-ui]` selector (`[data-ui] .row { … }`) so the linter can
tell it apart from page styles.
- If the user supplies real screenshots, show them large and uncropped at least once.
## 8. Drawing the subject
When nothing real can be photographed and the open collections hold no honest picture of it, draw it,
and draw it well. Inline SVG and CSS are enough. Drawings and found pictures can share a page: the
found ones show the world around the client, the drawings show the client's own things.
- **Draw the subject's own things,** not decoration: the stool's three legs and the grain of its seat,
the enamel sign over the bakery door, the hut's two windows and its stove pipe, the street plan, the
invoice the tool produces, the festival's stage. If you cannot name what a drawing depicts in the subject's world, it is decoration.
- **One graphic language for the whole page:** flat filled shapes, no outlines or one consistent
stroke weight, the same corner and curve vocabulary everywhere, drawn in the palette's real colours
(fields and accent at full strength, not greys). A set of drawings that obviously belong together
reads as an identity; a mix of styles reads as clip art.
- **Colour is the material.** A product sold on its finish is drawn in that finish's colours; a sea house in the
sea's blues and the sand; never a page of grey placeholders.
- **Draw with three levels of detail.** Jurors called simple drawings "basic" every time. Give each
drawing big shapes that read at a glance, middle shapes that describe the thing (the handle, the
window frame, the step), and small details that reward a second look (the maker's mark, the house
number, the chipped tile, the hinge, a bird on the roof). Make light with flat tone steps (a lit face
and a shaded face of each volume), not gradients. A drawing that would pass as a placeholder icon at
full size is not finished.
- **Draw large and let it work:** the drawing is the hero's dominant element, a full-width band, a set
of tiles, the footer mark. Small doodles scattered around read as filler.
- **Let it carry the idea through the page,** the way a motif does: the same drawn object returns at
different scales, the tile grid becomes the opening hours, the map becomes the booking section.
- **Honesty:** say plainly, in small text near the first drawing or in the footer, that the pictures
are drawn until photographs exist, if the client might otherwise be taken to be showing real
products. In your report, list which drawings are placeholders for photography.
- **Not this:** gradient-shaded 3D-looking blobs, isometric clip art, generic line illustrations of
people at laptops, abstract floating shapes, icons in squares. Those are the generated look.
## 9. Type and colour alone
This is a legitimate design, not a gap to disguise. Only 7 of the studied sites use no imagery at all,
and they hold their own: a median panel score of 7.5 against 7.2 for the whole set.
- Make the typography the image: one statement at 10–14vw, a list set at 5vw, numerals at display size.
- Use colour at scale: a full field, an inverted section, a two-field split.
- Use the subject's own shape as one flat motif if the direction calls for it (a circle, an arc, a
notch), drawn in CSS, cropped by the viewport.
- Do not add: icons in squares, abstract gradients, dot grids, wave dividers, floating shapes, emoji,
generic line illustrations, or an empty box where a picture "will go".
## 10. Markup and credits
A found picture, downloaded into the project with `--get`, used as the hero:
```html
<img src="assets/fig-engraving.jpg" width="1920" height="2807" fetchpriority="high" decoding="async"
alt="Hand-coloured engraving of a fig cut open, with its flowers and seeds, in black, pink and green">
```
A dealt photograph further down the page:
```html
<img src="https://picsum.photos/id/626/2400/1500" width="2400" height="1500"
alt="Red and white lighthouse in a turquoise sea below a chalk cliff" loading="lazy" decoding="async">
```
The credits, as `--credits` prints them, in the footer:
```html
<p class="credits">Pictures: <a href="https://commons.wikimedia.org/wiki/File:Fig_(Ficus_carica_L.);_57_segments_of_flowers_and_fruits_at_Wellcome_V0043000.jpg">“Fig (Ficus carica L.): 57 segments of flowers and fruits”</a>, <a href="https://creativecommons.org/publicdomain/mark/1.0/">public domain</a>, via Wikimedia Commons.</p>
```
- Always `width` and `height` (or `aspect-ratio` in CSS) so nothing jumps while loading.
- `loading="lazy"` on everything except the hero image, which gets `fetchpriority="high"`.
- `alt` describes the picture in plain words. Decorative repeats get `alt=""`. The script's suggested
alt is the archive's title; rewrite it to say what the picture shows.
- A coloured `background-color` on the image container (the ground or surface tone) so the layout is
whole before the image arrives.
- The credit line is part of the footer's design, at full contrast, with its links. Set it at the body
size; one step smaller only on a page with plenty of running text, because a long credit line in
small type can become the page's most common text size and fail the inspector's body-size check.
One line for all pictures; no credit captions laid over the pictures themselves. Credit only the
pictures the page still uses.
- A broken image fails the inspector. Check every picture in the screenshots.
## 11. Video
A moving picture of the subject's world can carry a section the way a photograph does: the river at
the end of the street, the roaster's drum turning, a timber frame going up. `assets.mjs --video`
searches real footage without keys: Wikimedia Commons (every video there comes with web-sized
transcodes; the best source for short clips), the Internet Archive's Prelinger Archives (industrial,
educational, city and home films from the 1920s to the 1970s, mostly public domain and mostly long)
and NASA (the Earth from orbit, launches, aircraft, laboratories). The rest of the Internet Archive is
left out: anyone can upload there, and the licence claims are unchecked.
```bash
node <skill>/scripts/assets.mjs "coffee roasting" --video
node <skill>/scripts/assets.mjs "river" --video --source commons,nasa --orientation landscape
node <skill>/scripts/assets.mjs --get commons:49769620 # the clip, a poster frame, the credit
```
It takes a rendition of 720p or smaller (`--res` changes that), lists short clips first, prints each
clip's length and file size, and flags anything over 25 MB or longer than a minute. `--get` refuses a
clip over 25 MB unless you pass `--max-mb`, and saves the source's still frame beside it as the
poster (Commons and NASA; the archive's own stills are too small). An archive film of ten minutes is
too heavy as it stands: trim a few seconds from it with ffmpeg if it is installed, or choose a
shorter clip. Licences, credits and honesty work exactly as for pictures (section 3).
**Choosing a clip.**
- **Short:** 5 to 20 seconds that loop without a visible jump: water, smoke, a turning drum, traffic,
leaves, a crowd from far away. Watch it to the end before choosing.
- **Light:** under about 5 MB for a section and 10 MB for a hero. In the study sources are 1920×1080
(the commonest) or 1280×720, median length 10 s; 720p is plenty behind type. For a full-bleed hero,
cut a tall 720×1280 version for the phone, or set `object-position` on the subject.
- **Clean:** no slate, title card, logo, watermark or burned-in caption. NASA and archive films often
open with a slate, and the source's poster frame may be that slate. No recognisable person who could
be taken for the client's staff or customers.
- **Never the client's own:** a found clip of a roastery is not this roastery.
- **Found clips: one per page, two at most.** A found clip shows the world around the client; a
third makes a theme of it. The project's own footage can carry more: in the study a site with
footage carries a median of three or four clips. Either way, **one moving picture per screen**;
two loops running side by side are noise.
**On the page.**
- Muted, looping, inline, with a poster and a fixed size, as `--get` prints it:
`<video autoplay muted loop playsinline preload="metadata" poster="assets/river-poster.jpg"
width="854" height="480" aria-hidden="true"><source src="assets/river.webm" type="video/webm"></video>`.
No sound and no controls on a decorative loop. `aria-hidden` because it is decoration: whatever the
clip says that matters belongs in the text beside it.
- The poster shows before the clip plays, on slow connections and under reduced motion, so it must
work as a picture on its own. If the source's still is a slate or a blur, take a frame from the clip
yourself or use a found picture.
- In the study only 31% of clips had a poster, and the panel recorded the blank and grey boxes
before they played as faults. Here every clip has one.
- Play a clip only while it is in view, and none under `prefers-reduced-motion: reduce`, where the
poster shows instead:
```html
<script>
const calm = matchMedia('(prefers-reduced-motion: reduce)');
const clips = [...document.querySelectorAll('video[autoplay]')];
const inView = new Set();
const sync = (v) => {
if (calm.matches) return;
if (inView.has(v)) v.play().catch(() => {}); else v.pause();
};
const settle = () => {
for (const v of clips) {
if (calm.matches) { v.removeAttribute('autoplay'); v.pause(); v.load(); }
else if (inView.has(v)) v.play().catch(() => {});
}
};
const watch = new IntersectionObserver((entries) => {
for (const e of entries) {
if (e.isIntersecting) inView.add(e.target); else inView.delete(e.target);
sync(e.target);
}
}, { threshold: 0.2 });
clips.forEach((v) => watch.observe(v));
settle();
calm.addEventListener('change', settle);
</script>
```
- Crop and place it like a photograph (sections 5 and 6): `object-fit: cover`, one ratio, square
corners, no frame, a ground-coloured `background-color` while it loads.
- Never under body text. On a clip put a headline, one line and one action at most, and check the
contrast where they sit across the whole loop, not only on the poster: footage that is dark at
one second can be bright at the next. If any frame drops below 4.5:1 (3:1 for 24px and up), use the
scrim in section 6 or move the text off the clip. Nothing lies on top of a headline or a statement.
- It is motion: let it be the page's one moving thing, or sit in a section where nothing else moves.
- Credit it with the pictures: `--credits` prints one footer line for both ("Pictures and footage:")
and the list for the report, with each clip's length.
- Where footage goes (hero background, inline loop, the frame that grows, a docked card), and what
the study measured about it: section 12. Nothing that matters plays on hover only, and a full film
with sound sits behind a play button that opens a player with controls.
## 12. Where pictures and footage go
What strong pages do with their pictures, measured on the latest 100 Awwwards Sites of the Day
(10 July to 17 October 2026, captured at 1440×900 and 390×844). 91 were usable. A panel described where
every picture sat and scored each site the Awwwards way, and a script measured how much of each screen
is picture. "Top" means a panel score of 7.5 or more (36 sites), "lowest" under 7.3 (16 sites). The
scores sit close together (6.7 to 7.9) and a single score carries about half a point of noise, so read
the links between a placement and the score as leanings, not laws.
### How much of each screen is picture
- **All or nothing.** Counting photographs, footage and picture backgrounds (not canvases), the median
scrolled screen in the study is 16% picture, but few screens sit near it: 42% hold no picture and
19% are 90% picture or more. Only 19% fall between 20% and 60%. A picture either leads its screen or
stays out of it.
- **About a third, with rests.** On the 67 sites that put pictures in the page, the average screen is
36% picture (middle half 18–69%). Top sites sit at 30% and leave 28% of their screens with no picture
at all: screens of type are the rests between picture screens.
- **The first screen.** A picture covers at least half of it on 41% of the sites and nearly all of it
on 32%. A full-bleed photograph or film is the whole first screen on 29%; footage is the main hero
picture on about a quarter.
- **A scene is not a picture.** 66 of the 91 keep a WebGL canvas over at least 30% of the screen, so
counting canvases the median screen is entirely covered. On the lowest-scored sites that canvas is
often all there is: the five lowest usable sites (6.7 to 7.0) draw everything in it and put two
pictures at most in the page.
- **Text is small in area.** The median screen is 8% text. Type is set large and sparse around the
pictures, not packed beside them.
- **Off-centre.** Measured per section across the crawled sites, pictures sit to the left or right
939 times, centred 273 times, and full-bleed or behind text 269 times.
- **The phone carries more picture,** not less: 19% of a phone screen at the median, and 27% of the
first phone screen against 17% on desktop. Full-bleed pictures re-crop to portrait and fill the
screen; splits put the picture first at full width.
### How many
- **Seven pictures per page** at the median (middle half 1–25). Top sites 8 (middle half 4–27), lowest
2 (0–4). The floor in SKILL.md step 6, four and usually six to twelve, is the strong half of the
study.
- **Footage on 42% of sites:** on half of the top sites and half of the middle band, and on one of the
16 lowest. A site with footage carries a median of three or four clips.
- **Three to five placements.** Sites that use two or fewer of the placements below average 7.31;
sites that use three or more average about 7.5. The median top site uses four, the median lowest
site one. Where the pictures go changes from section to section on a designed page.
### The placements, ranked
| Placement | Sites (of 91) | Top (of 36) | Lowest (of 16) | Mean score with / without |
| --- | --- | --- | --- | --- |
| Split: picture in a half or two-thirds | 32 | 17 | 2 | 7.50 / 7.38 |
| Horizontal gallery | 32 | 13 | 4 | 7.44 / 7.41 |
| Full-bleed band between sections | 31 | 14 | 3 | 7.48 / 7.40 |
| Full-bleed picture or film as the first screen | 26 | 10 | 3 | 7.45 / 7.41 |
| Uneven mosaic | 25 | 11 | 1 | 7.49 / 7.40 |
| Picture held while text scrolls | 23 | 12 | 1 | 7.52 / 7.39 |
| Footage behind text | 22 | 8 | 2 | 7.46 / 7.41 |
| Even grid | 20 | 11 | 2 | 7.48 / 7.41 |
| Scattered collage | 19 | 6 | 3 | 7.47 / 7.41 |
| Panel sliding over a held picture | 19 | 8 | 2 | 7.48 / 7.41 |
| Inset frame that grows | 17 | 7 | 1 | 7.52 / 7.40 |
| One picture centred on a field | 14 | 5 | 3 | 7.38 / 7.43 |
| Cut-out object on a flat field | 13 | 7 | 1 | 7.57 / 7.40 |
| Index with hover preview | 12 | 6 | 1 | 7.49 / 7.41 |
| 3D gallery | 12 | 4 | 4 | 7.30 / 7.44 |
| Small card docked in a corner | 11 | 5 | 1 | 7.47 / 7.42 |
| Picture inside or in front of display type | 11 | 7 | 1 | 7.50 / 7.41 |
| One small picture beside a statement | 8 | 5 | 0 | 7.56 / 7.41 |
| Small pictures between words | 6 | 4 | 0 | 7.52 / 7.42 |
- Found on top pages more than their share and almost never on the lowest: the split, the picture held
while text scrolls, the uneven mosaic, the inset frame that grows, the cut-out on a flat field, and
pictures set with the type (the last three rows together: 24 sites, 16 of them top, 1 lowest).
- The horizontal gallery is common in every band and lifts nothing. Use it where browsing is the point.
- The 3D gallery is the one placement that goes with lower scores, and with the lowest usability in the
table (5.9 against 6.35 for all sites).
### Each placement
Each entry gives when to use it, the geometry at 1440×900, the phone version and how it fails.
**Split: picture in a half or two-thirds**
- *When.* A claim and the picture that proves it. The commonest way strong pages pair words and a
picture.
- *Geometry.* The picture takes 42% of the width at the median (middle half 33–49%), up to two-thirds,
at the full height of its section: 680×850 and 720×900 are typical. 38% of these pictures are
portrait, mostly 4:5 or 3:4. The text starts on the 50% line (x = 720) or on the line where the
picture ends, top-aligned or docked low, not centred in its half. Let the picture touch the viewport
on its outer side: only about 30% do in the study, and the ones with margin on every side read as
placed by default (section 5). Alternate sides only across three or more rows.
- *Phone.* Picture first at full width (4:5, or 4:3 for a landscape), text under it.
- *Fails.* The same split in three sections running; a picture floating in the middle of its half; a
text column narrower than about 30 characters.
**Full-bleed band**
- *When.* One picture carries a whole screen between type screens: the place, the work, the object at
full size. One every three or four screens at most.
- *Geometry.* 100% wide and one to one and a half screens tall (median 900px at 1440×900, middle half
890–1,170px; ratios 1.4 to 1.9). For a slow parallax the picture is oversized in its frame by 4–10%
(32–72px a side in the study) and travels no further than that. Text is a headline and one line, or
nothing, docked 32–64px in from a corner over the calm part of the picture.
- *Phone.* A portrait re-crop that fills the screen, with `object-position` set on the subject; the
text moves to the bottom quarter.
- *Fails.* 11–14px white text over a bright area; three bands with nothing between them; a band that
only decorates.
**Picture held while text scrolls**
- *When.* A list, steps or a long paragraph that all belong to one picture. If the picture changes for
each item, it is the index with a preview, below.
- *Geometry.* A 100vh sticky stage inside a section two to six screens long (runways of 1,800–5,400px
are typical in the study). The picture holds one half (685×880 is a typical portrait), two-thirds, or
the whole screen with the text passing over it on a solid panel. Text rows about 150px apart, so two
or three are in view at once.
- *Phone.* Release the stick: picture first, text under it. If the picture swaps per item, keep it as
a band in the top 40% of the screen.
- *Fails.* Long runways where the text has faded out and the held picture is all that is left (the
panel recorded several empty stages on phones); body copy set on the held picture.
**Inset frame that grows**
- *When.* Once per page, for the film or picture the page is about, as the move from type into
picture.
- *Geometry.* The frame starts centred at 35–50% of the width (549×366, 710×480 and 864×540 in the
study) or as a 16px inset round a full screen, and grows to the full width and height over
720–2,700px of scroll. Any corner radius goes to 0 as it reaches the edges. A headline sits above it
before it starts; nothing else on the screen moves while it grows.
- *Phone.* Start at full width inside the 16px margins and grow to a full-bleed portrait, or skip the
growth.
- *Fails.* The growth is the only thing on screen for three scrolls; a loading spinner or a blank box
in the first frame (give it a poster, start loading before it enters); used twice.
**Uneven mosaic**
- *When.* A body of work, or one place seen several ways: five to twelve pictures.
- *Geometry.* Two columns, or rows of three on a 24-column grid. Two or three picture widths (for
example 32%, 48% and 65% of the width, or 5, 7 and 12 columns), neighbours offset vertically by
120–190px, 130–450px between rows, one or two ratios, a caption under each.
- *Phone.* One column alternating full width and about 70% width, or two columns of 120–180px tiles
that keep the offsets.
- *Fails.* Random ratios with random offsets, which read as a stock dump. Captions in 9–12px mono with
index numbers, the award-site habit: here the caption is one line in the text face at body size, with
no number.
**Horizontal gallery**
- *When.* A set people browse in any order: products, rooms, events, a team.
- *Geometry.* Cards 25–35% of the width (435×326 with 20px gaps at 1440: three in full and a fourth
cut by the edge), or a strip at one fixed height with mixed widths (576px tall in the study), or a
slider whose centre slide is about 73% of the width with 170–180px of each neighbour showing. Always
cut by the right edge so it reads as more. A marquee of pictures moves slowly: 60–120s per loop.
- *Phone.* One card at 75–80% of the width (290–300px of 390) with the next one showing; swipe.
- *Fails.* Arrows and no drag or swipe; a marquee of things people need to read; a carousel holding the
page's one important picture.
**Panel sliding over a held picture**
- *When.* A change of chapter: the picture holds, and the next section rises over it.
- *Geometry.* The picture is sticky at 100vh; the next section's ground slides up over it at full width,
its top edge straight or shaped from the subject (the study has the arch of a building's windows).
Here it is a solid panel with square corners: no rounded sheet tops, no frosted glass.
- *Phone.* Unchanged.
- *Fails.* Decks of five or six cards whose earlier cards linger as slivers; stacking for its own sake.
**Even grid**
- *When.* An archive only (layout.md §5): a shop range, the team, a menu, the full list of work.
- *Geometry.* Two, three or four across, equal cells, 8–16px gaps, one ratio (4:5 is the commonest),
the caption under each picture.
- *Phone.* Two across for products, one for work and people.
- *Fails.* Three equal cards as "features". Cards with a dark gradient at the foot so the label can sit
on the picture, common in the study: here the label goes under the picture.
**Index with hover preview**
- *When.* A list of eight or more projects or clients where the names matter as much as the pictures.
- *Geometry.* Rows on a 45–80px pitch, names at 30–60px. One picture at a time, about a third of the
width at 16:9 or a portrait 300–450px tall, at a fixed place (the right half from the 50% line, or
the centre), swapped within 0.3s. A version that needs no mouse: the names scroll across a picture
fixed at the centre and the picture shows whichever name is crossing it. In the study the picture
sits on top of the names; here nothing lies on top of large type, so the names pass in front of it.
- *Phone.* No hover. Give each row a thumbnail about 100px wide, or use the scroll version.
- *Fails.* Pictures that never appear on a touch screen. Rows divided by hairlines, the award-site
habit: here rows are separated by space, and the active row by ink or a change of ground.
**One picture on a field, or one small picture beside a statement**
- *When.* A pause in the page: a single portrait or object. Or a small picture that gives a statement
a face.
- *Geometry.* A single centred picture at 3:4 and 75% of the viewport height, on a ground taken from
the picture. Or a small picture 7–26% of the width (108–374px) beside, above or between the lines
of a 48–136px statement, aligned to one of its lines.
- *Phone.* The picture above the statement at 60–100% of the width.
- *Fails.* Used as the default for every picture: a picture with equal margins on all sides reads as
placed by default (section 5).
**Small pictures between words**
- *When.* A statement about the things themselves (materials, people, products), on a screen of its
own.
- *Geometry.* Two to four chips in the statement, from the cap height to three-quarters of the type
size tall and one to one and a half times as wide (50×46px in a 60px line; 113×75px between the two
words of a 150px name), all from one shoot, square corners. Give them a width and height so the lines
do not jump while they load.
- *Phone.* Keep them inline at the height of the text.
- *Fails.* More than four; chips from different shoots; a chip that carries information the words do
not.
**Picture inside or behind display type**
- *When.* The object is the subject: a product, a building, a person.
- *Geometry.* A small picture window sits inside a block of 150–200px display type and later opens
into the next screen; or the object stands behind the type, showing between and around the letters;
or it touches the edge of the word without covering a letter. In the study the object more often
stands in front and covers one or two letters, or a line of type runs behind a square picture; here
nothing lies on top of a headline (the inspector fails large text a fifth or more hidden), so the
type stays in front.
- *Phone.* Keep it, with the type at about 15vw, and check the word still reads.
- *Fails.* An object over the letters; type displaced or glitched by the picture.
**Cut-out object on a flat field**
- *When.* Products and objects. The highest mean score in the table, on 7 of the 36 top sites.
- *Geometry.* The object at 35–60% of the width on a flat ground taken from its own colours, standing
on the bottom edge or running off one side, with the type behind or beside it.
- *Phone.* The object at 80–90% of the width under the headline.
- *Fails.* A render floating in a void with no ground; a drop shadow added to a cut-out (no resting
shadows here; keep a contact shadow only if it was photographed).
**Scattered collage**
- *When.* Archives, ephemera, a festival of many small things. Sparingly: it is about as common on the
lowest pages as on the top ones.
- *Geometry.* Six to sixteen pictures of 100–300px at a few sizes around a centred heading, never over
it.
- *Phone.* Re-stack as overlapping pairs in one column.
- *Fails.* Tilted prints and stickers with offset shadows, common in the study and not a treatment
here (section 6); pictures crossing the text.
**Small card docked in a corner**
- *When.* A standing pointer to the reel or to the next thing.
- *Geometry.* About 216×144 to 240×140, 30–43px from the corner; one per page.
- *Phone.* Drop it, or move it into the flow.
- *Fails.* Content scrolling under it; a second one.
**3D gallery**
- *What.* Pictures on a ring, sphere, helix or tunnel in a WebGL scene, turned by the wheel or by
dragging.
- *In the study.* 12 sites, 4 of them among the 16 lowest; mean score 7.30 against 7.44, usability 5.9
against 6.35.
- *Here.* Do not build one. Show the same pictures as an index with previews or a horizontal gallery.
### Footage
**What the study measured.**
- Footage on 38 of 91 sites (42%): 18 of the 36 top sites, 19 of the 39 in the middle, 1 of the 16
lowest. A site with footage carries a median of three or four clips (middle half 1–8).
- Footage covers at least half of the first screen on about a quarter of the sites, and is full-bleed
on nearly all of them.
- Clips are short: median 10s, middle half 5–14s; 61% run 10s or less and 88% 20s or less.
- Every decorative clip is muted, loops, plays inline and has no controls. The autoplay attribute is on
44% of clips; the rest are started by script, mostly as they come into view.
- Only 31% of clips have a poster (13 of the 38 sites). The rest show a blank or grey box until they
play, and the panel recorded those as blank first paints. Here a poster is required (section 11).
- Sources: 1920×1080 is the commonest, then 1280×720; tall clips at 450×800 or 540×960 for phones and
portrait tiles. Median source width 1,080px.
- On the page: 16:9 41%, square 19%, portrait (4:5 to 9:16) 25%, 4:3 or 3:2 12%. Corners square on 75%
(the rest 8px or less, or 20–32px). `object-fit: cover` on 84%, contain on 16% (product strips, letterboxed
films).
**Where it goes.**
- **Hero background:** full-bleed, cover, often oversized by 1–25% so a drift or zoom never shows an
edge. Either one slow take or a montage cutting every second or two.
- **Inline, as a picture:** in splits, tiles and cards, at about a third of the width (median 33%,
middle half 24–45%); a third of these clips are under a quarter of the width and fewer than one in
ten is full width. Slow, near-still subjects (water, skin, light, a turning object) read as living
photographs and sit well among stills.
- **The frame that grows** (above), for a reel.
- **A docked card** (above): one small clip in a corner.
- **On hover:** only two or three sites play clips on hover, all of them in an index or list. Nothing
that matters should play on hover only; phones have no hover.
- **Full films:** about ten sites offer a full film behind a play button that opens a player with
controls. None autoplays it.
- **Sound:** six sites offer sound behind a toggle; no clip starts with sound.
**How text sits on it.**
- Of about 30 sites with a full-bleed picture or film behind text on the first screen, about 14 set the
type straight on it and rely on the footage being graded dark where the type sits; about 10 dim it
(to half brightness, under a 30–40% dark layer, or with a dark fade along the bottom 180–590px);
about 6 keep the text off the picture, above it or on a solid card.
- What goes on footage in the study is the name or a headline at 47–136px and one short line, docked
bottom-left or centred. Where body text at 11–14px went on footage or a busy photograph, the panel
named it as a fault, on top-scored sites too.
- Here: one headline, one line and one action on footage at most, and never body text. Check contrast
where the type sits across the whole loop, not only on the poster; if any frame drops below 4.5:1
(3:1 for type of 24px and up), use the scrim in section 6 or a solid band.
**On the phone.** Footage re-crops to a full-screen portrait (a separate tall source, or
`object-position` on the subject); inset frames go full-bleed. One site in the study adds a 40% scrim
on the phone only, where the type covers more of the picture.
### Crops and treatment
- **Ratios.** Of 1,356 pictures and clips measured: 4:5 21%, 16:9 19%, 1:1 16%, 16:10 10%, 3:2 7%, 2:3
5%, 4:3 5%, 5:4 4%, 3:4 3%, wider than 2:1 6%, 9:16 or taller 3%. Landscape 52%, portrait 33%,
square 15%.
- **Two ratios do most of the work.** On sites with four pictures or more, the two commonest ratios
hold 76% of the pictures. The others (five ratios in all at the median) are exceptions with a reason:
one tall portrait in a run of landscapes, one wide band.
- **Square corners** on 94% of pictures; 62 of the 68 sites with pictures in the page keep at least 80%
of them square. Where corners are rounded, it is 5px or less on two-thirds of them, or 20–32px on
card interfaces, one value per site.
- **Cover.** `object-fit: cover` on 78%, contain on 10% (a product on a tile, a letterboxed film).
- **Untouched.** CSS filters touch pictures on only 9 sites, mostly for hover states. A handful use
black-and-white photography as a set; two multiply scanned paper onto the ground. Several keep the
interface black, white and grey and let the photographs carry all the colour.
- **Common in the study, not treatments here** (section 6): a fixed grain layer over the whole page
(about a third of the sites), duotone or tinted photographs, blurred copies behind a picture as a
glow, tilted prints and stickers with offset shadows.
- **Oversize only by the distance it moves.** 22 sites move pictures with the scroll, oversizing them by
1–25%. Oversize by the travel and no more, on one picture per screen.
- **Resolution.** The study serves pictures at a median 1.9 times their CSS width, but 17% are served
smaller than they are shown and full-width pictures at a median of only 1,440px. Here: twice the CSS
width, capped at about 2,400px (section 4).
### Text over pictures
- In the study, text sits on pictures often and is checked rarely: about 28 notes record text set
straight on a picture with no scrim, and the faults of 16 sites include text lost over pictures: 8–14px
labels and body over photographs, and navigation in a difference blend or with no backing passing
over them.
- Where it works, the text is display size and sits over the calm part (sky, a dark wall, the shadow
side), docked in a corner or along one edge. Two other forms in the study: a solid card in the
ground colour inset over the photograph and carrying the heading, and text on the white of a picture
that fades into the page.
- Here: on a picture put only display text, one short line and one action. Body text, captions and
labels go beside or under it. Measure contrast at the text's own position (4.5:1, or 3:1 for 24px and
up). If it fails, move the text to a calmer part, put it on a solid card in the ground colour, or use
the scrim in section 6. A fixed navigation that passes over pictures gets a solid band, or hides
while scrolling down.
### Rules for a generator
1. **About a third of the page is picture.** Across the scrolled screens, pictures fill 30–40% of the
area, and at least one screen in four carries no picture (top sites: 30% and 28%).
2. **A picture leads its screen or stays small.** A leading picture takes at least 60% of its screen;
an accent stays under 10% of it. Avoid screen after screen of pictures at 20–40%: in the study 42% of
screens are 0% and 19% are 90% or more.
3. **Three to five placements per page,** and no two neighbouring sections with the same one. Sites
with two or fewer average 7.31, sites with three or more about 7.5.
4. **Six to twelve pictures, never fewer than four.** Study median 7; top sites 4–27; lowest 2.
5. **Two ratios for at least 75% of the pictures** (76% in the study), a third only for a deliberate
exception. Defaults: 4:5 for portraits, 16:9 or 3:2 for landscapes.
6. **Square corners on every picture** (94% in the study). If the direction rounds them, one radius of
4px for every picture and clip.
7. **Split pictures take 42–50% of the width, or two-thirds,** at the full height of the section and
touching the viewport on their outer side; the text starts on the 50% line. On the phone the
picture goes first, full width.
8. **Full-bleed bands are 100% wide and 100–150vh tall,** one every three or four screens, oversized by
10% at most for parallax, with text only in one corner at least 32px in. On the phone, re-crop to a
full-screen portrait.
9. **One inset frame that grows per page,** from 35–50% of the width to the full screen over
720–2,700px of scroll, with nothing else moving on that screen.
10. **Horizontal galleries show three cards and a cut fourth:** cards 25–35% of the width with 16–20px
gaps; on the phone one card at 75–80% of the width with the next one showing.
11. **One moving picture per screen.** Clips of 5–15s (median 10s), muted, looped, inline, with a
poster, paused out of view; a hero source of 1920×1080 under 10MB, inline clips at 1280×720 or
smaller under 5MB.
12. **Only display text on a picture:** a headline, one line and one action at most, with contrast
measured at the text's position across the whole loop. Body text, 16px or more, goes beside or
under the picture, never on it.
@@ -0,0 +1,292 @@
# Layout
How the page is put together: margins, the grid, where text sits, how sections differ, navigation,
endings and the phone. The direction sheet gives the specific compositions; this file gives the habits
behind them, taken from the panel's notes on the 114 studied sites that scored 7.3 or higher (counts
below are hand tallies from those notes, so read them as "about").
## Contents
1. What strong pages do
2. Margins and the grid
3. Where text sits
4. Empty space
5. The frame: margins, balance and titles
6. Sections: carry the idea, vary the arrangement
7. Navigation
8. Endings
9. The phone is a second composition
10. How pages fail
## 1. What strong pages do
- **Tight margins, content to the edges.** About 45 of 114 use outer margins of 32px or less; only
about 8 use 80px or more. The centred 1100–1300px container is the minority among strong sites and
the usual frame of the ones the panel called stock.
- **Text hangs from an off-centre line.** About 22 anchor text to one fixed line that is neither the
centre nor the margin: a third across, 42%, the midpoint.
- **Two or three type sizes with nothing between** (about 30): one very large with one small, or
everything small with the scale given to the image.
- **One thing per screen** (about 20): one statement, one object, one project.
- **Sections told apart by their ground.** About 20 change the whole background colour at each
section; about 12 alternate light and dark; about 10 never change and separate by space alone.
- **Sections recomposed, not repeated.** About 14 rearrange every section; about 12 alternate a
full-bleed image screen with a near-empty type screen.
- **The system broken once, on purpose** (about 14): one centred line on a split page, one paper card
on a dark page, colour held back until the last screen.
- **Often no navigation bar** (about 45), and endings that are a designed scene rather than link
columns.
- **Few words.** Median 575 on the whole study; some of the best carry under 200.
## 2. Margins and the grid
```css
:root {
--margin: clamp(1rem, 2.2vw, 2rem); /* 16–32px: tight, so content reaches the edges */
--gutter: clamp(0.75rem, 1.4vw, 1.5rem);
}
.wrap { padding-inline: var(--margin); }
.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gutter); }
```
- One pair of margins and one grid for the whole page. Every left edge is a column line.
- Do not wrap the page in a `max-width: 1200px; margin: auto` container by reflex. Let large things
(type, images, colour fields) run the full width, and constrain only the measure of running text
(`max-width` in `ch` on the paragraph, not on the page).
- On very wide screens cap the layout at about 1760–1920px, or let type sized in `vw` stop growing with
a `clamp()`.
- Directions that call for wide margins (uneven photographs, the quiet instrument) say so on the
sheet; follow the sheet.
- Never draw the grid. Column lines, crosshairs and guides are banned ornaments here even though they
are fashionable; the grid is felt through alignment.
## 3. Where text sits
- **Pick the anchor line and reuse it.** If the hero headline starts at column 5, the section
headings, the pricing figure and the footer links start there too. Two or three anchor lines on a
whole page; six means nothing was decided.
- **Off-centre beats centred.** A heading in the left third with its paragraph in the far-right
column on the same baseline and nothing between; a headline in the bottom third under a deliberate
void; text docked in corners with the middle given to the picture.
- **Opposite ends.** Two short texts at opposite ends of one baseline (name left, one-line description
right) is the commonest alternative to the centred stack.
- **Centred is a decision.** When a strong page centres, it uses one axis with wide empty flanks and a
single element per screen, not a centred stack of heading, paragraph and two buttons.
- **Rows for long copy:** heading in the left third, body in the right two-thirds, the same split for
every row on the page.
- Stagger sparingly: a second line indented by a third so a two-line headline steps across the screen.
## 4. Empty space
Empty space is placed, not left over. In the study of 97 Sites of the Day captured in October 2026,
readers judged every large empty area either a rest, which something closes, or a hole, which nothing
closes. Strong sites have plenty of rests. About half of them still leave at least one hole, and the
holes are what visitors name ("big random blank spaces").
**A void is placed when something closes it.** In the readers' notes the closer is, most often:
- the picture itself (sky, ground, the object, the film) with the type docked at its edge;
- a picture across the void: the photograph at the far side of an empty band, the full-width row
rising under a statement, the opposite picture in a stagger;
- the type: a headline large enough to span the space, or two short texts at the two ends of one
baseline;
- on a one-screen page, the wordmark, the navigation and one fact in the corners, like a mount around
a print.
About one closer in five in the study is a hairline or grid line. That device is banned here; close the
void with the picture, the type, a step in ground tone or a field of flat colour.
**Where holes come from.** In order of frequency in the study:
1. A gap between sections stretched too far: bands of 250–760px of plain ground.
2. Text in one half of a section and nothing in the other.
3. Content that arrives late: a reveal not yet fired, so the visitor scrolls onto bare ground while the
words fade in.
4. A short block stretched to a full screen: a footer padded to the viewport height, a section title
alone on a whole screen.
5. A pinned scene that holds almost nothing for several screens.
6. Pictures of different heights top-aligned in a row, leaving a hole under the short one.
**Rules**
- Decide where the void is and name what closes it. If nothing does, it is a hole: move the section's
picture into it, make the main element bigger, or take the space out.
- Keep the void clean: no stray caption, no decorative shape, no faint watermark, no grid lines.
- Sections sit 120–240px apart on desktop, counted from the last line of one to the first line of the
next (the median in the study is about 230–250px), and 72–112px on a phone. A change of ground
colour lets the gap be a little smaller. More than about 40% of a screen of plain ground between two
sections is a hole.
- The gap between sections is clearly larger than any gap inside one. Related things sit close and
unrelated things far; a heading has at least twice as much space above it as below.
- No stretch of the page taller than about half a screen holds nothing at all. In the study, runs of a
screen or more with nothing in the page were almost always scroll distance for a pinned scene; the
pinned stage must then hold something at every scroll position.
- Content is in place on the first paint. An entrance moves what is already there (see `motion.md`);
it never leaves a screen of bare ground while the words arrive.
- Size a section to its content. `min-height: 100vh` belongs to a section that fills the screen, not
to a footer or a lone heading.
- In a row of pictures of different heights, align the bottoms or put something under the short one,
or crop the row to one ratio.
- A rest is one thing on a screen: a single line, one object, one picture or a field of colour. Once
per page at most. A screen of nothing is never a rest, and the inspector fails two near-empty screens.
- When a section looks empty, the answer is almost never "add something". Make the main element
bigger, or cut the section.
## 5. The frame: margins, balance and titles
The frame is what readers judge before any detail: "no good margin", "everything is shifted to one
side", "no clear title", "random unclear sections". The inspector measures each of these. The numbers
are from the study of 97 Sites of the Day (October 2026).
**One pair of equal margins.**
- The left and right margins are the same, at every width. On the studied sites with scrolling
sections the two sides match to a few pixels: 16 and 16, 20 and 21, 32 and 32, 140 and 137. No
strong page in the study was pushed to one side.
- The usual margin is tight: median about 28px at 1440, half the sites between about 18 and 45px, a
few at 80px or more. Here it is 16–32px (section 2), and never under 16px at any width, on the phone
included. About one award site in seven sets text 8–15px from the edge; that studio habit is
excluded here.
- The navigation sits on the same margins as the content. The commonest small fault in the study is a
header on one inset and the page on another (40px against 20px, or 34px on the left against 10px on
the right).
- A bleed is deliberate and starts from the margin: a row of pictures that runs off the right edge
begins on the left margin; a display line that bleeds starts or ends on one. A bleed never moves the
margin.
- Asymmetry belongs inside the frame: an off-centre anchor line (a third across, 42%, the centre line)
on symmetric margins.
**Use the width the page has set up.**
- In the study about one text section in a hundred leaves half the width empty. Text that sits in one
half is common, in roughly one site in three, but the other half then holds the section's own
picture (most often), a full-bleed picture behind the text, a second column, a list or a figure.
- When the text takes one side, put the picture, the second column, the list or the large number on
the other. Never leave the other half to plain ground.
- The middle counts too: a card at the left edge and a lone button at the right with the middle third
empty is the same hole.
- The first screen counts too. When the sentence and the button take the left third above a name
docked at the foot, the upper right holds something (the picture or film, one fact, the action) or
the name grows up to meet them; in testing, 60% of a first screen left as plain ground under the
navigation read as a hole, and the inspector now warns when one empty block takes 28% of the first
screen or more.
- A display line fitted to the width is fitted by measurement (a small script that sets its size from
the content box, or SVG text with `textLength`), never by a size guessed for one face: a name that
starts on the margin and runs a few pixels off the other edge reads as a mistake.
- A centred composition has one axis, equal flanks and one thing on it, not a stack of heading,
paragraph and two buttons.
**A title that says what this is, on the first screen.**
- Asked whether a stranger could say what the page is within five seconds, readers found that 40 of
the 97 first screens say it in words, 42 say it only through pictures, small print or a bare name,
and 15 do not say it at all. The last group lost usability and content points, not design points.
- Size alone does not do it: 66 of the 97 first screens carry a display-size line, but 26 of those are
a name, a slogan or a place with nothing saying what is offered.
- The first screen has one `h1`, at display size (32px or more on desktop; on the clear first
screens in the study mostly 50–150px), two lines at most, that says what this is: the name with
what it does, the product with what it is for, the event with its date and place. If the `h1` is
the name, the line under it, at 18px or more, says what the thing is.
- It is there on the first paint. About a third of the studied pages still showed a loader or nothing readable 4.5
seconds.
**A heading on every section.**
- 82% of the text sections on the studied sites open with a heading: median 57px (40–96px for the
middle half), about three and a half times the body size, one or two lines.
- Headings start on the outer margin or on the page's inner anchor line (in the study about a third
each) and in the same place for every section. About a quarter of the headings in the study are
centred; centre one moment per page here.
- Where a studied section has no heading, the usual stand-in is a small label: caps, mono or a number
in brackets. All three are banned here. Put the label's words in a heading in the heading face at the
section size ("The rooms", "How it works", "Prices"), set them on the anchor line, and let the first
sentence carry the rest.
- A statement can open a section as its heading only when it names what the section holds.
## 6. Sections: carry the idea, vary the arrangement
The most common failure in every score band is a distinctive first screen followed by stock blocks:
icon cards, a three-up row, an accordion, a link-column footer. About 25 of the studied sites lose
points this way, including several above 7.5.
- **The idea survives to the last pixel.** Ask of each section, pricing and FAQ included: what does
the page's idea do here? If the page is a menu, the prices are menu lines. If the name is a
monument, the footer is the name again. If the object's shape is the motif, the button and the image
masks carry it.
- **No two neighbours share a layout.** Use the shape of each dealt blueprint (list, split, statement,
full-bleed, mosaic, table, strip, pinned …). Count them in the screenshot; a shape used three times
is the template showing.
- **Alternate dense and empty, image and type, light and dark** so the scroll has rhythm.
- **A repeated module is for archives only** (a work index, a menu, a schedule). Anywhere else,
repetition reads as a kit.
- **Break the system once.** After the page has established its rule, one planned exception (a single
centred line, one inverted section, one image at a different scale) is what makes the rule visible.
- **Short pages are fine.** A quarter of the studied sites are one or two screens. Do not add sections
to seem complete; each one must carry a claim (see `copy.md` §1).
## 7. Navigation
- The plain form: the wordmark at one end, three to five text links, one action, all at text size on
the page ground with no bar, border or background. Median link count in the study is 3.
- It is also normal to have less: small links in the corners, a single action, or the navigation
folded into the content (a list of section titles that is itself the index).
- If it stays fixed, it must never sit on top of content: check every scroll position in the
screenshots, especially on the phone. Floating pill bars are where the panel most often recorded a
nav covering text.
- On a phone: wordmark and one action in view; the links go in a simple full-screen sheet opened by a
text button ("Menu"), or move to the footer when there are only three.
- Navigation is small furniture, however it is drawn. On desktop it takes no more than about a tenth
of the first screen; on a phone it is one row. A blueprint that builds the navigation as a column of
colour blocks or a stack of tabs gets that treatment on desktop only, and collapses to one row on
the phone so the headline, the supporting sentence and the action stay in the first screen. In
blind tests a nav that ate the hero cost a whole point of usability.
## 8. Endings
- The page should end on a designed scene. In the study: the name at full width (about 18 of 114), the
hero repeated in inverted colours (about 12), a closing scene of its own such as the one contact
fact set as a headline (about 14), or simply one quiet line (about 22). Link-column footers appear
in the panel's notes only as criticism.
- The full-width name is strong but already a studio habit; use it when the direction is name-led,
otherwise prefer the blueprint dealt for the footer.
- Put the practical facts (address, hours, email, legal line) in the text face at a readable size, on
the same anchor lines as the rest of the page.
## 9. The phone is a second composition
Design the 390px screen as its own page, not the desktop page squeezed. Strong sites that neglect this
lose usability points; weak ones are often just broken.
- Each blueprint on the sheet has its own phone paragraph. Build it.
- Keep one dominant thing on the first screen. A wordmark that was 20vw wide on desktop might stack on
two lines at 26vw on the phone and be even stronger.
- Re-author line breaks; re-crop images to portrait (`object-position` per breakpoint, or a different
crop size in the image URL); keep unevenness with alternating widths rather than making everything
full width.
- Margins 16–20px. Body text 16–18px. Touch targets at least 44px high.
- Check 390px in the inspector's screenshots for every round: sideways scroll and three-line headlines
usually appear here first.
## 10. How pages fail
From the panel's notes on the 54 sites scoring under 6.8 (counts out of 54):
- The whole page is the category's stock template; swap the logo and it could be any competitor
(about 20).
- Centred headline, pill badge, two buttons, screenshot or tile grid below (about 9).
- A distinctive hero, then stock modules in the usual order (about 8, and about 17 more in the middle
band).
- A default typeface with no character (about 13).
- Text too small or too low in contrast (about 13); legibility lost over busy photographs (about 9).
- An undersized headline and no focal point (about 9).
- The page never says what the thing is, or shows no work (about 12 and 7).
- Thin content stretched over many screens, or a single gag (about 11).
- Gates, preloaders and entry pop-ups before any content (about 9).
- A phone layout that is the desktop shrunk, or broken (about 8).
- Fixed elements sitting on content; careless details such as colliding lines (about 7 and 5).
And the faults of the best sites, which are not to be copied: body text at 9–13px (about 24 of the 90
sites at 7.5 or more), information hidden behind the concept (about 20), the idea running out
mid-page (about 15), text over footage with no scrim (about 12), gates and blank first paints (about
12), floating elements colliding with content (about 11), and a first screen with no statement of what
this is and no action (about 9). Say what the thing is, and offer the action, on the first screen.
@@ -0,0 +1,980 @@
# Motion
Read this before adding any animation. The static page must already be finished and passing
`inspect.mjs`; motion is the last pass, never a way to rescue a weak layout.
## Contents
1. The budget
2. What strong pages do with motion
3. Four questions before any animation
4. Tokens
5. Scroll behaviours, ranked
6. Recipes (copy, then adapt)
7. Signature moves
8. Reduced motion and performance
9. What gives generated motion away
10. Rules for motion
## 1. The budget
A page that does not move reads as a mock-up; the inspector counts what moves while the page loads and
scrolls, and fails it under eight elements. The motion layer has five parts:
- **Two or three scroll scenes:** the big, scroll-driven moments award sites are remembered for (a
camera flying through a field of pictures, a picture that grows from a window to the whole screen,
type that crosses a held stage, cards that deal out and stack). The direction sheet deals them from
`library/scenes.json` when the library has scenes for the brief; `references/scenes.md` says how to
fill and fit them. They are where the page is bold; everything else stays quiet so they land.
- **One signature moment:** the thing a visitor would describe an hour later: the headline that rises
out of a mask, the image that opens like a shutter, the wordmark that scales with the scroll. The
direction sheet from `draw.mjs` names one. Build that one well; it is the only move allowed to be
loud.
- **Section entrances, choreographed per section:** the parts of each section arrive in reading order
(heading, text, picture, details), staggered 60–90ms, rising 16–32px with opacity, once. Vary the
manner with the content instead of stamping one preset everywhere: a list arrives row by row, a
picture opens from a mask or settles from a slight scale, a figure counts up once, a statement rises
line by line.
- **Feedback under the hand:** buttons that give on press, links that answer the pointer, cards and
pictures that respond on hover, a menu that opens from where it was clicked.
- **One moving picture:** a found clip as a muted loop with a poster frame, a slow drift or scale on
a large photograph, or a scroll-linked move on the hero picture.
What is still banned: the same fade-and-slide-up with the same timing on every block, which is the most
cited motion tell of generated pages; motion that hides content until a script runs, with no fallback;
bounce and elastic easing; anything that animates layout properties. Uniform motion says nobody decided
anything; choreographed motion with one protected moment says somebody did.
## 2. What strong pages do with motion
Measured on the latest 99 Awwwards Sites of the Day (July to October 2026), at 1440 and 390, with
the transitions, tweens and scroll triggers recorded while each page loaded and was scrolled. Where
the gallery does something the house rules ban, the house version follows.
- **The stack.** Two in three use a smooth-scroll library and three in five ship Three.js; GSAP is
named on 56. The quiet layer below (reveals, line rises, the ink fill) needs none of it: CSS scroll
timelines and view transitions do that work, though only 3 and 6 of the sites use them. The big
scroll scenes are built on GSAP ScrollTrigger, as on the award sites, through the shared stage in
`assets/scenes/` (`references/scenes.md`).
- **Curves.** The curves that really ran are strong ease-outs: expo out (`.16,1,.3,1` and
`.19,1,.22,1`) ran on 14 sites, cubic out on 13, quint out on 7, and by number of transitions the
quint and expo outs lead every chosen curve. In-out curves are kept for wipes and things that
travel (cubic and quart in-out, about 15 sites each). Ease-in appears only for exits, short, before
a slow arrival. Colour changes use plain `ease` (46 sites). Overshoot sits in 19 stylesheets and
ran on 2.
- **Durations.** Hover colour 200-300 ms; small interface moves 300-450 ms; reveals and line rises
600-1,000 ms; picture openings and wipes 1,100-1,600 ms. The median of everything that ran on a
site is 420 ms.
- **Staggers.** 20-40 ms between characters, 75-120 ms between lines, 50-100 ms between list items.
- **Scroll-linked motion is locked to the scrollbar.** Of the sites that scrub, 14 of 18 lock it with
no smoothing; the other four smooth it by 0.1-3 s. Reveals fire when the element's top is 80-95% of the way
down the viewport, once.
- **How much moves.** A median of 130 elements are moved by script on a page, but they come from a
few gestures: one split heading is 20-60 elements. The inspector's floor (8, warning under 16) is a
floor, not a target.
- **Waiting is the most common cost.** 60 of 99 open on a loading screen, 26 of them with a counter.
Half the usable pages were still unreadable 2.5 s after the browser said the page had loaded, a
third at 4.5 s. The preloaders were not covering slow pages (their documents loaded faster than the
rest) and the readers' design marks were the same whatever the wait; only usability fell. House
version: no preloader, the message readable on the first frame, the load sequence over by 1.2 s.
- **Pins cost length.** Two in three scrolling pages hold something sticky or pinned. Those pages
are twice as long (a median 16.6 screens against 7.5) for fewer words per screen (27 against 46).
House version: at most two held stages, about one screen per step, three screens per stage.
- **Reduced motion is mostly missing.** 35 of 99 have any `prefers-reduced-motion` rule, 16 have
three or more; 5 of 26 scroll-scrubbed scenes have one. Every recipe here keeps the finished state
as the reduced state.
## 3. Four questions before any animation
1. **How often will someone see it?** Once per visit (hero load, a section reveal): it can take its
time. Many times per visit (hover, menu, accordion): fast and small. Hundreds of times (keyboard
actions, typing): no animation.
2. **What is it for?** Valid answers: it shows where something came from, it confirms an action, it
keeps a change from being jarring, it explains how the product works, it is the signature moment.
"It looks nice" is only valid for the signature moment.
3. **Which easing?** Entering or leaving: ease-out, so movement starts the instant the eye arrives.
Moving between two places on screen: ease-in-out. Colour and opacity on hover: `ease`. Constant
travel (marquee, progress): `linear`. `ease-in` makes interfaces feel slow because the first frames
barely move; do not use it. The CSS keyword curves are weak, so use the tokens below.
4. **How long?** Press feedback 100–160ms. Hover 150–200ms. Menus and popovers 150–250ms. Dialogs and
drawers 250–450ms. Section and hero reveals 500–900ms. Leaving is quicker than arriving (about 70%).
## 4. Tokens
```css
:root {
--ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* entrances, moving hovers, UI under 600ms */
--ease-expo: cubic-bezier(0.16, 1, 0.3, 1); /* new: reveals of 800ms and longer, line rises, openings */
--ease-in-out: cubic-bezier(0.76, 0, 0.24, 1); /* wipes, panels, things that travel while visible */
--ease-soft: cubic-bezier(0.33, 1, 0.68, 1); /* slow scale and drift on photographs */
--ease-exit: cubic-bezier(0.55, 0.055, 0.675, 0.19); /* new: something leaving for good, short */
--t-press: 140ms;
--t-hover: 180ms;
--t-ui: 240ms;
--t-reveal: 800ms;
--t-open: 1200ms; /* new: picture openings and clip wipes */
--stagger-line: 90ms; /* new */
--stagger-item: 70ms; /* new */
}
```
How the study bears on each:
- `--ease-out` (quint out) is in 28 stylesheets with its near twin and is the single most frequent
chosen curve among transitions that ran. Keep it.
- `--ease-expo` is new. Expo out (`.16,1,.3,1` and `.19,1,.22,1`) is in 28 stylesheets and ran on 14
sites, more than any other chosen family, and it is the curve the long reveals use: line rises,
picture openings, card entrances of 0.9-1.6 s. Use it for anything at `--t-reveal` or longer; it
replaces `--ease-out` in the masked headline and the reveal-on-scroll recipes.
- `--ease-in-out` (quart in-out) matches its family (14 stylesheets, 7 ran). Keep it for wipes,
shutters, menus and page transitions.
- `--ease-soft` (cubic out) matches the cubic-out family (21 stylesheets, 13 ran), but the study uses it
for interface moves of 0.3-0.8 s, where `--ease-out` already serves. Narrow it to slow scale and
drift on photographs.
- `--ease-exit` is new. Ease-in is right for exactly one job: something that leaves for good (an
outgoing caption, a replaced slide, a closing panel), short, at about 60-70% of the matching
entrance. Never for anything the visitor is waiting to see.
- Not adopted: `.4,0,.2,1` (38 stylesheets) is a framework default, and overshoot curves (19) stay
banned on interface elements.
- `--t-hover` stays at 180ms. The study's colour hovers run 200-300ms; a little quicker reads as
more exact. A hover that moves something (a label roll, an arrow) takes `--t-ui` to 400ms.
## 5. Scroll behaviours, ranked
| Behaviour | Pages of 99 | What it serves | House version |
| --- | --- | --- | --- |
| Smooth-scroll library | 67 | A feeling of weight; makes scrubbed scenes easier to build | Optional, through `Stage.smooth()`: Lenis at lerp 0.1-0.14, off for touch and reduced motion |
| Held (sticky or pinned) stage | 44 | One picture or object carries several claims; chapters | Yes: the scroll scenes (`references/scenes.md`), two or three per page, one to four screens each; other sticky stages three screens at most |
| Split text | 40 | Lines and words arriving as they are read; hover rolls | Lines for headings and statements; words only for the ink fill; characters only for a heading of one to three words |
| Colour by chapter | 34 | Tells chapters apart without padding bands | Yes, as hard cuts at section edges; the header ink follows in 300ms |
| Scroll-scrubbed scene (camera, object, image sequence) | 26 | Explains a product by moving it | Yes, from the scene library, built on the shared stage with a finished composition for reduced motion |
| Parallax depth | 24 | Depth in collages; pictures drifting inside oversized frames | Travel under 8% of the element, pictures held at 106-110% in their frames |
| Marquee | 21 | Life on a static band | Existing "Edge-to-edge marquee": one per page, pausable |
| Masked line or word rise | 18 | Headings that arrive like set type | Yes: "Masked line reveal" on load, "Lines that rise on scroll" below the fold |
| Wheel drives a scene, no page scroll | 16 | Browsing one object | Not used (see 2.6) |
| Blur-in | 15 | Soft arrival | At most 8px of blur, 600ms, gone once settled; never on body text |
| Picture from inset frame to full-bleed | 14 | The hand-over from a type screen to the big picture | Yes: "Opening window", about one screen of scroll |
| Grey-to-ink text fill | 14 | Paces one statement | Yes, once per page, starting at 45% ink: "Ink fill" |
| Sheet slides over a held layer | 11 | Page-turn rhythm between chapters | Yes: new move "Sheet over a held picture" |
| Scramble or decode | 11 | A "code" voice | Not used (see 2.6) |
| Header ink by chapter | 10 | The fixed header stays legible | Yes, with colour by chapter |
| Horizontal track pinned to vertical scroll | 8 | Walking through rooms or a collection | Native drag row first; pinned only when the row is under three screens long, with keyboard support: "Sideways gallery" |
| Curtain footer | 8 | The page ends as a scene | Existing move |
| Rolling digits | 6 | Figures that arrive | Yes, for pages whose case is a handful of numbers: "Rolling figures" |
| Index with hover preview | 5 | Navigating work by name | Existing move, upgraded recipe |
Text, split by unit: characters on about 20 pages, lines on about 14, words on about 11. Character
splits serve short headings and button rolls; on long statements they leave fragments mid-scroll.
## 6. Recipes
All recipes keep the content visible when JavaScript is off, put every hidden state inside
`prefers-reduced-motion: no-preference`, and use the tokens above. Two notes that apply to several:
- **Scroll timelines need a scroller to watch.** `animation-timeline: view()` takes the nearest scroll
container, and `overflow: hidden` makes one. A picture inside an `overflow: hidden` frame therefore
never animates. Use `overflow: clip` on frames, or name the timeline on the section
(`view-timeline-name: --hero`) and point the children at that name. This also applies to the
existing "Scroll-linked progress" recipe.
- **Scroll-linked means linear.** The scroll is the easing. 14 of the 18 scrubbing sites in the study
lock motion to the scrollbar, which is what CSS scroll timelines do.
All recipes keep content visible when JavaScript is off or slow: the hidden state only applies under
`html.js`, which a one-line inline script sets.
```html
<script>document.documentElement.classList.add('js')</script>
```
### Load sequence in 1.2 seconds
Everything is in the HTML and painted on the first frame; the sequence only adds arrival. Nothing
waits for the load event, and nothing in the navigation animates.
```html
<header class="hero">
<h1 class="display lines">
<span class="line"><span data-line style="--i:0">Stools for</span></span>
<span class="line"><span data-line style="--i:1">small kitchens</span></span>
</h1>
<p class="lead" data-load style="--d:420ms">…</p>
<a class="button" data-load style="--d:480ms" href="#stools">See the stools</a>
<figure class="hero__picture" data-open><img src="stool.jpg" alt="…" fetchpriority="high"></figure>
</header>
```
```css
.hero__picture { overflow: clip; margin: 0; }
@media (prefers-reduced-motion: no-preference) {
.js .hero [data-line] { transform: translateY(105%); animation: load-line 800ms var(--ease-expo) forwards; animation-delay: calc(100ms + var(--i) * var(--stagger-line)); }
.js .hero [data-load] { opacity: 0; transform: translateY(12px); animation: load-rise 500ms var(--ease-out) forwards; animation-delay: var(--d); }
.js .hero [data-open] { clip-path: inset(0 0 100% 0); animation: load-open 1000ms var(--ease-in-out) 150ms forwards; }
.js .hero [data-open] img { transform: scale(1.08); animation: load-settle 1100ms var(--ease-expo) 100ms forwards; }
}
@keyframes load-line { to { transform: none; } }
@keyframes load-rise { to { opacity: 1; transform: none; } }
@keyframes load-open { to { clip-path: inset(0); } }
@keyframes load-settle { to { transform: none; } }
```
The timeline: the headline's lines rise from 100 to 990 ms, the picture opens from 150 to 1,150 ms,
the sentence and the button arrive from 420 to 980 ms. All settled by 1.2 s, four things at most.
Preload the display font (`<link rel="preload" as="font" type="font/woff2" crossorigin>`) so the lines
rise in the real face, and give the picture `fetchpriority="high"` and a ground colour sampled from it,
so a slow image opens onto its own colour rather than a blank box. Study: 22 of 91 pages were readable
at the first intro frame and 11 more by 1.2 s; the intros the readers called respectful finished in
about 1-1.2 s; a third of the pages were still unreadable at 4.5 s.
### Masked line reveal (the classic gallery headline)
Each line slides up from behind its own baseline. Because headlines are capped at two lines, you set
the line breaks yourself and wrap each line.
```html
<h1 class="lines">
<span class="line"><span data-line style="--i:0">Stools for</span></span>
<span class="line"><span data-line style="--i:1">small kitchens</span></span>
</h1>
```
```css
.lines .line { display: block; overflow: hidden; padding-bottom: 0.08em; margin-bottom: -0.08em; }
.lines [data-line] { display: block; }
@media (prefers-reduced-motion: no-preference) {
.js .lines [data-line] {
transform: translateY(105%);
animation: line-up 900ms var(--ease-out) forwards;
animation-delay: calc(var(--i) * 110ms + 150ms);
}
}
@keyframes line-up { to { transform: none; } }
```
The `padding-bottom` / negative margin pair stops descenders (g, y, p) being clipped by the mask.
On a narrow phone the two authored lines may each wrap; check the mobile screenshot, and if they do,
author a shorter mobile break with a `<br class="only-wide">` pattern or reduce the size.
### Reveal on scroll (once)
For the two or three sections that deserve it, not for every block.
```css
@media (prefers-reduced-motion: no-preference) {
.js [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity 700ms var(--ease-out), transform 900ms var(--ease-out); }
.js [data-reveal].is-in { opacity: 1; transform: none; }
}
```
```js
const io = new IntersectionObserver((entries) => {
for (const e of entries) if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); }
}, { rootMargin: '0px 0px -12% 0px', threshold: 0.1 });
document.querySelectorAll('[data-reveal]').forEach((el) => io.observe(el));
```
Never attach reveals with a scroll listener, never replay them when the element re-enters, and never
leave anything at `opacity: 0` if the observer fails to fire (the `html.js` gate handles no-JS; the
`unobserve` handles replays).
### Image shutter
An image opens from one edge while scaling down slightly inside its frame. Gallery staple; use it for
the one hero or showcase image.
```css
.shutter { overflow: hidden; }
@media (prefers-reduced-motion: no-preference) {
.js .shutter { clip-path: inset(0 0 100% 0); transition: clip-path 1100ms var(--ease-in-out); }
.js .shutter img { transform: scale(1.12); transition: transform 1500ms var(--ease-soft); }
.js .shutter.is-in { clip-path: inset(0); }
.js .shutter.is-in img { transform: none; }
}
```
### Press and hover
```css
.button { transition: transform var(--t-press) var(--ease-out), background-color var(--t-hover) ease, color var(--t-hover) ease; }
.button:active { transform: scale(0.97); }
.link { text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 0.2em; text-decoration-thickness: 0.07em; transition: text-decoration-color var(--t-hover) ease; }
@media (hover: hover) and (pointer: fine) {
.button:hover { background-color: var(--accent-strong); }
.link:hover { text-decoration-color: currentColor; }
}
```
Hover styles go behind `(hover: hover)` because touch devices fire hover on tap and leave it stuck.
Hover should raise contrast, never lower it. Do not change font weight on hover (the text reflows).
Do not lift or scale every card on hover; one consistent, small response is enough.
### Scroll-linked progress without JavaScript
For a wordmark that scales as the hero leaves, an image that drifts a few percent, or a progress bar.
Wrap in `@supports` so browsers without scroll timelines get the static state.
```css
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.drift { animation: drift linear both; animation-timeline: view(); animation-range: entry 0% exit 100%; }
@keyframes drift { from { transform: translateY(-4%); } to { transform: translateY(4%); } }
}
}
```
Keep the travel small (under 8% of the element). Large parallax makes text hard to read and looks
dated.
### Pinned story
A section whose heading stays put while items pass. Use `position: sticky`; do not hijack scrolling.
```css
.story { display: grid; grid-template-columns: 5fr 7fr; gap: var(--gutter); align-items: start; }
.story__title { position: sticky; top: 18vh; }
@media (max-width: 800px) { .story { grid-template-columns: 1fr; } .story__title { position: static; } }
```
### Index with preview, anchored to the row
Rows of names; pointing at a row or tabbing to it opens one frame level with that row. The frame
follows the row, never the pointer.
```html
<section class="index">
<ul class="index__list">
<li><a class="index__row" href="/work/harbour-house" data-preview="/img/harbour-house.jpg">
<span class="index__name">Harbour House</span><span class="index__year">2025</span>
<img class="index__thumb" src="/img/harbour-house-small.jpg" alt="" loading="lazy" width="640" height="400">
</a></li>
<!-- … -->
</ul>
<div class="index__frame" aria-hidden="true"><img alt=""></div>
</section>
```
```css
.index { position: relative; }
.index__list { list-style: none; margin: 0; padding: 0; }
.index__row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0 var(--gutter); padding-block: 0.3em; font-size: var(--step-head); color: inherit; text-decoration: none; transition: opacity var(--t-hover) ease; }
.index__thumb { order: -1; flex-basis: 100%; width: 100%; height: auto; margin-bottom: 0.4rem; }
.index__frame { display: none; }
@media (hover: hover) and (pointer: fine) {
.index__list { max-width: 62%; }
.index__thumb { display: none; }
.index__list:has(.index__row:hover, .index__row:focus-visible) .index__row:not(:hover, :focus-visible) { opacity: 0.4; }
.index__frame { display: block; position: absolute; top: 0; right: var(--margin); width: 30%; aspect-ratio: 4 / 3; z-index: 1; pointer-events: none; clip-path: inset(100% 0 0 0); transition: clip-path 320ms var(--ease-out), transform 360ms var(--ease-out); }
.index__frame.is-open { clip-path: inset(0); }
.index__frame img { width: 100%; height: 100%; object-fit: cover; }
}
@media (prefers-reduced-motion: reduce) { .index__frame { transition: none; } }
```
```js
(() => {
const root = document.querySelector('.index');
if (!root) return;
const frame = root.querySelector('.index__frame');
const picture = frame.querySelector('img');
const fine = matchMedia('(hover: hover) and (pointer: fine)');
let warmed = false;
const show = (row) => {
if (!fine.matches) return;
if (!warmed) { warmed = true; root.querySelectorAll('[data-preview]').forEach((a) => { new Image().src = a.dataset.preview; }); }
picture.src = row.dataset.preview;
const y = row.offsetTop + row.offsetHeight / 2 - frame.offsetHeight / 2;
const max = root.offsetHeight - frame.offsetHeight;
const opening = !frame.classList.contains('is-open');
if (opening) frame.style.transitionProperty = 'clip-path'; // open in place, do not slide in from the top
frame.style.transform = `translateY(${Math.min(max, Math.max(0, y))}px)`;
if (opening) { frame.getBoundingClientRect(); frame.style.transitionProperty = ''; }
frame.classList.add('is-open');
};
const hide = () => frame.classList.remove('is-open');
root.querySelectorAll('.index__row').forEach((row) => {
row.addEventListener('pointerenter', () => show(row));
row.addEventListener('focus', () => show(row));
});
root.addEventListener('pointerleave', hide);
root.addEventListener('focusout', (e) => { if (!root.contains(e.relatedTarget)) hide(); });
})();
```
On touch screens each row shows its own picture above the name. Study: 5 pages navigate work this way;
frames from about 470x270 to large plates behind the list; the other rows dimmed to about 0.3 (0.4 here, the floor for dimmed text). The
image trails and cursor-borne previews seen elsewhere in the study are the gimmick version.
### Lines that rise on scroll
For a section heading or a statement whose line breaks you do not control. Splits after the fonts
load, reveals once, then gives the plain text back so it rewraps normally. Plain text only (no links or
emphasis inside).
```html
<h2 class="head" data-lines>Turned by hand in one small workshop</h2>
```
```css
[data-lines] .line { display: block; overflow: clip; padding-bottom: 0.12em; margin-bottom: -0.12em; }
[data-lines] .line > span { display: block; }
@media (prefers-reduced-motion: no-preference) {
.is-split .line > span { transform: translateY(110%); }
.is-split.is-in .line > span { transform: none; transition: transform 900ms var(--ease-expo) calc(var(--i) * var(--stagger-line)); }
}
```
```js
(() => {
const els = [...document.querySelectorAll('[data-lines]')];
if (!els.length || !('IntersectionObserver' in window) || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const restore = (el) => { el.textContent = el.dataset.text; el.classList.remove('is-split', 'is-in'); };
const split = (el) => {
const text = el.textContent.trim().replace(/\s+/g, ' ');
el.dataset.text = text;
el.textContent = '';
const words = text.split(' ').map((word) => {
const s = document.createElement('span');
s.style.display = 'inline-block';
s.textContent = word;
el.append(s, ' ');
return s;
});
const lines = []; // group the words by the line they landed on
for (const w of words) {
const last = lines.at(-1);
if (last && Math.abs(last.top - w.offsetTop) < 4) last.words.push(w.textContent);
else lines.push({ top: w.offsetTop, words: [w.textContent] });
}
el.textContent = '';
lines.forEach((line, i) => {
const mask = document.createElement('span');
const inner = document.createElement('span');
mask.className = 'line';
inner.style.setProperty('--i', i);
inner.textContent = line.words.join(' ');
mask.append(inner);
el.append(mask, ' ');
});
el.classList.add('is-split');
};
const io = new IntersectionObserver((entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
io.unobserve(e.target);
e.target.classList.add('is-in');
e.target.querySelector('.line:last-of-type > span')
.addEventListener('transitionend', () => restore(e.target), { once: true });
}
}, { rootMargin: '0px 0px -12% 0px' });
document.fonts.ready.then(() => els.forEach((el) => { split(el); io.observe(el); }));
let width = innerWidth; // a width change before the reveal: give the text back
addEventListener('resize', () => {
if (innerWidth === width) return;
width = innerWidth;
els.filter((el) => el.matches('.is-split:not(.is-in)')).forEach((el) => { io.unobserve(el); restore(el); });
});
})();
```
Study: masked rises from 100-120% below the line, 0.6-1.2 s, 75-120 ms between lines, triggered when
the block's top is 80-95% down the viewport. Reduced motion: the script does not run; the text is plain.
### Opening window
The hand-over from a type screen to the one big picture: the picture first shows as a window in the
middle of a held stage and opens to full-bleed over about one screen of scroll.
```html
<section class="opening">
<div class="opening__stage">
<img src="workshop-wide.jpg" alt="The workshop at first light, benches cleared" width="2400" height="1500">
</div>
</section>
```
```css
.opening__stage { height: 100svh; overflow: clip; }
.opening__stage img { display: block; width: 100%; height: 100%; object-fit: cover; }
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.opening { height: 200svh; view-timeline-name: --opening; --inset-y: 20%; --inset-x: 28%; }
.opening__stage { position: sticky; top: 0; animation: opening-window linear both; animation-timeline: --opening; animation-range: contain 0% contain 75%; }
.opening__stage img { animation: opening-settle linear both; animation-timeline: --opening; animation-range: contain 0% contain 75%; }
}
}
@media (max-width: 700px) { .opening { --inset-y: 18%; --inset-x: 8%; } }
@keyframes opening-window { from { clip-path: inset(var(--inset-y) var(--inset-x)); } to { clip-path: inset(0); } }
@keyframes opening-settle { from { transform: scale(1.15); } to { transform: none; } }
```
The section is two screens tall; the stage is held for one, and the window is fully open after three
quarters of it, so the full-bleed picture rests for a moment before the page moves on. Study: 14 pages;
start windows 38-60% of the width; runways 600-1,800 px, and the readers asked for one to two screens.
Without scroll timelines, and under reduced motion, the picture is a full-bleed screen.
### Held picture, passing captions
The pinned story built for photographs: the picture holds on one side while short captions pass on
the other; the caption at the middle of the screen is lit and its picture is shown.
```html
<section class="steps">
<figure class="steps__media">
<div class="steps__pictures">
<img src="seasoning.jpg" alt="Ash boards stacked to season" class="is-on">
<img src="turning.jpg" alt="A leg being turned on the lathe">
<img src="finish.jpg" alt="Oil rubbed into a finished seat">
</div>
<figcaption class="steps__count" aria-hidden="true"></figcaption>
</figure>
<ol class="steps__list">
<li class="steps__item"><h3>Seasoned for a year</h3><p>…</p></li>
<li class="steps__item"><h3>Turned by hand</h3><p>…</p></li>
<li class="steps__item"><h3>Finished with oil</h3><p>…</p></li>
</ol>
</section>
```
```css
.steps { display: grid; grid-template-columns: 7fr 5fr; column-gap: var(--gutter); }
.steps__media { margin: 0; align-self: start; } /* a stretched grid item cannot stick */
.steps__pictures img { display: block; width: 100%; height: auto; }
.steps__list { list-style: none; margin: 0; padding: 0; }
.steps__item { min-height: 80svh; display: flex; flex-direction: column; justify-content: center; }
.js .steps__media { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; justify-content: center; gap: 0.75rem; }
.js .steps__pictures { position: relative; aspect-ratio: 4 / 5; max-height: 80svh; }
.js .steps__pictures img { position: absolute; inset: 0; height: 100%; object-fit: cover; opacity: 0; transition: opacity 500ms ease; }
.js .steps__pictures img.is-on { opacity: 1; }
.js .steps__item { opacity: 0.4; transition: opacity var(--t-ui) ease; }
.js .steps__item.is-active { opacity: 1; }
@media (max-width: 800px) {
.steps { display: block; } /* the section, not a grid area, contains the sticky picture */
.js .steps__media { height: auto; z-index: 1; background: var(--bg); padding-block: 0.5rem; }
.js .steps__pictures { aspect-ratio: 3 / 2; max-height: 42svh; }
.steps__item { min-height: 60svh; }
}
@media (prefers-reduced-motion: reduce) { .js .steps__pictures img, .js .steps__item { transition: none; } }
```
```js
(() => {
const root = document.querySelector('.steps');
if (!root || !('IntersectionObserver' in window)) return;
const items = [...root.querySelectorAll('.steps__item')];
const pictures = [...root.querySelectorAll('.steps__pictures img')];
const count = root.querySelector('.steps__count');
const show = (i) => {
items.forEach((el, k) => el.classList.toggle('is-active', k === i));
pictures.forEach((el, k) => el.classList.toggle('is-on', k === i));
if (count) count.textContent = `${i + 1} of ${items.length}`;
};
const io = new IntersectionObserver((entries) => {
for (const e of entries) if (e.isIntersecting) show(items.indexOf(e.target));
}, { rootMargin: '-45% 0px -45% 0px' }); // a band across the middle of the screen
items.forEach((el) => io.observe(el));
show(0);
})();
```
Without JavaScript the pictures and captions are two plain columns. Study: inactive items at 20-40%
(dims of 10-15% read as missing text, so the floor here is 40%), crossfades of 400-600 ms, about one
screen per step; the readers asked for a visible count on any held sequence of more than two steps.
The count is a plain sentence in the body face, not a zero-padded numeral.
### Sideways gallery
A row of pictures that the vertical scroll moves sideways while the stage is held, for a short
collection (eight items or fewer). The native drag row is the base; the pinned version is added only
on wide screens with motion allowed, and only when the row is under three screens long.
```html
<section class="gallery">
<div class="gallery__stage">
<h2 class="head">Eight kitchens, one stool</h2>
<ul class="gallery__track">
<li class="gallery__item"><a href="/kitchens/leith"><img src="leith.jpg" alt="A narrow galley kitchen in Leith" width="1200" height="1500"><span>Leith</span></a></li>
<!-- … -->
</ul>
</div>
</section>
```
```css
.gallery__track { display: flex; gap: var(--gutter); list-style: none; margin: 0; padding: 0 var(--margin); overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: var(--margin); }
.gallery__item { flex: 0 0 min(72vw, 28rem); scroll-snap-align: start; }
.gallery__item img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; }
.gallery.is-pinned .gallery__stage { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; justify-content: center; gap: 2rem; overflow: clip; }
.gallery.is-pinned .gallery__track { overflow: visible; scroll-snap-type: none; }
.gallery.is-pinned .gallery__item { flex-basis: calc(62svh * 0.8); }
```
```js
(() => {
const root = document.querySelector('.gallery');
if (!root) return;
const track = root.querySelector('.gallery__track');
const wide = matchMedia('(min-width: 900px) and (prefers-reduced-motion: no-preference)');
let dx = 0, queued = false;
const place = () => {
queued = false;
if (!root.classList.contains('is-pinned')) return;
const p = Math.min(1, Math.max(0, -root.getBoundingClientRect().top / dx));
track.style.transform = `translate3d(${(-p * dx).toFixed(1)}px, 0, 0)`;
};
const measure = () => {
root.classList.add('is-pinned'); // measure the row as it would be when pinned
track.style.transform = '';
dx = track.scrollWidth - track.clientWidth;
const pin = wide.matches && dx > 0 && dx < innerHeight * 3;
root.classList.toggle('is-pinned', pin);
root.style.height = pin ? `${innerHeight + dx}px` : ''; // one pixel of scroll moves the row one pixel
place();
};
track.addEventListener('focusin', (e) => { // keyboard: bring the focused item into view
if (!root.classList.contains('is-pinned')) return;
const item = e.target.closest('.gallery__item');
const x = Math.min(dx, Math.max(0, item.offsetLeft - parseFloat(getComputedStyle(track).paddingLeft)));
scrollTo({ top: root.getBoundingClientRect().top + scrollY + x, behavior: 'instant' });
});
addEventListener('scroll', () => { if (!queued) { queued = true; requestAnimationFrame(place); } }, { passive: true });
addEventListener('resize', measure);
addEventListener('load', measure);
wide.addEventListener('change', measure);
measure();
})();
```
Study: 8 pages pinned a horizontal track; their pages ran a median 25 screens, and the readers' worst
cases spent 9 screens on three cards and 10,000-23,000 px of wheel on one track. Here the speed is 1:1
and the hold ends when the last picture reaches the margin. Phones, narrow windows, reduced motion and
long rows all get the native row with snapping, which also works with touch, trackpads and the
keyboard. Never put the only copy of a call to action inside the moving row.
### Receding hero
As the first screen leaves, its picture sinks a little and darkens while the words lift away a little
faster, so the hero recedes instead of simply scrolling off.
```html
<header class="hero">
<img class="hero__picture" src="kitchen.jpg" alt="…" width="2400" height="1600" fetchpriority="high">
<div class="hero__copy"><h1 class="display">…</h1><p class="lead">…</p></div>
</header>
```
```css
.hero { position: relative; min-height: 100svh; overflow: clip; display: grid; align-items: end; view-timeline-name: --hero; }
.hero__picture { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero::after { content: ''; position: absolute; inset: 0; background: var(--ink); opacity: 0; pointer-events: none; }
.hero__copy { position: relative; z-index: 1; padding: var(--margin); }
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.hero__picture { animation: hero-sink linear both; animation-timeline: --hero; animation-range: exit 0% exit 100%; }
.hero::after { animation: hero-dim linear both; animation-timeline: --hero; animation-range: exit 0% exit 100%; }
.hero__copy { animation: hero-lift linear both; animation-timeline: --hero; animation-range: exit 0% exit 70%; }
}
}
@keyframes hero-sink { to { transform: translateY(8%) scale(1.06); } }
@keyframes hero-dim { to { opacity: 0.35; } }
@keyframes hero-lift { to { transform: translateY(-6%); opacity: 0; } }
```
The `exit` range runs from the moment the hero's top meets the top of the screen until its bottom
does, which for a first screen is the first screen of scroll. Study: hero pictures that sink and dim as
they leave, a heading scrubbed from 140 to 60 px, a title card that scales down on the phone; the
readers called it quiet and the page "one sheet". Travel stays under 8%; the overlay is a flat ink at
up to 35%, not a gradient. Static where unsupported or reduced.
### Page to page
Navigation between pages of the site with the browser's own view transitions: the old page fades
quickly, the new one rises in, and the picture the visitor clicked travels into place as the new
page's hero. No library, no full-screen curtain.
```css
/* on every page */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }
::view-transition-old(root) { animation: vt-leave 200ms var(--ease-exit) both; }
::view-transition-new(root) { animation: vt-arrive 380ms var(--ease-out) 60ms both; }
::view-transition-group(*) { animation-duration: 480ms; animation-timing-function: var(--ease-in-out); }
::view-transition-image-pair(*.picture) { overflow: clip; }
::view-transition-old(*.picture), ::view-transition-new(*.picture) { height: 100%; object-fit: cover; }
@keyframes vt-leave { to { opacity: 0; } }
@keyframes vt-arrive { from { opacity: 0; transform: translateY(16px); } }
```
```html
<!-- on the index: the card picture of one project -->
<img src="/img/leith.jpg" alt="…" style="view-transition-name: kitchen-leith; view-transition-class: picture">
<!-- on that project's page: its hero picture -->
<img src="/img/leith-wide.jpg" alt="…" style="view-transition-name: kitchen-leith; view-transition-class: picture">
```
Each name may appear once per page. Browsers without cross-document view transitions simply navigate.
Study: 16 pages load a page-transition library and run full-screen curtains of 0.7-2.4 s; 6 use view
transitions. The new page should be readable within half a second.
### Ink fill
One statement that fills from part-ink to full ink as it rises through the screen, line by line. It is
readable before it fills; the fill only sets the pace.
```html
<p class="statement head" data-fill>Nine people, one workshop, no catalogue: every stool is made for the kitchen it goes to.</p>
```
```css
[data-fill] { --fill: var(--ink); color: var(--fill); }
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.is-filling > span { display: inline-block; color: color-mix(in oklab, var(--fill) 45%, transparent); animation: ink-fill linear both; animation-timeline: view(); animation-range: cover 20% cover 45%; }
}
}
@keyframes ink-fill { to { color: var(--fill); } }
```
```js
(() => {
if (!CSS.supports('animation-timeline: view()') || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
document.querySelectorAll('[data-fill]').forEach((el) => {
const words = el.textContent.trim().split(/\s+/);
el.textContent = '';
words.forEach((word, i) => {
const s = document.createElement('span');
s.textContent = word;
el.append(s, i < words.length - 1 ? ' ' : '');
});
el.classList.add('is-filling');
});
})();
```
Each word watches its own position, so the words of a line fill together as the line moves from three
quarters of the way down the screen to just past half way. Set `--fill` on a dark chapter to its ink.
Study: 14 pages scrub a statement from grey to ink; unread words started at about 25-30% and settled
frames still caught statements half pale, which reads as disabled text. Here: one statement per page,
three to five lines, 32px or larger, starting at 45% (about 3:1 against the ground, enough for large
text). Full ink without scroll timelines or under reduced motion.
### Rolling figures
For pages whose case is a handful of numbers: each digit rolls up to its value once, the columns a
beat apart. The real number stays in the text for screen readers and search.
```html
<p class="figure"><span class="display" data-roll>1987</span> the year the first stool was sold</p>
```
```css
.roll__digits { display: inline-flex; font-variant-numeric: tabular-nums; line-height: 1; }
.roll__col { display: inline-block; height: 1em; overflow: clip; }
.roll__strip { display: flex; flex-direction: column; transform: translateY(calc(var(--d) * -1em)); }
.roll__strip > span { height: 1em; }
@media (prefers-reduced-motion: no-preference) {
.roll:not(.is-in) .roll__strip { transform: none; }
.roll.is-in .roll__strip { transition: transform 1100ms var(--ease-expo) calc(var(--i) * var(--stagger-item)); }
}
```
```js
(() => {
const els = [...document.querySelectorAll('[data-roll]')];
if (!els.length) return;
els.forEach((el) => {
const value = el.textContent.trim();
const said = Object.assign(document.createElement('span'), { className: 'sr-only', textContent: value });
const shown = Object.assign(document.createElement('span'), { className: 'roll__digits' });
shown.setAttribute('aria-hidden', 'true');
let i = 0;
for (const ch of value) {
if (!/\d/.test(ch)) { shown.append(ch); continue; }
const col = Object.assign(document.createElement('span'), { className: 'roll__col' });
const strip = Object.assign(document.createElement('span'), { className: 'roll__strip' });
strip.style.setProperty('--d', ch);
strip.style.setProperty('--i', i++);
for (let n = 0; n < 10; n++) strip.append(Object.assign(document.createElement('span'), { textContent: n }));
col.append(strip);
shown.append(col);
}
el.classList.add('roll');
el.replaceChildren(said, shown);
});
if (!('IntersectionObserver' in window)) { els.forEach((el) => el.classList.add('is-in')); return; }
const io = new IntersectionObserver((entries) => {
for (const e of entries) if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); }
}, { rootMargin: '0px 0px -15% 0px' });
els.forEach((el) => io.observe(el));
})();
```
Study: 6 pages roll digit columns (stats, a year, coordinates), about 0.9-1.1 s per column on an
expo-out, staggered by column. Use it for three or four figures at display size, never in running text
and never for a value that changes after load. Reduced motion: the figure is set at its value.
### Accordion
```css
.acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 320ms var(--ease-out); }
.acc__panel > div { overflow: hidden; }
.acc[open] .acc__panel, .acc__item.is-open .acc__panel { grid-template-rows: 1fr; }
```
Animating `grid-template-rows` from `0fr` to `1fr` opens to the content's real height without measuring
it in JavaScript.
### Menu or dialog
```css
.menu { transform-origin: top right; transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out); }
.menu[hidden], .menu.is-closed { opacity: 0; transform: scale(0.97) translateY(-6px); pointer-events: none; }
```
Things open from where they were triggered (`transform-origin` at the trigger). Nothing enters from
`scale(0)`; start at 0.95–0.98. A centred dialog is the one case that scales from its centre.
## 7. Signature moves
The direction sheet names one. Each of these was seen repeatedly on the studied gallery sites.
| Move | What happens | Build with |
| --- | --- | --- |
| Masked headline | Lines rise from behind their baselines on load | Masked line reveal recipe |
| Shutter image | Hero or showcase image opens from an edge, settling from 1.12 scale | Image shutter recipe |
| Scaling wordmark | A giant wordmark shrinks or grows a few percent as the hero scrolls out | Scroll-linked recipe on `scale` |
| Pinned story | A heading holds while its items scroll past | Sticky recipe |
| Index with preview | Text rows; the hovered row shows its image | Index with preview, anchored to the row |
| Curtain footer | The last section is revealed from under the page as you reach the end | `position: sticky; bottom: 0` footer under a main with a background and `z-index` |
| Slow crossfade | Hero image crossfades through 3–4 photographs, 5–6s each | Stacked images, opacity keyframes, pause when the tab is hidden |
| Edge-to-edge marquee | One line of very large type travels sideways, slowly | Duplicate the content, `translateX(-50%)` linear loop, pause on hover and under reduced motion |
| Drag row | A horizontally scrollable row of large images with scroll snapping | `overflow-x: auto; scroll-snap-type: x mandatory`, no library |
| Colour by chapter | Each chapter is its own full-bleed colour field, so the ground changes at section edges; a fixed header takes the ink of the chapter beneath | Section backgrounds in CSS; `base.js` sets `--header-ink` from `data-ink`. Never recolour the whole page from an observer |
| Opening window | A type screen hands over to the big picture: a window in the middle of a held stage opens to full-bleed over about one screen | Opening window recipe |
| Ink-fill statement | One statement fills from 45% to full ink as it rises through the screen | Ink fill recipe |
| Sheet over a held picture | A full-bleed photograph holds for one screen; the next chapter slides up over it as an opaque sheet | A 100svh `position: sticky; top: 0` figure followed by a section with its own background and a higher `z-index`; no script |
| Sideways gallery | A short row of pictures moves sideways while the vertical scroll holds the stage | Sideways gallery recipe |
| Pushed line | One line of very large type is pushed sideways by the scroll, reading whole at the middle of its run | `animation-timeline: view()` on the line, `translateX` from 12% to -12%, linear; static where unsupported |
| Rolling figures | The few figures that make the case roll up to their values once | Rolling figures recipe |
| Carried picture | The picture clicked in a list travels into place as the next page's hero | Page to page recipe |
Pick by subject. A portfolio wants the index with preview; a restaurant wants the slow crossfade; a
product wants the shutter on its hero object. If the move says nothing about the subject, it is
decoration and the page is better without it.
Pick by subject, as before: the opening window for a product or a place with one great photograph; the
sheet for chapters that each own a picture; the sideways gallery for a short set of rooms, kitchens or
pieces; the pushed line for a brand whose claim is a word; rolling figures for a report, a fund or a
practice that leads with numbers; the carried picture for a portfolio whose index and case pages are
both built.
## 8. Reduced motion and performance
- `prefers-reduced-motion: reduce` means fewer and gentler, not none: keep opacity and colour changes,
drop travel, scale, parallax and autoplay. All recipes above are written inside
`no-preference` blocks so the reduced state is simply the finished state.
- Animate `transform`, `opacity` and `clip-path`. Animating `width`, `height`, `top`, `left`, `margin`
or `padding` forces layout on every frame. `grid-template-rows` for accordions is the accepted
exception because it is short and rare.
- Do not drive many children from one changing CSS variable on a parent; set the transform on the
element that moves.
- `will-change` only on an element that is about to animate, removed afterwards.
- Pause loops when off screen or when the tab is hidden.
- No scroll hijacking and no custom cursor. They cost usability, which is 30% of an Awwwards score and
the line most winners score lowest on. Smoothing only through `Stage.smooth()`, which stays off for
touch and reduced motion.
## 9. What gives generated motion away
- The same fade-up on every block.
- A dozen staggered entrances in the hero.
- Hover lift or zoom on every card and image.
- Bounce or elastic easing on interface elements.
- Infinite loops that mean nothing: pulsing dots, floating shapes, shimmering text, rotating badges.
- A reveal that never fires and leaves a blank band in the page. Run `inspect.mjs` after the motion
pass and look at every screenshot for missing content.
### Seen in the latest Sites of the Day
Seen in the study, with how often, and what the house does instead.
- **Preloaders and counters (60 of 99; 26 with a counter).** Half the usable pages were unreadable
2.5 s after load, a third at 4.5 s; the preloaders sat mostly on pages that had already loaded, and
25 of the 29 that loaded in under 3 s still showed nothing readable at 4.5 s. Counters stalled,
outlasted the load or were tweens with nothing behind them. House: no preloader. If a scene must
load, the HTML first screen (headline, picture, action) is there at once and the scene fades in
behind it when ready.
- **Gates (10 of 99): enter, sound, age or gesture before any content.** House: none. Sound is a
control on the page, off by default.
- **Scroll hijacking (16 wheel-driven scenes, 9 of them endless, plus 14 single screens where the wheel
does nothing).** The visitor cannot tell where they are, how much is left or how to get out, and the
keyboard and screen readers get nothing. Usability 6.01 against 6.50 for pages that scroll. House:
native scroll; any scene is driven by scroll position on a sticky stage that ends, with a count.
- **Smooth-scroll libraries (67 of 99).** They make the scrubbed scenes feel weighted, and they also
put a delay between the hand and the page; with a long ease the page keeps sliding after the wheel
stops.
- **Custom cursors (28 of 99).** Followers, image trails, a lens that smears the headline it sits on,
labels that only exist on hover; 10 of the 28 have any reduced-motion rule. House: the system cursor;
labels are on the page.
- **Scramble and typewriter text (11 and 5).** The headline unreadable for four seconds, body copy
garbled mid-scroll, actions held back until the typing ends. House: not used. Real words arrive whole.
- **Long held runways.** Pages with pins average twice the length for fewer words per screen; 17 pages
are 20 screens or longer, some with whole screens that hold only a slow rotation. House: about one
screen per step, three per stage, every held screen carries a line of text.
- **Content held at zero opacity.** Reveals that keep text invisible until the scroll settles, inactive
items dimmed to 10-15%, statements left half pale. House: reveals only below the fold, once, never
longer than 900 ms in view; dims no lower than 40%; fills start at 45%.
- **Motion over words.** Marquees and drifting objects that cross statements, a sliding line that crops
its own words, blur left on text mid-move. House: nothing moves across text the visitor is reading;
a moving line must read whole at some point of its run.
- **Overshoot and bounce (19 stylesheets).** House: still banned on interface elements.
- **Endless loops with no off switch.** Grain, pulsing dots, equalisers, spinning badges, running with no
reduced-motion rule on most pages. House: loops pause off screen and stop under reduced motion.
Where the study's work does something the house bans, the house version: hairline progress bars and
tick rails on held stages become a plain count in the body face ("2 of 4"); zero-padded counters
("01/08") become a plain count or nothing; crossfading gradient grounds become flat chapter colours with
hard cuts; monospace HUD labels become small text in the text face.
## 10. Rules for motion
1. **First frame.** The headline, the sentence and the picture are in the HTML and painted on the first
frame. The load sequence starts without waiting for the load event and is settled by 1.2 s, with
four elements at most.
2. **No preloader, no counter, no gate, no custom cursor, no wheel capture.** Smoothing only through
`Stage.smooth()` (Lenis at lerp 0.1-0.14, off for touch and reduced motion): weight, never a page
that keeps sliding after the hand stops.
3. **Curves.** `--ease-out` for anything under 600ms; `--ease-expo` for reveals of 800ms or more;
`--ease-in-out` for wipes and things that travel; `--ease-exit` only for things leaving for good;
`linear` for scroll-linked motion and loops; plain `ease` for colour. No overshoot.
4. **Durations.** Press 140ms; hover colour 180-200ms; hover that moves 300-400ms; menus 240ms; reveals
700-900ms; picture openings 1,100-1,300ms; exits 60-70% of the matching entrance.
5. **Staggers.** Lines 80-110ms; list items 50-90ms; characters 20-40ms and only in a heading of one to
three words; four steps at most in any one sequence.
6. **Reveals.** Fire once, when the element's top is 85-90% of the way down the viewport; rise 12-32px
or from a line mask; nothing in view stays hidden for more than 900ms; nothing above the fold waits
for scroll.
7. **Scroll scenes are where the page is bold.** The big moves come from the scene library
(`references/scenes.md`): the direction sheet deals two or three per page, each built on
`assets/scenes/stage.js`, each held for one to four screens, each with a phone version and a
finished composition. Inside a scene the scene's own numbers apply (a picture may grow from a
window to the whole screen, type may cross the stage, a camera may fly through a field of
pictures); rules 8 and 10 still hold for the text a visitor is reading.
8. **Outside the scenes, scroll-linked travel stays quiet.** Linear and locked to the scrollbar;
parallax under 8% of the element; scale between 0.94 and 1.15; built with scroll timelines inside
`@supports`, static without them. Other held stages: sticky, about one screen per step, three
screens at most, a line of text on every held screen.
9. **One of each.** One signature move (a dealt scene can be it); one moving picture; one marquee at
most. A quiet section between two scenes, so each one lands.
10. **Text stays readable.** Fills start at 45% ink; dims no lower than 40%; blur at most 8px and gone
once settled; nothing moves across text being read.
11. **Colour by chapter** as hard cuts at section edges; the fixed header's ink follows in 300ms.
12. **Horizontal motion.** The native drag row first; the pinned sideways gallery only for rows under
three screens long, with keyboard support.
13. **Page to page.** View transitions, 480ms at most, off under reduced motion.
14. **Reduced motion.** Every hidden state lives inside `prefers-reduced-motion: no-preference`; the
reduced page is the finished page; loops stop. Two in three award pages skip this, which is a
reason to do it, not a licence to skip it.
@@ -0,0 +1,356 @@
# The study
Everything numeric in this skill comes from here. Cite these figures; do not round them up or invent
others.
## Contents
1. Method and its limits
2. Scores
3. Where the idea comes from
4. Headlines
5. Type
6. Devices: what separates strong from weak, and what does not
7. Colour and shape
8. Imagery and page structure
9. What the inspector says about the gallery itself
10. The 25 highest-scored sites and their ideas
11. How the skill was tested
12. The October 2026 study: 100 Sites of the Day
13. The app-screen study
## 1. Method and its limits
- **Sample.** 248 sites collected on 5 October 2026: 156 from One Page Love (the latest gallery pages
and the category listings) and 92 Awwwards Sites of the Day (the three most recent listing pages).
247 loaded; 246 were re-measured with the final probe.
- **Capture.** Headless Chrome at 1440×900 and 390×844. For each site: the first viewport, five more
frames while scrolling with real wheel input, the mobile first viewport, and a measurement pass in
the page (computed styles, text geometry, colours, fonts actually loaded).
- **Panel.** 21 model reviewers each studied about 12 sites from the screenshots and the measurements,
described them (idea, hero, type, colour, imagery, layout, sections) and scored them the Awwwards
way: design 40%, usability 30%, creativity 20%, content 10%. These are model judgements from still
frames, not human jurors and not the real Awwwards scores. Batches mixed both sources so each
reviewer saw a spread.
- **Usable sets.** 235 sites were usable for the panel (12 showed only a preloader, an entry gate or a
bot wall). 202 were measurable for typography (a page with at least 25 words and a detectable
headline). "Top" below means panel score 7.5 or higher (82 measurable sites); "weak" means under 6.5
(23 measurable sites).
- **Limits.** One capture per site on one day. Some heroes were caught mid-animation or behind a cookie
notice. WebGL-heavy sites are under-described by text measurements. The eyebrow detector counts any
small short label directly above a large heading, so it over-counts against the panel's stricter
reading (60% measured against 35% observed). Motion was inferred from frames, not watched. The sample
is whatever the two galleries had recently featured; it is fashion as much as quality.
## 2. Scores
| | All 235 | One Page Love (151) | Awwwards (84) |
| --- | --- | --- | --- |
| Median overall | 7.2 | 7.1 | 7.5 |
| Middle half | 6.8–7.6 | | |
| 8.0 or higher | 25 sites (11%) | | |
| 7.5 or higher | 90 sites (38%) | | |
| Under 6.5 | 29 sites | | |
Median by criterion: design 8, usability 7, creativity 7, content 7. Among the top 25: design 9,
usability 7, creativity 8, content 8. Usability is the one line that does not rise with the rest: the
best sites are no easier to use than the average one. Do not copy their gates, preloaders, tiny text
or hidden navigation.
## 3. Where the idea comes from
The clearest pattern in the panel's notes: high scores go with a concept taken from the subject, and
low scores go with its absence. The weakest entries' ideas were recorded in words like "none beyond the
standard dark SaaS launch formula", "no visual idea beyond clarity", "no strong idea". The strongest
were sentences only that subject could own (section 10).
Recurring engines among the 7.3+ sites, each the basis of a direction in the library:
- **The product demonstrates itself.** A colour picker's loupe reads the page's own background; a list
tool is sold with tiny working lists; a photo app's page is a wall of its own output.
- **The page is an artefact from the subject's world.** A wedding site that is the invitation; an
archive as tabbed folders; a coupon with a cut line; a portfolio as a contact sheet.
- **The name is the hero.** Wordmarks as wide as the screen, a photograph set inside the name like a
letter, a name split to opposite corners with the page between.
- **One motif from the subject drives everything.** A tool's octagonal section as button, mask and
loader; two circles for two accounts; a café's stripes.
- **Dressed as another category.** A money app as a streetwear drop; an industrial roaster as a design
object; a parcel service as a fairground.
- **The place carries it.** One full-bleed view and a name along the bottom edge.
- **A fixed stage.** A title or object stays put while content swaps beneath it.
- **One section, one colour.** Flat fields as chapters.
- **Radical understatement.** A 27px headline and tiny live demonstrations, carried through completely.
Hero types, by the panel's classification (top sites against weak sites): full-bleed media with type
over it 16% / 10%; giant wordmark 14% / 0%; typographic poster 13% / 7%; text beside media 10% / 3%;
**centred statement with media below 7% / 24%**. The centred headline over a screenshot is the weak
sites' signature layout.
## 4. Headlines
| Hero headline | All | Top | Weak |
| --- | --- | --- | --- |
| Words, median | 5 | 5 | 5 |
| Words, middle half | 3–9 | 2–8 | 3–8 |
| Characters, median | 32 | | |
| One line at 1440 | 47% | | |
| Two lines or fewer at 1440 | 70% | 71% | 83% |
| Two lines or fewer at 390 | 57% | | |
| Three lines or fewer at 390 | 82% | | |
Awwwards heroes are shorter (median 4 words, 76% within two lines) than One Page Love's (6 words, 66%).
Section headings have a median of 3 words. Length does not separate strong from weak; nearly everyone
in the gallery writes short headlines. The two-line limit in this skill is a house rule that most of
the gallery already meets on desktop and that takes care to meet on a phone.
## 5. Type
| | All | Top | Weak |
| --- | --- | --- | --- |
| Hero size, median | 72px (5.0vw) | 6.0vw | 4.7vw |
| Hero size, middle half | 3.0–7.6vw | 2.5–9.7vw | 2.7–6.1vw |
| Largest text on the page, median | 6.4vw | 8.4vw | 5.0vw |
| Largest text, upper quarter starts at | 10.4vw | 12.5vw | 6.7vw |
| Hero on a phone, median | 40px | | |
| Hero tracking, median (middle half) | -0.01em (-0.03 to 0) | | |
| Hero leading, median (middle half) | 1.0 (0.95–1.1) | | |
| Body size, median (middle half) | 16px (14–18) | | |
| Headline ÷ body, median | 4.3 | 4.7 | 4.0 |
| Typefaces carrying 3%+ of text | one 31%, two 45%, three 22% | | |
| Distinct font sizes, median | 8 | | |
Hero weight: regular 48%, medium 18%, semibold 6%, bold or heavier 22%, light 6%. Capitals 24%.
Centred 39% overall, 52% on weak sites. Display classes by the panel: neo-grotesk 48%, transitional
serif 11%, geometric sans 10%, condensed 6%, extended 6%, old-style serif 4%, custom lettering 4%.
Strong sites push the extremes: their biggest type is bigger and their range is wider. Weak sites
cluster in the middle.
## 6. Devices: what separates strong from weak, and what does not
Measured in the page unless marked (panel).
**More common on weak sites (the data supports avoiding them):**
| Device | Top | Weak |
| --- | --- | --- |
| Icon-topped feature cards (panel) | 1% | 28% |
| Rows of identical cards with a small icon (measured) | 7% | 26% |
| Pill badges (panel) | 14% | 34% |
| Pill-shaped label above a heading (measured) | 5% | 26% |
| Tiny uppercase letter-spaced labels, any | 16% | 57% |
| Tiny uppercase letter-spaced labels, three or more | 6% | 43% |
| Gradient fills (not counting scrims) | 22% | 52% |
| Gradient text | 2% | 13% |
| Shadows on resting elements, any | 24% | 48% |
| Shadows, five or more | 10% | 30% |
| Emoji in the page | 10% | 26% |
| Eyebrow labels (panel) | 32% | 45% |
**No real difference:**
| Device | Top | Weak |
| --- | --- | --- |
| Backdrop blur anywhere | 32% | 39% |
| Italic-serif accent word (panel) | 4% | 7% |
| Thin rules, any | 61% | 52% |
**More common on strong sites (banned here anyway, as house style):**
| Device | Top | Weak |
| --- | --- | --- |
| Monospace labels (panel) | 44% | 24% |
| Monospace anywhere outside code (measured) | 45% | 26% |
| Index numbering (panel) | 38% | 21% |
Monospace captions, index numbers and ruled grids are the present fashion of award sites. They are
excluded from this skill by the owner's choice, not by the data: they are the devices generators copy
first, and a page that cannot lean on them has to find structure in space, tone and scale.
## 7. Colour and shape
- Dark page ground 41%; pure white 25%; pure black 12%; a warm off-white 13%.
- Scheme (panel): monochrome plus one accent 23%, light neutral 22%, dark neutral 14%, several flat
colours 13%, two-tone 12%, photographic 9%, one colour field 6%.
- Primary buttons are neutral (black, white, grey) on 52% of sites. One site in 246 has a terracotta
button; none has a violet one.
- Median number of distinct section grounds: 2 on top sites, 1 on weak sites.
- Button shape: top sites square (0–2px) 52%, fully round 23%, small radius 19%, medium (9–16px) 6%.
Weak sites fully round 59%, medium 24%, square 18%.
- Media corners are square on 73% of sites; where rounded, the median radius is 12px.
- Buttons in the first viewport: none on 55% of top sites and 39% of weak ones.
## 8. Imagery and page structure
- Hero media covers at least half the first viewport on 60% of sites (top 68%, weak 48%) and nearly
all of it on 50% (top 56%, weak 39%). No hero media: 15%.
- Video is present on 49% of sites and a canvas element on 49% (top 62%, weak 35%).
- Images per page, median: 24 overall, 37 on top sites, 5 on weak ones.
- Imagery kind (panel): mixed 32%, photography 14%, generative or WebGL 13%, product interface 12%,
3D render 11%, illustration 9%, video 6%, type only 3%. Median panel score is lowest for
illustration-led (6.9) and product-interface-led (7.1) pages and highest for video and type-only
(7.5).
- Page length, median: 8.7 screens and 575 words (middle half 1.6–14.4 screens, 250–956 words). A
quarter of the sites are a single screen or two.
- Navigation: a median of 3 links; fixed or sticky on 66%.
- Density (panel): airy 46%, balanced 45%, dense 9%. Alignment: centred 33%, mixed 31%, left-anchored
17%, modular grid 13%, free 6%.
- Asked to describe each site's most interesting sections, the panel picked a pricing, testimonial or
FAQ section only 7, 12 and 9 times across 235 sites. Where strong sites have them at all, they are
not where the design effort goes, and they avoid the stock form.
**From the panel's written notes on the 114 sites scoring 7.3 or more** (hand tallies, approximate;
`layout.md` turns these into practice):
- Outer margins of 32px or less on about 45; 80px or more on about 8. A centred 700–1300px container
on about 15, several of them the pages the panel called stock.
- Text anchored to a fixed off-centre line on about 22. Text docked in corners or at opposite ends of
a baseline on about 25 and 15.
- Two or three type sizes with nothing between on about 30; no note describes a graded five-step
scale.
- One thing per screen on about 20. A full background colour per section on about 20; alternating
light and dark on about 12.
- Every section recomposed on about 14; one planned exception to the page's own system on about 14.
- No conventional top navigation bar on about 45; small corner links on about 22; a single link or
button on about 10.
- Endings: the name at full width about 18, the hero repeated inverted about 12, a closing scene of
its own about 14, no footer or a single line about 22. Link-column footers are mentioned only as
criticism.
- Motion (inferred from frames): autoplaying video about 50; pinned or fixed stages about 40; text
reveals about 30 (blur-in 10, masked line rises 8, scroll-tied word colouring 7); horizontal strips
about 27; marquees about 26; media swapped behind a fixed element about 15; scroll-linked scaling
about 12. Rare: hover effects (4 mentions), parallax (6), accordions (5), bounce or spring motion
(none). About 10 strong sites show little or no motion at all.
- How weak sites fail (54 sites under 6.8): the category's stock template about 20; centred headline,
pill badge, two buttons and a screenshot about 9; stock modules after a distinctive hero about 8,
plus about 17 in the middle band; a characterless default typeface about 13; text too small or too
faint about 13; never saying what the thing is about 12.
- Faults of the best sites (90 at 7.5 or more), not to be copied: text at 9–13px about 24;
information hidden behind the concept about 20; the idea running out mid-page about 15; text over
footage with no scrim about 12; gates, preloaders and blank first paints about 12.
## 9. What the inspector says about the gallery itself
Running this skill's own `inspect.mjs` rules over the 202 measurable gallery sites: the median site
has 4 `FAIL`s and only 4% have none. The rules that fire most: headline over two lines somewhere on
the page 60%, eyebrow-like label 60%, thin rules 50%, monospace 37%, shadows 32%, gradients 29%, index
numbering 29%.
So the house style is deliberately stricter than the gallery. It is not a description of award sites;
it is a narrower target inside what they do: their scale, their ideas, their imagery and restraint,
without the ornaments that have since become a uniform.
## 10. The 25 highest-scored sites and their ideas
The panel's one-sentence reading of each. Notice that every one names something only that subject
could own. (Several use devices this skill bans; take the idea, not the ornament.)
| Site | Source | Score | Idea |
| --- | --- | --- | --- |
| PP Neue Montreal | Awwwards | 8.6 | A typeface specimen dressed as a vintage city travel guide: the font named after Montreal is sold through guidebook titling, postcards and old tourist footage. |
| Seasats | Awwwards | 8.4 | Calm, dusk-lit seascapes sell autonomous boats as serene instruments, and the navigation is a column of stacked colour swatches taken from that sea-and-sand palette. |
| AI in Design Report 2026 | Awwwards | 8.4 | A research report laid out like a colour-blocked print annual: a video collage on top, an oversized tight grotesk title, and flat orange, lilac, sage and black blocks carrying stats, quotes and chapters. |
| House of Honey | Awwwards | 8.4 | A block-heavy wordmark pierced by a fine script: the bold-against-delicate contrast is repeated at every scale, with giant pink script behind quiet serif statements. |
| White Desert | Awwwards | 8.4 | One word, the destination, set a third of the screen tall over footage like a film title card; sheer scale stands in for remoteness. |
| Applied Imagination | One Page Love | 8.3 | An essay conceived on a flight is read as a descent: an altimeter ticks from 30,000 ft to zero while you scroll down through clouds to mountains at ground level. |
| Cursor Compile | One Page Love | 8.3 | The event name drawn as seven looping one-line curves in seven colours, more signature than logotype, on an otherwise whisper-quiet off-white page. |
| The New Industrials | One Page Love | 8.3 | A long-read essay laid out inside a drafting grid, where colour fields shift as you read and machine parts drift through the margins and finally assemble into the publisher's initials. |
| Wild | One Page Love | 8.3 | A studio essay in which everything decorative is drawn on one square-pixel grid: the hero heatmap, the icons, the diagrams, the lettering and the footer. |
| Hildén & Kaira | Awwwards | 8.3 | The agency name is cast in liquid chrome across the full width, and the same chrome becomes the objects that float through the page, tying the pitch to one material. |
| PX Push | Awwwards | 8.2 | A design subscription presented as a retro broadcast: the entire site is seen through a CRT screen, with a chrome logo token as its mascot. |
| Illoca | Awwwards | 8.2 | Software for architects sold as a drafting table: a two-colour illustrated studio on graph paper that the scroll pushes into until the drawing fills the screen. |
| Aspen Search | Awwwards | 8.2 | The page is a tile system with no margins: the two-word name is split across cells that alternate type, dithered image, black and one mint green. |
| Dragonfly Redux | Awwwards | 8.2 | A crypto fund presented as a terminal artefact: every picture is rendered in characters, framed by four letters of the name pinned to the viewport corners. |
| Monolog | Awwwards | 8.2 | A studio pitch run as an asymmetric sheet: a narrow left column carries the sales talk while a wide right column stacks a very heavy headline and the proof. |
| The Queue | One Page Love | 8.1 | An essay on queueing where the queue is the ornament: a line of drawn figures stands along the bottom of the hero and returns between chapters in changing period dress. |
| Paul Kalkbrenner | Awwwards | 8.1 | Swiss poster typography for a techno artist: the name as one black line with a photo set inside it like a glyph, then every chapter gets its own flat colour. |
| Vero New York | Awwwards | 8.1 | A keepsake treated like couture: a slow close-up film opens, then a white sculpture of a dress stays pinned at centre while one sentence passes over it. |
| Depo Luxe | Awwwards | 8.1 | A film production roster shown as a full-screen reel in which every step is one film with only a title, a director and a client, set like credits at body size. |
| Longbow | Awwwards | 8.1 | The page is built on a visible four-column construction grid, and cars, cards and a giant condensed marquee are slotted into its cells like objects on an artboard. |
| Mosby's Files | Awwwards | 8.0 | An archive of architects presented as a physical stack of coloured index-tab file folders that you flip through. |
| Sarah & Matt Wedding | One Page Love | 8.0 | The site is the invitation: a letterpress-style card opens the page, and its ornaments and small typographic parts become the components for everything after it. |
| Zara Webb | One Page Love | 8.0 | A one-woman newborn-care practice presented like a hand-made keepsake: a mascot rabbit, scalloped and flower-shaped cut-outs, candid film photographs. |
| Noho | Awwwards | 8.0 | Furniture sold through people misusing it (wearing, balancing on, hiding behind chairs on flat colour backdrops) while the interface stays quiet. |
| Juggle | One Page Love | 8.0 | The headline literally drowns: a flat blue field rises to half height and the lines below the waterline change colour. |
Full notes on any studied site: `node scripts/draw.mjs --site <id>` (ids appear on direction sheets).
## 11. How the skill was tested
Six client briefs (an invoicing tool, a restaurant, a designer's portfolio, a ceramic product, a
guesthouse, a design conference), each with no photographs. For every brief a fresh agent built the
page three ways: with no skill (baseline), with another published design skill (atelier, three briefs)
and with this skill. Every build was photographed by `inspect.mjs` in the same way and judged blind
by a separate agent with the juror brief (Awwwards weights, anonymous A/B/C folders, screenshots
only). The skill was revised after each round from the jurors' reasons and the builders' notes.
| Round | Briefs | This skill | Baseline | Atelier | Wins |
| --- | --- | --- | --- | --- | --- |
| 1 | 4 | 6.6 | 7.7 | 6.8 | 1 of 4 |
| 2 | 3 | 7.1 | 8.0 | | 0 of 3 |
| 3 | 3 | 7.5 | 7.5 | | 1 of 3 |
| 4 (unseen briefs) | 3 | 6.9 | 7.9 | 7.5 | 0 of 3 |
| 5 (same three) | 3 | 7.7 | 7.8 | 7.2 | 2 of 3 |
Under the inspector, the final builds measured 0 FAIL each; the baselines measured 8 to 13 FAIL each
(eyebrows, monospace labels, 01/02/03 numbering, hairlines, gradients, shadows, three-line headlines,
text under 13px).
What the jurors rewarded, in the order they named it: an idea that belongs to this client and not to
the category (the competitor test); one live thing on the page; full content at a readable size; a
first screen with one dominant element and a real button; drawings of the subject with detail. What
they punished: a quiet page that read as empty, a small headline, text sliding over pictures or cut
at the top, a page recoloured mid-section, rows cut at the right edge, a large sentence over three or
more lines (read as a long headline whatever its markup), navigation that took over the first screen.
Each of these is now a rule in SKILL.md or a check in the inspector.
Limits: one juror per brief per round, so single scores carry about half a point of noise; the round-5
briefs had been seen in round 4; jurors are models, not people.
## 12. The October 2026 study: 100 Sites of the Day
A second study, made for v2.1, of the latest 100 Awwwards Sites of the Day (10 July to 17 October
2026), with a different aim from the first: where the pictures and footage sit, how pages move, and
the frame (margins, balance, empty space, titles). About three in four of these sites were also in
the first study.
- **Capture.** Headless Chrome at 1440×900 and 390×844 on 18 October 2026: four frames of the first
4.5 seconds after load, every screen while scrolling with real wheel input (one frame at rest per
step; the mid-step frames were cut from the top of the document instead of the viewport, so they
were not used), five phone screens, and a measurement pass in the page: every picture and
clip with its box, ratio, fit and corners; every section's content edges and pictures; blank runs;
the CSS that loaded (curves, durations, keyframes, breakpoints, feature counts); the animations,
transitions and script-driven moves that actually ran; GSAP tweens and scroll triggers where the
page exposed them. Nothing a site owns (code, copy, pictures) is reproduced in the skill; only
measurements and descriptions in our own words.
- **Panel.** One model reader per site studied its frames and measurements, described the idea, the
first screen, the frame, every section, where each picture sits, the motion and what not to copy,
and scored it the Awwwards way. 91 of 99 captures were usable (the rest showed a gate, a bot wall or
a firewall page). Scores ran 6.7 to 7.9 (median 7.45); "top" means 7.5 or more (36 sites), "lowest"
under 7.3 (16).
- **What it changed.** Where pictures and footage go: `imagery.md` §12. What strong pages do with
motion, new tokens, recipes and the gimmicks to avoid: `motion.md` §2, §4–6 and §9–10. Empty space
and the frame: `layout.md` §4–5. New compositions in `library/heroes.json` and `sections.json` and
notes on media and motion in `library/sites.json` (`draw.mjs --site <id>`).
- **Headline numbers.** Pictures fill a median 16% of a scrolled screen, but in whole screens: 42% of
screens hold none and 19% are 90% picture or more. The median page carries 7 pictures (top 8,
lowest 2) and 42% carry footage (half of the top sites, one of the 16 lowest). 94% of pictures have
square corners. Strong ease-outs are the curves that ran (expo and quint out); the median of all
that ran on a site is 420ms. 60 of 99 open on a loading screen, and a third of the usable pages were
still unreadable 4.5 seconds after load, with no gain in the design marks. About 1 text section in
100 leaves half the width empty; the outer margins of strong pages match to a few pixels; 82% of
text sections open with a heading; 15 of 97 first screens never say what the thing is, and those
lose usability points.
- **Limits.** One capture per site on one day; a WebGL scene can render blank in a headless browser;
the scroll-linked frames are partly an artefact of smooth-scroll libraries; one reader per site, so
single scores carry about half a point of noise.
## 13. The app-screen study
For dashboards and app screens (v2.1), a separate study of 23 real product screens chosen by the
owner (17 products, 17 light and 6 dark, at 1440–1512px): each screen measured from its pixels by a
reader with full-resolution crops, six cross-cutting reports (shell, data and charts, tables and
forms, visual language, taste, styles), and a synthesis into `references/app-screens.md` and
`library/app.json` (seven styles, five shells, screen patterns). The screenshots stay private; the
skill carries only the measurements and the rules. Blind test rounds of app screens built with and
without the skill then shaped the warnings at the top of that file.
@@ -0,0 +1,174 @@
# Scroll scenes
The big moments of a page: stretches of scroll where the page stops being a column of sections and
becomes a scene. A camera flies through a field of pictures, a picture grows from a window to the whole
screen, a word buckles under the scroll, a set of photographs orbits and lands in a grid. These are what
visitors remember from award sites, and what a page built from tidy sections alone never has.
The scene library is taken apart from recent Awwwards Sites of the Day (October 2026: 43 sites recorded
frame by frame while scrolling, 261 scenes described, 30 types), rebuilt from scratch in our own code,
tested frame by frame at desktop and phone width and under reduced motion, and reviewed against the
scenes it came from. Nothing a site owns is in them. Until `library/scenes.json` ships the draw deals
none; a page that needs a scene builds one on the stage (section 8), with every rule here.
## Contents
1. What a scene is
2. Putting a scene on a page
3. Filling it
4. Fitting it to the page
5. The phone, reduced motion, no script
6. Checking it
7. Rules for scenes
8. When no scene in the library fits
9. The catalogue
## 1. What a scene is
A tall section whose stage holds still while the page scrolls past it. The stage is
`position: sticky`; the section is (hold + 1) screens tall, so the stage is held for `hold` screens;
a GSAP timeline is scrubbed from 0 to 1 by that scroll. Nothing captures the wheel: the scrollbar always
tells the truth, the keyboard and the trackpad work as everywhere else, and the scene ends.
What a scene is not: a fade-up, a small parallax, a sticky footer, a marquee. Those are the quiet layer
(`references/motion.md`). A scene changes the whole screen.
The direction sheet deals two or three (section 9 of the sheet), from different families, each placed
on the section of the argument it can carry. Build them in step 6, with that section: a scene is
composition, not decoration added at the end.
## 2. Putting a scene on a page
Copy the shared stage and the dealt scene's files into the project (`assets/scenes/stage.css`,
`stage.js`, `<id>.css`, `<id>.js`). Each scene file's header comment lists the markup it expects and
its options; `assets/scenes/demo/<id>.html` is a complete working page to read beside it.
```html
<head>
<script>document.documentElement.classList.add('js')</script>
<link rel="stylesheet" href="scenes/stage.css">
<link rel="stylesheet" href="scenes/<id>.css">
</head>
<body>
…
<section class="scene" data-scene="<id>" data-options='{"hold": 3}' aria-label="…">
<div class="scene-stage">
<!-- the markup from the scene file's header, filled with this page's content -->
</div>
</section>
…
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.min.js"></script>
<script src="scenes/stage.js"></script>
<script src="scenes/<id>.js"></script>
<script>Stage.smooth(); Stage.init();</script>
</body>
```
- Load SplitText (`gsap@3.15.0/dist/SplitText.min.js`) only when a scene file says it needs it.
- `Stage.smooth()` adds weighted wheel scrolling (Lenis at lerp 0.12). It stays off for touch and for
reduced motion. Leave it out on a quiet page; the scenes work either way.
- `Stage.init()` starts every `[data-scene]` on the page. A page with two scenes loads both files.
- The versions are pinned. Do not change them to `@latest` or an unpinned major.
## 3. Filling it
A scene is only as strong as what goes in it. The scene file's header and the sheet's "Content" line
say how many pictures and words it takes; these hold for all of them:
- **Pictures of one family.** Same light, same grade, same kind of subject: a field of mismatched stock
photographs breaks the illusion of one space faster than any timing error. Real pictures of the
subject's world (`assets.mjs`), the project's own, or the dealt photographs, in that order
(`references/imagery.md`). Size each to its largest display size, set `width` and `height`.
- **Words that read in a breath.** A held statement is 4 to 12 words; captions are one to four words in
the text face, sentence case. One statement on screen at a time.
- **The scene says something about the subject.** A fly-through of the studio's work, the route a
shipment really takes, the four rooms of the house, the product's change of state. A scene filled
with unrelated pictures is a gimmick and reads as one.
- **Real content only.** If the scene needs eight pictures of the subject and there are three, take the
`--redraw scenes`, never pad it with stand-ins.
## 4. Fitting it to the page
- The scene files read the page's tokens: `--ground`, `--ink`, `--muted`, `--accent`,
`--font-display`, `--font-text` (with fallbacks). Set them on `:root` from the sheet and the scene
takes the page's colour and type. Check the demo against your page, not the other way round.
- Display type in a scene uses the dealt display face and the two-line headline rule; statements stay
at statement size.
- Leave a quiet section between two scenes (a text section, a list, a plain picture) so each one lands.
Two scenes back to back read as one long ride and the second loses.
- A scene on the first screen must be readable at progress 0: the title, the sentence and the action are
there on the first frame (motion rule 1), and the scene starts moving only when the visitor scrolls.
- Give the section a heading. If the design has no visible heading in the scene, the section still
carries one (`aria-label` on the section and a real heading in the finished composition).
## 5. The phone, reduced motion, no script
- **The phone.** Each scene has a phone plan in its file: the same scene with changed numbers, a
simpler version, or `phone: 'rest'`, where the phone shows the finished composition. Touch scrolling
is never smoothed. Look at the phone sheet as carefully as the desktop one.
- **Reduced motion and no script.** The section is an ordinary section showing the finished
composition: the same content, laid out to read without motion. Nothing is hidden waiting for a
script. The inspector's `reduced-hidden` check and the reduced sheet of `scenes.mjs` show it.
- **A failed CDN** falls back to the finished composition too (the stage adds `.is-rest`). The
inspector fails a scene that rests on desktop (`scene-not-live`), so a broken script cannot pass.
## 6. Checking it
```bash
node <skill>/scripts/scenes.mjs <page> # every scene at 0, 10 … 100%, desktop and phone, and reduced motion
node <skill>/scripts/inspect.mjs <page> # the gate; with scenes on the sheet it also films the scroll
```
`scenes.mjs` writes one contact sheet per scene and width to `.taste-skill/scenes/`. Read every one.
At every frame ask: is there one clear thing to look at; can the statement on screen be read; is
anything popping, blank, cut off or crossing the words; is it as bold as it should be? The inspector's
`desktop-film-NN.jpg` shows the whole page's scroll a third of a screen at a time: check that the
scenes and the quiet sections between them make a rhythm, and that nothing between them is empty.
## 7. Rules for scenes
1. **Two or three per page,** from different families, a quiet section between them. The inspector
warns over four (`many-scenes`) and when scenes hold more than 60% of the page (`scene-heavy`).
2. **Each holds one to four screens:** about one screen per beat of the story; a hold with nothing new
to show is cut, not padded.
3. **Native scroll only.** No wheel capture, no locking the page, no scenes that only run on a click.
Smoothing only through `Stage.smooth()`.
4. **Readable at its rest points.** The statement on screen reads whole; nothing passes over text while
it is being read; text on a picture has a flat scrim or a calm area under it.
5. **Inside a scene the scene's numbers apply** (motion rule 7): a picture may grow from a window to the
whole screen, type may cross the stage, a camera may fly through pictures. Outside scenes, the quiet
limits of `references/motion.md` hold.
6. **Transform, opacity, clip-path.** Nothing that forces layout on every frame. Pictures decoded before
they move.
7. **The house rules hold inside scenes:** no monospace captions, no 01/07 counters (a plain "3 of 7" if
a count is needed), no gradients (flat scrims), no shadows or glows, no hairline decoration, no
decorative particles, no violet.
8. **The finished composition is designed,** not an afterthought: it is what reduced motion, a failed
script and some phones see.
## 8. When no scene in the library fits
Redraw first (`--redraw scenes`). If the brief really needs a scene the library does not have, build it
on the same stage so it inherits the guards:
```js
Scenes['my-scene'] = (root, opts = {}) => Stage.scene(root, {
hold: opts.hold ?? 2, // screens the stage is held for
scrub: opts.scrub ?? true, // true when Lenis runs; 0.4-0.8 without it reads as weight
phone: 'same', // or 'rest', or { hold, build } for a phone plan
build(tl, { q, phone }) { // positions are fractions of the scene, 0-1
tl.fromTo(q('.picture'), { clipPath: 'inset(30% 30% 30% 30%)' }, { clipPath: 'inset(0% 0% 0% 0%)', duration: 0.6 }, 0)
.to(q('.title'), { yPercent: -40, autoAlpha: 0, duration: 0.25 }, 0.7);
},
});
```
and write its CSS finished composition first, then the live layout under `.scene.is-live`. Check it
with `scenes.mjs` like any other.
## 9. The catalogue
(Generated from `library/scenes.json`; the draw deals from it. `node <skill>/scripts/draw.mjs --list
scenes` prints it.)
@@ -0,0 +1,115 @@
# Tells — the dated registry
A tell is a device that makes a page read as generated. Tells are not bad design in themselves; most
began as good ideas on good sites. They became tells by being produced by default, everywhere, without
a reason. That is why this registry is dated, and why it never names a replacement font, colour or
component: every published "use this instead" list has become the next wave of tells within months.
Replacements here are procedures (draw from the library, derive from the subject), not products.
`scripts/inspect.mjs` and `scripts/lint.mjs` detect most entries. Status: **banned** means the
inspector fails the page; **warned** means it flags it and you need a reason.
## Contents
1. First wave (2023–2024)
2. Second wave (2025)
3. Third wave (2025–2026): the "fix" that became the look
4. Gallery fashion, banned as house style
5. Structural tells
6. Copy tells
7. Motion tells
8. Why removal is not enough
## 1. First wave (2023–2024)
| Tell | Status | Do instead |
| --- | --- | --- |
| Violet / indigo gradients on hero, buttons and glows | banned | The dealt palette; flat colour |
| Gradient-filled headline text | banned | Solid ink |
| Glass panels: backdrop blur, translucent cards | warned (2 or more) | Opaque surfaces one tone step from the ground |
| Glowing or gradient "Get started" button | banned | Flat button named for its action |
| Soft grey drop shadow under every card | banned | Tone step, or no box |
| Three identical feature cards, each with an icon in a rounded square | banned | The dealt section blueprint |
| Emoji as icons or bullets | banned | Words, or a real picture |
| Centred headline, subtitle, two buttons, screenshot below | no rule; the draw avoids it | The dealt hero |
| The same typeface on every generated page | warned (lint: default-font) | The dealt type system |
## 2. Second wave (2025)
| Tell | Status | Do instead |
| --- | --- | --- |
| Eyebrow / kicker: tiny tracked uppercase label above every heading | banned | Fold it into the heading, or delete |
| Pill badge above the hero headline ("New", "Beta", "Introducing") | banned | Say it in the supporting sentence |
| One word of the headline in italic serif, colour or underline | warned | One face, one colour |
| Bento grid of rounded tiles as the feature section | warned as card rows | A composed section; tiles only in the dense-tiles direction, filled, strict sizes |
| Near-black ground with one neon accent and a glow | banned via shadow, gradient | A dealt dark palette, no glow |
| Floating rounded pill navigation with blur | warned | A plain bar |
| Fake dashboard hero built from grey bars and donut charts | no rule; see imagery.md §7 | A real interface fragment, or no image |
| Decorative status dot ("Available for work", pulsing "live") | lint: bounce | Remove |
| Fade-and-rise on every section | no rule; see motion.md | One signature move |
## 3. Third wave (2025–2026): the "fix" that became the look
The remedy published for the first two waves was "warm it up and make it editorial". Models adopted
the remedy wholesale and it is now the most recognisable generated look of all.
| Tell | Status | Do instead |
| --- | --- | --- |
| Yellow-cream ground + terracotta / rust / clay accent | banned together; terracotta banned alone; cream warned | The dealt palette. Warmth from a low-chroma stone, a deep ink, a photograph, or a full warm field |
| High-contrast or soft serif display as the shortcut to "premium" | no rule | The dealt type system, whatever class it is |
| Oversized italic serif headline | no rule | As dealt |
| Broadsheet cosplay: hairline rules everywhere, zero radius, dense columns | banned via hairline | Structure from space and tone |
| "Field notes", "Quietly trusted by", "On our desks": performative labels | lint: copy | Plain section names |
## 4. Gallery fashion, banned as house style
These are common on award sites right now. In the study they appear *more* often on the top-scored
sites than on the weak ones (panel observations: monospace labels 44% against 24%, index numbering 38%
against 21%; measured thin rules 61% against 52%). They are banned here regardless, by the owner's
decision: they are the first things a generator copies from the gallery, they carry no information,
and a page built without them has to find real structure.
| Tell | Status | Do instead |
| --- | --- | --- |
| Monospace micro-labels, mono as a "technical" voice, mono captions | banned | The text face; tabular figures for data |
| Index numbering: 01 / 02 / 03, (001), "Step 02", numbered section markers | banned | Order by position, scale, or the words; `<ol>` when it is truly a list |
| Thin rules: hairline dividers, ruled grids, 1px outlined boxes and tiles | banned (boxes: 3 or more) | Space, tone steps, filled shapes, a thick bar if a bar is the idea |
| Design-tool chrome as ornament: crosshairs, crop marks, selection handles, dashed guides, rulers | banned via hairline, dashed | Remove |
| Agency micro-meta: local time, coordinates, weather, version stamps, "©2026 — Scroll", middle-dot strings | lint: copy; micro-label | Remove |
| Tiny uppercase letter-spaced captions scattered around the viewport | banned (3 or more) | Sentence case at readable size, placed with the content it describes |
Tiny tracked capitals are the one item in this group the data condemns as well: 16% of top sites
against 57% of weak ones.
## 5. Structural tells
| Tell | Status | Do instead |
| --- | --- | --- |
| Headline as a full sentence at display size, three or four lines | banned (over two lines) | A title of about five words; the rest goes below |
| Stock section order: hero, logos, features, testimonial, pricing, CTA | no rule; see copy.md §1 | The order of the argument |
| Every section the same layout family | critique.md pass 9 | Different composition per section, from the sheet |
| Everything centred | warned over 72% | Left-anchored, one centred moment |
| One medium border radius (12–16px) on everything | none | Square or fully round, chosen once |
| Nine nearly equal font sizes | warned over 9 | Five sizes, far apart |
| Invented social proof: logos, quotes, star rows, "10,000+ teams" | copy.md §5 | Real proof, or none |
| Text or a CSS box where a picture belongs | critique.md cap | A real picture, or a design that needs none |
## 6. Copy tells
Flagged by `lint.mjs`: the genre's verbs and adjectives (unlock, supercharge, seamless, elevate,
effortless, cutting-edge, all-in-one …); the correction ("not just X, it's Y"); tricolon headlines;
"Get started" and "Learn more" as labels; more than a handful of em dashes; lorem ipsum. See
`copy.md` §6.
## 7. Motion tells
Identical fade-up on every block; a dozen staggered hero entrances; hover lift on every card; bounce
easing; infinite loops that mean nothing; reveals that fail and leave a blank band. See `motion.md` §9.
## 8. Why removal is not enough
A page with every tell removed and nothing put in their place is an empty page with good manners, and
that is a tell too (the "avoidance aesthetic": anyone could guess it from the category plus the list of
things to avoid). The tells were filling a gap where an idea should have been. The order of work in
`SKILL.md` exists to fill that gap first: the idea from the subject, the dealt composition, real
pictures or committed type, and only then the checks.
@@ -0,0 +1,170 @@
# Type
Typography is most of what a visitor sees and most of what makes a page look designed or assembled.
The direction sheet deals you a type system (faces, weights, settings). This file is how to use it.
## Contents
1. What the study measured
2. The scale: few sizes, far apart
3. Setting display type
4. Setting text
5. Loading the fonts properly
6. One family or two
7. Details that separate set type from typed text
8. Mistakes the inspector cannot see
## 1. What the study measured
From 202 measurable sites out of the 247 studied (Chrome, 1440×900 and 390×844):
| | Median | Middle half | Note |
| --- | --- | --- | --- |
| Hero headline size | 72px (5.0vw) | 43–109px | Top-scored sites: 6.0vw median |
| Largest type on the page | 6.4vw | up to 10.4vw | Top-scored sites 8.4vw, weak sites 5.0vw |
| Hero headline on a phone | 40px | 32–59px | |
| Hero tracking | -0.01em | -0.03 to 0 | Tighter as size grows |
| Hero leading | 1.0 | 0.95–1.1 | |
| Body size | 16px | 14–18px | |
| Headline ÷ body size | 4.3 | | Top-scored sites 4.7 |
| Typefaces in real use | 2 | 31% use one, 45% two, 22% three | |
| Hero weight | regular 48%, medium 18%, bold or heavier 22% | | Big type does not need to be bold |
| Hero in capitals | 24% | | |
| Hero centred | 39% | | 52% on the weak sites |
Two things to take from it. First, size contrast is the main lever and the best sites pull it harder.
Second, weight is not: half of the heroes are set in a regular weight. Scale and spacing make type
confident; bold is one option among several.
## 2. The scale: few sizes, far apart
The panel described about 30 of the 114 strongest sites as using two or three type sizes with nothing
in between, and none as using a graded five- or six-step scale. So: one display size, one text size
and a small size carry the page; a lead size and a heading size exist for when they are needed. Five
is the ceiling. The common failure is nine sizes that are all near each other (16, 18, 20, 24, 28,
32…), which reads as mush. Choose steps that are clearly different:
```css
:root {
--step-small: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem); /* 13–14px captions, legal */
--step-body: clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem); /* 17–19px paragraphs */
--step-lead: clamp(1.25rem, 1.1rem + 0.7vw, 1.625rem); /* 20–26px supporting line, large body */
--step-head: clamp(2rem, 1.3rem + 3.2vw, 4rem); /* 32–64px section headings */
--step-display: clamp(3rem, 1rem + 9vw, 9rem); /* set by the hero blueprint */
}
```
- `--step-display` belongs to the direction. A monument wordmark is 16–24vw; a quiet instrument's
headline is 28px. Use the size the hero blueprint gives, as a `clamp()` so it scales with the
viewport but is bounded on phones and very wide screens.
- Keep the gap: the display size should be at least four times the body size on desktop, and nothing
should sit between `--step-lead` and `--step-head` unless the design truly needs a sixth size.
- Resist a "subheading" size for every card and label. If two sizes look almost the same, make them
the same.
## 3. Setting display type
Large type exposes spacing that is invisible at text size. Apply all of these to anything over 40px:
```css
.display {
font-size: var(--step-display);
line-height: 0.95; /* 0.85–1.05; tighter for capitals and condensed faces */
letter-spacing: -0.035em; /* -0.02 to -0.05em; use the value on the direction sheet */
font-kerning: normal;
text-wrap: balance;
max-width: 14ch; /* choose the measure so lines break where you want */
margin: 0;
}
```
- **Tracking scales with size.** The larger the type, the tighter it needs to be; a value right for
120px will choke 32px. Set tracking per step, not once for all headings.
- **Leading below 1.0 needs checking.** Look for collisions between descenders and the ascenders or
accents of the next line. Two-line headlines make this easy to verify by eye.
- **All capitals** want slightly open tracking (0 to +0.03em) even at display size, and leading around
0.9.
- **Optical alignment.** Big letters with round or diagonal left edges (O, C, A, T, W) look indented.
Pull the headline left by about 0.04–0.06em (`margin-left: -0.05em`) so it aligns optically with the
text beneath it.
- **Viewport-wide type.** For a line that must span the screen, size it in `vw` and verify at 390 and
1440; set `white-space: nowrap` and let the container clip rather than wrap. Calculate the size from
the character count: `font-size ≈ container width ÷ (characters × k)` with k from `copy.md` §2.
- Never add a text shadow, an outline stroke, or a gradient fill to display type.
## 4. Setting text
```css
body { font-size: var(--step-body); line-height: 1.5; font-kerning: normal; text-rendering: optimizeLegibility; }
p { max-width: 62ch; text-wrap: pretty; margin: 0; }
p + p { margin-top: 0.9em; }
.lead { font-size: var(--step-lead); line-height: 1.35; max-width: 40ch; }
small, .caption { font-size: var(--step-small); line-height: 1.45; }
```
- Measure: 45–75 characters. Wider than that and the eye loses the next line; the inspector warns
past about 88.
- Leading: 1.45–1.6 for body, tighter (1.3–1.4) for the larger lead size, looser for long reading.
- Secondary text is the same face in a softer ink (`--muted`), not a lighter weight and not smaller
than 13px.
- Do not centre paragraphs longer than three lines. Do not justify.
- Light text on a dark ground looks heavier: drop one weight step or add 0.01em tracking, and raise
leading slightly.
## 5. Loading the fonts properly
The sheet gives a `<link>` tag. Use it exactly; do not swap in a face you prefer.
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="…from the direction sheet…" rel="stylesheet">
```
```css
:root {
--font-display: "Family From Sheet", "Helvetica Neue", Arial, sans-serif;
--font-text: "Family From Sheet", Georgia, serif;
}
```
- Request only the weights you use (usually two or three in total).
- Always give a fallback stack of the same broad kind (sans for sans, serif for serif).
- In a framework, use its font loader with the same family and weights (`next/font/google`, or a
self-hosted `@font-face` with `font-display: swap`). Fontshare faces load from the
`api.fontshare.com` stylesheet on the sheet.
- After building, `inspect.mjs` reports the families that actually rendered. If it warns that a face
did not load, the screenshots show a fallback and nothing you judge from them is real. Fix the link
first.
## 6. One family or two
A third of the studied sites use a single family for everything, and it is the safest way to look
coherent. When the dealt system has two faces:
- Give each a job and never swap them: one sets headlines, the other sets everything else.
- Do not introduce a third face for labels, numbers or quotes.
- Do not mix two faces inside one headline.
- Italic is for titles of works and real emphasis inside paragraphs, never as decoration.
## 7. Details that separate set type from typed text
- Curly quotes and apostrophes, real dashes, a real ellipsis (see `copy.md` §7).
- `font-variant-numeric: tabular-nums` for prices, tables, dates in columns; `oldstyle-nums` suits
serif body text.
- `text-underline-offset: 0.18em; text-decoration-thickness: 0.07em` on links, so underlines clear the
descenders.
- `hyphens: none` on headings. `overflow-wrap: anywhere` only on long URLs or emails.
- A heading belongs to the text after it: space above a heading is at least twice the space below.
- `::selection { background: var(--accent); color: var(--accent-ink); }` and a visible
`:focus-visible` outline in the accent, so even the browser's own states belong to the design.
- Do not change weight on hover; the line reflows.
## 8. Mistakes the inspector cannot see
- A headline that passes the two-line rule but breaks badly: one orphan word on line two, or a break
that splits a phrase ("Stools for small / kitchens"). Author the break.
- Display type so tight that letters touch, or so loose it falls apart.
- A confident hero followed by section headings almost as large. Keep one size dominant per screen.
- Body text set in the display face when that face was drawn for large sizes only.
- Everything bold. If every line is emphasised, none is.