@struq/skill 0.2.2 → 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.
- package/README.md +23 -35
- package/package.json +3 -2
- package/skills/struq/SKILL.md +148 -212
- package/skills/struq/blocks/CATALOG.md +426 -0
- package/skills/struq/blocks/backgrounds/background-aurora.tsx +818 -0
- package/skills/struq/blocks/backgrounds/background-facets.tsx +1283 -0
- package/skills/struq/blocks/backgrounds/background-silk.tsx +906 -0
- package/skills/struq/blocks/catalog.json +1302 -0
- package/skills/struq/blocks/footers/footer-contact.tsx +789 -0
- package/skills/struq/blocks/footers/footer-reveal.tsx +313 -0
- package/skills/struq/blocks/headers/header-bar.tsx +652 -0
- package/skills/struq/blocks/headers/menu-curtain.tsx +790 -0
- package/skills/struq/blocks/heroes/hero-framed.tsx +627 -0
- package/skills/struq/blocks/heroes/hero-index.tsx +855 -0
- package/skills/struq/blocks/heroes/hero-poster.tsx +1086 -0
- package/skills/struq/blocks/heroes/hero-split.tsx +685 -0
- package/skills/struq/blocks/heroes/hero-still.tsx +862 -0
- package/skills/struq/blocks/kit/Cursor.tsx +117 -0
- package/skills/struq/blocks/kit/HeroCanvas.tsx +278 -0
- package/skills/struq/blocks/kit/HorizontalGallery.tsx +106 -0
- package/skills/struq/blocks/kit/MagneticButton.tsx +92 -0
- package/skills/struq/blocks/kit/MediaFrame.tsx +96 -0
- package/skills/struq/blocks/kit/PinnedZoom.tsx +89 -0
- package/skills/struq/blocks/kit/Reveal.tsx +64 -0
- package/skills/struq/blocks/kit/SmoothScroll.tsx +57 -0
- package/skills/struq/blocks/kit/SplitHeadline.tsx +68 -0
- package/skills/struq/blocks/kit/blocks.css +2102 -0
- package/skills/struq/blocks/kit/gsap.ts +21 -0
- package/skills/struq/blocks/kit/images.ts +17 -0
- package/skills/struq/blocks/kit/photo-read.ts +102 -0
- package/skills/struq/blocks/kit/scenes/contours.ts +161 -0
- package/skills/struq/blocks/kit/scenes/dots.ts +110 -0
- package/skills/struq/blocks/kit/scenes/ripple.ts +99 -0
- package/skills/struq/blocks/kit/scenes/types.ts +114 -0
- package/skills/struq/blocks/page-transitions/page-transition-gate.tsx +706 -0
- package/skills/struq/blocks/page-transitions/page-transition-slices.tsx +469 -0
- package/skills/struq/blocks/page-transitions/page-transition-stack.tsx +583 -0
- package/skills/struq/blocks/preloaders/preloader-gate.tsx +358 -0
- package/skills/struq/blocks/preloaders/preloader-slices-uncovered.ts +50 -0
- package/skills/struq/blocks/preloaders/preloader-slices.tsx +289 -0
- package/skills/struq/blocks/preloaders/preloader-stack.tsx +367 -0
- package/skills/struq/blocks/preloaders/preloader-wordmark.tsx +568 -0
- package/skills/struq/blocks/sections/about-studio.tsx +436 -0
- package/skills/struq/blocks/sections/approach.tsx +661 -0
- package/skills/struq/blocks/sections/contact-form.tsx +664 -0
- package/skills/struq/blocks/sections/cta-closing.tsx +763 -0
- package/skills/struq/blocks/sections/fanned-cards.tsx +547 -0
- package/skills/struq/blocks/sections/film-strip.tsx +385 -0
- package/skills/struq/blocks/sections/grid-assembly.tsx +616 -0
- package/skills/struq/blocks/sections/hover-list.tsx +467 -0
- package/skills/struq/blocks/sections/image-trail.tsx +432 -0
- package/skills/struq/blocks/sections/manifesto.tsx +691 -0
- package/skills/struq/blocks/sections/panel-accordion.tsx +380 -0
- package/skills/struq/blocks/sections/reveals.tsx +416 -0
- package/skills/struq/blocks/sections/split-pair.tsx +670 -0
- package/skills/struq/blocks/sections/sticky-chapters.tsx +256 -0
- package/skills/struq/blocks/sections/velocity-type.tsx +269 -0
- package/skills/struq/blocks/showpieces/fill-rows.tsx +74 -0
- package/skills/struq/blocks/showpieces/hero-opener.tsx +128 -0
- package/skills/struq/blocks/showpieces/image-to-fullbleed.tsx +89 -0
- package/skills/struq/blocks/showpieces/manifesto-scrub.tsx +48 -0
- package/skills/struq/blocks/showpieces/panel-wipe.tsx +89 -0
- package/skills/struq/blocks/showpieces/pixel-resolve.tsx +107 -0
- package/skills/struq/blocks/showpieces/project-stack.tsx +86 -0
- package/skills/struq/blocks/showpieces/scrub-connector.tsx +307 -0
- package/skills/struq/blocks/showpieces/wordmark-finale.tsx +105 -0
- package/skills/struq/blocks/transitions/zoom-through.tsx +613 -0
- package/skills/struq/canon/canon.json +627 -0
- package/skills/struq/cinematic/README.md +7 -18
- package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +1 -1
- package/skills/struq/cinematic/examples/code/shared/styles.css +1 -1
- package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +1 -1
- package/skills/struq/cinematic/references/01-core-mental-model.md +1 -1
- package/skills/struq/cinematic/references/02-vocabulary.md +1 -1
- package/skills/struq/cinematic/references/07-stack-integration.md +2 -2
- package/skills/struq/recipes/app/fonts.ts +12 -0
- package/skills/struq/recipes/app/globals.css +42 -0
- package/skills/struq/recipes/app/layout.tsx +30 -0
- package/skills/struq/references/craft.md +77 -0
- package/skills/struq/references/iconography.md +28 -230
- package/skills/struq/references/stack-targets.md +23 -74
- package/skills/struq/references/tells.md +52 -0
- package/skills/struq/references/triage.md +6 -7
- package/skills/struq/scripts/blocks.mjs +156 -0
- package/skills/struq/scripts/canon.mjs +134 -0
- package/skills/struq/scripts/check.mjs +513 -0
- package/skills/struq/scripts/images.mjs +360 -0
- package/skills/struq/scripts/log.mjs +75 -0
- package/skills/struq/scripts/packs.mjs +156 -0
- package/skills/struq/scripts/tokens.mjs +222 -0
- package/skills/struq/references/anti-patterns.md +0 -265
- package/skills/struq/references/baseline-prompt.md +0 -118
- package/skills/struq/references/brand.md +0 -143
- package/skills/struq/references/brief-router.md +0 -219
- package/skills/struq/references/composition.md +0 -191
- package/skills/struq/references/depth-parallax.md +0 -235
- package/skills/struq/references/direction.md +0 -188
- package/skills/struq/references/feel-layer.md +0 -107
- package/skills/struq/references/gsap-api.md +0 -293
- package/skills/struq/references/imagery.md +0 -144
- package/skills/struq/references/loaders.md +0 -287
- package/skills/struq/references/micro-interactions.md +0 -270
- package/skills/struq/references/mobile-composition.md +0 -70
- package/skills/struq/references/nav-header.md +0 -296
- package/skills/struq/references/page-architecture.md +0 -134
- package/skills/struq/references/page-entrances.md +0 -198
- package/skills/struq/references/performance.md +0 -235
- package/skills/struq/references/pinning-scrub.md +0 -341
- package/skills/struq/references/pitfalls.md +0 -290
- package/skills/struq/references/route-transitions.md +0 -281
- package/skills/struq/references/scaling.md +0 -151
- package/skills/struq/references/scroll-reveals.md +0 -280
- package/skills/struq/references/seo.md +0 -219
- package/skills/struq/references/setup.md +0 -397
- package/skills/struq/references/showcase.md +0 -87
- package/skills/struq/references/struq-imagery.md +0 -107
- package/skills/struq/references/svg-motion.md +0 -390
- package/skills/struq/references/transitions.md +0 -160
- package/skills/struq/references/typography.md +0 -720
- package/skills/struq/references/vibe-presets.md +0 -287
- package/skills/struq/references/webgl-r3f.md +0 -274
- package/skills/struq/references/webgl-scenes.md +0 -593
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# Brand: name, logo, mark and the files a site needs
|
|
2
|
-
|
|
3
|
-
Run this when the project has no name or no logo. When a brand exists, do not redesign it; extract it ([direction.md](direction.md) §2). The output is small on purpose: a name, a wordmark, a mark, the favicon set, and one voice line. A brand guideline PDF is not the job. A site that carries the brand is.
|
|
4
|
-
|
|
5
|
-
Order matters: **positioning, then name, then type, then logo.** A name chosen first forces the positioning to be bent around it. A logo drawn before the typefaces are chosen never sits right in the site.
|
|
6
|
-
|
|
7
|
-
## 1. Positioning in two lines
|
|
8
|
-
|
|
9
|
-
Before naming anything, write:
|
|
10
|
-
|
|
11
|
-
- **For** <who, specifically> **who** <the need or the frustration>, **<name> is** <the category> **that** <the one thing it does differently>.
|
|
12
|
-
- **Unlike** <the real alternative, including "do it yourself"> **it** <the difference that matters to the buyer>.
|
|
13
|
-
|
|
14
|
-
If these two lines cannot be written, the brief is missing the facts the name depends on. Ask for them (one round) instead of naming in the dark.
|
|
15
|
-
|
|
16
|
-
## 2. Naming
|
|
17
|
-
|
|
18
|
-
**Skip rule.** Sites for a person, a practice or a founder-named business (portfolios, studios named after people, clinics) use the person's name. If the name is unknown, use a placeholder, record it under ASSUMPTIONS in STRUQ.md, skip this section and go straight to the wordmark (§3).
|
|
19
|
-
|
|
20
|
-
### Territories
|
|
21
|
-
|
|
22
|
-
Generate across all four, three to four candidates each, so the shortlist is a real choice and not twelve variations of one idea:
|
|
23
|
-
|
|
24
|
-
| Territory | What it is | Strength | Risk |
|
|
25
|
-
|---|---|---|---|
|
|
26
|
-
| **Descriptive** | Says what it does (a real word or two) | Instantly clear, good for local services | Hard to own, hard to trademark |
|
|
27
|
-
| **Evocative** | A real word that carries the feeling or the metaphor from the concept | Memorable, ownable, carries the motif | Needs the site to explain it |
|
|
28
|
-
| **Invented** | A coined word built from meaningful roots or sounds | Fully ownable, domain-friendly | Empty until the brand fills it |
|
|
29
|
-
| **Founder or place** | A surname, a street, a landmark, a region | Credible, personal, honest | Hard to sell later |
|
|
30
|
-
|
|
31
|
-
### The filter
|
|
32
|
-
|
|
33
|
-
Score every candidate, drop anything that fails a hard rule:
|
|
34
|
-
|
|
35
|
-
- **Hard:** easy to say on first sight, easy to spell after hearing it once, no obvious clash with a major brand in the same category, no unfortunate meaning in the client's main languages.
|
|
36
|
-
- **Soft (rank by):** one or two syllables beats three; a distinctive first letter; works as a wordmark (letter shapes with character, no awkward pairs); a plausible domain or handle pattern (`name.com`, `getname.com`, `name.studio`, the country TLD).
|
|
37
|
-
|
|
38
|
-
**You cannot verify trademarks or domain availability.** Say so plainly next to the shortlist and tell the user what to check before committing.
|
|
39
|
-
|
|
40
|
-
### The deliverable
|
|
41
|
-
|
|
42
|
-
A shortlist of five with one line each (territory, why it fits the positioning, the risk), and one recommendation with the reason. Write it to `brand/NAMING.md`. In a non-interactive run, take the recommendation and record it under ASSUMPTIONS.
|
|
43
|
-
|
|
44
|
-
## 3. The wordmark
|
|
45
|
-
|
|
46
|
-
The wordmark is the name set in type, then drawn into something ownable. Start from the **display face chosen in the SYSTEM phase** ([typography.md](typography.md)), so the logo and the headlines are one family of shapes.
|
|
47
|
-
|
|
48
|
-
1. Set the name in the display face at the weight the site uses (light, 300 to 400) and one step heavier. Pick the one that holds at 24px tall.
|
|
49
|
-
2. Tune it as a logo, not as text: tracking a little tighter than headline tracking, kern every pair by eye, decide on case (lowercase reads friendly, caps read institutional, title case reads like a person's name).
|
|
50
|
-
3. Make **one** deliberate modification. That single change turns a font into a logo: a cut terminal, a joined pair (ligature), a letter replaced by the mark, a counter filled, a stroke extended into a line that the site's motif can continue. Never more than one.
|
|
51
|
-
4. Convert to outlines, so the logo does not depend on the font loading:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npm i -D opentype.js
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
// scripts/wordmark.mjs:
|
|
59
|
-
// node scripts/wordmark.mjs "Name" path/to/Display-Light.ttf -0.02 > brand/logo.svg
|
|
60
|
-
// Third argument: tracking in em (the tuned value from step 2). Default -0.02.
|
|
61
|
-
import { readFile } from "node:fs/promises";
|
|
62
|
-
import opentype from "opentype.js";
|
|
63
|
-
|
|
64
|
-
const [text, fontPath, trackingEm = "-0.02"] = process.argv.slice(2);
|
|
65
|
-
// Newer opentype.js majors removed load(); parse() works on every version.
|
|
66
|
-
const font = opentype.parse(await readFile(fontPath));
|
|
67
|
-
const escapeAttr = (s) =>
|
|
68
|
-
s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
69
|
-
const size = 100;
|
|
70
|
-
const tracking = Number(trackingEm) * size;
|
|
71
|
-
|
|
72
|
-
// One glyph per character, no shaping: font.stringToGlyphs() runs the ccmp feature
|
|
73
|
-
// and throws on GSUB lookups opentype.js does not support yet (seen on variable fonts).
|
|
74
|
-
const glyphs = Array.from(text, (ch) => font.charToGlyph(ch));
|
|
75
|
-
if (glyphs.some((g) => g.index === 0)) console.error("warning: a character is missing from the font");
|
|
76
|
-
|
|
77
|
-
let x = 0;
|
|
78
|
-
const parts = [];
|
|
79
|
-
const box = { x1: Infinity, y1: Infinity, x2: -Infinity, y2: -Infinity };
|
|
80
|
-
glyphs.forEach((glyph, i) => {
|
|
81
|
-
const path = glyph.getPath(x, 0, size); // baseline at y = 0
|
|
82
|
-
const b = path.getBoundingBox();
|
|
83
|
-
if (!b.isEmpty()) {
|
|
84
|
-
box.x1 = Math.min(box.x1, b.x1); box.y1 = Math.min(box.y1, b.y1);
|
|
85
|
-
box.x2 = Math.max(box.x2, b.x2); box.y2 = Math.max(box.y2, b.y2);
|
|
86
|
-
}
|
|
87
|
-
parts.push(path.toPathData(2)); // a number keeps y unflipped
|
|
88
|
-
const kern = glyphs[i + 1] ? font.getKerningValue(glyph, glyphs[i + 1]) : 0;
|
|
89
|
-
x += ((glyph.advanceWidth + kern) / font.unitsPerEm) * size + tracking;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// The viewBox hugs the ink, descenders included, so the logo aligns by its real edges.
|
|
93
|
-
const vx = Math.floor(box.x1), vy = Math.floor(box.y1);
|
|
94
|
-
const vw = Math.ceil(box.x2) - vx, vh = Math.ceil(box.y2) - vy;
|
|
95
|
-
console.log(
|
|
96
|
-
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${vx} ${vy} ${vw} ${vh}" fill="currentColor" role="img" aria-label="${escapeAttr(text)}"><path d="${parts.join(" ")}"/></svg>`
|
|
97
|
-
);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Use a **static** file of the chosen weight. A variable file outlines its default instance, usually 400, not the light weight the site uses. The reliable source an agent can fetch without downloading archives is Fontsource on npm, which ships one static file per weight, including for families Google serves as variable only (Sora, for one): `npm i -D @fontsource/<family>`, then use `node_modules/@fontsource/<family>/files/<family>-latin-300-normal.woff`. opentype.js parses WOFF (not WOFF2), and `next/og` reads the same WOFF ([seo.md](seo.md) §3), so copy it once to `assets/fonts/` (outside `public/`) and point both scripts at it. A static TTF or OTF from the foundry works the same way. The script applies the font's own pair kerning plus your tracking; for a hand-kerned result, export each glyph as its own `<path>` and adjust `x` per pair, or finish the outline in a vector editor. Apply the one modification from step 3 on the outlined paths.
|
|
101
|
-
|
|
102
|
-
5. Use `fill="currentColor"`, so the logo takes the ink color in light and dark surfaces without a second file.
|
|
103
|
-
6. **Test the modification at nav size** (about 1.25 to 1.5rem tall). A thin full-width rule under a word reads as a link underline there, and a cut terminal can vanish. If it fails at nav size, make it thicker, shorter or part of a letter. The modification goes into the SVG as its own element after the outlined path (a `<rect>`, a `<path>`), so it can be tuned without re-outlining.
|
|
104
|
-
|
|
105
|
-
## 4. The mark
|
|
106
|
-
|
|
107
|
-
The mark is the logo reduced to one shape that works at 16px: the favicon, the loader, the menu button, the social avatar. Draw it as SVG by construction, never by freehand path guessing:
|
|
108
|
-
|
|
109
|
-
- **Monogram:** the first letter from the wordmark outlines, with the same modification, sitting in a deliberate bounding shape (or none).
|
|
110
|
-
- **Symbol:** built from the concept's motif with primitives (`circle`, `rect`, `line`, arcs) on a 24 or 48 unit grid. Whole or half units only. One stroke weight or one fill, never both.
|
|
111
|
-
- **Test** at 16, 32 and 512px, on the darkest and lightest surface in the palette. If it turns to mush at 16px, simplify.
|
|
112
|
-
|
|
113
|
-
Build it so it can move. Strokes (not fills) can be drawn on with DrawSVG for the `draw-mark-loader`; a mark built from the same number of points as a second shape can morph into it for `logo-morph` ([svg-motion.md](svg-motion.md), [showcase.md](showcase.md)).
|
|
114
|
-
|
|
115
|
-
## 5. The file set
|
|
116
|
-
|
|
117
|
-
Write these, wired into the framework's conventions (Next.js App Router shown):
|
|
118
|
-
|
|
119
|
-
| File | What |
|
|
120
|
-
|---|---|
|
|
121
|
-
| `brand/logo.svg` | Wordmark, outlined, `currentColor` |
|
|
122
|
-
| `brand/mark.svg` | Mark, outlined, `currentColor` |
|
|
123
|
-
| `components/brand/Logo.tsx` | Both inlined as components with a `title` for accessibility, so they can animate and take color |
|
|
124
|
-
| `app/icon.svg` | The mark on a solid surface tile; check it at 16px |
|
|
125
|
-
| `app/apple-icon.png` | 180 by 180, the mark on the surface color with padding |
|
|
126
|
-
| `app/opengraph-image.tsx` | Owned by [seo.md](seo.md) §3, and built in the SEO workflow step once the routes exist. The brand supplies the wordmark, surface color and display face it uses. |
|
|
127
|
-
|
|
128
|
-
Vite: the same SVGs in `public/` and `<link rel="icon" type="image/svg+xml">`; the static OG PNGs are covered in [seo.md](seo.md) §7.
|
|
129
|
-
|
|
130
|
-
## 6. Voice
|
|
131
|
-
|
|
132
|
-
One line in STRUQ.md that every piece of copy is checked against: register, sentence length, what it never does. For example: "plain and certain; short sentences; numbers instead of adjectives; never exclamation marks, never 'unlock' or 'elevate'." The copy rules in [page-architecture.md](page-architecture.md) §5 apply on top.
|
|
133
|
-
|
|
134
|
-
## Tells
|
|
135
|
-
|
|
136
|
-
- A name chosen before the positioning exists
|
|
137
|
-
- Twelve candidates that are one idea in twelve spellings
|
|
138
|
-
- A wordmark in a different family from the site's display face, for no reason
|
|
139
|
-
- Three modifications on one wordmark
|
|
140
|
-
- A mark that is a generic icon (a leaf, a lightbulb, a rocket, an abstract swoosh, a hexagon network)
|
|
141
|
-
- A gradient logo, a logo that only works on one background
|
|
142
|
-
- A favicon that is the full wordmark at 16px
|
|
143
|
-
- Claiming a name is "available" without having checked
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
# Brief Router: the decisions you fill into STRUQ.md
|
|
2
|
-
|
|
3
|
-
This file is a procedure, not a phase. [direction.md](direction.md) is the orchestrator and owns the order of work, the single contract file (`STRUQ.md`) and the one gate. The router supplies three pieces of it:
|
|
4
|
-
|
|
5
|
-
| Router part | Runs inside direction.md | What it produces |
|
|
6
|
-
|---|---|---|
|
|
7
|
-
| §1 Extract, §1b Wizard | Phase 0, **Intake** | the facts: buyer, fear, register, stack, mode, materials, scope |
|
|
8
|
-
| §2 Route | Phase 2, **Concept** (the tension) and phase 4, **Site plan** | NICHE, arc, reveals, showpieces, transitions, WebGL, copy budget, shot list |
|
|
9
|
-
| §3 Field map | the write-up before the gate | where each decision lands in STRUQ.md |
|
|
10
|
-
|
|
11
|
-
SYSTEM decisions (type, palette, icons, imagery, motion) are owned by [direction.md](direction.md) §4 only. §2 points there; it does not decide them a second time.
|
|
12
|
-
|
|
13
|
-
Where a brief is merely *silent* on a detail, **decide and record the assumption** under ASSUMPTIONS. A wrong but stated default beats a question that blocks the build. The wizard (§1b) is the one exception, reserved for when the plan-deciding facts themselves are missing.
|
|
14
|
-
|
|
15
|
-
If the brief is "fix or improve an existing site", [triage.md](triage.md) rules override this file: identity (brand color, copy, component primitives) is preserved and recorded under PRESERVED, and the router only re-decides execution.
|
|
16
|
-
|
|
17
|
-
If the brief asks for a **directed WebGL experience** (one continuous scene drives the whole page), direction.md phases 0 to 3 still run, then [../cinematic/README.md](../cinematic/README.md) takes over. Use this router only for the content, copy and asset decisions.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 1. Extract: answer these nine from the brief, in order
|
|
22
|
-
|
|
23
|
-
Silent by default. With a usable brief, answer all nine without asking anything; whatever is missing becomes an assumption, and the materials request rides along in the single gate message ([direction.md](direction.md) §5).
|
|
24
|
-
|
|
25
|
-
1. **Who signs the check, and what is their business model?** This picks the genre row and the preset, by buyer and never by surface topic ([page-architecture.md](page-architecture.md) §2b). A legal-tech SaaS is a software launch page, not a legal-practice page.
|
|
26
|
-
2. **What is the visitor afraid of?** Write it as the TENSION line (convince / never signal). It drives everything else, so write it first.
|
|
27
|
-
3. **Register:** marketing or narrative (this skill), a directed cinematic experience ([../cinematic/README.md](../cinematic/README.md)), or dashboard and product UI (out of register: keep the type and surface rules, drop Lenis, cinematic durations and showpieces, per [SKILL.md](../SKILL.md)).
|
|
28
|
-
4. **Stack:** Next.js App Router (default) or Vite, with the installed majors read from `package.json` ([setup.md](setup.md) §0), never assumed. Record them on the `Stack:` line in STRUQ.md; Vite needs the [stack-targets.md](stack-targets.md) translations.
|
|
29
|
-
5. **Mode:** greenfield or remediation ([triage.md](triage.md) preserve-list applies).
|
|
30
|
-
6. **Existing brand?** Logo, colors, fonts, voice. Real assets override the preset's palette and type; the preset still supplies token structure, motion and tension. A named brand with no logo file is not a rebrand: [direction.md](direction.md) §2 (provisional wordmark, no renaming). Brand colours map onto the ladder by role, not by name ([direction.md](direction.md) §4).
|
|
31
|
-
7. **Existing content?** Copy and imagery on hand against what must be produced. This decides the shot list ([imagery.md](imagery.md) §4 sourcing ladder).
|
|
32
|
-
8. **Scope:** one-pager or multi-page; which pages; any sections the client demands (pricing, careers, booking). Multi-page puts full-screen route transitions on the table ([route-transitions.md](route-transitions.md)).
|
|
33
|
-
9. **Deadline or effort signal:** a "this week" brief caps the plan at the highest-leverage items (the ranking in [SKILL.md](../SKILL.md)); showpieces and WebGL need room.
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 1b. The wizard: when there is no usable brief
|
|
38
|
-
|
|
39
|
-
This is the only case that blocks before the gate. Switch from silent extraction to the wizard when the request is empty or vague ("make me a website", "I don't know what I want"), the user asks to be guided, or §1 questions 1 and 2 (buyer and visitor's fear) cannot be answered. Those two pick the genre and the tension and cannot be assumed safely; everything downstream of them can.
|
|
40
|
-
|
|
41
|
-
**Wizard rules:**
|
|
42
|
-
|
|
43
|
-
- **At most 3 rounds, at most 4 questions per round.** Anything still unknown after round 2 goes back to the assumption path. The wizard discovers the direction; it does not run a workshop.
|
|
44
|
-
- **Multiple choice with concrete options wherever possible.** If the harness has an interactive question tool (for example `AskUserQuestion` in Claude Code), use it: 2 to 4 options per question, multi-select for "what exists". Otherwise ask in plain text, options listed.
|
|
45
|
-
- **Ask in the user's world, never in designer vocabulary.** Not "serif or sans?", not "warm or cool palette?" The user picks scenes and outcomes; the router translates them to genre, tokens and motion.
|
|
46
|
-
- **Each round prunes the next.** Round 1 narrows to 2 or 3 candidate directions; round 2 picks between them; round 3 is never questions.
|
|
47
|
-
|
|
48
|
-
**Round 1: the project (always, 3 questions):**
|
|
49
|
-
|
|
50
|
-
1. *"What is this site for?"* Options as niche clusters, not industries: **sell a product** (skincare, food and drink, fashion: Product launch genre) · **promote a service or practice** (legal, medical, consulting, trades, real estate: Studio genre) · **show work** (portfolio, agency, photography, architecture: Portfolio genre) · **launch something** (product, campaign, game, record: Product launch or Campaign genre) · **a place people visit** (restaurant, hotel, gym, clinic: Campaign genre with the room as the hero) · **a cause or institution** (nonprofit, university: Campaign genre).
|
|
51
|
-
2. *"When the site works, what does a visitor do?"* **book** · **buy** · **hire or contact** · **sign up or wishlist** · **donate or apply**. This pins the Invite beat and the conversion posture.
|
|
52
|
-
3. *"What already exists?"* (multi-select) **logo or colors** · **written copy** · **photos or video** · **an existing site** ([triage.md](triage.md) rules) · **nothing yet**. This drives MATERIALS and the mode.
|
|
53
|
-
|
|
54
|
-
**Round 2: the feel (only what round 1 left open):**
|
|
55
|
-
|
|
56
|
-
4. *"Pick the room."* Three contrasting one-line scenes generated from the surviving candidate presets, plus one deliberate outlier. The scene *is* the preset choice; the user never sees the preset name. Example set for "show work":
|
|
57
|
-
- *"A white gallery: huge quiet type, your work does the talking."* (Luxury Minimal)
|
|
58
|
-
- *"Warm paper and ink: editorial, opinionated, a magazine about you."* (Warm Editorial)
|
|
59
|
-
- *"A dark cinema: slow, heavy, one image at a time."* (Dark Cinematic)
|
|
60
|
-
- *"Loud poster walls: thick type, hard edges."* (the outlier: the Brutalist genre appendix, taken only when the user picks this scene)
|
|
61
|
-
|
|
62
|
-
For any other cluster, draw the scenes from the four presets: Soft Modern (*"a calm, exact studio: cool stone, one green note"*), Warm Editorial, Dark Cinematic, Luxury Minimal.
|
|
63
|
-
5. *"One long page, or several?"* Plus any must-have sections (pricing, menu, booking, careers). Demanded sections are reconciled against the arc in §2. "Several" switches on route transitions.
|
|
64
|
-
6. Optional, free text: *"Name one site or brand whose vibe you envy."* Used to bias type and palette *inside* the chosen preset, never to clone.
|
|
65
|
-
|
|
66
|
-
**Round 3: no questions.** Write STRUQ.md with its ASSUMPTIONS filled and go to the [direction.md](direction.md) §5 gate. The gate's one message is the only confirmation; edits are applied to the file, not re-interviewed.
|
|
67
|
-
|
|
68
|
-
**Cold start** ("I don't know what I want to make"): ask only question 1 reframed, *"What do you do, or what do you have?"* (a product, a practice, work to show, an event, a cause), then continue from question 2. The user does not need to know what a website is for; they know what they have.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## 2. Route: decisions in dependency order
|
|
73
|
-
|
|
74
|
-
Steps marked **SYSTEM** are decided in [direction.md](direction.md) §4; they are listed here only so the order is visible.
|
|
75
|
-
|
|
76
|
-
1. **Niche read.** Pick the genre row in [page-architecture.md](page-architecture.md) §2 and §2b (the niche-to-genre mapping), then choose the nearest preset in [vibe-presets.md](vibe-presets.md): **Soft Modern** (the default), **Warm Editorial**, **Dark Cinematic** or **Luxury Minimal**. **Brutalist** is a genre appendix, taken only when the brief declares the genre. Fill its seven axes with the TENSION written first. Primary preset = the buyer's; borrow at most TYPE *or* PALETTE from a second preset, never both and never anything else (not imagery, not motion). Both presets' "never signal" lines join the audit.
|
|
77
|
-
2. **Palette and tokens.** SYSTEM. The preset's palette unless real brand colors exist, in which case they pour into the preset's token structure.
|
|
78
|
-
3. **Type.** SYSTEM. Exactly two faces, picked in [typography.md](typography.md) Decide here: the Signature pairing that matches the brand's temperament first (it is usually the preset's own pairing), Extended only when no Signature temperament fits. Record the pairing id (S1 to S7, E1 to E6), its role weights, the one signature move and the hero entrance.
|
|
79
|
-
4. **Arc.** Start from the genre's inventory, then reconcile it with the client's demanded sections via [page-architecture.md](page-architecture.md): beats in the §1 order (Hook, Orient, Prove, Deepen, Invite); every section gets a beat or gets cut; 5 to 8 sections; density alternates; exactly one proof form; at least two grid-breaks marked.
|
|
80
|
-
5. **Reveals.** Name the reveals the build uses ([scroll-reveals.md](scroll-reveals.md)): which headlines get SplitText line masks, which blocks get the transform and autoAlpha stagger, what images do on entry. If the plan has drawn or morphing line art, note the [svg-motion.md](svg-motion.md) moment here. Lenis lerp is SYSTEM.
|
|
81
|
-
6. **Showpieces.** Budget 1 to 2 ids tagged `showpiece` in [showcase.md](showcase.md); ids tagged `system` (route curtain, menu, hero line rise and the like) do not count. Fewer is stronger, and for some genres 0 is correct (utility and brutalist pages, tight deadlines). Place each at an act break per [page-architecture.md](page-architecture.md) §3: a breather before it and after it (a showpiece is its own density class, so "breather, showpiece, breather" is the pattern, not two breathers in a row), never directly under the hero, never in the Invite beat, never back to back. Name the technique section ([pinning-scrub.md](pinning-scrub.md) §1 or §2, [depth-parallax.md](depth-parallax.md) §3) so the mobile fallback is planned, not patched. No real imagery? Prefer a type-led showpiece ([showcase.md](showcase.md) Choosing).
|
|
82
|
-
7. **Route transitions.** Multi-page: plan a full-screen transition and its variant ([route-transitions.md](route-transitions.md), with [page-entrances.md](page-entrances.md) for what happens after the curtain). One-pager: none, but reserve the hook if routes are coming. Loader yes or no from the genre's register (loaders suit narrative, portfolio and launch only, see [loaders.md](loaders.md)).
|
|
83
|
-
8. **WebGL gate.** Only if a section genuinely calls for it AND the deadline allows. If yes, plan the poster first and name the moment (what the user sees, where in the arc, the fallback, one gate for canvas and pin) per [webgl-r3f.md](webgl-r3f.md) §1 and [webgl-scenes.md](webgl-scenes.md) §5. **No model file?** Walk the ladder in [webgl-scenes.md](webgl-scenes.md) §4 ("No model file yet"): ask for the GLB in the gate, a declared primitive stand-in built to be replaced, or a downgrade to photography or a type-led showpiece; label the rung under ASSUMPTIONS. A whole page as one directed scene is [../cinematic/README.md](../cinematic/README.md) instead.
|
|
84
|
-
9. **Motion, icons, imagery grade.** SYSTEM. Motion is the [SKILL.md](../SKILL.md) baseline unless a deviation is recorded with its reason.
|
|
85
|
-
10. **Copy budget.** Per section, from [page-architecture.md](page-architecture.md) §5: hero h1 at most 8 words, eyebrows 1 to 3 words, section headlines at most 10, body 2 to 4 sentences at 44 to 52ch, CTAs 2 to 4 verb-first words, exactly one manifesto line on the whole page.
|
|
86
|
-
11. **Shot list.** Per section: subject, grade, ratio and source (real, generate, treated stock), mapped onto the arc. The grade and ratio set come from SYSTEM; missing imagery is planned as placeholders per [imagery.md](imagery.md) §4b.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## 3. Field map: where each decision lands in STRUQ.md
|
|
91
|
-
|
|
92
|
-
The template lives in [direction.md](direction.md). Every field is resolved or moved to ASSUMPTIONS; nothing ships as a placeholder.
|
|
93
|
-
|
|
94
|
-
| Decision | STRUQ.md field | Decided in |
|
|
95
|
-
|---|---|---|
|
|
96
|
-
| Stack, mode | `Stack:`, `Mode:` (header) | §1 questions 4 and 5 |
|
|
97
|
-
| What exists, what is missing | MATERIALS | §1 questions 6 and 7, or the wizard round 1 |
|
|
98
|
-
| Tension | CONCEPT `Tension:` | §1 question 2 |
|
|
99
|
-
| Buyer, genre, preset, borrowed field | SYSTEM `Niche:` | §2 step 1 |
|
|
100
|
-
| Type, palette, radius, icons, imagery, scroll, motion | SYSTEM | [direction.md](direction.md) §4 |
|
|
101
|
-
| Pages and slugs | PLAN `Pages:` | §1 question 8, [seo.md](seo.md) §1 |
|
|
102
|
-
| Arc rows, proof form, grid-breaks | PLAN `Arc:` | §2 step 4 |
|
|
103
|
-
| Reveals | PLAN `Reveals:` | §2 step 5 |
|
|
104
|
-
| Showpieces and system recipes | PLAN `Showpieces:`, `System recipes:` | §2 step 6 |
|
|
105
|
-
| Transitions, loader | PLAN `Transitions:` | §2 step 7 |
|
|
106
|
-
| WebGL | PLAN `WebGL:` | §2 step 8 |
|
|
107
|
-
| Copy budget | PLAN `Copy budget:` | §2 step 10 |
|
|
108
|
-
| Shot list | PLAN `Assets to produce:` | §2 step 11 |
|
|
109
|
-
| What a redesign keeps | PRESERVED | [triage.md](triage.md) |
|
|
110
|
-
|
|
111
|
-
Before the gate: the arc passes the [page-architecture.md](page-architecture.md) checklist, and every showpiece and system id exists in [showcase.md](showcase.md).
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## 4. Load manifest: which reference files, at which step
|
|
116
|
-
|
|
117
|
-
Keyed to the workflow step names in [SKILL.md](../SKILL.md). The baseline block and leverage ranking are already in context. Load the rest when the step starts, never earlier:
|
|
118
|
-
|
|
119
|
-
| Workflow step | Load | Only if |
|
|
120
|
-
|---|---|---|
|
|
121
|
-
| 0 Detect versions | [setup.md](setup.md) §0 (with Known API shifts) | always |
|
|
122
|
-
| 1 Direction | [direction.md](direction.md), this file, [page-architecture.md](page-architecture.md), [vibe-presets.md](vibe-presets.md) (token contract and the chosen preset), [showcase.md](showcase.md); the Decide-here sections of [typography.md](typography.md) and [iconography.md](iconography.md); [brand.md](brand.md); [stack-targets.md](stack-targets.md); [triage.md](triage.md); the preset's hero entrance in [page-entrances.md](page-entrances.md); [webgl-r3f.md](webgl-r3f.md) §1 and [webgl-scenes.md](webgl-scenes.md) §4 ("No model file yet") and §5 | brand.md when there is no brand or no logo · stack-targets.md on Vite · triage.md on a redesign · page-entrances.md when the entrance is `match-cut-hero` or `clip-line-hero`, on any site · the WebGL sections when the brief asks for 3D (the poster, gate and fallback are planning decisions) |
|
|
123
|
-
| 2 Brand files and tokens | [brand.md](brand.md) §3 to §5, [typography.md](typography.md) §2 to §4 plus only the §5 recipes STRUQ.md names, [scaling.md](scaling.md), [imagery.md](imagery.md), [iconography.md](iconography.md) (full) | brand.md only when the brand was generated or the logo is provisional |
|
|
124
|
-
| 3 Structure and SEO | [seo.md](seo.md) | always |
|
|
125
|
-
| 4 Wire providers | [setup.md](setup.md); [route-transitions.md](route-transitions.md) and [page-entrances.md](page-entrances.md); [loaders.md](loaders.md) | route transitions on multi-page sites · loader only if PLAN says so |
|
|
126
|
-
| 5 Static layout | [nav-header.md](nav-header.md), [composition.md](composition.md), [mobile-composition.md](mobile-composition.md) | always |
|
|
127
|
-
| 6 Motion in layers | [pitfalls.md](pitfalls.md) (before the first tween), [scroll-reveals.md](scroll-reveals.md), [gsap-api.md](gsap-api.md), [feel-layer.md](feel-layer.md); [pinning-scrub.md](pinning-scrub.md) or [depth-parallax.md](depth-parallax.md); [svg-motion.md](svg-motion.md); [micro-interactions.md](micro-interactions.md); [transitions.md](transitions.md); [webgl-r3f.md](webgl-r3f.md), [webgl-scenes.md](webgl-scenes.md) | showpiece files only for the ids in PLAN · svg-motion.md only with SVG motion · transitions.md for section curtains and match cuts · WebGL files only if PLAN has a WebGL moment |
|
|
128
|
-
| 7 Optimize and sweep | [performance.md](performance.md), [anti-patterns.md](anti-patterns.md), the [seo.md](seo.md) checklist | always |
|
|
129
|
-
| 8 Verify | [anti-patterns.md](anti-patterns.md) tell scan (plus the TENSION's "never signal" line), [feel-layer.md](feel-layer.md) §5, [mobile-composition.md](mobile-composition.md) §4, [pitfalls.md](pitfalls.md) (descender test, pin checks), [performance.md](performance.md) §8 (port check, first-load JS, LCP probe, frame trace) | always; run against screenshots and the production server |
|
|
130
|
-
|
|
131
|
-
Two rules: the "never signal" line joins the verify sweep alongside the generic tells, and both must pass; and a file already loaded is not reloaded. The manifest is a ceiling, not a ritual.
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## 5. Worked example
|
|
136
|
-
|
|
137
|
-
**Brief:** *"We're a specialty coffee roaster in Rotterdam. We roast weekly, run a small tasting bar, and want a site that sells beans online eventually. For now it should make us look like the best roaster in the city. We have a logo (black wordmark) and good photos of the roastery. Next.js. No copy written yet."*
|
|
138
|
-
|
|
139
|
-
The brief is usable (buyer and fear are answerable), so nothing blocks: §1 runs silently and the gate message asks for the roastery photo set. The STRUQ.md excerpt it produces:
|
|
140
|
-
|
|
141
|
-
```markdown
|
|
142
|
-
# STRUQ.md: Roastery (Rotterdam)
|
|
143
|
-
|
|
144
|
-
Mode: build
|
|
145
|
-
Stack: Next.js App Router · React · Tailwind · GSAP · Lenis, each with the installed major that package.json reports at step 0 (setup.md §0)
|
|
146
|
-
|
|
147
|
-
## MATERIALS
|
|
148
|
-
Have: black wordmark (SVG requested), roastery photo set (bar room, hands and pour, roaster machine)
|
|
149
|
-
Missing: all copy (written from the first draft, no lorem); roast bag and bean shots
|
|
150
|
-
(planned as graded placeholders, imagery.md §4b); no numbers or quotes (no proof faked)
|
|
151
|
-
|
|
152
|
-
## CONCEPT
|
|
153
|
-
Idea: the site reads like a roast log: one week, one batch, written down by people who taste everything
|
|
154
|
-
Tension: convince "these people are obsessive about roasting and it tastes like it"
|
|
155
|
-
/ never signal "another kraft-paper artisan brand"
|
|
156
|
-
Motif: the roast date, set in the display face, stamped once per roast and once in the footer
|
|
157
|
-
|
|
158
|
-
## SYSTEM
|
|
159
|
-
Niche: food and drink product brand, buyer = the person buying beans · genre Product launch
|
|
160
|
-
· preset Warm Editorial · borrowed: none (the tasting bar is a room, but imagery is not a
|
|
161
|
-
borrowable field; Warm Editorial already carries the warmth)
|
|
162
|
-
Display: Fraunces 300, SOFT 50 (Google Fonts, OFL) · pairing S2
|
|
163
|
-
Text: Urbanist 400 body, 500 eyebrow and UI (Google Fonts, OFL)
|
|
164
|
-
· roles per typography.md §3.1 S2: display 300, h2 300, h3 400, eyebrow 500, body 400
|
|
165
|
-
Signature move: soft-scrub (the roast date headline softens as the reader leaves the hero)
|
|
166
|
-
Hero entrance: Warm Editorial's soft-scrub plus line rise
|
|
167
|
-
Palette: light · --surface-0 #FAF6F0 --surface-1 #F3EDE2 --surface-2 #EAE1D2 --surface-3 #DFD3BF
|
|
168
|
-
--ink #1E1A16 --ink-muted #5E554C --ink-faint #A59C8F (non-text only) --accent #7A2230
|
|
169
|
-
--line rgb(30 26 22 / 0.14)
|
|
170
|
-
Radius: 2px buttons and frames
|
|
171
|
-
Icons: Struq Icons, @struq/icons, strokeWidth 1.5, accent = --color-accent (iconography.md §2 default)
|
|
172
|
-
Imagery: warm film grade, ratios 21/9, 4/5, 1/1, grain 0.04
|
|
173
|
-
Scroll: Lenis lerp 0.1 on gsap.ticker
|
|
174
|
-
Motion: baseline (micro 0.2 to 0.3s, hero 1.0 to 1.6s, exits power2.in, swaps
|
|
175
|
-
quart-in-out, curtains quint-in-out); deviations: reveals power3.out 0.7 to 0.9s
|
|
176
|
-
in place of expo.out (Warm Editorial's editorial-calm, its declared deviation)
|
|
177
|
-
|
|
178
|
-
## PLAN
|
|
179
|
-
Pages: / (one-pager now); /shop reserved in the nav
|
|
180
|
-
Arc (home):
|
|
181
|
-
1 HERO [Hook] claim + bean macro bleeding off the right edge (grid-break 1), 14rem
|
|
182
|
-
2 WHY [Orient] the manifesto line alone in 20rem of air (breather)
|
|
183
|
-
3 ROASTS [Prove] the lineup: pin-horizontal-gallery, 4 roast frames, dense 6rem
|
|
184
|
-
4 THE BAR [Deepen] one full-bleed room image overlapping into section 5 (grid-break 2),
|
|
185
|
-
hours under it, 16rem (breather)
|
|
186
|
-
5 PROCESS [Deepen] roast-week rhythm, 3 hairline editorial rows, dense 6rem
|
|
187
|
-
6 VISIT [Invite] address, hours, newsletter (shop CTA when /shop ships), footer
|
|
188
|
-
per page-architecture §4, 14rem
|
|
189
|
-
Proof form: the roasts themselves. Pacing: vast, breather, dense, breather, dense, vast.
|
|
190
|
-
Reveals: hero h1 line-rise-hero then soft-scrub (the preset's named entrance) · rows and roast frames transform + autoAlpha stagger 0.08
|
|
191
|
-
· images curtain-reveal · svg none
|
|
192
|
-
Showpieces: pin-horizontal-gallery at arc row 3 (breather 2 before it, breather 4 after it),
|
|
193
|
-
mobile: native horizontal swipe with snap (pinning-scrub.md §2 and §4). Total 1.
|
|
194
|
-
System recipes: line-rise-hero, soft-scrub, curtain-reveal, fullscreen-menu
|
|
195
|
-
Transitions: none now (one-pager); route-curtain added when /shop ships · loader none (shop-bound)
|
|
196
|
-
WebGL: none
|
|
197
|
-
Copy budget: h1 at most 8 words ("Roasted Tuesday. Gone by Friday."), manifesto = row 2's
|
|
198
|
-
line only, roast frames = name + 2 tasting words + origin, CTAs "Visit the bar", "Get the beans"
|
|
199
|
-
Assets to produce (8 images):
|
|
200
|
-
1 hero bean macro, 21/9, generate on-brief
|
|
201
|
-
4 roast bags, 1/1, shoot (placeholders until then)
|
|
202
|
-
1 bar room, 21/9, existing set, regraded
|
|
203
|
-
1 hands and pour, 4/5, existing set, regraded
|
|
204
|
-
1 roaster machine, 4/5, existing set, regraded
|
|
205
|
-
all through one warm film grading pass (imagery.md §1)
|
|
206
|
-
|
|
207
|
-
## ASSUMPTIONS
|
|
208
|
-
- "best roaster in the city" read as craft-serious: one accent (the preset's oxblood, not the
|
|
209
|
-
cream-plus-terracotta reflex), no second accent
|
|
210
|
-
- wordmark stays black on cream (ink #1E1A16)
|
|
211
|
-
- the gallery is never staged on placeholders: until the 4 roast shots exist, row 3 is a
|
|
212
|
-
static 2 by 2 grid of placeholder frames (no pin), and the pin switches on when they land
|
|
213
|
-
- shop deferred: row 6 collects newsletter sign-ups instead of a cart
|
|
214
|
-
- one-pager now, /shop reserved in the nav
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
The gate message then reads: name, idea line, Fraunces and Urbanist (S2), soft-scrub as the signature move, the eight hex values, Struq Icons, the arc in one line, `pin-horizontal-gallery`, the five assumptions, and: "Veto anything? If you have the roast bag photos or real numbers (kilos a week, years roasting), send them now."
|
|
218
|
-
|
|
219
|
-
Note what the router did that a template could not: it kept the display face light in a consumer genre that pulls toward weight; it refused to borrow imagery from a hospitality direction because only TYPE or PALETTE may be borrowed; it spent exactly one showpiece and sat it between two breathers instead of under the hero; it deferred route transitions and the shop without losing their arc slots; and it turned "no copy written" into a copy-budget line instead of lorem. That judgment is the router's job; the reference files supply the raw material.
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
# Composition: Overlap, Bleed, and Z-Choreography
|
|
2
|
-
|
|
3
|
-
No JavaScript in most of this file. Composition is the cheapest "custom build" signal there is: templates stack self-contained sections; art direction makes elements trespass across boundaries. If a build looks generic with motion already in place, the problem is almost always here.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Section bleed (negative-margin overlap)
|
|
8
|
-
|
|
9
|
-
**Term:** Section bleed / overlap seam.
|
|
10
|
-
**Mechanic:** An element from section A extends into section B's space via negative margin (or translate), so the boundary between sections is interrupted. The two sections must sit on different surface tones for the overlap to register.
|
|
11
|
-
|
|
12
|
-
**When:** Every page should have 1 to 3 seams. Best at hero→content and content→footer joints.
|
|
13
|
-
**Overkill when:** every seam overlaps, because then nothing reads as intentional.
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
// Hero media punching down into the next section
|
|
17
|
-
export function HeroToIntro() {
|
|
18
|
-
return (
|
|
19
|
-
<>
|
|
20
|
-
<section className="bg-surface-1 px-6 pb-0 pt-40">
|
|
21
|
-
<h1 className="font-display text-display max-w-[12ch]">
|
|
22
|
-
Studio for durable brands
|
|
23
|
-
</h1>
|
|
24
|
-
{/* The trespasser: pulled down across the boundary */}
|
|
25
|
-
<div className="relative z-10 mx-auto -mb-32 mt-24 max-w-5xl overflow-hidden">
|
|
26
|
-
<img src="/hero.jpg" alt="" className="aspect-[16/9] w-full object-cover" />
|
|
27
|
-
</div>
|
|
28
|
-
</section>
|
|
29
|
-
|
|
30
|
-
{/* Receiving section pays for the overlap with top padding */}
|
|
31
|
-
<section className="bg-surface-0 px-6 pb-28 pt-56">
|
|
32
|
-
<p className="text-lead max-w-[44ch] text-ink-muted">…</p>
|
|
33
|
-
</section>
|
|
34
|
-
</>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
The arithmetic: trespasser gets `-mb-32` + `relative z-10`; receiver gets `pt-56` (≥ the overlap + breathing room). Forgetting the receiver's compensating padding is why naive attempts collide with content.
|
|
40
|
-
|
|
41
|
-
**Cheap tell:** sections are watertight boxes: each starts where the last ended, every child inside its container. **Instead:** At least one element per major seam belongs to both sections.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## 2. Full-bleed breakouts from a constrained container
|
|
46
|
-
|
|
47
|
-
**Term:** Breakout / edge bleed.
|
|
48
|
-
**Mechanic:** Page content sits in a max-width column, but specific elements escape to the viewport edge. The contrast between contained text and escaping media is the editorial rhythm.
|
|
49
|
-
|
|
50
|
-
```css
|
|
51
|
-
/* globals.css: a reusable content grid, one class, three lanes */
|
|
52
|
-
.content-grid {
|
|
53
|
-
display: grid;
|
|
54
|
-
grid-template-columns:
|
|
55
|
-
[full-start] minmax(1.5rem, 1fr)
|
|
56
|
-
[wide-start] minmax(0, 6rem)
|
|
57
|
-
[content-start] min(100% - 3rem, 68rem) [content-end]
|
|
58
|
-
minmax(0, 6rem) [wide-end]
|
|
59
|
-
minmax(1.5rem, 1fr) [full-end];
|
|
60
|
-
}
|
|
61
|
-
.content-grid > * { grid-column: content; }
|
|
62
|
-
.content-grid > .bleed-wide { grid-column: wide; }
|
|
63
|
-
.content-grid > .bleed-full { grid-column: full; }
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
<section className="content-grid gap-y-10 py-32">
|
|
68
|
-
<p className="eyebrow text-eyebrow text-ink-muted">Selected Work</p>
|
|
69
|
-
<h2 className="font-display text-h2">Selected projects</h2>
|
|
70
|
-
<p className="text-body max-w-[52ch] text-ink-muted">…</p>
|
|
71
|
-
<img className="bleed-full aspect-[21/9] w-full object-cover" src="/wide.jpg" alt="" />
|
|
72
|
-
<blockquote className="bleed-wide type-h3 text-h3 max-w-[40ch]">…</blockquote>
|
|
73
|
-
</section>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
This named-lines grid replaces the `max-w-6xl mx-auto` wrapper-div habit and makes breakouts a one-class decision. Use `wide` for pull-quotes/secondary media, `full` for one hero-grade image per few screens.
|
|
77
|
-
|
|
78
|
-
**Cheap tell:** everything lives inside the same `max-w-7xl mx-auto px-4`, so the page is a centered column of boxes. **Instead:** Text contained, media escaping: the page has margins *and* moments that ignore them.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## 3. Type/media overlap with z-index choreography
|
|
83
|
-
|
|
84
|
-
**Term:** Layered lockup.
|
|
85
|
-
**Mechanic:** Display type and imagery occupy the same grid cells and are layered deliberately: type *behind* the image (emerging from it) or *across* it (stacked above). Requires committing to a z-order story: background texture (z-0) → media (z-10) → display type (z-20) → UI/nav (z-50).
|
|
86
|
-
|
|
87
|
-
**When:** Heroes, project headers, section openers. One strong lockup beats five timid ones.
|
|
88
|
-
|
|
89
|
-
```tsx
|
|
90
|
-
export function ProjectHeader() {
|
|
91
|
-
return (
|
|
92
|
-
<header className="relative grid grid-cols-12 items-end gap-4 px-6 pt-44 pb-24">
|
|
93
|
-
{/* Image occupies cols 5-12 */}
|
|
94
|
-
<div className="col-span-8 col-start-5 row-start-1 overflow-hidden">
|
|
95
|
-
<img src="/project.jpg" alt="" className="aspect-[4/3] w-full object-cover" />
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
{/* Headline occupies cols 1-9 in the SAME row: overlap is the design */}
|
|
99
|
-
<h1 className="font-display text-display pointer-events-none relative z-10 col-span-9 col-start-1 row-start-1
|
|
100
|
-
-mb-6 mix-blend-difference text-white">
|
|
101
|
-
Atelier Nord
|
|
102
|
-
</h1>
|
|
103
|
-
|
|
104
|
-
<p className="text-small col-span-3 col-start-1 mt-10 text-ink-muted">
|
|
105
|
-
Identity & digital flagship for a Scandinavian furniture house.
|
|
106
|
-
</p>
|
|
107
|
-
</header>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Load-bearing details:
|
|
113
|
-
- **Same `row-start-1`** on both children: overlap via grid stacking, not absolute positioning, so it stays in document flow and responsive.
|
|
114
|
-
- `mix-blend-difference` (or `exclusion`) keeps type legible across image edges without a scrim. Costs a compositing layer, which is fine for one headline but don't blanket it.
|
|
115
|
-
- `pointer-events-none` on the overlapping type so the image stays interactive.
|
|
116
|
-
- Type slightly *exceeding* the image (starting left of it, or `-mb-6` below it) is what makes it a lockup instead of a caption accident.
|
|
117
|
-
|
|
118
|
-
**Cheap tell:** image left, text right, 50/50, `gap-8`, both vertically centered: the eternal "alternating zigzag" section. **Instead:** Type and image share cells and contest the space.
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## 4. Off-canvas type (deliberate crop)
|
|
123
|
-
|
|
124
|
-
**Term:** Cropped display type.
|
|
125
|
-
**Mechanic:** Display type intentionally runs off the viewport edge. The crop signals the word is bigger than the page, i.e. confidence. Usually paired with a scrub-tied drift ([depth-parallax.md](depth-parallax.md) §4).
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
<div className="relative overflow-hidden py-24">
|
|
129
|
-
<span aria-hidden className="font-display text-ghost block whitespace-nowrap font-light
|
|
130
|
-
text-ink/[0.05] -translate-x-[6%] select-none">
|
|
131
|
-
PORTFOLIO / PORTFOLIO /
|
|
132
|
-
</span>
|
|
133
|
-
{/* real content layered above */}
|
|
134
|
-
<div className="relative z-10 -mt-[9vw] px-6">…</div>
|
|
135
|
-
</div>
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Always `aria-hidden` + a real heading elsewhere; this is texture, not content. `text-ghost` is the role token for texture type ([typography.md](typography.md) §2.4); the `text-ink/[0.05]` muted solid fill (0.04 to 0.06) doubles as section wayfinding.
|
|
139
|
-
|
|
140
|
-
**Cheap tell:** all type fully visible, all centered. **Instead:** One enormous word cropped by the viewport, content overlapping it.
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## 5. Z-index choreography during scroll
|
|
145
|
-
|
|
146
|
-
**Term:** Stacking-order storytelling.
|
|
147
|
-
**Mechanic:** Scroll changes which layer is "on top": a section slides over the previous one (the prior section appears pinned beneath), or the footer is revealed from *under* the page. These need ~5 lines each and read as wildly custom.
|
|
148
|
-
|
|
149
|
-
```tsx
|
|
150
|
-
// A) Footer reveal: page slides up to UNCOVER a fixed-height footer beneath it.
|
|
151
|
-
export function FooterReveal() {
|
|
152
|
-
return (
|
|
153
|
-
<>
|
|
154
|
-
{/* main page content gets a bottom z-layer guarantee */}
|
|
155
|
-
<main className="relative z-10 bg-surface-0 mb-[60vh]">…</main>
|
|
156
|
-
|
|
157
|
-
<footer className="fixed bottom-0 left-0 z-0 h-[60vh] w-full bg-surface-1">
|
|
158
|
-
<div className="flex h-full flex-col justify-between p-10">
|
|
159
|
-
<p className="font-display text-display">Let’s talk.</p>
|
|
160
|
-
<div className="text-small flex justify-between text-ink-muted">…</div>
|
|
161
|
-
</div>
|
|
162
|
-
</footer>
|
|
163
|
-
</>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Mechanic: `main` has `mb-[60vh]` (a hole at the end of the document) and a higher z-index; the `fixed` footer sits in that hole and is uncovered as the page scrolls past. Zero JS. The sibling pattern, sections covering *each other*, is sticky-stacking, covered with its scrub polish in [depth-parallax.md](depth-parallax.md).
|
|
169
|
-
|
|
170
|
-
Z-order constitution (declare once, never improvise):
|
|
171
|
-
|
|
172
|
-
| Layer | z | |
|
|
173
|
-
|---|---|---|
|
|
174
|
-
| Uncovered footer, ghost type | 0 | beneath the page |
|
|
175
|
-
| Page sections | 10 | default |
|
|
176
|
-
| Overlapping/trespassing elements | 20 | seams & lockups |
|
|
177
|
-
| Sticky UI (nav) | 50 | |
|
|
178
|
-
| Page-transition curtain, cursor | 100 | [transitions.md](transitions.md) |
|
|
179
|
-
|
|
180
|
-
**Cheap tell:** ad-hoc `z-50`/`z-[999]` sprinkled until things look right. **Instead:** A declared ladder; every overlap is a decision.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Composition checklist (run on every page)
|
|
185
|
-
|
|
186
|
-
- [ ] ≥2 grid breaks (bleed, overlap, crop, sticky), and they're *the* moments, not noise
|
|
187
|
-
- [ ] No two consecutive sections share the same layout skeleton
|
|
188
|
-
- [ ] At least one seam where an element belongs to two sections
|
|
189
|
-
- [ ] One full-bleed media moment per ~3 viewports
|
|
190
|
-
- [ ] Z-ladder declared; `mix-blend` and `pointer-events-none` on overlapping type
|
|
191
|
-
- [ ] Section surfaces alternate tonally (surface-0 / surface-1) so seams and overlaps register
|