From 56cc2bd8147a4ef9e1ede1a56e38ea9371eebf29 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:11:26 -0700 Subject: [PATCH] =?UTF-8?q?feat(skills):=20scrollcraft=20=E2=80=94=20premi?= =?UTF-8?q?um=20scroll-driven=20landing=20pages=20(port=20of=20nateherkai/?= =?UTF-8?q?scroll-craft,=201.2k=E2=98=85=20MIT)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Optional skill: scroll-as-timeline landing pages on a deterministic CSS/JS engine, with interview → page grammar → signature move workflow and screenshot-based scroll verification. Engine and scripts vendored verbatim; asset generation re-anchored on image_generate with the upstream kie.ai flow kept as an optional path. --- .../web-development/scrollcraft/LICENSE.txt | 21 + .../web-development/scrollcraft/SKILL.md | 242 ++++ .../scrollcraft/engine/scrollcraft.css | 432 ++++++ .../scrollcraft/engine/scrollcraft.js | 1167 +++++++++++++++++ .../scrollcraft/references/assets.md | 286 ++++ .../scrollcraft/references/device-diag.html | 214 +++ .../scrollcraft/references/devices.md | 466 +++++++ .../scrollcraft/references/feel.md | 277 ++++ .../scrollcraft/references/taste.md | 304 +++++ .../scrollcraft/references/template.html | 138 ++ .../scrollcraft/references/uniqueness.md | 479 +++++++ .../scrollcraft/references/verify.md | 381 ++++++ .../scrollcraft/references/worldflight.md | 349 +++++ .../scrollcraft/references/worlds.md | 178 +++ .../scrollcraft/scripts/doctor.mjs | 177 +++ .../scrollcraft/scripts/encode.sh | 80 ++ .../scrollcraft/scripts/kie.mjs | 202 +++ .../scrollcraft/scripts/serve.mjs | 52 + .../scrollcraft/scripts/shoot.mjs | 644 +++++++++ .../scrollcraft/scripts/workspace.mjs | 106 ++ .../scripts/worldflight-assert.mjs | 273 ++++ .../scrollcraft/templates/FINGERPRINTS.md | 65 + tests/skills/test_scrollcraft_skill.py | 92 ++ .../docs/reference/optional-skills-catalog.md | 1 + .../web-development-scrollcraft.md | 257 ++++ website/sidebars.ts | 1 + 26 files changed, 6884 insertions(+) create mode 100644 optional-skills/web-development/scrollcraft/LICENSE.txt create mode 100644 optional-skills/web-development/scrollcraft/SKILL.md create mode 100644 optional-skills/web-development/scrollcraft/engine/scrollcraft.css create mode 100644 optional-skills/web-development/scrollcraft/engine/scrollcraft.js create mode 100644 optional-skills/web-development/scrollcraft/references/assets.md create mode 100644 optional-skills/web-development/scrollcraft/references/device-diag.html create mode 100644 optional-skills/web-development/scrollcraft/references/devices.md create mode 100644 optional-skills/web-development/scrollcraft/references/feel.md create mode 100644 optional-skills/web-development/scrollcraft/references/taste.md create mode 100644 optional-skills/web-development/scrollcraft/references/template.html create mode 100644 optional-skills/web-development/scrollcraft/references/uniqueness.md create mode 100644 optional-skills/web-development/scrollcraft/references/verify.md create mode 100644 optional-skills/web-development/scrollcraft/references/worldflight.md create mode 100644 optional-skills/web-development/scrollcraft/references/worlds.md create mode 100644 optional-skills/web-development/scrollcraft/scripts/doctor.mjs create mode 100644 optional-skills/web-development/scrollcraft/scripts/encode.sh create mode 100644 optional-skills/web-development/scrollcraft/scripts/kie.mjs create mode 100644 optional-skills/web-development/scrollcraft/scripts/serve.mjs create mode 100644 optional-skills/web-development/scrollcraft/scripts/shoot.mjs create mode 100644 optional-skills/web-development/scrollcraft/scripts/workspace.mjs create mode 100644 optional-skills/web-development/scrollcraft/scripts/worldflight-assert.mjs create mode 100644 optional-skills/web-development/scrollcraft/templates/FINGERPRINTS.md create mode 100644 tests/skills/test_scrollcraft_skill.py create mode 100644 website/docs/user-guide/skills/optional/web-development/web-development-scrollcraft.md diff --git a/optional-skills/web-development/scrollcraft/LICENSE.txt b/optional-skills/web-development/scrollcraft/LICENSE.txt new file mode 100644 index 0000000000..d24ab43509 --- /dev/null +++ b/optional-skills/web-development/scrollcraft/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Nate Herk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/optional-skills/web-development/scrollcraft/SKILL.md b/optional-skills/web-development/scrollcraft/SKILL.md new file mode 100644 index 0000000000..b7faac789f --- /dev/null +++ b/optional-skills/web-development/scrollcraft/SKILL.md @@ -0,0 +1,242 @@ +--- +name: scrollcraft +description: "Premium scroll-driven landing pages; scroll = timeline." +version: 1.0.0 +author: 'nateherkai (upstream scroll-craft), ported by Hermes Agent' +license: MIT +platforms: [linux, macos, windows] +metadata: + hermes: + tags: [web-development, landing-page, scrollytelling, animation, design, frontend] + category: web-development + homepage: https://github.com/nateherkai/scroll-craft + related_skills: [] +--- + +# scrollcraft + +Scroll is the only input every visitor already knows. This skill treats it as a +timeline: the wheel is a scrubber, the page is a film with real text on top, +and each section behaves differently enough that the visitor keeps going. + +**What you produce:** an interview brief, a page grammar, a customer-journey +map, a feeling curve with one engineered peak, a scroll score, one signature +move, assets, one real HTML page on a token-driven design floor, and a strip of +screenshots proving it holds up at every scroll position. + +Use for: "scrollytelling", "scroll animation site", "a site where scrolling +plays a video", "Apple-style landing page", "3D scroll world", "make my brand a +scroll experience", "this looks like a template", or any request for a site +that should feel like an experience rather than a document. + +## What this is not + +It is not "generate a flythrough and drop text on it." That produces one device +applied to a whole page, recognisable at a glance. Four spine rules: + +1. **Variety is the product.** At least four device families, never the same + device twice in a row. Read [references/devices.md](references/devices.md). +2. **The world is photographic** unless the brand is genuinely illustrated. + Clay/low-poly diorama is banned as a default. Read [references/worlds.md](references/worlds.md). +3. **No continuous chain** unless the brief is literally "one continuous + journey" (then see [references/worldflight.md](references/worldflight.md)). +4. **A different world is not a different page.** Structure is a separate axis; + decide it deliberately. Read [references/uniqueness.md](references/uniqueness.md). + +## Step 0: The interview + +**Always ask the user in chat before building anything.** Real questions, asked +and answered in the conversation, written down — not a brief inferred from the +brand name. Eight questions in one pass: + +1. **Vibe in three to five words**, plus up to three references from any medium + (film, album cover, shop, magazine, game — not "sites you like"). +2. **The scroll journey, section by section, in their words.** +3. **The energy curve** — where calm, where intense. +4. **How should someone feel while scrolling, stage by stage, and what is the + ONE moment they should remember?** Becomes the feeling curve and the peak. + See [references/feel.md](references/feel.md). +5. **One thing this site should do that no site they have seen does** — the + seed of the signature move. +6. **How far from premium-minimal?** Offer the range in + [references/uniqueness.md](references/uniqueness.md) §5: brutalist, + maximalist, playful, retro, dense, editorial, premium-minimal. +7. **One unbroken world, or distinct scenes?** The biggest structural fork, and + it is their call. +8. **What assets do they already have?** Footage, photos, product shots, brand + kit. "Nothing" is fine and means a fully generated world. + +Write the answers verbatim into `/builds//BRIEF.md` (use +write_file) before any act planning. BRIEF.md must contain the eight answers, +the feeling curve (one line per act: emotion, then cause), the peak (as the +sentence a visitor would say to a friend), the completed "It's the site where +___" sentence, and any authored silence. If the user is genuinely unreachable +in a fully autonomous run, self-author BRIEF.md, mark it +`Self-authored, not interviewed`, and say so in the report. + +## Bootstrap + +Run the preflight rather than checking by hand (it catches a stripped ffmpeg +that reports missing filters as syntax errors): + +```bash +node /scripts/doctor.mjs +node /scripts/workspace.mjs --ensure # prints workspace, seeds registry +``` + +Workspace resolution order: `SCROLLCRAFT_HOME` env var; nearest +`.scrollcraft.json` (`{ "workspace": "..." }`) walking up from cwd; +`/scrollcraft`. Builds live at `/builds//`, the +fingerprint registry at `/FINGERPRINTS.md` (seeded from +[templates/FINGERPRINTS.md](templates/FINGERPRINTS.md), starts empty — the gate +stops you repeating *yourself*). + +Copy `engine/scrollcraft.js` and `engine/scrollcraft.css` into the build +folder. **Never edit the engine per-project.** Theme with tokens; write your +own markup. Bespoke behaviour is bespoke JS in the page, driven off `--sc-p` +and your own `data-sc-*` attributes. + +## Step 1: The brief, journey first + +Ask the subject open, in plain prose. Then ask only what Step 0 did not cover: +what is this and who is it for; the one sentence the page installs; the one +next action (one label, used everywhere); what they already have; art +direction from [references/worlds.md](references/worlds.md). Then write the +**journey**: four to seven beats, each a shift in what the visitor knows or +feels. Beats are the spine; a section serving no beat is cut. Confirm the +journey with the user before generating assets — assets are the expensive part. + +## Step 2: Grammar, gate, then score + +Full detail in [references/uniqueness.md](references/uniqueness.md). + +- **Pick a grammar.** Eight, mutually exclusive. Choosing filmic one-shot means + saying in the report why the other seven lost. Nav, hero and close follow + from the grammar. +- **Invent the signature move.** One bespoke interaction coded in the page, not + a parameter change to a kit device. Interview question 5 is the seed. +- **Run the fingerprint gate.** The planned build must differ from every row in + `/FINGERPRINTS.md` on at least 4 of 6 dimensions: grammar, nav + treatment, hero device, act-sequence shape, close pattern, signature move. + If it fails, change the plan, not the log. +- **Write the feeling curve before the score table** (method: + [references/feel.md](references/feel.md)). Then assign each beat a device in + a written table (beat / device / why). + +Checks before building: grammar bans hold; 4+ device families; no device +twice in a row; at most two `scrub` acts; no two adjacent acts with the same +feeling; one peak with the largest span; total page length 8–14 +viewport-heights. + +## Step 3: Assets + +Full pipeline, prompt scaffolds and model notes: [references/assets.md](references/assets.md). + +**Hermes-native paths first:** + +- **User-supplied footage and photos** — no key, no spend, a first-class route. + Grade and encode them. +- **The `image_generate` tool** for stills: one style preamble reused verbatim + in every prompt is what makes six images look like one shoot. Inspect every + asset (vision_analyze) before use; rerolling beats shipping a bad frame. + +**Optional upstream path — kie.ai** (vendored verbatim as +[scripts/kie.mjs](scripts/kie.mjs)): photoreal stills and camera-move clips. +Requires the `KIE_AI_API_KEY` environment variable (export it in your shell; +there is no bundled env file in this port). Check balance with +`node /scripts/kie.mjs probe`; a still costs cents, a 5s clip more. + +```bash +node /scripts/kie.mjs still " + + +
+

scrub diagnostic — SCROLL UP AND DOWN A FEW TIMES, then read the verdicts

+
A: suspect clip, blob URL (how the engine loads it)   B: suspect clip, direct file   C: a clip that works on this device, blob URL
+
+
+
+
+ + + diff --git a/optional-skills/web-development/scrollcraft/references/devices.md b/optional-skills/web-development/scrollcraft/references/devices.md new file mode 100644 index 0000000000..14fe442a5d --- /dev/null +++ b/optional-skills/web-development/scrollcraft/references/devices.md @@ -0,0 +1,466 @@ +# The device kit + +Nine ways for scroll to change the page. Each one is a different answer to "what +does the visitor's hand actually do here." + +Pick per beat, never per page. The variety law from SKILL.md Step 2 applies: +four or more families, never the same one twice in a row. + +Every act publishes `--sc-p` (0 to 1) on its own element, so anything you want +to drive that the kit does not cover, you can drive from CSS with `calc()` +against that variable. Reach for that before asking for a new device. + +--- + +## 1. `scrub`: the wheel is a scrubber + +The anchor device. A pre-rendered camera move plays under the reader's hand, +one frame per notch. This is the thing people screenshot and send to each other, +so spend it on the open. + +```html +
+
+ + +
+ +
+

+ Your morning shouldn't need two drinks. +

+
+
+
+``` + +- `data-sc-span` is the act's scroll length in viewport-heights. 2.2 to 3.0 for + a hero. Below 1.8 the clip flies past; above 3.5 the reader starts wondering + whether the page is broken. +- `data-sc-dwell` (0 to 0.6) remaps time so the camera settles mid-act, exactly + where the copy peaks, and moves quicker at the edges. It is the difference + between a clip that plays and a shot that lands. Keep it at or below 0.6. +- `data-sc-src` (not `src`) is deliberate: the engine fetches the clip as a Blob + so it seeks without needing HTTP range support, and skips the fetch entirely + under reduced motion. +- The poster is a live frame-holder. It stays up until a real video frame has + painted, because iOS keeps a seeked-but-never-played muted video blank and + hiding the poster on metadata alone flashes an empty stage. + +**At most two scrub acts per page.** The third one is no longer a surprise, and +it is the heaviest thing on the page. + +### Clip time is not cue time + +The single most damaging bug this device has, and it is invisible in every +screenshot taken one at a time. + +A pinned stage is on screen for **one viewport before** its pinned travel begins, +sliding up into view, and **one viewport after** it ends, sliding off the top. +The act's progress `p` is 0 through the whole entry and 1 through the whole exit. +So a clip driven by `p` sits frozen on its first frame while it slides in, and +frozen on its last frame while it slides out. The reader has been scrubbing a +film with their hand, the film stops, and then the whole page slides a still +photograph past them. It reads as the site breaking, and it is the fastest way to +make an expensive page feel cheap. + +The engine therefore maps the clip across the stage's **entire visible life**, not +across its pinned travel, and this is the **default**. Both ends are clamped to +scroll that actually exists, so a hero at the top of the document still starts on +frame one and an act near the bottom still reaches its last frame. Cues keep +using `p`, because cues belong to the pin. + +**Pair it with `data-sc-dwell`.** Dwell moves quickly at the edges and settles in +the middle, which is exactly the shape this mapping wants: the fast motion lands +on the two slides, and the settle lands inside the pin where the copy is. The two +were built for each other. + +`data-sc-clip-map="travel"` restores the old pinned-travel mapping. There is +almost no reason to reach for it, and reaching for it reintroduces the freeze. + +The harness checks this now (see [verify.md](verify.md)), so a frozen clip fails +verification instead of shipping. Do not rely on noticing it by eye: every +individual frame of a frozen clip looks completely correct. + +### The playhead is lerped + +Scroll never writes `currentTime`. It writes a target, and a standalone rAF loop +walks the clip toward that target at a fixed fraction per frame. Wheel events do +not arrive at a constant rate, so a 1:1 write reproduces every gap in them and +the clip reads as a stutter rather than a glide. Three mechanisms, all on by +default: + +- **Lerp 0.18 per frame.** `data-sc-lerp` overrides it, on the mount root for the + whole page or on one `