@struq/skill 0.3.0 → 0.4.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 (129) hide show
  1. package/README.md +22 -36
  2. package/package.json +3 -2
  3. package/skills/struq/SKILL.md +139 -307
  4. package/skills/struq/blocks/CATALOG.md +426 -0
  5. package/skills/struq/blocks/backgrounds/background-aurora.tsx +818 -0
  6. package/skills/struq/blocks/backgrounds/background-facets.tsx +1283 -0
  7. package/skills/struq/blocks/backgrounds/background-silk.tsx +906 -0
  8. package/skills/struq/blocks/catalog.json +1302 -0
  9. package/skills/struq/blocks/footers/footer-contact.tsx +789 -0
  10. package/skills/struq/blocks/footers/footer-reveal.tsx +313 -0
  11. package/skills/struq/blocks/headers/header-bar.tsx +652 -0
  12. package/skills/struq/blocks/headers/menu-curtain.tsx +790 -0
  13. package/skills/struq/blocks/heroes/hero-framed.tsx +627 -0
  14. package/skills/struq/blocks/heroes/hero-index.tsx +855 -0
  15. package/skills/struq/blocks/heroes/hero-poster.tsx +1086 -0
  16. package/skills/struq/blocks/heroes/hero-split.tsx +685 -0
  17. package/skills/struq/blocks/heroes/hero-still.tsx +862 -0
  18. package/skills/struq/{recipes/components/struq → blocks/kit}/Cursor.tsx +1 -1
  19. package/skills/struq/{recipes/components/struq → blocks/kit}/HeroCanvas.tsx +2 -2
  20. package/skills/struq/{recipes/components/struq → blocks/kit}/HorizontalGallery.tsx +2 -2
  21. package/skills/struq/{recipes/components/struq → blocks/kit}/MagneticButton.tsx +1 -1
  22. package/skills/struq/{recipes/components/struq → blocks/kit}/MediaFrame.tsx +2 -2
  23. package/skills/struq/{recipes/components/struq → blocks/kit}/PinnedZoom.tsx +14 -4
  24. package/skills/struq/{recipes/components/struq → blocks/kit}/Reveal.tsx +1 -1
  25. package/skills/struq/{recipes/components/struq → blocks/kit}/SmoothScroll.tsx +1 -1
  26. package/skills/struq/{recipes/components/struq → blocks/kit}/SplitHeadline.tsx +1 -1
  27. package/skills/struq/blocks/kit/blocks.css +2102 -0
  28. package/skills/struq/blocks/kit/images.ts +17 -0
  29. package/skills/struq/blocks/kit/photo-read.ts +102 -0
  30. package/skills/struq/blocks/page-transitions/page-transition-gate.tsx +706 -0
  31. package/skills/struq/blocks/page-transitions/page-transition-slices.tsx +469 -0
  32. package/skills/struq/blocks/page-transitions/page-transition-stack.tsx +583 -0
  33. package/skills/struq/blocks/preloaders/preloader-gate.tsx +358 -0
  34. package/skills/struq/blocks/preloaders/preloader-slices-uncovered.ts +50 -0
  35. package/skills/struq/blocks/preloaders/preloader-slices.tsx +289 -0
  36. package/skills/struq/blocks/preloaders/preloader-stack.tsx +367 -0
  37. package/skills/struq/blocks/preloaders/preloader-wordmark.tsx +568 -0
  38. package/skills/struq/blocks/sections/about-studio.tsx +436 -0
  39. package/skills/struq/blocks/sections/approach.tsx +661 -0
  40. package/skills/struq/blocks/sections/contact-form.tsx +664 -0
  41. package/skills/struq/blocks/sections/cta-closing.tsx +763 -0
  42. package/skills/struq/blocks/sections/fanned-cards.tsx +547 -0
  43. package/skills/struq/blocks/sections/film-strip.tsx +385 -0
  44. package/skills/struq/blocks/sections/grid-assembly.tsx +616 -0
  45. package/skills/struq/blocks/sections/hover-list.tsx +467 -0
  46. package/skills/struq/blocks/sections/image-trail.tsx +432 -0
  47. package/skills/struq/blocks/sections/manifesto.tsx +691 -0
  48. package/skills/struq/blocks/sections/panel-accordion.tsx +380 -0
  49. package/skills/struq/blocks/sections/reveals.tsx +416 -0
  50. package/skills/struq/blocks/sections/split-pair.tsx +670 -0
  51. package/skills/struq/blocks/sections/sticky-chapters.tsx +256 -0
  52. package/skills/struq/blocks/sections/velocity-type.tsx +269 -0
  53. package/skills/struq/{cinematic/showpieces/toonstuk-fill-rows.tsx → blocks/showpieces/fill-rows.tsx} +1 -1
  54. package/skills/struq/{cinematic/showpieces/toonstuk-hero-opener.tsx → blocks/showpieces/hero-opener.tsx} +5 -5
  55. package/skills/struq/{cinematic/showpieces/toonstuk-image-to-fullbleed.tsx → blocks/showpieces/image-to-fullbleed.tsx} +16 -5
  56. package/skills/struq/blocks/showpieces/manifesto-scrub.tsx +48 -0
  57. package/skills/struq/{cinematic/showpieces/toonstuk-panel-wipe.tsx → blocks/showpieces/panel-wipe.tsx} +2 -2
  58. package/skills/struq/{cinematic/showpieces/toonstuk-pixel-resolve.tsx → blocks/showpieces/pixel-resolve.tsx} +1 -1
  59. package/skills/struq/{cinematic/showpieces/toonstuk-project-stack.tsx → blocks/showpieces/project-stack.tsx} +2 -2
  60. package/skills/struq/blocks/showpieces/scrub-connector.tsx +307 -0
  61. package/skills/struq/{cinematic/showpieces/toonstuk-wordmark-finale.tsx → blocks/showpieces/wordmark-finale.tsx} +1 -1
  62. package/skills/struq/blocks/transitions/zoom-through.tsx +613 -0
  63. package/skills/struq/canon/canon.json +627 -0
  64. package/skills/struq/cinematic/README.md +7 -18
  65. package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +1 -1
  66. package/skills/struq/cinematic/examples/code/shared/styles.css +1 -1
  67. package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +1 -1
  68. package/skills/struq/cinematic/references/01-core-mental-model.md +1 -1
  69. package/skills/struq/cinematic/references/02-vocabulary.md +1 -1
  70. package/skills/struq/cinematic/references/07-stack-integration.md +2 -2
  71. package/skills/struq/recipes/app/fonts.ts +12 -22
  72. package/skills/struq/recipes/app/globals.css +34 -177
  73. package/skills/struq/recipes/app/layout.tsx +13 -11
  74. package/skills/struq/references/craft.md +77 -0
  75. package/skills/struq/references/iconography.md +28 -230
  76. package/skills/struq/references/stack-targets.md +23 -74
  77. package/skills/struq/references/tells.md +52 -0
  78. package/skills/struq/references/triage.md +6 -7
  79. package/skills/struq/scripts/blocks.mjs +156 -0
  80. package/skills/struq/scripts/canon.mjs +106 -281
  81. package/skills/struq/scripts/check.mjs +92 -126
  82. package/skills/struq/scripts/images.mjs +2 -2
  83. package/skills/struq/scripts/log.mjs +6 -23
  84. package/skills/struq/scripts/tokens.mjs +222 -0
  85. package/skills/struq/cinematic/showpieces/README.md +0 -18
  86. package/skills/struq/cinematic/showpieces/toonstuk-manifesto-scrub.tsx +0 -81
  87. package/skills/struq/cinematic/showpieces/toonstuk-scrub-connector.tsx +0 -78
  88. package/skills/struq/references/anti-patterns.md +0 -330
  89. package/skills/struq/references/baseline-prompt.md +0 -122
  90. package/skills/struq/references/brand.md +0 -143
  91. package/skills/struq/references/brief-router.md +0 -218
  92. package/skills/struq/references/composition.md +0 -190
  93. package/skills/struq/references/depth-parallax.md +0 -235
  94. package/skills/struq/references/direction.md +0 -188
  95. package/skills/struq/references/feel-layer.md +0 -107
  96. package/skills/struq/references/gsap-api.md +0 -293
  97. package/skills/struq/references/hero-scenes.md +0 -107
  98. package/skills/struq/references/imagery.md +0 -125
  99. package/skills/struq/references/loaders.md +0 -287
  100. package/skills/struq/references/micro-interactions.md +0 -270
  101. package/skills/struq/references/mobile-composition.md +0 -70
  102. package/skills/struq/references/nav-header.md +0 -296
  103. package/skills/struq/references/niche-packets.md +0 -338
  104. package/skills/struq/references/page-architecture.md +0 -134
  105. package/skills/struq/references/page-entrances.md +0 -198
  106. package/skills/struq/references/performance.md +0 -235
  107. package/skills/struq/references/pinning-scrub.md +0 -340
  108. package/skills/struq/references/pitfalls.md +0 -290
  109. package/skills/struq/references/route-transitions.md +0 -281
  110. package/skills/struq/references/scaling.md +0 -151
  111. package/skills/struq/references/scroll-reveals.md +0 -279
  112. package/skills/struq/references/sections.md +0 -141
  113. package/skills/struq/references/seo.md +0 -219
  114. package/skills/struq/references/setup.md +0 -397
  115. package/skills/struq/references/showcase.md +0 -87
  116. package/skills/struq/references/struq-imagery.md +0 -107
  117. package/skills/struq/references/svg-motion.md +0 -390
  118. package/skills/struq/references/transitions.md +0 -160
  119. package/skills/struq/references/typography.md +0 -720
  120. package/skills/struq/references/vibe-presets.md +0 -287
  121. package/skills/struq/references/webgl-r3f.md +0 -274
  122. package/skills/struq/references/webgl-scenes.md +0 -593
  123. package/skills/struq/scripts/font-names.mjs +0 -40
  124. package/skills/struq/scripts/recipes.mjs +0 -135
  125. /package/skills/struq/{recipes/lib → blocks/kit}/gsap.ts +0 -0
  126. /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/contours.ts +0 -0
  127. /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/dots.ts +0 -0
  128. /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/ripple.ts +0 -0
  129. /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/types.ts +0 -0
@@ -1,341 +1,173 @@
1
1
  ---
2
2
  name: struq
3
- description: "Award-tier web design for coding agents. Builds cinematic, agency-grade marketing sites, landing pages and portfolios: two-face typography with light display weights, curated palettes, art-directed CC0 photography from the Struq beeldbank, Lenis smooth scroll on one GSAP clock, SplitText headlines, masked reveals, a pinned or horizontal showpiece, a WebGL hero layer with a poster fallback, and a magnetic CTA. Ships tested copy-in components and a self-check script. Use when designing, building, redesigning, animating or auditing a marketing or narrative site, or when a page must feel premium, custom, cinematic or award-winning instead of templated. Stack: Next.js App Router + Tailwind v4 (Vite supported)."
3
+ description: "Award-tier studio websites for coding agents, built from Struq's catalogue of admitted blocks: preloaders, page transitions, headers, heroes, WebGL backgrounds, showpieces, sections and footers, each with three real variants, judged at FWA level and shipped as files. Struq's palettes and type pairings as one token block, art-directed CC0 photos from the Struq beeldbank, Lenis on one GSAP clock, and a self-check against the AI tells. Works offline from its own copy; uses the Struq MCP (struq_richting, struq_blokken, struq_blok, struq_controle) when connected. Use when designing, building, redesigning, animating or auditing a marketing, studio, agency or portfolio site that must feel cinematic and custom instead of templated. Stack: Next.js App Router + Tailwind v4."
4
4
  argument-hint: "[brief] | redesign <path> | audit <path-or-url> | direct <brief>"
5
5
  ---
6
6
 
7
7
  # Struq
8
8
 
9
- A site looks like a template because every choice in it is the likeliest one. Struq replaces those choices with the ones an award-winning studio makes, and ships them as **tested components and two scripts**, so the build spends its effort on the page, not on plumbing. Follow the Build checklist below in order. The references are depth for when a step needs it; you do not need to read them to finish a good build.
9
+ A site looks like a template because every choice in it is the likeliest one. Struq replaces those choices with
10
+ blocks a studio would ship: each one built, reviewed against a named award-tier reference and admitted by hand. Your
11
+ job is the direction, the order, the copy and the seams between blocks. Do not rebuild what a block already does.
10
12
 
11
- `<skill-dir>` below is the folder this SKILL.md is in (Claude Code prints it as "Base directory for this skill"). Typical paths: `.claude/skills/struq`, `~/.claude/skills/struq`, `.agents/skills/struq`.
12
-
13
- ## Modes
13
+ `<skill-dir>` is the folder this file is in. The catalogue is [blocks/CATALOG.md](blocks/CATALOG.md); the craft that
14
+ makes the blocks feel high end is [references/craft.md](references/craft.md); the tell list is
15
+ [references/tells.md](references/tells.md).
14
16
 
15
17
  | Invocation | Mode |
16
18
  |---|---|
17
- | `/struq <brief>` | **Build**: the Build checklist below, start to finish. |
18
- | `/struq redesign <path>` | **Redesign**: keep the brand, copy and components; read [references/triage.md](references/triage.md), then run the checklist from step 3 on the existing project. The log starts with what you found. |
19
- | `/struq audit <path-or-url>` | **Audit**: change nothing. Run `node <skill-dir>/scripts/check.mjs` on the project and report its FAIL and WARN lines, a score (100, minus 5 per FAIL, minus 2 per WARN) and the three fixes with the most visual payoff. The check appends its result to the project's STRUQ-LOG.md; log the audit itself there too. |
20
- | `/struq direct <brief>` | **Directed**: one continuous WebGL experience across chapters. Run the checklist through step 5, then [cinematic/README.md](cinematic/README.md), logging every chapter to the same STRUQ-LOG.md. |
19
+ | `/struq <brief>` | **Build**: the checklist below, start to finish. |
20
+ | `/struq redesign <path>` | Keep the brand, copy and components ([references/triage.md](references/triage.md)); swap in blocks where the page is weakest, then run the checklist from step 3. |
21
+ | `/struq audit <path-or-url>` | Change nothing. Run `check.mjs` (and `struq_controle` when connected), report every FAIL and WARN and the three fixes with the most visual payoff. Never a numeric score. |
22
+ | `/struq direct <brief>` | One continuous WebGL experience across chapters: steps 1 to 4, then [cinematic/README.md](cinematic/README.md). |
21
23
 
22
- Register: this is for marketing, landing, portfolio and narrative sites. For dashboards and product UI keep the type, palette and surface rules and drop Lenis, showpieces and the hero canvas.
24
+ For a dashboard or product UI keep the tokens, faces and tells; drop the preloader, Lenis and the pinned pieces.
23
25
 
24
26
  ## Three rules that never bend
25
27
 
26
- 1. **Never an empty frame.** Every image slot holds a real image. Without photos from the user, the Struq beeldbank pack is used automatically: it is free, CC0, made for this, and needs no permission. If struq.nl is unreachable, the script writes graded fallback plates in the site palette. A grey box, a "photo to come" caption or a placeholder component never ships.
27
- 2. **Never invent facts.** No testimonials, client logos, stats, awards, prices, addresses, opening hours, bios or quotes the brief did not give. Write real, specific running copy from what the brief says; anything the layout needs that is not known (an address, hours) is left out or written as a plain invitation ("Bel of mail voor een tafel"), and listed under Assumptions in STRUQ.md. A beeldbank photo shows the world around the client, never their own room, team or product: never caption it as theirs.
28
- 3. **Never the AI tells.** The one combined list (Struq's tells, the slop audit and Roy's rules), each with its fix in [references/anti-patterns.md](references/anti-patterns.md) and each detected by `check.mjs`:
29
- - **Type and labels:** eyebrows and any uppercase label or marquee; monospace as a design face; Inter, Roboto, Arial, Helvetica, Open Sans, Poppins, Montserrat, Lato, Geist, Space Grotesk; heavy display; 01 / 02 / 03 numbering; raw text sizes and breakpoint size ladders.
30
- - **Colour and surface:** gradients of any kind; gradient text; tinted translucent fills and glass; pastel status badges; pills, above all a pill with a dot; pulsing live dots; blue or purple unless the brand is that colour; drop-shadow elevation; side-stripe borders.
31
- - **Layout and motion:** three identical cards; uniform section padding; opacity-only entrances; the same hover on everything; single-layer parallax; `anticipatePin` with smooth scroll; empty or placeholder frames.
32
- - **Copy and icons:** emoji and the cliche icons (sparkles, rocket, lightning, bulb, wand, flame); lorem, Acme and leftover template text; em and en dashes in any form; generic CTAs ("Learn more", "Lees meer"); generic headings ("Our Services", "Over ons"); hype words ("seamless", "naadloos"). Judged by eye, not by the script: the hero-metric template, category-reflex colours, a heading the paragraph restates.
28
+ 1. **Never an empty frame.** Every image slot holds a real photo: the client's, else the beeldbank pack (free, CC0,
29
+ no permission needed). A photo shows the world around the client, never their own room, team or product unless it
30
+ is theirs; captions are written from the alt.
31
+ 2. **Never invent facts.** No testimonials, logos, stats, awards, prices, addresses, hours, bios or quotes the brief
32
+ did not give. Leave them out and list them under Assumptions in STRUQ.md.
33
+ 3. **Never the tells** ([references/tells.md](references/tells.md)): no eyebrows, pills, gradients or gradient text,
34
+ glow, mono on marketing, Inter or the other generator faces, default purple or blue, numbered steps, icon grids,
35
+ stat banners, generic CTAs, em or en dashes.
33
36
 
34
- ## The Build checklist
37
+ ## The MCP, or the skill's own copy
35
38
 
36
- **Struq MCP connected (`struq_*` tools)?** Then the MCP build line (see "With the Struq MCP" below) replaces steps 2, 4 and 7: bouwplan, the one stop, projectschil, motion, achtergrond, and one sectiespec per arc row. Copy its block into STRUQ.md too.
39
+ The Struq MCP and this skill serve the same blocks and the same derivation. Use the tool when it is connected; the
40
+ offline column does the same from the skill's copy.
37
41
 
38
- Copy this block into `STRUQ.md` in the project root (under the short plan, step 2) and tick each line (`- [x]`) as you finish it. `check.mjs` fails while any line is open.
42
+ | Need | MCP connected | Offline |
43
+ |---|---|---|
44
+ | A direction: palette, pairing, photo pack as tokens and fonts | `struq_richting(brief)` (or with `palette`, `type` ids) | `canon.mjs list`, then `canon.mjs palette <id>` and `canon.mjs type <id>`; `images.mjs --brief` |
45
+ | What blocks exist | `struq_blokken(kind?, query?)` | `blocks.mjs list [kind]`, [blocks/CATALOG.md](blocks/CATALOG.md) |
46
+ | A block as files | `struq_blok(slug, variant?)` | `blocks.mjs add <slug>` |
47
+ | The tell check | `struq_controle()`: run its detectors on your own files | `check.mjs` |
39
48
 
40
- **The site log.** Every site also keeps `STRUQ-LOG.md` in the project root, from the first step on: append only, one entry per step, a heading with the step name and the local time, then two to five short lines of what and why. Log the brief received, the canon picks and why, the approval asked and the answer, each section built (files, recipe), every check run, every fix after a failure, every deviation and assumption, and the final state. `canon.mjs`, `images.mjs`, `recipes.mjs` and `check.mjs` append their own entries; you add the rest with `log.mjs`. `check.mjs` fails when the log is missing or a ticked step has no entry. STRUQ.md stays the plan; the log is what happened. Do not report the build as done until every line is ticked and `check.mjs` prints `RESULT: PASS`.
49
+ Put `struq_richting`'s token block in `app/globals.css` in place of the STRUQ TOKENS block and its font URLs in
50
+ `app/fonts.ts`; drop `struq_blok`'s files into `components/struq/` at the paths it gives. Log each tool call:
51
+ `node <skill-dir>/scripts/log.mjs mcp <tool> --note "<what you took>"`. Run `check.mjs` either way.
41
52
 
42
- ```
43
53
  ## Build checklist
44
- Rule: keep STRUQ-LOG.md at every step: node <skill-dir>/scripts/log.mjs "Step N Name" "what" "why" (canon, images, recipes and check log themselves)
45
- - [ ] 1 Project: Next.js App Router + Tailwind v4 in place (scaffolded if the folder was empty)
46
- - [ ] 2 Plan and approval: palette, pairing, design system and pack picked from the canon (canon.mjs list); STRUQ.md written; the one stop for approval sent and answered
47
- - [ ] 3 Recipes: node <skill-dir>/scripts/recipes.mjs printed "OK recipes ready"
48
- - [ ] 4 Canon: canon.mjs palette and canon.mjs type wrote the tokens and the fonts of the approved picks
49
- - [ ] 5 Images: images.mjs --brief printed "OK"; only photos whose alt fits the client are used, captions written from the alts; title and lang set in app/layout.tsx
50
- - [ ] 6 Hero: the full-bleed HeroOpener (photo edge to edge, line at 14 to 20vw, lead, large CTA, header with the data-wordmark name), or a hand-built hero under the same rules
51
- - [ ] 7 Sections: 5 to 7 sections on the arc, every image in a MediaFrame, text groups in Reveal, two grid breaks; one contrast scene (scene-inverse or scene-field), at most two
52
- - [ ] 8 Showpieces: the lead toonstuk from the picker (or the one the user named) and at most one supporting move
53
- - [ ] 9 Footer: toonstuk-wordmark-finale, with the image credit
54
- - [ ] 10 Build: npm run build passes
55
- - [ ] 11 Look: screenshots at 390 and 1440 and one full scroll, fixes made (or "not seen: no browser" written under Verify)
56
- - [ ] 12 Check: node <skill-dir>/scripts/check.mjs prints RESULT: PASS
57
- ```
58
-
59
- ### Step 1: Project
60
-
61
- Empty folder: `npx create-next-app@latest . --ts --tailwind --app --eslint --yes`. Existing project: read `package.json`; when the installed version's types disagree with a recipe, the installed version wins (adapt the recipe, keep its intent). Vite: translate each step through [references/stack-targets.md](references/stack-targets.md).
62
-
63
- ### Step 2: Plan, then the one stop for approval
64
-
65
- Run `node <skill-dir>/scripts/canon.mjs list` (it reads the canon and changes nothing) and pick the palette, the pairing, the design system and the beeldbank pack as step 4 and step 5 describe. Then write this at the top of `STRUQ.md`, with the checklist under it:
66
-
67
- ```
68
- # STRUQ.md: <name>
69
- Brief: <one line: what it is, for whom, where>
70
- Idea: <the one idea the site is about, and the one visual motif that carries it>
71
- Palette: <canon palette slug> (<its mood>)
72
- Type: <canon pairing slug>: <display face> + <text face>
73
- System: <canon design system slug> (<its section order>)
74
- Images: pack <slug> (or: the client's photos, then pack <slug>)
75
- Hero: <contours | ripple | dots | the MCP background>
76
- Showpiece: <HorizontalGallery | PinnedZoom>, after section <n>
77
- Arc: 1 hero, 2 <...>, 3 <...>, 4 showpiece, 5 <...>, 6 invite, footer
78
- Assumptions: <every drafted line and every fact left out>
79
- Verify: <filled in at step 11>
80
- ```
81
-
82
- Then **stop, once**: send the plan in five lines (idea, palette, type, pack, arc with the showpiece) and the materials request, in the user's language:
83
-
84
- > Akkoord? Heb je een logo, foto's of echte cijfers, stuur ze nu.
85
-
86
- (English: "Approve? If you have a logo, photos or real numbers, send them now.") Wait for the answer. Any yes, or "bouw maar" with nothing attached, builds with the canon picks as planned; what the user sends replaces the matching pick or pack images; a veto changes only what it names. **This is the only stop: never ask a second time**, not for permission to download the images (they are free and CC0) and not to confirm a build step. Only a brief that does not say what the business is gets one question before the plan, instead of this stop.
87
-
88
- **No photos that pretend.** When the brief's own sector words point at no pack (a product launch: an e-bike, a device, a drink), no beeldbank photo shows the client's product. Then add this line to the stop: "Ik heb geen foto's van je <product>: stuur ze, dan komen ze in de hero." (English: "I have no photos of your <product>: send them and they go in the hero.")
89
-
90
- **The build request is not the approval.** "Build me a site for ..." is the brief; the approval is the user's answer to this stop, and nothing else counts as one. Log it word for word, then go on: `node <skill-dir>/scripts/log.mjs "Step 2 Approval" "Asked: Akkoord? Heb je een logo, foto's of echte cijfers, stuur ze nu." "Reply: \"<the user's own words>\""`. `check.mjs` fails without that entry and its Reply line.
91
-
92
- ### Step 3: Recipes
93
-
94
- ```bash
95
- node <skill-dir>/scripts/recipes.mjs
96
- ```
97
-
98
- Copies the tested components into the project and installs `gsap @gsap/react lenis three @types/three`. On a fresh scaffold it also writes `app/globals.css`, `app/layout.tsx` and `app/fonts.ts`; on your own files it prints what to merge. The components (all in `components/struq/`):
99
-
100
- | Component | What it gives the page |
101
- |---|---|
102
- | `SmoothScroll` | Lenis on `gsap.ticker`, one clock for the whole page. Already in the layout. `getLenis()?.stop()` / `.start()` for menus. |
103
- | `SplitHeadline` | SplitText line-mask rise. `as="h1"` for the hero, `on="scroll"` for a statement further down. Plain text children. |
104
- | `Reveal` | Staggered rise of its direct children on scroll (`on="load"` in the hero). |
105
- | `MediaFrame` | The only way an image goes on the page: curtain reveal, photo settle, scrubbed parallax. Props `image`, `ratio`, `sizes`, `priority` (the hero image only), `reveal`. |
106
- | `HeroCanvas` | The WebGL hero layer: `scene="contours"`, `"ripple"` (needs `image`) or `"dots"`. Lazy three.js chunk, poster first, DPR cap, pixel budget, pause offscreen, still frame under reduced motion, colours read from the tokens. |
107
- | `HorizontalGallery` | Showpiece: pinned section, a track of photos sliding sideways, each photo panning. Native swipe row on phones. |
108
- | `PinnedZoom` | Showpiece: a small framed photo opens to full bleed while the statement lifts away. |
109
- | `MagneticButton` | The primary CTA: leans to the pointer, ink fill rises on hover. `variant="line"` for the secondary. |
110
- | `Cursor` | Dot and trailing ring, inverted against the page. Already in the layout; delete it for a booking tool or a dense shop. |
111
-
112
- ### Step 4: Canon (Struq's own palette, type and system, every build)
113
-
114
- A generic AI falls back on its standard palette and fonts. A Struq build starts from Struq's canon: curated palettes, pairings and design systems, public, no account. **Never invent a palette or a pairing while the canon has a fit.**
115
-
116
- ```bash
117
- node <skill-dir>/scripts/canon.mjs list # every palette (slug, mood, light or dark), pairing, system, pack
118
- node <skill-dir>/scripts/canon.mjs palette <slug> # writes the STRUQ TOKENS block in app/globals.css
119
- node <skill-dir>/scripts/canon.mjs type <slug> # writes app/fonts.ts (Google via next/font, Fontshare as local woff2)
120
- node <skill-dir>/scripts/canon.mjs system <slug> # prints the section order
121
- ```
122
-
123
- Each command prints the line to paste into STRUQ.md (`Palette:`, `Type:`, `System:`). The raw API, if you want to read it yourself:
124
-
125
- | Endpoint | Read |
126
- |---|---|
127
- | `curl -s "https://struq.nl/api/assets?filter=palette"` | `items[].slug`, `items[].data.mood`, `items[].data.colors` with `background`, `surface`, `text`, `accent`, `accentAlt` (OKLCH or hex), `items[].tags` (`sfeer:licht`, `sfeer:donker`, `sfeer:warm`, `sfeer:koel`, `sfeer:redactioneel`, `sfeer:rustig`, `sfeer:gedurfd`, `sfeer:technisch`) |
128
- | `curl -s "https://struq.nl/api/assets?filter=typography"` | `items[].slug`, `items[].data.display.name`, `items[].data.body.name`, their `stack`, `items[].data.intent` |
129
- | `curl -s "https://struq.nl/api/assets?filter=design_system"` | `items[].slug`, `items[].data.silhouette.sectionOrder`, `items[].data.paletteRef`, `items[].data.typographyRef` |
130
- | `curl -s "https://struq.nl/api/media/packs"` | `packs[].slug`, `packs[].description` (step 5) |
131
-
132
- Testing only: `STRUQ_API_BASE=https://staging.struq.nl` points `canon.mjs` and `images.mjs` (API and image downloads) at another Struq environment. Users never set it.
133
-
134
- **How to pick.** Palette by the brief's mood first (`data.mood` and the `sfeer:` tags), then by sector: a restaurant by a wood fire reads `frontier-olea-flour-terracotta` or `frontier-lumen-firelit-flour`; an architect `graphite-ivory` or `midnight-brass`; a mobility launch `frontier-aperture-technical-dark` or `frontier-velar-graphite-scarlet`; care `frontier-thuispost-oat-petrol` or `frontier-stillwater-oat-sage`; fashion `frontier-seve-bone-oxblood`. A palette whose accent is blue or purple (`canon.mjs list` marks it) only when the brand is that colour, declared in STRUQ.md as `Cool hue: declared because <the brand colour>`. The brand's own colours, when the brief gives them, beat the canon: write them into the same token names.
135
-
136
- Pairing by temperament: `serif-editorial` (Fraunces, warm and premium), `newsreader-classic` (Newsreader, literate, long-form), `editorial-sans-precision` (Sora, architectural, calm), `geometric-modern` (Manrope, self-assured), `display-drama` (Syne, expressive campaigns). `canon.mjs type` applies Struq's overrides for you: Inter is replaced (Fraunces gets Urbanist), a commercial face gets its free stand-in (Neue Haas Grotesk Text becomes General Sans from Fontshare), and `grotesk-workhorse` (Space Grotesk + Inter) is refused. When no canon pairing fits the temperament (quiet luxury, a fashion house, a poster voice), use the Signature set in [references/typography.md](references/typography.md) §3.1 through `canon.mjs type --display "Cormorant Garamond" --body "Jost"`.
137
-
138
- Design system by the business: `gallery-first` when the work is visual (restaurant, architecture, shop), `proof-forward` when the brief has real facts to show, `story-led` for founder-led and care, `loud-campaign` for a launch, `editorial-restrained` for professional services. Use its section order for the arc; drop any section that would need invented content (`proof` and `story-proof` only with real facts, `faq` only with answers the brief gives), and add the Struq floor: the hero canvas, one showpiece at the act break, the designed footer.
139
54
 
140
- Every string then takes a role: `font-display text-display` (the h1), `font-display text-h2`, `font-display text-h3`, `text-lead`, `text-body`, `text-small`. Never `text-xl` or `text-5xl`, never `font-bold` on a heading: hierarchy comes from scale. The accent move is one word in the display italic, or the accent colour on one word.
55
+ Copy this into `STRUQ.md` under the plan and tick each line as you finish it. Keep `STRUQ-LOG.md` from the first
56
+ step: append only, one entry per step (`node <skill-dir>/scripts/log.mjs "Step N Name" "what" "why"`; the scripts log
57
+ their own). `check.mjs` fails while a line is open or a ticked step has no entry.
141
58
 
142
- No network: keep the `Paper` tokens the recipe ships, set `Palette: offline default (paper)` in STRUQ.md, and run `canon.mjs` again when struq.nl answers.
143
-
144
- ### Step 5: Images (automatic, every build)
145
-
146
- ```bash
147
- node <skill-dir>/scripts/images.mjs --brief "<the brief, in the user's words>"
148
- node <skill-dir>/scripts/images.mjs <pack-slug> --brief "<the brief>" # when you picked the pack yourself
149
59
  ```
150
-
151
- Always pass `--brief`. When the brief's sector words do not point at the pack (a product launch handed `vitrine`, a pack you named yourself), the script keeps only the photos whose alt names something the brief is about (the material, the city, the street), and none when nothing does: `lib/struq-images.ts` then says `imageFit = "none"`. **An off-topic photo is worse than no photo:** with `imageFit` "none" use no photos at all, and the opener is typographic over the WebGL scene (`<HeroOpener>` without `image`); with "partial" use the few that fit, only where their alt fits. `check.mjs` fails a page whose photos share nothing with the brief. With the brief's own sector pack, the script picks the pack by Struq's rule when you name none, and **leaves out the photos whose alt names a subject this client does not do** (a fish restaurant gets the dining room and the wood fire of `proof`, not its flour and dough); the best fits come first in each slot. It downloads what is left to `public/images/`, verifies every file is a real image, and writes `lib/struq-images.ts` with each photo's alt and description. Read every alt before you place a photo: use only photos whose alt fits what this client actually does, and **write every caption from what the alt says**. Never caption a photo with a subject it does not show (no "Vis van het seizoen" under an apron with flour on it). Use them as `images.hero[0]`, `images.card[3]`, and so on; `imageCredit` goes in the footer. With the user's own photos: put them in `public/images/` and use them first; fill the rest from the pack.
152
-
153
- The pack rule, the same one Struq's MCP uses (`scripts/packs.mjs`): a sector word in the brief picks that sector's pack, matched as whole words; a product launch with no sector word gets `vitrine` (product photography) and the hero scene carries the product; vehicle and mobility words never pull a brief to freight; anything else gets `halide`. The table, by what the photos show:
154
-
155
- | The brief is | Pack | The photos |
156
- |---|---|---|
157
- | Restaurant, bistro, bakery, café, wine bar, catering | `proof` | wood-fire kitchen, dining room before service, bread, plates |
158
- | Shop, ceramics, cosmetics, homeware, product brand | `vitrine` | objects on stone under soft light |
159
- | Architect, real estate, makelaar, property, interiors studio | `datum` | concrete, glass and timber, corrected verticals, no people |
160
- | Construction, trades, installation, renovation | `mortar` | scaffold, timber frame, tools, backs and hands |
161
- | Hotel, holiday home, travel, terrace, pool | `portico` | limewash, shutters, still water, late warm light |
162
- | Law, accountancy, consultancy, finance, notary | `ledger` | oak desks, closed volumes, brass |
163
- | Design studio, agency, print, illustration, writer | `quire` | studio desk mid-project, paper, ink, swatches |
164
- | Software, SaaS, AI, anything with nothing to photograph | `halide` | glass, machined metal and light in darkness |
165
- | Electronics, hardware, devices, engineering, deep tech | `circuit` | copper, solder and connectors at macro range |
166
- | Logistics, freight, distribution, warehousing (vracht, distributie, magazijn) | `transit` | depots, wet road, containers under sodium light |
167
- | A product launch with no sector of its own: an e-bike, a cargo bike, a device, a drink | `vitrine` | the product's world on stone under one light; the hero scene carries the product |
168
- | Sport, gym, running, coaching for performance | `cadence` | training spaces after dark, chalk and steel |
169
- | Yoga, physio, spa, wellness, movement | `vessel` | studio floor, still water, linen, sand tones |
170
- | Care, therapy, coaching, psychology, a small B&B | `linen` | calm rooms, linen, quiet hands |
171
- | Hair salon, beauty, barber, nails | `sable` | salon light in taupe and brass |
172
- | Fashion, textile, tailoring, clothing | `bolt` | cloth, rails, folds and seams, no bodies |
173
- | Farm, growers, farm shop, food provenance | `loam` | soil, crops, greenhouse in low morning light |
174
- | Nature, outdoor, coast, landscape, sustainability | `brume` | Atlantic coast under overcast, fog, stone |
175
- | Crafts, metalwork, furniture making, workshop | `forge` | hot steel, worn tools, warm darkness |
176
- | School, childcare, education | `primer` | empty rooms at child height, birch and felt |
177
- | Events, venues, theatre, conferences | `assembly` | the room before it fills, chairs and stage light |
178
-
179
- The table reads the photos, never the API's `sector` field: production still carries old labels that do not match the pictures (`vessel` is wellness, not kitchen; `datum` is architecture, not data), so the skill never chooses by that field. Nothing fits closely: take the pack whose light matches the palette (warm and light `linen`, warm and dark `forge`, cool and dark `halide`, cool and light `datum`). Never skip images. One pack per site. Translate the alts in `lib/struq-images.ts` when the site is not Dutch. Then set the title, description and `lang` in `app/layout.tsx`.
180
-
181
- ### Step 6: The hero: a full-bleed cinematic opener
182
-
183
- The first screen is the photo, edge to edge, with giant type set across it. Not a text block beside a picture. Use `toonstuk-hero-opener` (copied to `components/struq/showpieces/`):
184
-
185
- ```tsx
186
- <HeroOpener
187
- image={images.hero[0]}
188
- title="Koken op"
189
- accent="vuur."
190
- lead="Een houtoven, een wijnbar en een zaal aan het water."
191
- cta={{ href: "#reserveren", label: "Reserveer een tafel" }}
192
- header={<SiteHeader />}
193
- />
194
- // or the name split to both edges with the photo between:
195
- <HeroOpener image={images.hero[0]} split={["Vuur", "Noord"]} lead="..." cta={{ href: "#reserveren", label: "Reserveer een tafel" }} header={<SiteHeader />} />
60
+ ## Build checklist
61
+ - [ ] 1 Project: Next.js App Router + Tailwind v4; blocks.mjs init printed OK
62
+ - [ ] 2 Direction and approval: palette, pairing, pack and the block order in STRUQ.md; the one stop sent and answered
63
+ - [ ] 3 Canon: the token block and fonts written (canon.mjs palette and type, or struq_richting)
64
+ - [ ] 4 Images: images.mjs --brief printed OK; only photos whose alt fits the client
65
+ - [ ] 5 Blocks: every block on the order installed and composed in app/page.tsx with real copy
66
+ - [ ] 6 Build: npm run build passes
67
+ - [ ] 7 Look: 390 and 1440 screenshots and one full scroll, fixes made (or "Verify: not seen, no browser")
68
+ - [ ] 8 Check: check.mjs prints RESULT: PASS
196
69
  ```
197
70
 
198
- - **The photo fills the viewport,** the ripple WebGL layer living in it, with a flat scrim at a fixed opacity so the copy reads at 4.5:1. Never a gradient.
199
- - **The display line runs at 14 to 20vw** (the opener sets 19vw on phones, 16vw on desktop) along the held-clear bottom, or as a split wordmark. Eight words at most; one word may be the italic accent. `check.mjs` fails a hand-built hero line under 10vw.
200
- - **One short lead and one confident rectangular CTA** above the line (`MagneticButton size="lg"`): one clean button, never a pill, never inside a container card or a box of its own.
201
- - **The lead sits on the photo's calm side.** The opener reads the photo's `negativeSpace` from the beeldbank (written into lib/struq-images.ts) and puts the lead there, the CTA on the other side; pass `calm="left"` or `calm="right"` for your own photo. Where there is no calm side, the lead gets a flat backing at a fixed opacity and is set a step larger (text-h3). Never a lead on bright, busy detail without one.
202
- - **Display lines get word spacing in step with their size:** 0.06 to 0.1em at poster sizes (the opener uses 0.09em), the italic accent word included, so tight tracking never runs two words into one.
203
- - **The handover is a move:** on scroll the opener pins, scales down and darkens while the next section slides up over it. The section after it needs nothing extra: globals.css gives it a ground and the layer above.
204
- - **The header** with the wordmark (`data-wordmark`) goes in the `header` prop, set over the photo in `text-on-photo`.
205
- - **Never model the client's product from primitives:** a launch uses the `vitrine` product photography in the opener. For a portfolio brief ("laat ons werk zien"), the opener photo is a real project of the client.
206
- - Phones and reduced motion get the composed still: the same full-bleed photo and line, no pin.
207
-
208
- A hand-built hero is allowed when the brief needs something the opener does not do, under the same rules: a pack photo in the hero (`check.mjs` fails one without), the copy marked `data-quiet` (the `SplitHeadline` h1 marks itself), the line at 10vw or more, a header with the wordmark.
209
-
210
- **One contrast scene.** Every site has one dark or full-colour scene, at most two, so the page does not stay on one ground: wrap one section in `scene-inverse` (the palette's dark ground with light type) or `scene-field` (a full-strength colour field). The tokens turn inside, so any recipe in it follows. Good places: the agency's scrub connector, the services rows, the project stack, the invitation. `canon.mjs palette` writes the `inverse` and `field` roles from the canon palette when it has them, and swaps text and ground when it does not. `check.mjs` fails a site with none, or with more than two.
211
-
212
- ### Step 7: Sections on the arc
213
-
214
- Five to seven sections after the hero, in this order of beats: **orient** (what it is, one statement beside a big image), **prove** (the real specifics: the menu, the services, the projects, typeset), the **showpiece**, **deepen** (detail photos and short text), **invite** (the one CTA again). Then the footer. Patterns with code: [references/sections.md](references/sections.md).
215
-
216
- - Every image in a `MediaFrame` from `lib/struq-images.ts`, declared ratios only (16/9, 3/2, 4/5, 1/1, 21/9), never the same photo twice.
217
- - Text groups in `<Reveal>`: a heading and its paragraph rise together.
218
- - Break the grid at least twice: an image bleeding off the viewport edge, an image overlapping the next section (`-mb-[12vh] relative z-10`), oversized type running off the canvas.
219
- - Vary the layouts: after a 5/7 split comes a full-bleed band or hairline rows, never three equal cards.
220
- - Section padding varies by role: dense 6 to 8rem, breathers 12 to 20rem. One section should feel too empty by SaaS standards.
221
- - Wayfinding without eyebrows: the heading itself, a hairline rule (`border-t border-line`), a change of surface step, a jump in scale.
222
- - **Captions on media are allowed:** a metadata line directly under or on an image or frame, describing it ("Klant · Woningbouw · 2025"), may be small and tracked. Set it as `<figcaption>` or mark it `data-caption`. A label above a section heading stays an eyebrow and stays banned, and so do numbered steps.
223
- - **A display heading never sits under or behind an image box,** and never inside an `overflow-hidden` box that would clip its letters: give it its own grid cell beside the image. `check.mjs` fails a clipped display heading and an image reference past what the pack holds (`images.card[5]` when the pack kept four), which renders as an empty swatch. The inline photos of `toonstuk-manifesto-scrub` are a word's size: give them a subject (card, column or hero photos), never a texture (detail, backdrop), which reads as a grey swatch.
224
-
225
- ### Step 8: The showpieces (toonstuk)
226
-
227
- Struq builds impressive agency, brand, product and company sites, not simple conversion pages. The showpieces are what make them so: directed, pinned, scroll-scrubbed scenes on the one clock. `recipes.mjs` copies them to `components/struq/showpieces/`, one file per stable slug; each has a composed still for phones and reduced motion built in.
228
-
229
- **Pick one lead showpiece and at most one supporting move,** from this table, by the kind of client. Do not improvise a third. When the user names one in the brief ("met de project-stack", "with the panel wipe"), use that one as the lead, whatever the table says.
230
-
231
- | The client is | Lead showpiece | Supporting move (at most one) |
232
- |---|---|---|
233
- | Architect, studio or portfolio with real project photos | `toonstuk-project-stack` | `toonstuk-image-to-fullbleed` |
234
- | Architect, studio or portfolio without project photos | `toonstuk-image-to-fullbleed` | `toonstuk-manifesto-scrub` |
235
- | Product launch | `toonstuk-image-to-fullbleed`, with the hero scene carrying the product | `toonstuk-pixel-resolve` in the hero |
236
- | Restaurant, hospitality, hotel | `toonstuk-panel-wipe` | `toonstuk-manifesto-scrub` |
237
- | Agency or creative brand | `toonstuk-scrub-connector` | `toonstuk-fill-rows` for the services |
238
- | Editorial, a writer, a publication | `toonstuk-manifesto-scrub` | `toonstuk-image-to-fullbleed` |
239
- | Anything else | `toonstuk-image-to-fullbleed` | none |
240
-
241
- Every site closes with `toonstuk-wordmark-finale` as its footer; it does not count as the supporting move.
242
-
243
- | Slug | Component | What it does |
244
- |---|---|---|
245
- | `toonstuk-image-to-fullbleed` | `ImageToFullbleed` | A small centred photo unclips to the full viewport while pinned, the name split to either side and travelling off the edges, one line rising over the open photo. |
246
- | `toonstuk-panel-wipe` | `PanelWipe` | Two ground-colour panels, each carrying one word, part to reveal a full-bleed photo and close again over it, carrying the headline into the next scene. |
247
- | `toonstuk-scrub-connector` | `ScrubConnector` | One accent bar rotates, stretches and travels across a pinned stretch until it lands as a level line between two words. |
248
- | `toonstuk-project-stack` | `ProjectStack` | Pinned full-bleed project cards slide over each other, the one beneath receding; the cursor becomes a round "Bekijk" label over them. Only with the client's own project photos. |
249
- | `toonstuk-manifesto-scrub` | `ManifestoScrub` | One large statement fills word by word on scroll, with small photos opening inside the line (`{0}`, `{1}` in the text). |
250
- | `toonstuk-fill-rows` | `FillRows` | Large hairline rows of services or chapters; on hover and focus a row floods with the accent and its detail slides in. |
251
- | `toonstuk-wordmark-finale` | `WordmarkFinale` | The footer: the name fitted edge to edge, letters rising out of a mask, one closing line and a live local clock. |
252
- | `toonstuk-pixel-resolve` | `PixelResolve` | The hero photo arrives as coarse pixels and resolves to sharp in held steps, then the plain image stays. |
253
-
254
- **The direction these showpieces need** (they look cheap without it):
255
- - Two or three colours from the canon palette, at full strength: the ground, the ink, the accent. No tints.
256
- - Display type at 12 to 20vw against small body copy. The display type is the composition, not a heading above it.
257
- - One italic serif accent at most, on one word.
258
- - Full-bleed beeldbank photography (or the client's own), never framed small in a box when a scene can carry it.
259
- - One idea per screen. Hold on it.
260
- - Transitions are scale, mask and clip moves between full-bleed scenes, never fades between boxes.
261
- - SplitText on display lines, long eases, holds on every key frame; nothing bounces.
262
- - Struq's rules still win over any reference: no eyebrow or dot labels, no pills (the round cursor label is not a pill), no stat rows ("20+", "51+"), no three identical cards, no numbered sections, no mono flavour, no Inter, no dashes, no blue or purple by default, and never a reference's layout, copy or images.
71
+ **1 Project.** Empty folder: `npx create-next-app@latest . --ts --tailwind --app --eslint --yes`, then
72
+ `node <skill-dir>/scripts/blocks.mjs init`. It copies the kit to `components/struq/kit/`, writes the layout (the
73
+ `sq-js` script before paint, the font links, `data-blocks` on `<body>`, `SmoothScroll`), `globals.css` (the kit
74
+ import, the theme names, the STRUQ TOKENS block) and `fonts.ts` over a starter, and installs gsap, @gsap/react and
75
+ lenis. Your own layout or stylesheet is never overwritten: merge from `recipes/app/`.
263
76
 
264
- Never two pinned showpieces back to back: put a breathing section between them. The Struq MCP or a brief may budget more; never fewer than one lead.
265
-
266
- ### Step 9: The footer
267
-
268
- `<WordmarkFinale name="<the name>" line="<one closing line>" place="<the city>" timeZone="Europe/Amsterdam">` with the one CTA, the contact facts the brief gave (and only those) and the image credit as children: `<a href={imageCredit.href}>{imageCredit.label}</a>`. The name is fitted to the full width and marked `data-wordmark`.
269
-
270
- ### Steps 10 to 12: Build, look, check
271
-
272
- - `npm run build` must pass. Fix type errors in your own code; never delete a recipe to get green.
273
- - **Look**, when you have a browser tool: serve the production build (`npx next start -p 4321`), screenshot 390 and 1440, scroll the page once top to bottom. Check: the headline reads, no frame is blank after its reveal, the showpiece engages and releases without a jump, nothing overflows sideways at 390. No browser tool: write `Verify: not seen, no browser` in STRUQ.md. Never claim you saw what you did not.
274
- - **Check**: `node <skill-dir>/scripts/check.mjs`. Fix every FAIL at its file and line, run it again, and repeat until `RESULT: PASS`. Then tick line 12 and report.
275
-
276
- ## With the Struq MCP (Pro): the build line
277
-
278
- When the `struq_*` tools are connected, the build runs on Struq's build line, and this checklist replaces steps 2, 4 and 7 of the Build checklist (the others stay). Every call is logged with `log.mjs mcp`; `check.mjs` fails a build whose log shows a bouwplan but fewer sectiespec calls than the plan's arc rows, and an approval stop that is not between the bouwplan and the projectschil.
77
+ **2 Direction and the one stop.** Pick from the canon, never invent a palette or pairing while one fits. Palette by
78
+ the brief's mood, then its sector; a palette marked "kept out" (blue, purple, teal) only for a brand that is that
79
+ colour, declared as `Cool hue: declared because <brand colour>`. The brand's own colours and faces beat the canon:
80
+ write them into the same token names. Pairing by temperament: Fraunces (serif-editorial) warm and premium, Newsreader
81
+ literate, Sora calm and architectural, Urbanist soft and current, Bodoni Moda for fashion, Big Shoulders
82
+ (condensed-poster) for a campaign at poster scale, Unbounded for a wide agency voice. Then choose the order (below)
83
+ and write the top of STRUQ.md:
279
84
 
280
85
  ```
281
- ## MCP build line
282
- - [ ] M1 struq_bouwplan with the brief; keep its planToken; count its arc rows
283
- node <skill-dir>/scripts/log.mjs mcp struq_bouwplan --rows <arc rows> --token <planToken>
284
- - [ ] M2 The one approval stop: show the plan (palette, type, arc, showpiece) and stop once with
285
- "Akkoord? Heb je een logo, foto's of echte cijfers, stuur ze nu." The plan's own needs (its
286
- `needs` questions, missing photos) go into that same question. Log the reply as Step 2 Approval.
287
- - [ ] M3 struq_projectschil with the planToken: build the files it returns
288
- node <skill-dir>/scripts/log.mjs mcp struq_projectschil --token <planToken>
289
- - [ ] M4 struq_motion with the planToken (a marketing site)
290
- node <skill-dir>/scripts/log.mjs mcp struq_motion --token <planToken>
291
- - [ ] M5 struq_achtergrond: build the background it returns as the hero scene (below)
292
- node <skill-dir>/scripts/log.mjs mcp struq_achtergrond --token <planToken>
293
- - [ ] M6 For EVERY arc row, in order: struq_sectiespec with the planToken and the row, then write
294
- that row's code from the spec, then log it. Never write a row without its spec.
295
- node <skill-dir>/scripts/log.mjs mcp struq_sectiespec --row <n> --id <arc id> --token <planToken>
86
+ # STRUQ.md: <name>
87
+ Brief: <one line: what, for whom, where>
88
+ Idea: <the one idea the site is about>
89
+ Palette: <id> (<mood>)
90
+ Type: <pairing id>: <display> + <body>
91
+ Images: pack <slug> (or the client's photos, then pack <slug>)
92
+ Order: <block slug:variant>, <block slug:variant>, ...
93
+ Assumptions: <every drafted line and every fact left out>
94
+ Verify: <filled in at step 7>
296
95
  ```
297
96
 
298
- The plan wins for palette, type, arc, copy budget and motion values. The floor in this file still holds on top of it: the hero opener, one showpiece at least, the contrast scene, every check passing. A refusal from `struq_sectiespec` names the step that comes first: do that step, never work around it.
299
-
300
- **Photos: the skill's rule wins.** When the plan names a pack or photos that do not fit the brief (a cargo bike handed product shots of vases), run `images.mjs <that pack> --brief "<the brief>"` anyway: it keeps only the photos whose alt names the brief, and none when nothing fits. No off-topic photo ever ships because a plan named it; the opener then goes typographic over the WebGL scene, and the approval stop asks the client for photos.
301
-
302
- **When `struq_achtergrond` returns a background, build that one.** It is the hero layer. Write it as `components/struq/scenes/<name>.ts` with the same `createScene` contract as the three shipped scenes ([references/hero-scenes.md](references/hero-scenes.md)), take its colours from the palette passed in (so it is recoloured to the site's tokens), add it to the `SCENES` map in `HeroCanvas.tsx`, and use `<HeroCanvas scene="<name>" />`. Never skip it because it is hard to check without a browser: HeroCanvas keeps the poster when the scene fails, so building it can never leave the hero empty.
303
-
304
- ## The taste baseline
305
-
306
- What the recipes and the check enforce, and what you keep when you write your own code:
307
-
308
- - **Type:** two faces; display large and light (300 to 400), leading 0.92 to 1.0, tracking tighter at size; body 1 to 1.0625rem at 1.6+; nothing under 12px; body at 4.5:1. Headings `text-wrap: balance`, body `pretty`. Mono only for literal code.
309
- - **Scale:** one fluid root-rem knob (in globals.css). Author in rem; breakpoints change layout, never size.
310
- - **Surface:** elevation is a tonal step plus a hairline (`border-line`), never a drop shadow. Radius 0 to 4px.
311
- - **Colour:** temperature-shifted neutrals, one accent, flat fills. No gradient anywhere, no gradient text, no glass.
312
- - **Motion:** every entrance moves and fades (never opacity alone), expo.out, stagger 0.06 to 0.1s, reveals 0.6 to 1.0s, hero 1.0 to 1.6s; scroll-tied effects scrub; one clock; reduced motion keeps opacity and drops transforms.
313
- - **Composition:** break the grid twice, vary section layouts, one too-empty section, the footer designed as hard as the hero.
314
- - **Details:** real running copy, custom `::selection`, designed `:focus-visible`, one icon family if any icons at all ([references/iconography.md](references/iconography.md)), never Sparkles, Rocket or Zap.
315
-
316
- Before and after pairs for every tell: [references/anti-patterns.md](references/anti-patterns.md).
317
-
318
- ## The canonical references, and where Struq overrides them
319
-
320
- For craft (layout, type, motion, Lenis, SplitText, reveals, pinned and horizontal showpieces, WebGL heroes, custom cursors) the canonical sources are Roy's `deck-design` skill and the `cinematic-web-experience` skill; this skill's references and recipes follow them. Where Struq deliberately differs, Struq wins, and these are all the differences:
321
-
322
- 1. **No eyebrows.** deck-design recommends tracked uppercase labels as wayfinding; Struq treats them as a tell. The heading, a hairline and scale do the wayfinding.
323
- 2. **No gradients and no tinted fills at all,** not even the subtle same-hue ramps deck-design allows. A flat black scrim over a photo is the only translucent layer.
324
- 3. **No pills, no live dots, no uppercase marquees.**
325
- 4. **The canon first.** Palette, pairing and design system come from Struq's canon (step 4), not from a preset you write yourself; the brand's own colours and faces beat the canon.
326
- 5. **Never an empty frame.** deck-design allows a graded placeholder frame; Struq fills every slot from the beeldbank, or a generated fallback plate.
327
- 6. **The cinematic floor is mandatory, not "when the page calls for it":** Lenis on one GSAP clock, a SplitText hero, masked reveals, one showpiece, a WebGL hero layer with poster, a magnetic CTA, on every marketing site.
328
- 7. **No Inter anywhere,** including the two canon pairings that name it (`canon.mjs` swaps it), and no commercial face without a licence (its free stand-in instead).
329
- 8. **Exactly one stop.** The plan stops once for approval with the materials request, and never again; the canon picks are the defaults when the answer is a plain yes. deck-design's longer planning stays available in [references/direction.md](references/direction.md) for a long interactive session.
97
+ Then **stop once**, in the user's language: the plan in five lines (idea, palette, type, pack, order) and
98
+ "Akkoord? Heb je een logo, foto's of echte cijfers, stuur ze nu." (English: "Approve? If you have a logo, photos or
99
+ real numbers, send them now."). The build request is not the approval. Log the reply word for word:
100
+ `log.mjs "Step 2 Approval" "Asked: ..." "Reply: \"<their words>\""`. Any yes builds the plan; what they send replaces
101
+ the matching pick; a veto changes only what it names. Never ask a second time. When no pack shows the client's own
102
+ product, add: "Ik heb geen foto's van je <product>: stuur ze, dan komen ze in de hero."
103
+
104
+ **3 Canon.** `canon.mjs palette <id>` and `canon.mjs type <id>` rewrite the one STRUQ TOKENS block and `app/fonts.ts`:
105
+ the surface ladder, the field and inverse roles, `--photo-ground`, the accent's ink, the pairing's display weight and
106
+ faces, and each Google face asked for its real weights. It is the block Struq's lab judged every block in.
107
+
108
+ **4 Images.** `node <skill-dir>/scripts/images.mjs --brief "<the brief, in the user's words>"` picks the pack, keeps
109
+ only the photos whose alt fits the client, downloads and verifies them to `public/images/` and writes
110
+ `lib/struq-images.ts` (`images.hero`, `.band`, `.card`, `.column`, `.detail`, `.backdrop`, `imageCredit`, `imageFit`).
111
+ `images.mjs --list` shows every pack. Read the alts before you place a photo; with `imageFit` "none" use no photos and
112
+ let a background block carry the hero. Offline it writes plates graded to the palette. Set the title, description and
113
+ `lang` in the layout.
114
+
115
+ **5 Blocks.** Install the order with `blocks.mjs add <slug> ...` (or `struq_blok`), read each block's file header for
116
+ its props, variants and how it mounts, and compose `app/page.tsx` with the brief's real copy. Glue between blocks uses
117
+ the kit (`Reveal`, `MediaFrame`, `SplitHeadline`, `MagneticButton`) and the craft rules, never a fresh motion system.
118
+
119
+ **6 to 8.** `npm run build` passes (fix your code, never delete a block to get green). Look at the production build
120
+ (`npx next start -p 4321`) at 390 and 1440 and scroll it once: the preloader lands on the hero, every frame fills, each
121
+ pin engages and releases without a jump, nothing overflows at 390, scrolling back reverses. Never claim you saw what
122
+ you did not. Then `check.mjs` until `RESULT: PASS`.
123
+
124
+ ## The order of a studio site
125
+
126
+ Read top to bottom; each line is a slot with the blocks that fill it. Seven to nine slots, not all of them every time.
127
+
128
+ 1. **Arrival, once:** a preloader on the first visit (`preloader-wordmark`, `-slices`, `-gate`, `-stack`) and the
129
+ page transition of the same language on every navigation (`page-transition-slices`, `-gate`, `-stack`; mount it in
130
+ the layout). Slices with slices, gate with gate, stack with stack: they share one handover.
131
+ 2. **Header:** `header-bar` (quiet, settle, split) or `menu-curtain` for a full-screen menu. Sections under it say
132
+ their ground with `data-bar-tone`.
133
+ 3. **Hero:** `hero-still` (one photo, calm side measured), `hero-framed` (the print on the wall), `hero-split` (a
134
+ seam), `hero-index` (the work list as the first screen), `hero-poster` (type as poster), `hero-opener` (full bleed
135
+ with the WebGL layer), or a background block (`background-aurora`, `-silk`, `-facets`) when the hero is type on a
136
+ live field.
137
+ 4. **Manifesto:** `manifesto`, `manifesto-scrub`, `velocity-type` or `reveals`: the one statement, given a screen.
138
+ 5. **About:** `about-studio` or `split-pair`.
139
+ 6. **Approach:** `approach`, `sticky-chapters`, `panel-accordion` or `fill-rows`. Never numbered steps.
140
+ 7. **Work:** `project-stack`, `hover-list`, `image-trail`, `film-strip`, `grid-assembly`, `fanned-cards`, or a scene
141
+ move (`image-to-fullbleed`, `panel-wipe`, `zoom-through`, `scrub-connector`, `pixel-resolve`) between two slots.
142
+ 8. **Closing call to action:** `cta-closing`, with `contact-form` when the site asks for input.
143
+ 9. **Footer:** `footer-reveal`, `footer-contact` or `wordmark-finale`.
144
+
145
+ **Picking.** One lead move per page, the hero or a work piece, and the rest quieter around it. Never two pinned
146
+ pieces back to back: a plain slot breathes between them. One contrast scene, at most two: wrap a slot in
147
+ `scene-inverse` or `scene-field`. Choose the variant by the brief and the photos, not by novelty: a variant is a
148
+ different composition of the same idea, and every variant works light and dark. Name a block the user asks for first.
149
+ A studio with real project photos leads with work (`hero-index`, `project-stack`); a studio without them leads with
150
+ type and one photo (`hero-poster`, `hero-still`, `manifesto`).
151
+
152
+ ## What makes it feel high end
153
+
154
+ The blocks hold this already; your glue must too. Detail in [references/craft.md](references/craft.md).
155
+
156
+ - Reveals clip from numbers (inset values tweened as numbers), never scale a box; positions in whole pixels.
157
+ - Photos are decoded before they show, cut or swap.
158
+ - Scroll effects are scroll-tied and reverse when the visitor scrolls back; key frames hold.
159
+ - The gate: `sq-js` on `<html>` before paint hides only what will animate, and shows it anyway after 3 s.
160
+ - Type on a photo sits on the measured calm side over the lightest flat veil that reaches 3:1.
161
+ - Display faces run light (300, or the poster pairing's own weight); fitted lines are measured with the real face
162
+ after `document.fonts.ready`.
163
+ - Tokens only, mixes in oklab, a surface step and a hairline instead of a shadow, one accent.
164
+ - Icons: Struq Icons (`@struq/icons`), functional only ([references/iconography.md](references/iconography.md)).
330
165
 
331
166
  ## When something goes wrong
332
167
 
333
- - **The page does not scroll:** `<html>` has `lenis-stopped`? An overlay called `stop()` without `start()`. Otherwise the ticker is not driving Lenis: keep `SmoothScroll` as shipped. [references/setup.md](references/setup.md) §2.
334
- - **A pin jerks on engage:** never `anticipatePin` with smooth scroll; `scrub: 1`. [references/pinning-scrub.md](references/pinning-scrub.md) §2.
335
- - **Descenders clipped in the split headline:** the `.struq-line-mask` rule in globals.css gives them room; keep it.
336
- - **Content stays invisible:** something removed `data-ready` handling or the `js` class script. Restore the layout and globals.css from `<skill-dir>/recipes/app/`.
337
- - **Anything else in motion:** [references/pitfalls.md](references/pitfalls.md), symptom first.
338
-
339
- ## Depth, when a step needs it
340
-
341
- Per client type (tension, type, palette fallback, motion, arc, the niche tells, the pack) [niche-packets.md](references/niche-packets.md) · Typography [typography.md](references/typography.md) · palettes and presets [vibe-presets.md](references/vibe-presets.md) · page arc and footer [page-architecture.md](references/page-architecture.md) · composition [composition.md](references/composition.md), on phones [mobile-composition.md](references/mobile-composition.md) · imagery and grading [imagery.md](references/imagery.md), the beeldbank [struq-imagery.md](references/struq-imagery.md) · reveals [scroll-reveals.md](references/scroll-reveals.md) · more showpieces [showcase.md](references/showcase.md), [pinning-scrub.md](references/pinning-scrub.md) · the feel layer [feel-layer.md](references/feel-layer.md) · nav and menus [nav-header.md](references/nav-header.md) · route transitions [route-transitions.md](references/route-transitions.md), loaders [loaders.md](references/loaders.md) · SVG motion [svg-motion.md](references/svg-motion.md) · pointer [micro-interactions.md](references/micro-interactions.md) · WebGL beyond the hero [webgl-r3f.md](references/webgl-r3f.md), [webgl-scenes.md](references/webgl-scenes.md), hero scenes [hero-scenes.md](references/hero-scenes.md) · brand from nothing [brand.md](references/brand.md) · SEO [seo.md](references/seo.md) · performance [performance.md](references/performance.md) · a long, interactive planning session [direction.md](references/direction.md).
168
+ - **Nothing scrolls:** an overlay called `getLenis()?.stop()` without `start()`, or `SmoothScroll` is not mounted.
169
+ - **Content stays invisible for 3 s:** the block never set `data-ready`; usually it threw. Read the console.
170
+ - **Blocks render in the wrong colours or faces:** `data-blocks` is missing on `<body>`, or the STRUQ TOKENS block
171
+ sits before the kit import in `globals.css`.
172
+ - **A fitted line overflows or jumps:** the face did not load (check `app/fonts.ts` and the links in the layout).
173
+ - **Vite:** [references/stack-targets.md](references/stack-targets.md).