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:
@@ -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.)
|
||||
Reference in New Issue
Block a user