Files
Arah2/.agents/skills/design-taste-frontend/references/scenes.md
T
araharah-executor 506ff68976 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)
2026-10-22 07:33:04 +08:00

175 lines
9.6 KiB
Markdown

# 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.)