@ericrisco/rsc 3.1.3 → 3.3.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.
- package/README.md +8 -6
- package/manifest.json +43 -69
- package/package.json +6 -1
- package/scripts/design-upstream.js +92 -0
- package/scripts/doctor.js +0 -24
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/recommend.js +1 -3
- package/scripts/lib/retired-skills.js +7 -1
- package/skills/ai-media/SKILL.md +2 -2
- package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
- package/skills/design/SKILL.md +79 -328
- package/skills/design/THIRD_PARTY.md +26 -0
- package/skills/design/engine/LICENSE +21 -0
- package/skills/design/engine/RUN.md +515 -0
- package/skills/design/engine/UPSTREAM.json +4 -0
- package/skills/design/engine/assets/app.css +216 -0
- package/skills/design/engine/assets/base.css +105 -0
- package/skills/design/engine/assets/base.js +37 -0
- package/skills/design/engine/assets/charts.js +617 -0
- package/skills/design/engine/assets/scenes/stage.css +28 -0
- package/skills/design/engine/assets/scenes/stage.js +133 -0
- package/skills/design/engine/library/app.json +1327 -0
- package/skills/design/engine/library/directions.json +1078 -0
- package/skills/design/engine/library/heroes.json +1086 -0
- package/skills/design/engine/library/motions.json +92 -0
- package/skills/design/engine/library/palettes.json +1485 -0
- package/skills/design/engine/library/photos.json +7240 -0
- package/skills/design/engine/library/sections.json +4429 -0
- package/skills/design/engine/library/sites.json +8350 -0
- package/skills/design/engine/library/type.json +2024 -0
- package/skills/design/engine/references/app-screens.md +1199 -0
- package/skills/design/engine/references/colour.md +141 -0
- package/skills/design/engine/references/copy.md +177 -0
- package/skills/design/engine/references/critique.md +182 -0
- package/skills/design/engine/references/imagery.md +791 -0
- package/skills/design/engine/references/layout.md +292 -0
- package/skills/design/engine/references/motion.md +980 -0
- package/skills/design/engine/references/research.md +356 -0
- package/skills/design/engine/references/scenes.md +174 -0
- package/skills/design/engine/references/tells.md +115 -0
- package/skills/design/engine/references/type.md +170 -0
- package/skills/design/engine/scripts/assets.mjs +865 -0
- package/skills/design/engine/scripts/draw.mjs +584 -0
- package/skills/design/engine/scripts/inspect.mjs +303 -0
- package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
- package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
- package/skills/design/engine/scripts/lib/probe.mjs +909 -0
- package/skills/design/engine/scripts/lib/rules.mjs +243 -0
- package/skills/design/engine/scripts/lint.mjs +119 -0
- package/skills/design/engine/scripts/scenes.mjs +124 -0
- package/skills/design/evals/cases.yaml +35 -46
- package/skills/design/references/design-md.md +49 -0
- package/skills/design/references/finish-review.md +46 -0
- package/skills/design/references/modes.md +51 -0
- package/skills/expo/SKILL.md +3 -4
- package/skills/marketing/references/landing-copy.md +2 -2
- package/skills/react/SKILL.md +2 -2
- package/skills/react-native/SKILL.md +3 -3
- package/skills/scroll-film/SKILL.md +220 -0
- package/skills/scroll-film/assets/page-template.html +128 -0
- package/skills/scroll-film/assets/scrub-engine.js +352 -0
- package/skills/scroll-film/evals/README.md +26 -0
- package/skills/scroll-film/evals/cases.yaml +81 -0
- package/skills/scroll-film/references/chain.md +218 -0
- package/skills/scroll-film/references/engine.md +132 -0
- package/skills/scroll-film/scripts/verify.sh +158 -0
- package/skills/swift-ios/SKILL.md +1 -1
- package/skills/ui-engineering/SKILL.md +3 -4
- package/skills/ui-engineering/evals/cases.yaml +2 -2
- package/skills/variant-explorer/SKILL.md +4 -4
- package/skills/variant-explorer/evals/cases.yaml +1 -1
- package/targets/clone-bootstrap.mjs +1 -1
- package/scripts/lib/design-identity.js +0 -117
- package/scripts/lib/starting-point.js +0 -314
- package/skills/design/evals/README.md +0 -53
- package/skills/design/references/ai-tells.md +0 -75
- package/skills/design/references/brand-grounding.md +0 -197
- package/skills/design/references/copywriting-frameworks.md +0 -138
- package/skills/design/references/design-systems.md +0 -49
- package/skills/design/references/inspiration-sources.md +0 -96
- package/skills/design/references/landing-anatomy-and-cro.md +0 -202
- package/skills/design/references/motion-and-interaction.md +0 -228
- package/skills/design/references/research-method.md +0 -154
- package/skills/design/references/signature-and-craft.md +0 -148
- package/skills/design/references/starting-point.md +0 -75
- package/skills/design/references/trends-2026.md +0 -80
- package/skills/design/references/visual-system.md +0 -237
- package/skills/design/scripts/verify.sh +0 -357
- package/skills/design-dna/PROMPT.md +0 -211
- package/skills/design-dna/SKILL.md +0 -162
- package/skills/design-dna/design-dna.schema.json +0 -230
- package/skills/design-dna/evals/README.md +0 -64
- package/skills/design-dna/evals/cases.yaml +0 -68
- package/skills/design-dna/references/method.md +0 -436
- package/skills/design-dna/scripts/emit.py +0 -164
- package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
- package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
- package/skills/design-dna/templates/check.py.tmpl +0 -143
- package/skills/design-loop/SKILL.md +0 -160
- package/skills/design-loop/evals/README.md +0 -56
- package/skills/design-loop/evals/cases.yaml +0 -60
- package/skills/motion-craft/SKILL.md +0 -125
- package/skills/motion-craft/evals/cases.yaml +0 -38
- package/skills/motion-craft/scripts/verify.sh +0 -75
|
@@ -1,56 +1,45 @@
|
|
|
1
1
|
skill: design
|
|
2
2
|
|
|
3
3
|
should_trigger:
|
|
4
|
-
- prompt: "Design a landing page for our new analytics SaaS
|
|
5
|
-
why: "
|
|
6
|
-
- prompt: "Our homepage looks generic, like every other AI-generated startup site.
|
|
7
|
-
why: "
|
|
8
|
-
- prompt: "
|
|
9
|
-
why: "
|
|
10
|
-
- prompt: "
|
|
11
|
-
why: "
|
|
12
|
-
- prompt: "
|
|
13
|
-
why: "A
|
|
14
|
-
- prompt: "
|
|
15
|
-
why: "
|
|
16
|
-
- prompt: "Add a bento-grid features section to our marketing site that doesn't look like a template."
|
|
17
|
-
why: "'bento section' is an explicit named trigger; involves layout/composition for a marketing surface."
|
|
4
|
+
- prompt: "Design a landing page for our new analytics SaaS."
|
|
5
|
+
why: "A public page to design from scratch is the page run's core case (Persuade mode)."
|
|
6
|
+
- prompt: "Our homepage looks generic, like every other AI-generated startup site. Make it look designed."
|
|
7
|
+
why: "'Looks generic / AI-made' is named in the description; the skill exists to replace the model's default."
|
|
8
|
+
- prompt: "Hazme la web de un restaurante de mi pueblo, que no parezca una plantilla."
|
|
9
|
+
why: "A local business page in Spanish; the inventory of the subject and real pictures are exactly what the run forces."
|
|
10
|
+
- prompt: "Rediseña el dashboard de facturación, que se ve feo y desordenado."
|
|
11
|
+
why: "An app screen redesign (Operate mode) follows the app-screen run with its own draw and inspector."
|
|
12
|
+
- prompt: "Review this portfolio page and tell me the three changes that would improve it most."
|
|
13
|
+
why: "A review of an existing page: record the before in the browser, critique the screenshots, stop at findings."
|
|
14
|
+
- prompt: "Restyle our docs site so it stops looking like every other docs template."
|
|
15
|
+
why: "Read mode: the style owns the frame while the reading column stays calm."
|
|
18
16
|
|
|
19
17
|
should_not_trigger:
|
|
20
|
-
- prompt: "Write the
|
|
21
|
-
route_to: "
|
|
22
|
-
why: "
|
|
23
|
-
- prompt: "
|
|
18
|
+
- prompt: "Write the hero headline, offer and CTA copy for our pricing page."
|
|
19
|
+
route_to: "landing-copy"
|
|
20
|
+
why: "The words on a conversion page belong to landing-copy; design builds the page around them."
|
|
21
|
+
- prompt: "Where should the filter state live, in the URL or in the component, and how do we handle loading and errors?"
|
|
22
|
+
route_to: "ui-engineering"
|
|
23
|
+
why: "How interface is built (state, boundaries, async states) is ui-engineering, not visual design."
|
|
24
|
+
- prompt: "Make our web app pass WCAG 2.2 AA: fix the axe violations and keyboard traps."
|
|
25
|
+
route_to: "accessibility"
|
|
26
|
+
why: "Conformance remediation is the accessibility skill's job."
|
|
27
|
+
- prompt: "Create our logo brief and a full brand book with print colours."
|
|
28
|
+
route_to: "brand-identity"
|
|
29
|
+
why: "A brand system for every medium is brand-identity; design consumes it with --keep palette,type."
|
|
30
|
+
- prompt: "Set up Next.js App Router routing and server actions for these pages."
|
|
24
31
|
route_to: "nextjs"
|
|
25
|
-
why: "
|
|
26
|
-
- prompt: "Build a daily-use internal ops dashboard for our support team to triage tickets fast — pure data tables, no marketing."
|
|
27
|
-
route_to: "none"
|
|
28
|
-
why: "Dense internal operational tooling is explicitly a do-NOT-force-a-landing case; the skill says not to paint a marketing skin on a daily-use tool. Routes to product judgment, not a sibling."
|
|
29
|
-
- prompt: "Mirror our brand colors and typography into the Flutter mobile app's theme."
|
|
30
|
-
route_to: "flutter"
|
|
31
|
-
why: "Translating brand tokens into a Flutter app is the flutter sibling's job, not web UI design."
|
|
32
|
-
- prompt: "Implement an iOS 26 SwiftUI Liquid Glass material for this native settings panel."
|
|
33
|
-
route_to: "none"
|
|
34
|
-
why: "Native iOS Liquid Glass is the external liquid-glass-design reference; this skill ships only the web glass approximation, so it should decline/defer (no in-repo sibling)."
|
|
35
|
-
- prompt: "Help me wire up the spring physics and AnimatePresence layout animations for this modal in Framer Motion."
|
|
36
|
-
route_to: "none"
|
|
37
|
-
why: "Deep motion-code mechanics (springs, AnimatePresence internals) are explicitly deferred to the external motion-ui references; design only sets motion intent + budget."
|
|
32
|
+
why: "Framework build mechanics are nextjs."
|
|
38
33
|
|
|
39
34
|
capability:
|
|
40
|
-
- scenario: "
|
|
35
|
+
- scenario: "Asked to build a landing page for a bakery with no brand and no photographs."
|
|
41
36
|
must_include:
|
|
42
|
-
- "
|
|
43
|
-
- "
|
|
44
|
-
- "
|
|
45
|
-
- "
|
|
46
|
-
- "
|
|
47
|
-
|
|
48
|
-
- "Benefit-led, specific copy that avoids the ban-list (revolutionary, seamless, game-changer, unlock, supercharge, elevate, etc.)."
|
|
49
|
-
- scenario: "User asks: 'Here's our current pricing page — do a design review and tell me how good it actually is.'"
|
|
37
|
+
- "inventory of eight to twelve things only this client has"
|
|
38
|
+
- "draw.mjs deals three directions; one is chosen against the inventory with a written reason"
|
|
39
|
+
- "an idea sentence that fails the competitor test, and one live thing"
|
|
40
|
+
- "at least four real pictures found with assets.mjs and credited"
|
|
41
|
+
- "inspect.mjs at 1440, 768 and 390 with zero FAIL before reporting"
|
|
42
|
+
- scenario: "The machine has no Chrome and Node 20."
|
|
50
43
|
must_include:
|
|
51
|
-
- "
|
|
52
|
-
- "
|
|
53
|
-
- "For each dimension scoring below 8, gives a concrete actionable fix (e.g. exact spacing/token change), not a vague 'improve spacing'."
|
|
54
|
-
- "Checks copy against the ban-list and flags hype/vague value props that fail the 5s test."
|
|
55
|
-
- "Flags any transition: all, magic hex/px, cards-in-cards, or purple->blue everything gradient as defects, not preferences."
|
|
56
|
-
- "Ties findings back to the brand study / cited trend source where the bar is set, rather than asserting taste."
|
|
44
|
+
- "runs lint.mjs and critiques screenshots from another browser tool"
|
|
45
|
+
- "the report says 'inspector not run' with the reason and claims no gate pass"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# DESIGN.md: the record of the system
|
|
2
|
+
|
|
3
|
+
Adapted from impeccable's `document` by Paul Bakaus (Apache-2.0), which follows the open
|
|
4
|
+
`design.md` format. Rewritten and shortened.
|
|
5
|
+
|
|
6
|
+
The point: the next edit, the next session and the next tool keep the look instead of drifting back
|
|
7
|
+
to the model's default. Write it **after** the build, from what was actually built and measured,
|
|
8
|
+
never before. A rulebook written ahead of the build gets defended against reality.
|
|
9
|
+
|
|
10
|
+
## Where and when
|
|
11
|
+
|
|
12
|
+
- `DESIGN.md` at the project root, committed with the code.
|
|
13
|
+
- Written when a surface creates or replaces the visual system. Extending an existing surface reads
|
|
14
|
+
it and does not rewrite it.
|
|
15
|
+
- If `02-DOCS/wiki/brand/` exists, `DESIGN.md` uses the brand's colours and faces and names that
|
|
16
|
+
file as its source.
|
|
17
|
+
|
|
18
|
+
## Shape
|
|
19
|
+
|
|
20
|
+
YAML front matter holds the tokens; the body explains them.
|
|
21
|
+
|
|
22
|
+
```markdown
|
|
23
|
+
---
|
|
24
|
+
name: <product>
|
|
25
|
+
description: <one line: the idea sentence>
|
|
26
|
+
colors:
|
|
27
|
+
ground: "#…"
|
|
28
|
+
ink: "#…"
|
|
29
|
+
accent: "#…"
|
|
30
|
+
field: "#…"
|
|
31
|
+
typography:
|
|
32
|
+
display: { fontFamily: "…", fontWeight: 600, letterSpacing: "-0.03em", lineHeight: 0.95 }
|
|
33
|
+
text: { fontFamily: "…", fontSize: "18px", lineHeight: 1.5 }
|
|
34
|
+
rounded: { button: "0px", media: "0px" }
|
|
35
|
+
spacing: { margin: "24px", section: "200px" }
|
|
36
|
+
direction: { id: "<dealt id>", seed: <n> }
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Overview the idea, the mode, the direction and why it was chosen
|
|
40
|
+
## Colors roles and proportions (one ground, one ink, one accent, fields)
|
|
41
|
+
## Typography the scale actually used, tracking, measure
|
|
42
|
+
## Layout grid, outer margins, section rhythm, the phone composition
|
|
43
|
+
## Imagery sources, crops, treatment, where credits live
|
|
44
|
+
## Motion the signature move and the entrance recipe
|
|
45
|
+
## Components buttons, navigation, the live thing, footer
|
|
46
|
+
## Do's and Don'ts the house rules this project relies on, plus any `--allow` and its reason
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Values come from the built CSS and the inspector's measurements, not from memory.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# The fresh review
|
|
2
|
+
|
|
3
|
+
Adapted from impeccable's finish reviewer by Paul Bakaus (Apache-2.0); rewritten and shortened.
|
|
4
|
+
|
|
5
|
+
The builder believes its own page. The review runs **outside** the build: a fresh subagent with no
|
|
6
|
+
history of this conversation, so it does not inherit the builder's framing or optimism. Without
|
|
7
|
+
subagents, step fully out of the build and run the same checks yourself, and say in the report that
|
|
8
|
+
the review was not independent.
|
|
9
|
+
|
|
10
|
+
## The packet
|
|
11
|
+
|
|
12
|
+
Hand it only this:
|
|
13
|
+
|
|
14
|
+
- the original request and the user's answers;
|
|
15
|
+
- the screenshots from `.taste-skill/inspect/` at every width (the reviewer has no browser);
|
|
16
|
+
- the last `inspect.mjs` result;
|
|
17
|
+
- the direction sheet (`draw.mjs --show`), the idea sentence and the live thing;
|
|
18
|
+
- the paths of `../engine/RUN.md` (house rules) and `../engine/references/critique.md`.
|
|
19
|
+
|
|
20
|
+
## The checks, in order
|
|
21
|
+
|
|
22
|
+
0. **Evidence.** Every width captured, no blank or half-loaded screenshot, content where the
|
|
23
|
+
filename says. If one fails, the answer is `recapture` and nothing else.
|
|
24
|
+
1. **First screen.** Within five seconds: what is this, why does it matter, what do I do?
|
|
25
|
+
2. **The idea.** Is the idea sentence visible on the page, in every section, and does it fail the
|
|
26
|
+
competitor test (would it work for the nearest competitor)?
|
|
27
|
+
3. **The direction.** Was the dealt direction built, or did the build drift back to the layout the
|
|
28
|
+
model would have made anyway?
|
|
29
|
+
4. **Substance.** Real pictures, full content, no screen that is mostly empty ground.
|
|
30
|
+
5. **Truth.** No invented customers, quotes, numbers or logos. Sample content marked.
|
|
31
|
+
6. **Floor.** The house rules in `../engine/RUN.md`, plus what screenshots show and code hides:
|
|
32
|
+
states (hover, focus, loading, empty, error), themed selection and focus rings, text that fits.
|
|
33
|
+
|
|
34
|
+
## The four words
|
|
35
|
+
|
|
36
|
+
The first line of the answer is exactly one of these. The word follows from the checks:
|
|
37
|
+
|
|
38
|
+
- `recapture`: the evidence failed, not the page. Capture again, then a full review.
|
|
39
|
+
- `rebuild`: the focal element or the direction itself is wrong. Re-derive it; patches will not do.
|
|
40
|
+
- `fix`: an ordered list of material fixes, at most eight, most important first.
|
|
41
|
+
- `ship`: nothing material remains.
|
|
42
|
+
|
|
43
|
+
Then: `fixes` (when `fix`) and `keep`, one line naming what must not be diluted while fixing.
|
|
44
|
+
Apply the fixes in one batch, run the gate again, and send the new screenshots back for a verdict
|
|
45
|
+
on each fix: resolved, partial or unresolved. Two rounds at most. A partial or unresolved fix
|
|
46
|
+
never becomes `ship`. Report the reviewer's word as it was given.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# The four modes
|
|
2
|
+
|
|
3
|
+
Adapted from impeccable by Paul Bakaus (Apache-2.0); rewritten and shortened. The mode names what
|
|
4
|
+
the visitor's success looks like on this surface. Pick it from the surface, never the product: a
|
|
5
|
+
developer tool's landing page is Persuade, a fashion house's docs are Read.
|
|
6
|
+
|
|
7
|
+
## Persuade
|
|
8
|
+
|
|
9
|
+
The visitor decides and acts. The design is what they judge.
|
|
10
|
+
|
|
11
|
+
- The first screen makes the offer clear and shows **the action visitors came to take, working**: a
|
|
12
|
+
flight search, open appointment slots, add to cart. A button that points somewhere else is not it.
|
|
13
|
+
- The subject's world carries the page; it is never painted over it as a poster with no working parts.
|
|
14
|
+
- Refuse both ends: the poster, and the stock product page (headline, subline, button, a row of
|
|
15
|
+
equal cards; or the split hero with a badge row under it).
|
|
16
|
+
|
|
17
|
+
## Experience
|
|
18
|
+
|
|
19
|
+
The visitor is inside the work: portfolio, gallery, showcase. The work leads from the first
|
|
20
|
+
viewport and the interface recedes. Same page run as Persuade.
|
|
21
|
+
|
|
22
|
+
## Operate
|
|
23
|
+
|
|
24
|
+
The visitor completes a task. Someone who uses tools like this should know where everything is
|
|
25
|
+
before they notice the style.
|
|
26
|
+
|
|
27
|
+
- The direction lends four things only: **type, palette, density and one signature move** (service
|
|
28
|
+
health drawn as a transit map, a schedule in a timetable's column rhythm). Never the layout, the
|
|
29
|
+
navigation model or the controls.
|
|
30
|
+
- Every control is the standard one the visitor already knows. No costume of a terminal, an
|
|
31
|
+
instrument bezel or ledger paper.
|
|
32
|
+
- Colour does real jobs: a committed shell, state colours, data series, selection. A grey screen
|
|
33
|
+
with one accent is the category default without its polish.
|
|
34
|
+
- Realistic data with units and one state worth seeing (an alert, a degraded service, an empty
|
|
35
|
+
filter). Never grey placeholder bars.
|
|
36
|
+
- The bar is the cleanest product in its category; the signature move adds to that, never replaces it.
|
|
37
|
+
|
|
38
|
+
The run, rules and checks for these screens: `../engine/references/app-screens.md`
|
|
39
|
+
(`draw.mjs --app`, `inspect.mjs --app`).
|
|
40
|
+
|
|
41
|
+
## Read
|
|
42
|
+
|
|
43
|
+
The visitor understands something: docs, guide, article, changelog.
|
|
44
|
+
|
|
45
|
+
- The style owns **the frame**: masthead, rail, ground, title and section openers, how code and
|
|
46
|
+
tables are set. That is where palette, type and the signature move commit fully.
|
|
47
|
+
- The reading column stays calm: a reading face, real contrast, about 60–75 characters at about
|
|
48
|
+
16px. No monospace prose. Nothing performs behind the text.
|
|
49
|
+
- The style never supplies the reading order or replaces the navigation with something to learn.
|
|
50
|
+
- Check: can a reader tell where they are, where to go next, and read comfortably? Cover the name:
|
|
51
|
+
could this be any other product's docs? Then the frame has not taken the style yet.
|
package/skills/expo/SKILL.md
CHANGED
|
@@ -8,10 +8,9 @@ origin: risco
|
|
|
8
8
|
|
|
9
9
|
# Expo & EAS: shipping React Native
|
|
10
10
|
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`../motion-craft/SKILL.md`'s.
|
|
11
|
+
**Scope.** This skill owns the Expo platform: router, config plugins, EAS, native modules, store
|
|
12
|
+
builds. Animation and gestures (Reanimated worklets, sheets, press feedback, haptics) follow
|
|
13
|
+
`../react-native/SKILL.md`. Visual design of web pages and screens is `../design/SKILL.md`'s.
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
## What this skill owns
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Landing Copy by Section
|
|
2
2
|
|
|
3
|
-
The words for every landing section
|
|
3
|
+
The words for every landing section. `../../design/SKILL.md` owns the visual treatment, the layout and which sections exist (its argument step: one claim per section); this file owns the copy that goes inside each section. Each section has **one copy job**; cut any section whose job you can't name. Ground every line in the brand study (`brand-grounding.md`) and run the value prop through the canvas (`copy-frameworks.md`) first.
|
|
4
4
|
|
|
5
5
|
## Section map (copy job + framework)
|
|
6
6
|
|
|
@@ -187,5 +187,5 @@ Good — "Stop babysitting staging. Start free — no credit card."
|
|
|
187
187
|
|
|
188
188
|
- `copy-frameworks.md` — the frameworks and headline formulas each section uses.
|
|
189
189
|
- `brand-grounding.md` — the value prop, proof, and voice every line is grounded in.
|
|
190
|
-
- `../../design/
|
|
190
|
+
- `../../design/SKILL.md` — the page these words fill; write the copy deck before its markup.
|
|
191
191
|
- `campaigns-and-channels.md` — driving traffic to this page.
|
package/skills/react/SKILL.md
CHANGED
|
@@ -10,8 +10,8 @@ origin: risco
|
|
|
10
10
|
|
|
11
11
|
**Hand-off — picking dependencies.** Which library to reach for (UI primitives, command menus,
|
|
12
12
|
toasts, OTP inputs, charts, virtualization, drag and drop, state, styling, theming) is
|
|
13
|
-
`../ui-engineering/SKILL.md`'s — build vs adopt is decided there, before hand-rolling a component.
|
|
14
|
-
|
|
13
|
+
`../ui-engineering/SKILL.md`'s — build vs adopt is decided there, before hand-rolling a component. The look of a page
|
|
14
|
+
or screen, and its motion layer, is `../design/SKILL.md`'s.
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
> Build or review a fast, typed, correctly-architected **client-side** React single-page app bundled by Vite. No server runtime, no RSC tree, no framework router. Server-rendered React (App Router, server actions, SSR/SSG) is not this skill — that is `../nextjs/SKILL.md`.
|
|
@@ -8,9 +8,9 @@ origin: risco
|
|
|
8
8
|
|
|
9
9
|
# React Native (app code)
|
|
10
10
|
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
`../
|
|
11
|
+
**Scope.** App code, including animation and gestures (Reanimated worklets on the UI runtime, gesture
|
|
12
|
+
handoff, sheets, press feedback, haptics), in bare React Native and in Expo. Visual design of web
|
|
13
|
+
pages and screens is `../design/SKILL.md`'s.
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
You write the code that runs *inside* the app: render, navigate, animate, list, persist, and author native modules. Mobile is split across two skills by **verb** — run the verb test before doing anything:
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scroll-film
|
|
3
|
+
description: "Use when a page's hero is a pre-rendered camera flight the visitor scrubs by scrolling, Apple-style: AI clips chained frame to frame so seams never pop, encoded for seeking, mounted with an engine that holds up on phones. NOT scroll reveals or pinning (that is `design`), NOT a finished MP4 (that is `ai-media`)."
|
|
4
|
+
tags: [scroll-scrub, scroll-video, scrollytelling, landing-hero, image-to-video, camera-flight, seamless-video]
|
|
5
|
+
recommends: [ai-media, fal, replicate-images, landing-copy, design, performance]
|
|
6
|
+
profiles: [full]
|
|
7
|
+
origin: risco
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# scroll-film — the camera moves, the scroll is the playhead
|
|
11
|
+
|
|
12
|
+
The page plays nothing. It holds a chain of pre-rendered clips and maps the scroll position to
|
|
13
|
+
their playhead, so the visitor flies through scenes — outside a building, inside it, on to the
|
|
14
|
+
next — without a cut. Three parts must be right, and each fails in its own way: the **chain**
|
|
15
|
+
(seams), the **encode** (seeking) and the **page** (phones). This skill owns all three.
|
|
16
|
+
|
|
17
|
+
**Not this skill — delegate:**
|
|
18
|
+
- Scroll-linked reveals, parallax, pinned sections, a horizontal pan, with no video → `../design/SKILL.md`.
|
|
19
|
+
- A finished MP4 with voice and music → `../ai-media/SKILL.md`.
|
|
20
|
+
- The call to the model endpoint (queue, webhooks, cost per call) → `../fal/SKILL.md` or `../replicate/SKILL.md`.
|
|
21
|
+
- The scene stills themselves → `../replicate-images/SKILL.md`.
|
|
22
|
+
- The words over the film → `../landing-copy/SKILL.md`. A video rendered from code → `../remotion-video/SKILL.md`.
|
|
23
|
+
|
|
24
|
+
## 1. The gate: nothing renders before the user agrees to a number
|
|
25
|
+
|
|
26
|
+
A chain is many clips, each one slow and billed. Interview, price it, then wait for a yes.
|
|
27
|
+
|
|
28
|
+
1. **Subject.** Ask it as an open question: what the world is about, in a sentence. A menu of
|
|
29
|
+
industries decides their business for them.
|
|
30
|
+
2. **Scenes.** 4 to 7, in the order the camera visits them, proposed from their own value chain and
|
|
31
|
+
edited by them. The last one carries the call to action.
|
|
32
|
+
3. **Look.** One art direction, written once as a style preamble and pasted unchanged into every
|
|
33
|
+
still prompt. Identical text is what makes separate renders read as one world.
|
|
34
|
+
4. **Camera feel.** Always ask. It picks the chain shape (section 3).
|
|
35
|
+
5. **Phones.** Always ask. A real phone version is a second chain (section 6) and doubles the video bill.
|
|
36
|
+
6. **Brand.** 4 to 6 hex colours, a name, a tone. The page background is the scene background.
|
|
37
|
+
|
|
38
|
+
Then the bill:
|
|
39
|
+
|
|
40
|
+
| | Forward take (A) | Dive and hop (B) |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| Video clips, desktop only | N | 2N − 1 |
|
|
43
|
+
| Video clips, with the phone chain | 2N | 4N − 2 |
|
|
44
|
+
|
|
45
|
+
Estimate = clips × seconds per clip × today's price per second, plus about 20% for re-rolls, plus N
|
|
46
|
+
stills. Read the price from the provider at build time; it moves (reading cost per call →
|
|
47
|
+
`../fal/SKILL.md`). State the total against the balance and wait for the yes. When money is tight,
|
|
48
|
+
offer a previz pass: the whole chain on the **same** model at its lowest resolution, approve the
|
|
49
|
+
journey, then render the finals.
|
|
50
|
+
|
|
51
|
+
## 2. The model must continue a shot, not just start one
|
|
52
|
+
|
|
53
|
+
A seam holds only if the model starts a clip on an exact frame you hand it. That capability, not
|
|
54
|
+
quality or price, qualifies a model.
|
|
55
|
+
|
|
56
|
+
- Every clip needs a **start-frame** input. Hops (shape B) also need an **end-frame** input. Field
|
|
57
|
+
names vary by endpoint (`image_url`, `start_image`, `first_frame`; `end_image_url`,
|
|
58
|
+
`tail_image_url`, `last_frame`): read the input schema before batching. Model landscape →
|
|
59
|
+
`../ai-media/SKILL.md`.
|
|
60
|
+
- A **reference-image** input does not qualify. It steers the style; frame 0 is still a new
|
|
61
|
+
render, and the seam pops.
|
|
62
|
+
- **Probe once before the batch**, with one cheap clip from a real still prepared at the clip's
|
|
63
|
+
aspect (16:9, or 9:16 for phones). Frame 0 must match the still and the bill must match the
|
|
64
|
+
advertised price. For hop duty, add an end frame from another still: the last frame must land on
|
|
65
|
+
that composition. A near miss is normal; the engine's dissolve covers it. Commands →
|
|
66
|
+
`references/chain.md`.
|
|
67
|
+
- **One model for the whole chain.** Each model has its own grain, colour and motion; a swap
|
|
68
|
+
mid-chain keeps positions continuous and still pops. The one exception is a single clip a content
|
|
69
|
+
filter keeps blocking (`references/chain.md`).
|
|
70
|
+
|
|
71
|
+
## 3. Pick the chain shape from the camera feel
|
|
72
|
+
|
|
73
|
+
| The user says | Shape | Clip k starts on | Clip k ends on | Fits |
|
|
74
|
+
| --- | --- | --- | --- | --- |
|
|
75
|
+
| "one continuous walkthrough", "one fixed angle gliding over it" | **A — forward take** | leg k−1's real last frame (leg 0: the first still) | nothing — no end frame | photoreal, interiors, product, real estate |
|
|
76
|
+
| "fly from one island to the next" | **B — dive and hop** | dive: its still. hop: dive k's real last frame | hop: dive k+1's real first frame | miniature, diorama, map-like worlds |
|
|
77
|
+
|
|
78
|
+
- A renders one leg at a time, because each needs the previous one's last frame. B renders every
|
|
79
|
+
dive in parallel, then every hop.
|
|
80
|
+
- Never give an A leg a wide establishing shot as its end frame. The camera pulls back to meet it,
|
|
81
|
+
and the take reverses.
|
|
82
|
+
- **The velocity rule.** Inside one clip the camera is free: orbit, rise, push in and ease back.
|
|
83
|
+
Across a seam it never reverses. Position matches, the motion flips, and it reads as a rewind,
|
|
84
|
+
in both scroll directions. So every leg ends by settling into a slow forward drift, and the next
|
|
85
|
+
one begins inside it. B reverses at every seam on purpose (pull out, hop, dive): charming over a
|
|
86
|
+
miniature map, a stutter in a realistic walkthrough. If the user picks B for a photoreal look, say
|
|
87
|
+
so before rendering.
|
|
88
|
+
|
|
89
|
+
Prompt templates (still, leg, dive, hop), the camera-move table by concept, the fixed-angle clause
|
|
90
|
+
and the batch loops → `references/chain.md`.
|
|
91
|
+
|
|
92
|
+
## 4. The seam law: hand off pixels, not descriptions
|
|
93
|
+
|
|
94
|
+
Every render redraws its scene a little differently. A hop that ends on the *original still* of
|
|
95
|
+
scene 3 meets a dive that starts on *its own render* of scene 3: two pictures of one place, and the
|
|
96
|
+
seam pops. So both ends of every clip come from the neighbours' actual renders.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# The true last frame: -update 1 overwrites per decoded frame, so the final one is what stays.
|
|
100
|
+
ffmpeg -v error -y -sseof -1 -i leg_02.mp4 -update 1 -q:v 2 leg_02_last.png
|
|
101
|
+
ffmpeg -v error -y -i dive_03.mp4 -frames:v 1 -q:v 2 dive_03_first.png
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A plain `-sseof -0.15 -frames:v 1` stops a few frames short of the end: 15 dB PSNR from the real
|
|
105
|
+
last frame on a 24 fps test clip. The engine parks the outgoing clip on its real last frame, so a
|
|
106
|
+
handoff from the earlier one starts the seam already off.
|
|
107
|
+
|
|
108
|
+
- Look at each last frame before chaining the next clip. In A, one bad handoff poisons every leg
|
|
109
|
+
after it: re-roll that leg.
|
|
110
|
+
- The engine dissolves over a tenth of a viewport at each seam. That hides an end frame that landed
|
|
111
|
+
close. It cannot hide a different picture.
|
|
112
|
+
|
|
113
|
+
## 5. Encode for seeking, not for playback
|
|
114
|
+
|
|
115
|
+
Scrubbing seeks on every frame, and each seek decodes every frame since the last keyframe. The
|
|
116
|
+
keyframe interval sets how smooth the scrub feels.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Desktop: native resolution (never upscale), a keyframe every 8 frames, no audio, index first.
|
|
120
|
+
ffmpeg -i leg_01.mp4 -an -c:v libx264 -preset slow -crf 20 -pix_fmt yuv420p \
|
|
121
|
+
-g 8 -keyint_min 8 -sc_threshold 0 -movflags +faststart assets/vid/shot-01.mp4
|
|
122
|
+
# Phone: 720 wide, a keyframe every 4 frames; a phone decoder pays more per frame.
|
|
123
|
+
ffmpeg -i leg_01_portrait.mp4 -an -vf scale=720:-2 -c:v libx264 -preset slow -crf 23 -pix_fmt yuv420p \
|
|
124
|
+
-g 4 -keyint_min 4 -sc_threshold 0 -movflags +faststart assets/vid/shot-01-m.mp4
|
|
125
|
+
# Poster: the clip's first frame. It is the LCP image and the reduced-motion fallback.
|
|
126
|
+
ffmpeg -v error -i assets/vid/shot-01.mp4 -frames:v 1 -c:v libwebp -quality 82 assets/shot-01.webp
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
One recipe for every clip in the chain. All-intra (`-g 1`) is the last resort for a low-end phone
|
|
130
|
+
that still stutters, at about three times the size.
|
|
131
|
+
|
|
132
|
+
## 6. Phones get their own chain, not a crop
|
|
133
|
+
|
|
134
|
+
A 16:9 film on a portrait phone shows its middle quarter. When the user asked for phones, render
|
|
135
|
+
the chain again at 9:16, frame-locked to its own renders. A landscape frame inside the portrait
|
|
136
|
+
chain pops on both sides. Start images: each still composited onto a 1080×1920 canvas in the page
|
|
137
|
+
background colour. The encodes are the `-m.mp4` files above, and each portrait clip's first frame is
|
|
138
|
+
its `posterMobile`.
|
|
139
|
+
|
|
140
|
+
No budget for it: the engine crops the landscape film with `object-fit: cover`, steered by
|
|
141
|
+
`--film-focus`. Tell the user this is a stopgap. Never ship it as "the mobile version".
|
|
142
|
+
|
|
143
|
+
## 7. Mount the page
|
|
144
|
+
|
|
145
|
+
Copy `assets/scrub-engine.js` and `assets/page-template.html` into the project. The template is a
|
|
146
|
+
working page: its copy is real HTML that reads without JavaScript, and the script moves it onto the
|
|
147
|
+
film once the engine mounts.
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const film = ScrollFilm.mount(document.getElementById('film'), {
|
|
151
|
+
shots: [
|
|
152
|
+
{ section: 0, src: 'assets/vid/shot-01.mp4', srcMobile: 'assets/vid/shot-01-m.mp4',
|
|
153
|
+
poster: 'assets/shot-01.webp', posterMobile: 'assets/shot-01-m.webp', length: 1.4, hold: 0.4 },
|
|
154
|
+
{ section: 0, src: 'assets/vid/hop-01.mp4', poster: 'assets/hop-01.webp', length: 0.9 },
|
|
155
|
+
// one entry per clip, in chain order
|
|
156
|
+
],
|
|
157
|
+
onChange: ({ section, sectionProgress }) => { /* fade the copy block for `section` */ },
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
What the engine does, and the failure each part prevents:
|
|
162
|
+
|
|
163
|
+
- **Loads each clip as a Blob.** On a host that ignores byte-range requests, a streamed `<video>`
|
|
164
|
+
reports `seekable` as [0, 0] and every seek lands on frame 0. A Blob is always seekable.
|
|
165
|
+
- **Keeps one seek in flight per clip.** Queued seeks are what freeze a phone on a fast flick.
|
|
166
|
+
- **Keeps the poster up until a frame has painted**, and primes each clip with a muted play and
|
|
167
|
+
pause, again on the first touch. iOS paints nothing for a seeked video that never played.
|
|
168
|
+
- **Picks phone sources at mount**, ignores height-only resizes on touch (the URL bar sliding), and
|
|
169
|
+
re-lays out on rotation.
|
|
170
|
+
- **Reduced motion or Save-Data:** no video is fetched; posters dissolve with the scroll and the
|
|
171
|
+
copy still works.
|
|
172
|
+
- **Fetches a clip once**, when the scroll comes within 1.5 viewports of it.
|
|
173
|
+
- **`destroy()`** removes everything it added. Call it from the effect cleanup in React or Vue.
|
|
174
|
+
|
|
175
|
+
`design` bans raw scroll listeners for animation. This one is passive and only requests a frame. If
|
|
176
|
+
the page already owns a scroll source (ScrollTrigger, `useScroll`), pass `listen: false` and call
|
|
177
|
+
`film.update()` from it. All options, the state object, theming, pacing and the Next.js and Vue
|
|
178
|
+
wiring → `references/engine.md`.
|
|
179
|
+
|
|
180
|
+
The film layer is `aria-hidden`; the meaning lives in the copy. Its words → `../landing-copy/SKILL.md`.
|
|
181
|
+
Its type, colour and spacing → `../design/SKILL.md`.
|
|
182
|
+
|
|
183
|
+
## 8. Verify before calling it done
|
|
184
|
+
|
|
185
|
+
1. **The chain, by script.** Pass the clips in chain order:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
bash <skill-dir>/scripts/verify.sh --frames qa/seams \
|
|
189
|
+
assets/vid/shot-01.mp4 assets/vid/hop-01.mp4 assets/vid/shot-02.mp4
|
|
190
|
+
bash <skill-dir>/scripts/verify.sh --gop 4 --portrait \
|
|
191
|
+
assets/vid/shot-01-m.mp4 assets/vid/hop-01-m.mp4 assets/vid/shot-02-m.mp4
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
It checks each clip's encode (H.264, yuv420p, no audio, index first, keyframe interval), that
|
|
195
|
+
the chain shares one size and frame rate, and scores every seam against the clips' own
|
|
196
|
+
frame-to-frame step. A fixed SSIM threshold cannot tell: on a soft landscape photo, a cut to a
|
|
197
|
+
different picture still scored 0.76 and a fresh render of the same scene 0.92. Measured against
|
|
198
|
+
the step (0.98 there), the frame-locked seam passed at 0.987 and both others were flagged. Open
|
|
199
|
+
every flagged pair in `qa/seams/` and judge by what you see: a wrong seam shows different objects.
|
|
200
|
+
2. **In a browser.** Scroll slowly across each seam on desktop. Then emulate a phone at 4 to 6× CPU
|
|
201
|
+
throttle and flick fast. Turn reduced motion on: posters only. In the Network panel, the phone
|
|
202
|
+
gets the `-m.mp4` files and the desktop gets the masters.
|
|
203
|
+
3. **On a real iPhone**, if phones shipped: the first scene shows at once and no scene goes blank.
|
|
204
|
+
|
|
205
|
+
## Anti-patterns
|
|
206
|
+
|
|
207
|
+
| Failure mode | Usually | Fix |
|
|
208
|
+
| --- | --- | --- |
|
|
209
|
+
| A visible pop at one seam | The hop's end frame was the original still, or came from `-sseof` a few frames early | Extract the true frames from the neighbouring renders (section 4) |
|
|
210
|
+
| The camera seems to rewind at every seam | Velocity reverses across the seam: B in a realistic walkthrough, or an A leg given a wide end frame | Shape A with the settle-into-drift clauses and no end frame |
|
|
211
|
+
| Stuck on the first frame in production, fine locally | The host ignores byte ranges | Load as Blob; keep it when porting the engine to a framework |
|
|
212
|
+
| A phone freezes on a fast flick | A 1080p, 8-frame-GOP master on a phone decoder, plus queued seeks | `-m.mp4` encodes at GOP 4; keep one seek in flight |
|
|
213
|
+
| Black scenes on iPhone until the first touch | The poster was hidden on `loadedmetadata`, or `muted`/`playsinline` lost in a port | Keep the poster until a frame paints; keep both attributes |
|
|
214
|
+
| The film scrolls away instead of sticking | An ancestor has `overflow: hidden`, often `body { overflow-x: hidden }` | `overflow-x: clip` |
|
|
215
|
+
| A cheaper model "just for the hops" | Each model renders its own grain and motion | One model for the chain; previz on the same model at low resolution |
|
|
216
|
+
| A content filter blocks a harmless interior | Words like bed, pool, spa or wine in the prompt | Re-roll, neutral wording, then the remedies in `references/chain.md` |
|
|
217
|
+
|
|
218
|
+
## Orientación (siempre)
|
|
219
|
+
|
|
220
|
+
Habla con la voz de `orient`: frases cortas, una idea por frase, y cada respuesta se entiende sola. Registro técnico o con analogías según `technical_level` en `02-DOCS/wiki/harness/user-profile.md`. Cierra cada turno con el **bloque-brújula** (📍 dónde estás · ➡️ siguiente, terminando en pregunta; ✅ y 🧭 cuando hay algo hecho o decidido). **Nunca termines en seco.** Protocolo completo: skill `orient` → `skills/orient/references/orientation-contract.md`. (Defiere a `suggest` el "¿instalo la skill que falta?".)
|