dsh-claude-motion 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/lib/index.js +1 -1
  2. package/package.json +14 -14
  3. package/skills/claude-motion/SKILL.md +138 -108
  4. package/skills/claude-motion/artifact-type/reference/by-hand.md +23 -9
  5. package/skills/claude-motion/artifact-type/reference/design-systems.md +50 -0
  6. package/skills/claude-motion/artifact-type/reference/film-api.md +129 -252
  7. package/skills/claude-motion/artifact-type/reference/film-folder.md +107 -0
  8. package/skills/claude-motion/artifact-type/reference/finalize.md +118 -203
  9. package/skills/claude-motion/artifact-type/reference/kinds.md +43 -0
  10. package/skills/claude-motion/artifact-type/reference/later.md +130 -0
  11. package/skills/claude-motion/artifact-type/reference/motion.md +43 -239
  12. package/skills/claude-motion/artifact-type/reference/plan.md +4 -4
  13. package/skills/claude-motion/artifact-type/reference/questions.md +91 -152
  14. package/skills/claude-motion/artifact-type/reference/recording.md +186 -0
  15. package/skills/claude-motion/artifact-type/reference/recreate-ui.md +128 -3
  16. package/skills/claude-motion/artifact-type/reference/render-offline.md +3 -1
  17. package/skills/claude-motion/artifact-type/reference/rough-cut.md +109 -213
  18. package/skills/claude-motion/artifact-type/reference/sound.md +49 -100
  19. package/skills/claude-motion/artifact-type/reference/voice.md +63 -0
  20. package/skills/claude-motion/artifact-type/reference/walkthrough.md +4 -4
  21. package/skills/claude-motion/scripts/artifact.mjs +96 -9
  22. package/skills/claude-motion/scripts/editor/artifact-type/app.css +1 -1
  23. package/skills/claude-motion/scripts/editor/artifact-type/app.js +1177 -823
  24. package/skills/claude-motion/scripts/editor/index.html +1 -1
  25. package/skills/claude-motion/scripts/editor/local-host.js +6 -6
  26. package/skills/claude-motion/scripts/editor/share-ui.js +10 -10
@@ -7,8 +7,7 @@ to what the camera sees. Claude is far better at recreating an interface
7
7
  from code than from memory or a screenshot, so when the source is
8
8
  reachable (their repo, a pasted component, a design file, a running
9
9
  build), read it and build from it; screenshots are for checking, not for
10
- measuring. It has to be THEIR product (or an original design): another
11
- company's distinctive UI is not yours to recreate.
10
+ measuring. It has to be THEIR product or an original design.
12
11
 
13
12
  ## 1. Find the real thing
14
13
 
@@ -79,7 +78,7 @@ product chrome.
79
78
  - **Strip behavior**: no `<script>`, handlers, iframes, external
80
79
  `src`/`href`/`url()`; a product video becomes a clip asset. Product imagery becomes film assets
81
80
  (`assets/…`, referenced through `kit.assets.url(name)`) or inline SVG.
82
- - **Fonts**: ship the product's faces as font assets (a Google family, in a film of scenes: a slot of `fonts`, finalize.md's **Fonts**) and map its
81
+ - **Fonts**: ship the product's faces as font assets (a Google family, in a film of scenes: a slot of `fonts`, rough-cut.md's `fonts`) and map its
83
82
  families onto them with a few override rules at the end of your module
84
83
  (`--font-ui: "BrandSans-500"`); a look-alike face is the first thing
85
84
  that reads "mock". **Icons**: product chrome is half icons, and a wrong
@@ -138,3 +137,129 @@ Say in the hand-off which parts are design rather than product, and put
138
137
  "Concept mock · Month YYYY" small and grey on the closing card. Never
139
138
  invent product features to fill a beat; film what exists or what the
140
139
  design shows.
140
+
141
+ ## When the film shows a product being used
142
+
143
+ - **The person touches the real thing, not its caption** — retype the
144
+ placeholder inline, scrub the number field, recolor through the real
145
+ popover; never edit a heading that merely labels the design. Best
146
+ single beat in the walkthroughs: the cursor drifting down a menu with
147
+ the row tint following before it settles — a person deciding.
148
+ - **Fidelity is the whole trick.** Called out as
149
+ blockers: a 28 px toolbar button drawn 24, a selection ring spanning the
150
+ row instead of hugging the text, a drag ghost painting under artboards,
151
+ text snapping under a slow zoom. Names and data are fictional ("Acme");
152
+ anything speculative gets a tiny grey "Concept mock · Month YYYY" on the
153
+ closing card.
154
+
155
+ ## Small things that are easy to get wrong
156
+
157
+ - Cursor: about 30 px on screen at any zoom (counter-scale by 1/z), a click ring and press dip, a 5–8% hover tint on targets.
158
+ - Marks that annotate UI ride with the camera; 8–10 px at 1080p, drawn on over 0.3–0.45 s, held 0.8 s or more.
159
+ - Zoom: body UI text is about 26 px on screen or more, so 14 px UI text wants 1.9× or more. Build a mock window smaller and denser (about 1440×900 on a 1920-wide stage) so that a 2–3× camera has somewhere to go, and never sit on a whole window longer than about 1.2 s.
160
+ - Causality: the field is clicked, text types with visible keystrokes (20–26 characters a second, with jitter), the button lights, the result builds.
161
+
162
+ ## The kit for product films
163
+
164
+ **Marks** draw themselves on, as if by hand: `const marks = new kit.Marks(kit.svgLayer()); marks.add({id, kind, t0, dur, out,
165
+ color, width, …geometry}); marks.render(t)`. Set `color` from the film's palette: a mark with none draws red. Kinds and geometry: `circle {x,y,w,h}`, `underline {x,y,w}`, `arrow {from:[x,y], to:[x,y]}`,
166
+ `bracket {x,y,h,side}`, `scribble {x,y,w,h}`, `burst {x,y,r}`, `check {x,y,size}`, `cross`, `path {d:[[x,y],…]}`.
167
+
168
+ **Geometry** for camera keys and cursor targets (stage px; measure in
169
+ `init` after building, or per frame for moving things): `kit.srect(node)` → `{x,y,w,h}` as seen on the
170
+ stage (camera included); `kit.camrect(node, cam)` → the same in the
171
+ camera's un-zoomed scene space (author camera keys, marks and cursor
172
+ targets in this space); `kit.center(node, dx, dy)` → a `() => [x,y]`
173
+ for cursor keys; `kit.phraseRect(el, 'phrase')` /
174
+ `kit.phraseCamRect(el, phrase, cam)` → the box of a sub-string (null
175
+ until it has streamed in) for underlines and circles.
176
+
177
+ **Cursor** — `const cur = kit.cursor()` mounts the arrow at stage level
178
+ and returns it; move `cur.el` into your camera layer to author in scene
179
+ coordinates (it then scales with the zoom; counter-scale by 1/z if you
180
+ want it a constant ~30 px). `cur.keys = [{t, at: [x,y] | () => [x,y],
181
+ bow?: 0.12, lin?}]` (eased, arced travel between keys), `cur.clicks =
182
+ [t…]` (ring + press dip), `cur.holds = [[a,b]…]`, `cur.ewSpans`,
183
+ `cur.shapes = [{spans: [[a,b]], gfx: kit.cursors.text}]`,
184
+ `cur.visible = t => opacity`; `cur.render(t)`.
185
+
186
+ **Walkthrough devices** — `kit.chip(text, {x, y, anchor: [ax, ay]})`
187
+ → a caption chip (white pill, 26 px system sans unless `style:
188
+ 'font-family:…'` names a font asset; leader + dot to the anchor);
189
+ `kit.show(node, t, tIn, tOut?)` fades/raises it from t;
190
+ `kit.spotlight(rect, amount 0..1, {pad = 24, radius = 18})` dims
191
+ `kit.fx` outside the rect (call inside `render`, after your own fx
192
+ drawing).
193
+
194
+ **Plates (HTML sprites)** — `await kit.raster(htmlOrNode, {css, width,
195
+ height, scale = 1})` in `init` → a canvas of that fragment rendered with
196
+ the stage fonts by the export's own DOM rasterizer; `drawImage` it per
197
+ frame for heavy static screens you only pan or zoom. Whatever `init`
198
+ makes or decodes (plates, `kit.assets.image()`, measured rects) lives
199
+ for the whole film; frames never reload assets. `kit.plateStats()`.
200
+
201
+ ## Content and design rules shared by every appifact family
202
+
203
+ <!-- The block between the shared:* markers below is generated from
204
+ skills/_shared/content-design.md by scripts/inline-shared.ts — edit the
205
+ fragment and re-run it; hand edits here fail scripts/shared-inline.test.ts. -->
206
+
207
+ In a film, motion.md and finalize.md decide how much to draw and when to ask.
208
+
209
+ <!-- shared:content-design -->
210
+ These rules are about the CONTENT authored into the appifact — the
211
+ deck, the artboards, the dashboard, the seeded cards and rows — as
212
+ opposed to its chrome (the kit, the toolbar, the document machinery).
213
+ They apply across every family (family skills add their own craft on
214
+ top), and none of them changes a kit rule.
215
+
216
+ - **Do not add filler content.** Never pad a design with placeholder
217
+ text, dummy sections, or informational material just to fill space.
218
+ Every element should earn its place. If a section feels empty, that's
219
+ a design problem to solve with layout and composition — not by
220
+ inventing content. One thousand no's for every yes. Avoid "data slop"
221
+ — unnecessary numbers, icons, or stats that are not useful. Less is
222
+ more; bias towards minimalism.
223
+ - **Ask before adding material.** If you think additional sections,
224
+ pages, copy, or content would improve the design, ask the user first
225
+ rather than unilaterally adding it. The user knows their audience and
226
+ goals better than you do.
227
+ - **Targeted changes stay targeted.** When the user asks for a small,
228
+ targeted change — some text, a color, one element — change ONLY that:
229
+ leave all other layout, spacing, margins, fonts, sizes, positions,
230
+ colors, and content exactly as they are; don't redesign or "improve"
231
+ parts you weren't asked to touch. A redesign, a new direction, or a
232
+ from-scratch request is different — then make the substantial changes
233
+ they're asking for. If you think a broader change would help a small
234
+ request, finish what they asked and SUGGEST the rest rather than
235
+ applying it unprompted.
236
+ - **Follow an existing design's visual vocabulary.** When adding to an
237
+ existing UI or document, understand its visual vocabulary first, and
238
+ follow it: match copywriting style, color palette, tone, hover/click
239
+ states, animation styles, shadow + card + layout patterns, density,
240
+ etc.
241
+ - **Avoid AI slop tropes:** including but not limited to aggressive use
242
+ of gradient backgrounds, emoji (unless explicitly part of the brand),
243
+ and containers with rounded corners and left-border accent color.
244
+ Emoji in content: only if the brand or design system uses them.
245
+ (Appifact chrome is stricter still — never emoji as UI glyphs —
246
+ that rule is unconditional.)
247
+ - **Recreate from source, not from memory or screenshots.** When asked
248
+ to recreate a UI or design whose source you can reach — a repo, a
249
+ pasted file, an attached design system — read the real source and
250
+ build from it, not from your training-data memory of the app: read
251
+ the components and styles, copy the assets the design actually uses,
252
+ and copy exact numeric values (paddings, radii, font sizes,
253
+ line-heights) rather than rounding or snapping them to a 4/8-px grid
254
+ or a framework default. Claude is better at recreating interfaces
255
+ from code and design context than from screenshots; when source is
256
+ available, treat screenshots as high-level guidance only.
257
+ - **Do not recreate copyrighted designs.** If asked to recreate a
258
+ company's distinctive UI patterns, proprietary command structures, or
259
+ branded visual elements, you must refuse, unless the user's email
260
+ domain indicates they work at that company. Instead, understand what
261
+ the user wants to build and help them create an original design while
262
+ respecting intellectual property. (A Claude Code session has no
263
+ account email-domain signal, so this rests on what the user tells you
264
+ about where they work — ask when it's unclear.)
265
+ <!-- /shared:content-design -->
@@ -120,7 +120,9 @@ PNGs (ImageMagick: `montage t_*.png -tile 4x -geometry 480x270+4+24 sheet.png`).
120
120
  frames made here and the viewer's screen can differ.
121
121
  - A Google family named in `fonts` is not fetched here: a still shows its `or` (words that the ten
122
122
  shipped faces, rough-cut.md, can't spell: the machine's face). Say so with any
123
- still you hand over (finalize.md's "Of fonts", F), and judge no spacing by it, only that each
123
+ still you hand over (later.md's "What to say about fonts", F), and judge no spacing by it, only that each
124
124
  line fits.
125
125
  - claude.ai chat may be unable to `read` an artifact's type files; there, ask
126
126
  for the skill's zip or work from Claude Code / Cowork.
127
+
128
+ The film folder and the scripts that work on it are in film-folder.md.
@@ -1,251 +1,147 @@
1
- # A film of scenes, in a look of your own: its records
1
+ # The film's records
2
2
 
3
- Part of "A new film: three questions, then the film" in this type's
4
- instructions, which say when each step happens and what you reply. This page is
5
- the records' format and how to write them. The example is another film's (a bell
6
- foundry's open day): copy how it is built, never its scenes, words, colors,
7
- typeface, shapes or times. `if_version`, wherever this page says it: only if your tool's
8
- input lists it; the version is in your last `list`, `get` or write of that document.
3
+ This page holds the first batch and how a scene module is built. SKILL.md says when each step happens; finalize.md says
4
+ how the film gets drawn. The example is another film's (a bell foundry's open day): copy how it's built, never its
5
+ scenes, words, colors, typeface, shapes or times. `if_version` applies only if your tool's input lists it.
9
6
 
10
7
  ## The first batch
11
8
 
12
- Sent at once, before any scene is designed, in this order. The film then plays:
13
- the player draws a plain card with the scene's name for each scene still to come.
9
+ Send it at once, before you design any scene, in this order. The film then plays, with a plain card for each scene
10
+ still to come.
14
11
 
15
12
  **`film/meta`**, then **`modules/scenes.json`** (`kind` json):
16
13
 
17
14
  ```json
18
- {"v":1,"stage":"rough","look":"Cast bronze: Heavy metal shapes lit by furnace glow, rising slowly into place",
15
+ {"v":1,"stage":"rough","look":"First bell: A drawn brick yard in morning light, with smoke and long shadows",
19
16
  "palettes":[["#17455C","#FFC24B","#FFF1DE","#2A1410"],["#10201D","#3E8F84","#C9E4DC","#D98A3D"],["#F3EFE6","#7A4B2A","#2B211A","#C8962E"]],"theirs":0,
20
- "width":1920,"height":1080,"fps":60,"background":"#10201D",
17
+ "width":1920,"height":1080,"fps":60,"background":"#F3EFE6",
21
18
  "fonts":{"display":{"family":"…","or":"…"},"sans":{"family":"…","or":"…"}},"scenes":[
22
19
  {"id":"yard","name":"The yard","dur":3,"desc":"Gates open on the foundry yard; the date sets in."},
23
20
  {"id":"pour","name":"The pour","dur":4.5,"desc":"The ladle tips; bronze runs into the mould."},
24
- {"id":"ring","name":"First ring","dur":4,"desc":"The bell swings once; rings spread."},
21
+ {"id":"ring","name":"First ring","dur":4,"desc":"The bell swings once; the birds go up."},
25
22
  {"id":"come","name":"Come and see","dur":3.5,"desc":"End card: day, place, free entry."}]}
26
23
  ```
27
24
 
28
- - Three or four scenes for 20 s, five or six for 30, 24 at most, each ONE subject and ONE
29
- action: an establishing open → the subject arrives → the thing happens →
30
- closer scenes, each landing on motion → back out → an end card held 3 s or
31
- more. The seconds add up to the Length answer; 300 at most.
32
- - `id`: lower-case letters, digits and `-`, a letter first, 32 at most. It never
33
- changes, and no two are alike: comments and the user's own edits hang on it.
34
- `name`: one to three words (48 characters at most), for a listener ("The pour",
35
- not "Scene 2"): it labels the timeline and the card. `dur`: seconds, above 0.
36
- `desc`: one plain sentence on what happens. A bad `id`,
37
- `dur` or count stops the whole film.
38
- - One continuous action (one window, one camera move, one cursor's errand) is ONE scene, however
39
- long: nothing carries over a cut. The one exception: a character or object that moves about or
40
- travels (a window, screen, cursor, card or logo is not one). Where two scenes in a row show it,
41
- the second must **open in the same framing, with it as the first's code left it**: place, size,
42
- pose, speed; plan none of its moves ahead, in thought or in `shared.js`.
25
+ **Scenes.** Plan two or three for 10 seconds, three or four for 20 and five or six for 30; the player takes 24 at most, and the seconds add
26
+ up to the Length answer, 300 at most. Judge how rich the film should be (motion.md), and say "rich" or "plain" in `look`. Each scene makes one clear point, not one symbol, that the viewer can take in at once: cut what doesn't serve it, and keep the detail that does. Many films go from an establishing
27
+ open, through the subject arriving and the thing happening, to an end card held 3 s or more, but that's a habit, not a
28
+ rule: a poem may want one long drifting shot. Nothing blinks or cuts more than three times a second, rough or finished.
29
+
30
+ - `id`: lower-case letters, digits and `-`, a letter first, 32 at most. It never changes and no two are alike, because
31
+ comments and the user's own edits hang on it. A bad `id`, `dur` or count stops the whole film.
32
+ - `name`: one to three words (48 characters at most), for a listener: "The pour", not "Scene 2".
33
+ - `dur`: seconds, above 0. `desc`: one plain sentence on what happens. A person is in it only if the ask needs one (motion.md).
34
+
35
+ One continuous action (one window, one camera move, one cursor's errand) is ONE scene, however long: nothing carries
36
+ over a cut. The one exception: a character or object that moves about or travels (a window, screen, cursor, card or logo
37
+ is not one). Where two scenes in a row show it, the second must **open in the same framing, with it as the first's code
38
+ left it**: place, size, pose, speed; plan none of its moves ahead, in thought or in `shared.js`.
39
+
40
+ **Other fields.**
41
+
43
42
  - `stage`: `"rough"` until the film's last write: to the page, "not done yet".
44
- - `width`, `height`: vertical (Reels, TikTok, Shorts) is 1080 by 1920. `background`:
45
- the picked ground (an overlay: `"transparent"`, and `.ground` paints nothing). `look`: the look in words, for you when you draw the scenes, which may
46
- be another conversation: the picked option's name and line, and whatever they typed
47
- or asked for, word for word; you decided: a name and line of your own.
48
- - `palettes`: every palette the Colors question offered, picked or not (a skipped
49
- form's too), in the order shown (the picked one is not moved up), each its four
50
- colors as `#rrggbb` in role order (ground, main, second, accent); offered in words
51
- alone: settle each one's four now. `theirs`: how many of them, counted from the
52
- first, are the user's (their brand, a design system), picked or not. One system's
53
- tile and two palettes of your own: 1. The example: three of your own, the second
54
- picked: 0. They answered Other, or in words of their own: the palette you made of
55
- that stands ahead of the rest, and counts as theirs. Hexes alone: no name, no
56
- title. No Colors question asked: the film's own four alone, `theirs` 1 if they are
57
- the user's, else 0. For the page to offer again: keep both keys, as they are, at
58
- every later write of this record.
59
- - `fonts`: the faces the film is set in. Four optional slots: `sans`, `serif`, `mono` for text,
60
- `display` for headlines. A slot takes one of the ten faces that ship with the player, by id
61
- (a plain string), or nearly any Google Fonts family with the nearest of the ten to stand in (in a text slot, one for text):
62
- `{"family":"…","or":"…"}`. From memory: look nothing up. The player fetches it; `or` shows
63
- where it can't (no such name; a family that also holds Chinese, Japanese or Korean; no
64
- network). A family brings weights 400 and 700, or its nearest, never thickened; for others
65
- name them all, four at most, beside `or`: `"weights":[400,800]`. `display` unnamed: headlines
66
- take the film's `sans`, so a film set in a serif or a mono names it. Keep `fonts` at every
67
- later write.
68
-
69
- The ten. For text: `plus-jakarta-sans` (wide), `manrope` (round), `space-grotesk` (angular),
70
- `instrument-sans` (narrow), `fraunces` (serif), `dm-mono`, `departure-mono` (pixel), these two
71
- of one weight. Headlines: `fraunces-display` (hairline), `inter-display` (loud),
72
- `shantell-sans` (hand-drawn).
73
-
74
- **Pick for THIS film**, from the look's words and the subject, never from habit: films
75
- in the same faces look machine-made, and your habit is the same quiet sans under every
76
- headline, with mono labels. Think where this look's lettering would be at
77
- home (a road sign, a dashboard, a shop sign, a terminal), then what KIND of face
78
- is found there (an old-style serif, a didone, a humanist, rounded or wide
79
- sans, a typewriter, a brush script), then a family of that kind, fit for work (questions.md). The first that comes to
80
- mind is the one every film would get, so look past it, to a name you are sure of. Most
81
- films want two, a `display` over a text face that has a bold, unlike in kind, weight or
82
- voice. A third only as `mono`: for code, a terminal, or figures that count up or sit in
83
- columns.
84
-
85
- The user names a family: that one, spelt right, in the slot they mean (titles:
86
- `display`; the text: `sans`, serif or not; the whole film or a house face: `sans`, and no
87
- `display`; one of the ten: its id). If it isn't Google's (Helvetica Neue): the nearest
88
- Google family. Files they gave: the nearest of the ten under them, no `display` of yours,
89
- their names in `look`; finalize.md's step 2 brings theirs in. The ten have Western European letters only (no ł, ő,
90
- ş). Any other letters (Polish, Turkish, Greek, Cyrillic, Arabic): two slots at most, each a well-known
91
- Google family that has them and Latin, none that the player ships, two weights each; `or` as ever (it won't show). Chinese, Japanese, Korean: leave `fonts` out,
92
- whatever they named.
93
-
94
- **`modules/base.css`** (`kind` css), six lines at most for now: the picked colors
95
- as variables (`--ground`, `--main`, `--second`, `--accent`, each once, a plain
96
- `#rrggbb`, no two alike), the ground painted from them (`.ground`, one per scene, at the
97
- bottom), the type stacks the film uses, written as below or as
98
- `var(--serif, Georgia, serif)`, `var(--mono, ui-monospace, monospace)`, never a family's
99
- name (`#stage` takes the one most words wear; in a serif or mono film `.line` has that one
100
- inside, in place of the sans one), the few classes every scene uses. It is applied
101
- before any scene, and keeps scenes written apart looking like one film.
43
+ - `width`, `height`: vertical (Reels, TikTok, Shorts) is 1080 by 1920.
44
+ - `background`: the picked ground (an overlay: `"transparent"`, and `.ground` paints nothing).
45
+ - `look`: the look in words, for whoever draws the scenes, perhaps in another conversation: the picked option's name and
46
+ line, and whatever they typed, word for word. If you decided, a name and line of your own. Either way, add rich or plain. Before you write the look line, hold it against motion.md's never-draw list: no red rising sun with rays; no spread eagle or heroic figure against a red or pale disc, rays or a field of red. A sunset, a moon or a sunrise that the person asks for stays.
47
+ - `palettes`: every palette the Colors question offered, picked or not, in the order shown (the picked one is not moved
48
+ up), each its four colors as `#rrggbb` in role order (ground, main, second, accent); for a palette offered in words alone, settle its
49
+ four now. `theirs`: how many of them, from the first, are the user's. If they answered Other or in words of their
50
+ own, the palette you made of that stands first and counts as theirs.
51
+ With no Colors question: the film's own four alone, `theirs` 1 if they are the user's, else 0. Keep both keys at every
52
+ later write: the page offers them again.
53
+
54
+ **`fonts`** names the film's faces, in four optional slots: `sans`, `serif` and `mono` for text, `display` for headlines. A
55
+ slot takes one of the ten faces that ship with the player, by id, or nearly any Google Fonts family with the nearest of
56
+ the ten to stand in: `{"family":"…","or":"…"}`. Name it from memory and look nothing up; the player fetches it, and `or`
57
+ shows where it can't. A family brings weights 400 and 700, or its nearest, never thickened; for others list them, four at most:
58
+ `"weights":[400,800]`. With `display` unnamed, headlines take `sans`. A line must still fit where `or` shows, so leave it room.
59
+ With a design system in play, its faces come first: from `type.families` in its `tokens.json`, a family Google hosts goes in its slot by name. Keep `fonts` at every later write. Never download a font or search the machine for one.
60
+
61
+ The ten. Text: `plus-jakarta-sans` (wide), `manrope` (round), `space-grotesk` (angular), `instrument-sans` (narrow),
62
+ `fraunces` (serif), `dm-mono`, `departure-mono` (pixel), these two of one weight. Headlines: `fraunces-display` (hairline,
63
+ one weight, the only italic), `inter-display` (loud, to 900), `shantell-sans` (hand-drawn, slants from 600 up). `manrope` goes to 800.
64
+
65
+ **Type is Inter and Space Grotesk unless the brief calls for other lettering.** With Western European words, no design
66
+ system, and no family or lettering asked for in the request or in `Style (other)`, `display` is `space-grotesk` and `sans` is
67
+ `{"family":"Inter","or":"instrument-sans"}`. A Style option alone leaves this default in place. Inter sets wider than
68
+ `instrument-sans`, so leave each line room for it. If only `display` is named, `sans` still takes that default; if a text face
69
+ is named, leave `display` unnamed. For other lettering, think what kind of face is at home there (a hymn book, a shop
70
+ sign), then a family of that kind fit for work. Two faces are enough; a third only as `mono`, for code or figures in
71
+ columns; figures that count up need `tabular-nums` in a mono, `instrument-sans`, `space-grotesk`, `plus-jakarta-sans` or
72
+ `inter-display`.
73
+
74
+ If the user names a family, use it, spelt right, in the slot they mean (titles: `display`; the text: `sans`; the whole film:
75
+ `sans` and no `display`; one of the ten: its id), or the nearest Google family if it isn't Google's, and say so:
76
+ "X isn't on Google Fonts, so I chose Y, the closest match there, and I'll use X if you send me its font files." Of a fetched family
77
+ they named (Z is the shipped face underneath, or "a system font" for words the ten can't spell), say "X loads from Google Fonts when the animation opens, and Z shows instead if X can't load." With no `fonts` and no font file of theirs, say "The text uses each device's default font, so send me the font files if you want a specific one." In the one closing sentence, what you refused or
78
+ the Export note comes first, then these font notes; after a font note, what you made up gets a short sentence of its own. Font files they gave go in as assets over the nearest of the ten, with no
79
+ `display` of yours, each named by its stem in `base.css` ahead of the stack (`#stage { font-synthesis:style; font-family:"Face-400", var(--sans, system-ui, sans-serif) }`); a bold is a face of its own. The ten have Western European letters only: for other scripts use one or two well-known Google families that hold
80
+ them, and for Chinese, Japanese or Korean leave `fonts` out.
81
+
82
+ **`modules/base.css`** (`kind` css), six lines at most for now: the picked colors as variables, each once, a plain
83
+ `#rrggbb`, no two alike (the page reads them to offer palettes); `.ground`, the element every scene paints first, at the bottom; the type stacks,
84
+ written as below or as `var(--serif, Georgia, serif)` or `var(--mono, ui-monospace, monospace)`, never a family's name
85
+ (`#stage` wears the one most words wear); the few classes every scene uses. Pin a group of words by the edge it grows
86
+ away from, so that a line that wraps has room on any shape of film.
102
87
 
103
88
  ```css
104
- #stage { --ground:#10201D; --main:#3E8F84; --second:#C9E4DC; --accent:#D98A3D; color:var(--second); font-family:var(--sans, system-ui, sans-serif) }
89
+ #stage { --ground:#F3EFE6; --main:#7A4B2A; --second:#2B211A; --accent:#C8962E; color:var(--second); font-family:var(--sans, system-ui, sans-serif) }
105
90
  .ground { position:absolute; inset:0; background:var(--ground) }
106
91
  .words { position:absolute; margin:0; display:flex; flex-direction:column; gap:24px } .words > * { margin:0 }
107
92
  .line { font-family:var(--display, var(--sans, system-ui, sans-serif)); font-size:84px; line-height:1.05; font-weight:700 } .sub { font-size:36px }
108
93
  ```
109
94
 
110
- **`film/script`**, last: `{"code":"window.CUT = kit.scenes();"}`, that line and
111
- nothing else, for the film's whole life. No later step writes it again.
95
+ **`film/script`**, last: `{"code":"window.CUT = kit.scenes();"}`, that line and nothing else, for the film's whole life.
112
96
 
113
97
  ## A scene
114
98
 
115
- One document per scene, `modules/scene-pour.js` (`kind` js) for the scene `pour`, one
116
- `set` each, in film order, ONE call per message: think about one scene, write it,
117
- send it, read the reply, go to the next. Calls sent together arrive together.
99
+ One document per scene: `modules/scene-pour.js` (`kind` js) for the scene `pour`. The player looks for that name and no
100
+ other. finalize.md has a whole one; this is the bare shape.
118
101
 
119
102
  ```js
120
103
  const { seg, eio, lerp } = kit;
121
104
  kit.scene('pour', {
122
105
  init(kit) {
123
106
  kit.stage.append(kit.h('<div class="ground"><div class="pour-ladle"></div></div>'));
124
- this.ladle = kit.stage.querySelector('.pour-ladle').style;
125
- },
126
- render(t) {
127
- this.ladle.transform = 'rotate(' + lerp(4, -24, eio(seg(t, 0.3, 1.2))) + 'deg)';
128
- },
129
- });
130
- ```
131
-
132
- That is the bare shape. A finished scene, whole: finalize.md. A rough one: "How rough".
133
-
134
- - A scene is a small cut of its own. One `kit.scene` call per module, for the
135
- module's own id. `init(kit)` builds the scene once inside `kit.stage` (optional;
136
- may be async). `render(t, kit)` is required, synchronous and pure: every varying
137
- property set from `t` alone, SECONDS since the scene began. No CSS transitions
138
- or animations, no timers. Both are called as methods, so `this` carries what
139
- `init` built over to `render` (under any names but `lead` and `tail`, the player's).
140
- At a module's top level: constants and that call, no DOM. `init` awaits whatever
141
- `render` needs: the player tries `render(0)` as soon as `init` settles, and a throw
142
- there fails the scene.
143
- - The whole film loads again at every write: keep each `init` light.
144
- - A scene's `kit` is its own: `kit.stage` is the scene's box, which fills the stage
145
- and has no ground of its own (`scenes.json`'s `background` lies behind it); what
146
- `kit.css`, `kit.cursor`, `kit.svgLayer` and `kit.chip` make stays in the scene;
147
- `kit.video`'s `start` and `kit.typist`'s `t0` count from the scene's beginning.
148
- `kit.dur`: the scene's seconds. `kit.start`: where it begins in the film. `kit.fx`
149
- is one canvas, shared by all scenes: set its state (lineWidth, fillStyle,
150
- a clip) in `render`, never once in `init`.
151
- - A scene's own class names begin with its id (`pour-ladle`).
152
- - Scenes cut from one to the next: open what moves about as it was left; else build each to stand
153
- alone, and to hold its first and last picture for a `t` below 0 or past `kit.dur` (`seg` does).
154
- The first scene at `t` 0 is the poster and thumbnail: never blank.
155
- - No flashes: nothing blinks or cuts more than three times a second, rough or
156
- finished. No scene under a second.
157
- - `scenes.json`, `base.css`, `shared.js`, `score.js` and any name that begins `scene-`
158
- or `layer-` are the player's: name no other module so.
159
- - A line of words is ONE string in ONE element, as the viewer reads it: the page
160
- lets the user retype it, and finds it by those words. So no id, class name or
161
- key spelt like words on screen.
162
- - Lines that belong together (headline, subtitle, button) sit in ONE flow
163
- container (`.words`, a flex column with a gap), placed as a group, so that a line
164
- that wraps pushes the next one down; never each at its own `top`. A lone line sits
165
- in a `.words` too: `.line` has no place of its own.
166
- - Markup in single-quoted strings, `style="…"` in double quotes, no quotes inside
167
- a style: a nested quote silently ends the style; the rest is lost.
168
- - Helpers: `kit.el`, `kit.h`, `seg(t,a,b)` (0 to 1 across a to b), `lerp`, `clamp01`,
169
- `eo` `ei` `eio` (eases). The rest of the kit waits for finalize.md's step 1 (film-api.md).
170
- `kit.h` returns ONE element, the first in its string: wrap siblings in one root,
171
- or call it once for each.
172
-
173
- ## How rough
174
-
175
- Only for a film whose scenes are rough already (a change to it, Continue). No new film
176
- is written so.
177
-
178
- A wireframe: the real words, places, sizes and timing.
179
-
180
- - What you write: the picked ground, one ink that reads on it (`--second`), and type in
181
- the sizes and weights it will have, from `base.css`. All four picked colors stand
182
- there as variables from the start; `--main` and `--accent` wait for Finalize.
183
- - What the player shows: a rough scene as if drawn by hand in black and white: not
184
- yours to imitate, mend or undo. No picked color shows until Finalize redraws the
185
- scene: say so only if asked, or after a change of color.
186
- - Every object is an outline (2 px, no fill). One that holds no words of its own
187
- gets a short label in small capitals saying what it will be: LADLE, LOGO, CHART.
188
- A label is a placeholder, not the film's words: write it `data-label="LADLE"`
189
- (`base.css` draws it), so that the page never offers it for retyping, and at
190
- Finalize the drawing takes its place. Words that belong to the film (a chart's
191
- BEFORE and AFTER) are real text, and stay.
192
- - No fill, texture, shadow, secondary motion, camera, sound, font file or image asset.
193
-
194
- `base.css` holds these too:
195
-
196
- ```css
197
- .box { position:absolute; margin:0 }
198
- .box { border:2px solid var(--second); box-sizing:border-box; font-size:30px; letter-spacing:.12em }
199
- .box::after { content:attr(data-label); position:absolute; left:14px; bottom:10px }
200
- ```
201
-
202
- ```js
203
- const { seg, eo, lerp } = kit;
204
- kit.scene('pour', {
205
- rough: true,
206
- init(kit) {
207
- kit.stage.append(kit.h('<div class="ground">' +
208
- '<div class="box" data-label="MOULD" style="left:760px;top:560px;width:400px;height:360px"></div>' +
209
- '<div class="box pour-stream" style="left:942px;top:330px;width:36px;height:230px"></div>' +
210
- '<div class="box pour-ladle" data-label="LADLE" style="left:960px;top:170px;width:300px;height:170px;transform-origin:0 100%"></div>' +
211
- '<div class="words" style="left:140px;top:150px;width:560px"><h2 class="line">Eleven hundred degrees, four minutes</h2>' +
212
- '<p class="sub">Bronze, poured by hand</p></div></div>'));
213
- for (const k of ['ladle', 'stream']) this[k] = kit.stage.querySelector('.pour-' + k).style;
107
+ this.ladle = kit.stage.querySelector('.pour-ladle');
214
108
  },
215
109
  render(t) {
216
- this.ladle.transform = 'rotate(' + lerp(0, -24, eo(seg(t, 0.4, 1.2))) + 'deg)';
217
- this.stream.opacity = t > 1 && t < 3.6 ? 1 : 0;
110
+ this.ladle.style.transform = 'rotate(' + lerp(4, -24, eio(seg(t, 0.3, 1.2))) + 'deg)';
218
111
  },
219
112
  });
220
113
  ```
221
114
 
222
- ## Scenes missing or broken
223
-
224
- A turn cut short leaves listed scenes with no module; a scene whose code fails
225
- shows its name card in place of the scene, and nobody tells you. At "Continue the
226
- animation." (it may go on "Scenes left: 2, 3.": positions in `scenes.json`, from 1,
227
- never a count: here `pour` and `ring`; "Scenes left: 2." is `pour` alone), or when you find it so: `list` `modules`; read the named scenes that have a module, or all,
228
- for a slip and mend it (`if_version`). No scene is rough: finalize.md's "Cut short"
229
- (not in this conversation: `read` it first): what is missing is written finished, then, the
230
- stage still rough, the score and `"final"`. Else write what is missing, rough, in film order,
231
- one `set` per message, and go no further.
232
-
233
- ## A change
234
-
235
- `list` `modules` (every document whole, with its version), change the scenes it
236
- touches at the same roughness (`rough: true` stays), `set` each with `if_version`. A
237
- change of color: the variables in `base.css` (a new ground: `scenes.json`'s
238
- `background` too); where scenes are rough, the one sentence more says it will show at Finalize. A scene added,
239
- dropped, renamed, retimed or moved is an edit to `scenes.json` (`if_version`;
240
- `"stage"` stays `"rough"`; no scene is rough: finalize.md's "Cut short" follows): a new scene's module after it, a dropped scene's module
241
- `delete`d (`if_version`). Scenes that stay keep their ids. On a final film the
242
- same, finished, as finalize.md says of a final film (not in this conversation: `read`
243
- `artifact-type/reference/finalize.md` first): a new scene has no `rough: true`, the stage stays `"final"`, and
244
- `score.js` is fitted to the new times, with no `T.` of a dropped id left (it would
245
- sound at 0:00). `modules/{retyped,recolored,refaced,fetched-fonts,recut,retouched}.json` are the page's own
246
- records: never write or delete them. If the film holds one besides fetched-fonts.json,
247
- first `read` `artifact-type/reference/by-hand.md`, which says what counts.
248
-
249
- ## Finalize
250
-
251
- Its steps are on a page of their own, `artifact-type/reference/finalize.md`.
115
+ - One `kit.scene` call per module, for its own id. At the top level: constants, the drawings it adds to `W` for
116
+ later scenes (finalize.md) and that call, no DOM.
117
+ - `init(kit)` builds the scene once inside `kit.stage`. It's optional and may be async. The whole film loads again at
118
+ every write, so keep it light, and await whatever `render` needs: the player tries `render(0)` as soon as `init` settles,
119
+ and a throw there fails the scene.
120
+ - `render(t, kit)` is required, synchronous and pure: it sets every varying property from `t` alone, in seconds since the
121
+ scene began. No CSS transitions or animations, no timers, nothing remembered from the frame before. What `init` built
122
+ goes on `this`, under any names but `init`, `render` and `rough`.
123
+ - A scene's `kit` is its own: `kit.stage` fills the stage and has no ground of its own, and what `kit.css` and `kit.svgLayer`
124
+ make stays in the scene. `kit.dur` is the scene's seconds, `kit.start` where it begins; `kit.video`'s `start` and `kit.typist`'s
125
+ `t0` count from the scene's beginning. `kit.fx` is one canvas shared by all scenes: set its state in `render`, never in `init`.
126
+ - Scenes cut from one to the next. Open what moves about as it was left; else build each to stand alone, holding its
127
+ first and last picture for a `t` below 0 or past `kit.dur` (`seg` does). The first scene at `t` 0 is the poster and
128
+ thumbnail: never blank.
129
+ - A scene's class names and ids begin with its id (`pour-ladle`). `scenes.json`, `base.css`, `shared.js`, `score.js` and any
130
+ name that begins `scene-` or `layer-` are the player's: name no other module so.
131
+ - Helpers: `kit.el`, `kit.h` (returns ONE element: wrap siblings in one root), `seg(t,a,b)` (0 to 1 across a to b), `lerp`,
132
+ `clamp01`, `eo` `ei` `eio` (eases). The rest is in film-api.md.
133
+
134
+ Three things the page's own tools depend on:
135
+
136
+ - **A line of words, a drawing's labels and signs too, is ONE whole string literal in an HTML element, never svg `<text>`
137
+ or `fillText` (svg the user gave aside).** The page lets the user retype it and finds it by those words, so no id, class or key is spelt like
138
+ them. Word by word: `kit.TextFx` or `line.split(' ')`, never written in pieces.
139
+ - **Lines that belong together** sit in ONE flow container (`.words`), placed as a group, so that a line that wraps pushes
140
+ the next one along. A lone line sits in a `.words` too.
141
+ - **Markup goes in single-quoted strings, `style="…"` in double quotes, no quotes inside a style:** a nested quote silently
142
+ ends the style, and the rest is lost.
143
+
144
+ `modules/{retyped,recolored,refaced,fetched-fonts,recut,retouched}.json` are the page's own records: never write or
145
+ delete them. If the film holds one besides fetched-fonts.json, first read `artifact-type/reference/by-hand.md`.
146
+
147
+ A change, Scenes missing or broken, How rough, Finalize ("A rough cut redrawn"), Cut short: now in `artifact-type/reference/later.md`.