dreative 0.1.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 +35 -0
- package/dist/cli/index.js +169 -0
- package/dist/server/agentQueue.js +70 -0
- package/dist/server/ai.js +177 -0
- package/dist/server/diff.js +97 -0
- package/dist/server/index.js +328 -0
- package/dist/server/jobs.js +31 -0
- package/dist/server/preview.js +110 -0
- package/dist/server/store.js +71 -0
- package/dist/shared/design.js +163 -0
- package/dist/shared/types.js +1 -0
- package/dist/ui/assets/index--vztc_MR.js +71 -0
- package/dist/ui/assets/index-y0gVjC7u.css +1 -0
- package/dist/ui/index.html +13 -0
- package/package.json +44 -0
- package/skill/dreative/DESIGN.md +358 -0
- package/skill/dreative/SKILL.md +108 -0
- package/skill/dreative/skills/3d.md +131 -0
- package/skill/dreative/skills/interaction.md +126 -0
- package/skill/dreative/skills/motion.md +125 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Dreative Specialist Skill — 3D, WebGL & Shaders
|
|
2
|
+
|
|
3
|
+
Load this file when `plan.skills` includes `3d`, or the brief/prompt mentions 3D,
|
|
4
|
+
WebGL, three.js, shaders, particles, globes, or point clouds. DESIGN.md still
|
|
5
|
+
governs everything around the canvas (layout, color, type, motion budgets).
|
|
6
|
+
|
|
7
|
+
## 0. Does 3D earn its place?
|
|
8
|
+
|
|
9
|
+
3D is the most expensive pixel on the web — in bytes, battery, and attention.
|
|
10
|
+
It earns its place when the SUBJECT is spatial (a physical product, a device, a
|
|
11
|
+
map/globe, an environment, data with real dimensionality) or when the brand brief
|
|
12
|
+
explicitly buys a signature 3D moment. It does not earn its place as ambient
|
|
13
|
+
decoration: a random floating torus/blob behind hero text is the 3D equivalent of
|
|
14
|
+
the gradient blob — banned as a default reach. One 3D moment per page; make it the
|
|
15
|
+
signature element (DESIGN.md §2) or don't ship it.
|
|
16
|
+
|
|
17
|
+
Cheaper substitutes to consider first: CSS 3D transforms (`perspective` +
|
|
18
|
+
`rotateX/Y` on real DOM — free, accessible, great for cards/mockups/tilt), a
|
|
19
|
+
pre-rendered video/image sequence scrubbed on scroll, or a Lottie/Rive vector
|
|
20
|
+
animation. If the object never needs live lighting or user-driven rotation,
|
|
21
|
+
pre-render it.
|
|
22
|
+
|
|
23
|
+
## 1. Stack
|
|
24
|
+
|
|
25
|
+
- **React app → React Three Fiber (`@react-three/fiber`) + `@react-three/drei`.**
|
|
26
|
+
Never hand-roll a three.js imperative setup inside React. Drei gives you 80%:
|
|
27
|
+
`Environment`, `ContactShadows`, `Float`, `ScrollControls`, `Html`, `Text`,
|
|
28
|
+
`useGLTF`, `MeshTransmissionMaterial`, `Center`, `Bounds`.
|
|
29
|
+
- **Non-React → three.js** with a small render-loop module; or **OGL** when the
|
|
30
|
+
scene is one shader plane and three.js is overkill.
|
|
31
|
+
- Post-processing via `@react-three/postprocessing` (Bloom, DoF, Noise) — 1-2
|
|
32
|
+
effects max; the stacked-postFX look reads as demo-scene, not product.
|
|
33
|
+
- Physics only if interaction demands it (`@react-three/rapier`).
|
|
34
|
+
|
|
35
|
+
Lazy-load the entire 3D bundle (`next/dynamic` / `React.lazy`) with an SSR-off
|
|
36
|
+
canvas and a styled poster fallback. Three.js must never be in the critical path.
|
|
37
|
+
|
|
38
|
+
## 2. Scene fundamentals (where amateur scenes fail)
|
|
39
|
+
|
|
40
|
+
- **Lighting is the craft.** Default: `<Environment preset="studio"|"city">` for
|
|
41
|
+
image-based lighting + ONE key directional light for shadows. Ambient light
|
|
42
|
+
alone = flat plastic. Match light temperature to the page palette.
|
|
43
|
+
- **Color management:** renderer defaults in modern three are correct
|
|
44
|
+
(`outputColorSpace: SRGBColorSpace`, `ACESFilmicToneMapping`) — don't disable
|
|
45
|
+
them; DO set `toneMappingExposure` deliberately. Convert hex from the page
|
|
46
|
+
palette with `new THREE.Color(hex)` — scene colors and page colors must read as
|
|
47
|
+
one system.
|
|
48
|
+
- **Ground the object:** `<ContactShadows opacity={0.4} blur={2.5}>` or a real
|
|
49
|
+
shadow plane. Floating unanchored objects read as clipart.
|
|
50
|
+
- Camera: 35-50° FOV for product shots (85°+ distorts); frame with
|
|
51
|
+
`<Bounds fit clip>` or `<Center>`; subtle idle via `<Float>` or damped
|
|
52
|
+
mouse-parallax (`useFrame` + `THREE.MathUtils.damp`), amplitude ≤ 3°.
|
|
53
|
+
- Materials: `meshStandardMaterial`/`meshPhysicalMaterial` with `roughness`/
|
|
54
|
+
`metalness` chosen from the real object; `MeshTransmissionMaterial` for glass
|
|
55
|
+
(expensive — one object max). Untextured default-gray = unfinished.
|
|
56
|
+
|
|
57
|
+
## 3. Models
|
|
58
|
+
|
|
59
|
+
- glTF/GLB only, Draco or Meshopt compressed (`gltf-transform optimize in.glb
|
|
60
|
+
out.glb --compress draco`). Budget: hero model ≤ 2-3 MB, ≤ 100k triangles.
|
|
61
|
+
- `useGLTF.preload(url)`; wrap in `<Suspense>` with a poster (not a spinner).
|
|
62
|
+
- No model available? Build from primitives + good materials/lighting (a laptop
|
|
63
|
+
from rounded boxes with an `Html` screen reads better than a bad download), or
|
|
64
|
+
tell the user what asset is needed — never ship an unrelated placeholder model.
|
|
65
|
+
|
|
66
|
+
## 4. Shaders (the highest-leverage 100 lines)
|
|
67
|
+
|
|
68
|
+
A fullscreen/plane fragment shader is the cheapest way to a signature visual:
|
|
69
|
+
animated gradient meshes, flowfields, displacement on hover, image transitions.
|
|
70
|
+
|
|
71
|
+
- Structure: `shaderMaterial` (drei) with `uTime`, `uMouse`, `uResolution`,
|
|
72
|
+
palette colors as `uColorA/B/C` uniforms fed FROM the page palette.
|
|
73
|
+
- Vocabulary: fbm/simplex noise for organic movement (inline a noise fn; don't
|
|
74
|
+
import a library for it), `smoothstep` for banding-free ramps, domain warping
|
|
75
|
+
(`noise(p + noise(p))`) for the expensive look, `mix()` between palette colors
|
|
76
|
+
only — never rainbow hue-rotation.
|
|
77
|
+
- Displace vertices in the vertex shader for cloth/terrain/blob effects; compute
|
|
78
|
+
normals or fake lighting with the noise derivative.
|
|
79
|
+
- Animate `uTime` at 0.05-0.2 speed; slow reads as premium, fast as screensaver.
|
|
80
|
+
- Image effects: sample two textures + a noise mask for transitions; barrel/flow
|
|
81
|
+
distortion on scroll velocity for editorial galleries.
|
|
82
|
+
|
|
83
|
+
## 5. Particles and points
|
|
84
|
+
|
|
85
|
+
Points/instancing, never meshes-per-particle: `<Points>` + `BufferAttribute`
|
|
86
|
+
positions animated in a shader (`uTime` in the vertex shader), or
|
|
87
|
+
`<Instances>`/`InstancedMesh` for repeated geometry (≤ ~10k instances). Counts:
|
|
88
|
+
2-5k points desktop, halve on mobile. Particle color from the page palette at 1-2
|
|
89
|
+
hues; size attenuation on; additive blending only on dark surfaces.
|
|
90
|
+
|
|
91
|
+
## 6. Scroll- and layout-integration
|
|
92
|
+
|
|
93
|
+
- Canvas placement: either a bounded block IN the layout (preferred — it composes
|
|
94
|
+
with the grid) or a full-bleed fixed background with content scrolling over.
|
|
95
|
+
Full-page persistent canvases with DOM scrolled on top: use drei
|
|
96
|
+
`ScrollControls` + `<Scroll html>` so 3D and DOM share one scroll.
|
|
97
|
+
- Scroll-linked 3D: map scroll progress (Motion `useScroll` / ScrollTrigger /
|
|
98
|
+
drei `useScroll`) to camera position along a path or object rotation — damped
|
|
99
|
+
(`damp3`, `dampE`), never raw-set per frame.
|
|
100
|
+
- DOM/3D registration (labels pinned to model parts): drei `<Html transform
|
|
101
|
+
occlude>`; keep text as real DOM for accessibility, never texture-baked type.
|
|
102
|
+
- The block's `data-dreative-id` goes on the wrapper div around the Canvas.
|
|
103
|
+
|
|
104
|
+
## 7. Performance & fallbacks (ship-blockers)
|
|
105
|
+
|
|
106
|
+
- `<Canvas dpr={[1, 2]}>` (cap device pixel ratio), `frameloop="demand"` +
|
|
107
|
+
`invalidate()` for static-until-interacted scenes; pause the loop when the
|
|
108
|
+
canvas leaves the viewport (IntersectionObserver) and on `document.hidden`.
|
|
109
|
+
- One `useFrame` per concern; no allocations inside it (reuse vectors created
|
|
110
|
+
outside). Dispose on unmount (R3F handles it; imperative three must
|
|
111
|
+
`dispose()` geometries/materials/textures).
|
|
112
|
+
- Mobile: halve particle counts, drop post-processing, consider a poster image
|
|
113
|
+
below 768px — a janky 3D hero is worse than none.
|
|
114
|
+
- `prefers-reduced-motion`: stop idle rotation/particle drift; render one static
|
|
115
|
+
well-lit frame (`frameloop="demand"` makes this free).
|
|
116
|
+
- WebGL unavailable/context-lost → the poster fallback, never a blank block.
|
|
117
|
+
- Keyboard/screen-reader: canvas gets `role="img"` + `aria-label`; any 3D-driven
|
|
118
|
+
interaction has a DOM equivalent.
|
|
119
|
+
|
|
120
|
+
## 8. Recipes
|
|
121
|
+
|
|
122
|
+
- **Product hero**: GLB + `Environment` + key light + `ContactShadows`, `Float`
|
|
123
|
+
idle, damped mouse parallax, scroll scrubs a 90° orbit. Poster until loaded.
|
|
124
|
+
- **Gradient-mesh hero background**: one plane, fbm + domain-warp fragment shader
|
|
125
|
+
mixing 3 palette colors, `uTime` 0.08, `uMouse` warps locally. ~120 lines, no
|
|
126
|
+
model, no lighting — the best effort/impact ratio in this file.
|
|
127
|
+
- **Point-cloud globe**: sphere-distributed points, shader-animated shimmer,
|
|
128
|
+
arcs as `TubeGeometry`/fat lines between coords, slow idle rotation, drag to
|
|
129
|
+
spin (damped).
|
|
130
|
+
- **Scroll story**: `ScrollControls pages={4}` + camera on a `CatmullRomCurve3`
|
|
131
|
+
path, DOM copy in `<Scroll html>`, one damped lookAt target per section.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Dreative Specialist Skill — Micro-interactions & Effect Craft
|
|
2
|
+
|
|
3
|
+
Load this file when `plan.skills` includes `interaction`, or the brief/prompt asks
|
|
4
|
+
for hover effects, cursor work, tilt, glow, "make it feel alive/tactile/premium".
|
|
5
|
+
It EXTENDS DESIGN.md §6/§9/§10 — every ban there still stands (no hover-scale on
|
|
6
|
+
`<img>`, custom cursors only on explicit request, one marquee max). This skill is
|
|
7
|
+
about the last 5% of feel: small, physical, consistent responses to input.
|
|
8
|
+
|
|
9
|
+
## 0. The feel system
|
|
10
|
+
|
|
11
|
+
Micro-interactions are a SYSTEM, not garnish. Before adding any, fix three global
|
|
12
|
+
constants and apply them everywhere: one hover response vocabulary, one press
|
|
13
|
+
response, one focus style. A page where every card hovers differently reads as
|
|
14
|
+
assembled, not designed.
|
|
15
|
+
|
|
16
|
+
- **Press** (all clickables): `active:scale-[0.98]` (buttons) or
|
|
17
|
+
`active:scale-[0.99]` (large cards) + 100ms ease-out. This single rule adds more
|
|
18
|
+
perceived quality per byte than anything else in this file.
|
|
19
|
+
- **Hover** (pick ONE per element class): background tint shift, border-color
|
|
20
|
+
lift, shadow deepen (tinted to bg hue), or underline draw. 150-200ms.
|
|
21
|
+
- **Focus**: `:focus-visible` ring, 2px, offset 2px, accent color — identical on
|
|
22
|
+
every interactive element, never removed.
|
|
23
|
+
- Everything targets the 80ms "feels instant" threshold for input feedback.
|
|
24
|
+
|
|
25
|
+
## 1. Pointer-tracking effects (the premium tier)
|
|
26
|
+
|
|
27
|
+
All pointer tracking is springs/damping (Motion `useSpring` on `useMotionValue`,
|
|
28
|
+
or lerp in rAF: `x += (target - x) * 0.12`) — raw 1:1 tracking feels robotic.
|
|
29
|
+
Desktop-only: gate behind `@media (hover: hover) and (pointer: fine)`; touch gets
|
|
30
|
+
the static design, not a broken half-effect. All of these are decoration — cap at
|
|
31
|
+
1-2 distinct pointer effects per page, on the signature element's territory.
|
|
32
|
+
|
|
33
|
+
- **Magnetic elements**: on pointer-move within a proximity zone, translate the
|
|
34
|
+
element toward the cursor at 15-25% of offset (buttons) with the LABEL moving
|
|
35
|
+
a further ~10% for parallax depth; spring back on leave
|
|
36
|
+
(`stiffness 150, damping 15`). Reserve for 1-2 primary CTAs or nav items.
|
|
37
|
+
- **Spotlight cards**: radial-gradient that follows the pointer —
|
|
38
|
+
`background: radial-gradient(240px circle at var(--x) var(--y),
|
|
39
|
+
<accent 6-10% alpha>, transparent 65%)` on an overlay div; update CSS vars in
|
|
40
|
+
rAF. For grids, track on the CONTAINER and light borders of all cards from one
|
|
41
|
+
listener (the Aceternity/Linear look). Alpha ≤ 0.1; visible-but-subtle.
|
|
42
|
+
- **3D tilt**: perspective 800-1000px, max tilt 4-8° (10°+ is 2015 flash),
|
|
43
|
+
computed from pointer offset to card center, damped; add a moving specular
|
|
44
|
+
`linear-gradient` highlight at low alpha for material realism. Cards with text:
|
|
45
|
+
≤ 5° or readability suffers.
|
|
46
|
+
- **Image distortion / lens effects** need WebGL — that's `skills/3d.md` §4
|
|
47
|
+
territory; don't fake them with CSS filters.
|
|
48
|
+
|
|
49
|
+
## 2. Hover reveals and state morphs
|
|
50
|
+
|
|
51
|
+
- **Underline draw**: `background-image: linear-gradient(accent, accent)` sized
|
|
52
|
+
`0% 1.5px → 100% 1.5px` on hover, 250ms expo-out, origin left. The correct link
|
|
53
|
+
hover for editorial registers.
|
|
54
|
+
- **Text swap/scramble**: label slides up revealing a duplicate from below
|
|
55
|
+
(wrap in `overflow-hidden`, translateY both spans). Scramble/decode effects
|
|
56
|
+
only on dark-tech/playful briefs, ≤ 600ms, never on body copy.
|
|
57
|
+
- **Card reveals**: secondary info (arrow, meta row, CTA) slides/fades in ON TOP
|
|
58
|
+
of a stable layout — the card must never change size on hover (layout shift =
|
|
59
|
+
amateur tell). Arrows translate 2-4px on hover (`group-hover:translate-x-1`),
|
|
60
|
+
they don't appear from nothing.
|
|
61
|
+
- **Icon morphs**: hamburger↔close, play↔pause as two-path SVG morphs or rotated
|
|
62
|
+
strokes, 200ms. Icons that rotate/bounce on every hover = noise; icons respond
|
|
63
|
+
only when the response means something (chevron rotates because the section
|
|
64
|
+
opened).
|
|
65
|
+
- Checkbox/toggle/radio: hand-styled with an SVG check that draws in
|
|
66
|
+
(`stroke-dashoffset` 200ms) — the native-looking-but-smoother tier. Toggles
|
|
67
|
+
slide with a spring, thumb squashes slightly at rest ends.
|
|
68
|
+
|
|
69
|
+
## 3. Surface effects (no pointer needed)
|
|
70
|
+
|
|
71
|
+
- **Glow borders**: `conic-gradient` rotating behind a masked 1px border
|
|
72
|
+
(`@property --angle` + animation, or padding-box/border-box double background).
|
|
73
|
+
One element per page, dark surfaces only, accent-hued — this is a signature
|
|
74
|
+
move, not a card default.
|
|
75
|
+
- **Border beam / tracer**: an accent segment orbiting a card border via
|
|
76
|
+
`offset-path: border-box` (or rect()) — same rationing as glow.
|
|
77
|
+
- **Gradient shift**: oversized `background-size: 200%` + position animation on
|
|
78
|
+
a CTA, ≤ 2 hues from the palette. Never on text (gradient text is banned).
|
|
79
|
+
- **Noise/grain**: a tiled 128px PNG/data-URI at 2-4% opacity as a fixed overlay
|
|
80
|
+
can kill flat-gradient banding on brand pages; `feTurbulence` inline SVG is
|
|
81
|
+
banned (DESIGN.md §7) — use a pre-baked texture.
|
|
82
|
+
- **Blend modes**: `mix-blend-mode: difference` for text crossing light/dark
|
|
83
|
+
boundaries (fixed nav over sections); `multiply/screen` to sit imagery into
|
|
84
|
+
tinted surfaces. Test in both themes; blend modes are the #1 source of
|
|
85
|
+
invisible-text bugs.
|
|
86
|
+
- Shadows that feel physical: 2-3 stacked layers, tinted to the background hue,
|
|
87
|
+
y-offset ≈ blur/2 (`0 1px 2px, 0 4px 8px, 0 16px 32px` at descending alpha).
|
|
88
|
+
One elevation scale page-wide.
|
|
89
|
+
|
|
90
|
+
## 4. Scroll-adjacent feel
|
|
91
|
+
|
|
92
|
+
(Choreography lives in `skills/motion.md`; these are ambient behaviors.)
|
|
93
|
+
|
|
94
|
+
- **Sticky nav transform**: past ~80px scroll, nav gains blur backdrop
|
|
95
|
+
(`backdrop-blur-md` + bg at 70-85% alpha + hairline border-b), height eases
|
|
96
|
+
72→60px. Drive from IntersectionObserver on a sentinel, not scroll position.
|
|
97
|
+
- **Scroll progress**: thin accent bar (2px) via `scroll-timeline` /
|
|
98
|
+
`animation-timeline: scroll()`, CSS-only. Long-form articles only.
|
|
99
|
+
- **Hide-on-scroll-down nav**: product register only, translate ±100% with 300ms
|
|
100
|
+
ease, 8px hysteresis so it doesn't flicker.
|
|
101
|
+
|
|
102
|
+
## 5. Feedback micro-moments (product register)
|
|
103
|
+
|
|
104
|
+
- Button loading: label crossfades to spinner INSIDE the button at fixed width
|
|
105
|
+
(measure or `min-w`), never a layout jump; success flashes a check 600ms before
|
|
106
|
+
reverting.
|
|
107
|
+
- Copy-to-clipboard: icon morphs to check + 1.2s revert; optional 150ms
|
|
108
|
+
background pulse. No toast for copy.
|
|
109
|
+
- Form errors: input border→error color + one 4px x-axis shake (3 oscillations,
|
|
110
|
+
250ms total, reduced-motion: none) + message fades in below. Shake ONCE per
|
|
111
|
+
submit, never loop.
|
|
112
|
+
- Optimistic toggles (likes, stars): fill instantly with a 1→1.2→1 spring pop on
|
|
113
|
+
the icon; reconcile silently on failure with an inline note.
|
|
114
|
+
- Skeletons: shaped like the real layout, shimmer via one masked gradient sweep
|
|
115
|
+
(1.4s linear infinite), same radius system as the content it becomes.
|
|
116
|
+
|
|
117
|
+
## 6. Guardrails
|
|
118
|
+
|
|
119
|
+
- Decorative pointer effects: max 1-2 distinct kinds per page, desktop-only,
|
|
120
|
+
`prefers-reduced-motion` disables tracking (element stays styled, static).
|
|
121
|
+
- Nothing in this file may cause layout shift, block scrolling, steal focus, or
|
|
122
|
+
attach a raw `scroll`/`mousemove` listener without rAF batching + cleanup.
|
|
123
|
+
- Every effect must survive keyboard use: whatever hover reveals, focus reveals.
|
|
124
|
+
- Effects use palette tokens (accent/neutral), never introduce new hues.
|
|
125
|
+
- If an effect can't be justified in one sentence of communication value
|
|
126
|
+
(affordance, state, hierarchy, brand voice), it's noise — cut it (DESIGN.md §6).
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Dreative Specialist Skill — Motion & Scroll Choreography
|
|
2
|
+
|
|
3
|
+
Load this file when `plan.skills` includes `motion`, or the brief/prompt asks for
|
|
4
|
+
animation, parallax, scroll storytelling, kinetic type, or "cinematic" feel.
|
|
5
|
+
It EXTENDS DESIGN.md §6 (which still applies: 100/300/500 durations, ease-out
|
|
6
|
+
exponential curves, reduced-motion fallbacks, no scroll listeners, no bounce).
|
|
7
|
+
This file is about doing ambitious motion WELL, not about adding more of it.
|
|
8
|
+
|
|
9
|
+
## 0. Pick the tool by the job, not by habit
|
|
10
|
+
|
|
11
|
+
- **CSS only** — hover/focus/press states, one-shot entrances, marquees, simple
|
|
12
|
+
scroll reveals via `animation-timeline: view()`. Zero JS cost; always first choice.
|
|
13
|
+
- **Motion (`motion/react`, formerly Framer Motion)** — React apps: presence
|
|
14
|
+
(`AnimatePresence`), layout animation (`layout` prop = free FLIP), springs,
|
|
15
|
+
`whileInView`, gesture props. The default for React product+brand pages.
|
|
16
|
+
- **GSAP + ScrollTrigger** — timeline choreography: multiple elements coordinated
|
|
17
|
+
over one scroll range, scrubbed sequences, pinned sections, kinetic type.
|
|
18
|
+
Use when a Motion version would need 4+ manually-synced `useScroll` hooks.
|
|
19
|
+
- **Lenis** — smooth scroll wrapper for scroll-driven brand pages. Only on brand
|
|
20
|
+
register with motion dial ≥7; never in product UI. Sync ScrollTrigger with
|
|
21
|
+
`lenis.on('scroll', ScrollTrigger.update)`.
|
|
22
|
+
|
|
23
|
+
One animation system per page. Motion AND GSAP on the same page = two spring
|
|
24
|
+
vocabularies fighting; pick one and commit.
|
|
25
|
+
|
|
26
|
+
## 1. Springs beat durations for anything interactive
|
|
27
|
+
|
|
28
|
+
Duration+easing is for one-shot choreography (entrances, exits). Anything that
|
|
29
|
+
tracks input or can be interrupted (drag, hover follow, toggles, layout shifts)
|
|
30
|
+
uses springs — they redirect mid-flight without a visible restart.
|
|
31
|
+
|
|
32
|
+
- Motion defaults: `{ type: "spring", stiffness: 300, damping: 30 }` = snappy UI;
|
|
33
|
+
`stiffness: 120, damping: 20` = soft brand movement. Overshoot (`damping < 15`)
|
|
34
|
+
only on playful briefs, and only on small elements — never on layout.
|
|
35
|
+
- `visualDuration` (Motion ≥11.13) sets spring feel by time: easier to reason about.
|
|
36
|
+
- Never spring opacity or color; springs are for transform. Crossfade those.
|
|
37
|
+
|
|
38
|
+
## 2. Orchestration: one timeline, one story
|
|
39
|
+
|
|
40
|
+
The scattered-reveals look (every section fades up independently) is the #1 motion
|
|
41
|
+
tell. Instead:
|
|
42
|
+
|
|
43
|
+
- **Hero entrance** = ONE composed sequence: parent `staggerChildren: 0.08`,
|
|
44
|
+
`delayChildren` after nav settles; elements enter in reading order; total ≤ 900ms.
|
|
45
|
+
Nothing else on the page gets entrance choreography.
|
|
46
|
+
- **Below the fold**: at most 2-3 sections get `whileInView` reveals (`once: true`,
|
|
47
|
+
`margin: "-20% 0px"`), and they should differ (a clip reveal here, a stagger
|
|
48
|
+
there) — not the same fade-and-rise stamped on everything.
|
|
49
|
+
- Stagger direction carries meaning: top-down for lists, center-out for grids
|
|
50
|
+
(`delay: Math.abs(i - mid) * 0.05`), left-right only for horizontal sequences.
|
|
51
|
+
- Exits are faster than entrances (~75%) and simpler (fade + small offset; never
|
|
52
|
+
reverse the full entrance).
|
|
53
|
+
|
|
54
|
+
## 3. Scroll choreography (motion dial ≥ 7)
|
|
55
|
+
|
|
56
|
+
- **Scrub vs trigger**: scrub (`scrub: 0.5–1` or Motion `useScroll`+`useTransform`)
|
|
57
|
+
for progress-bound narrative (a product rotating, a number counting, a diagram
|
|
58
|
+
assembling). Triggered one-shots for everything else. Scrubbed text reveals are
|
|
59
|
+
worn out; scrub things that MOVE, trigger things that APPEAR.
|
|
60
|
+
- **Pinning**: max ONE pinned sequence per page (`ScrollTrigger pin: true`, or
|
|
61
|
+
`position: sticky` + a tall parent — prefer sticky, it's free and un-janks).
|
|
62
|
+
Pin length ≤ 2.5 viewport heights; past that users feel trapped.
|
|
63
|
+
- **Parallax**: subtle = 8-15% differential (`useTransform(scrollYProgress, [0,1],
|
|
64
|
+
["-8%","8%"])`); apply to media/decoration, never body text. 2-3 layers max.
|
|
65
|
+
- **CSS scroll-driven animations** (`animation-timeline: view(); animation-range:
|
|
66
|
+
entry 0% cover 40%`) handle simple reveals/parallax with zero JS — use for
|
|
67
|
+
progressive enhancement, gate with `@supports (animation-timeline: view())`.
|
|
68
|
+
- Progress-linked values snap to keyframes users can feel: use eased ranges
|
|
69
|
+
(`useTransform` with cubic arrays), not raw linear mapping.
|
|
70
|
+
- Never scroll-jack (hijacking wheel delta into slide-snapping). `scroll-snap` on
|
|
71
|
+
a horizontal strip is fine; taking over vertical page scroll is not.
|
|
72
|
+
|
|
73
|
+
## 4. Kinetic type and text reveal
|
|
74
|
+
|
|
75
|
+
- Split text with `SplitText` (GSAP, now free) or manual word-`<span>` wrapping;
|
|
76
|
+
animate **words or lines, not characters** for readability (chars only for short
|
|
77
|
+
display words ≤ 12 chars). Wrap split parents in `overflow-hidden` line masks and
|
|
78
|
+
slide lines up `y: "110%" → 0` — the line-mask reveal reads as premium; per-char
|
|
79
|
+
fade reads as 2021 template.
|
|
80
|
+
- `aria-label` the original string on the split container; split spans get
|
|
81
|
+
`aria-hidden`. Re-splitting on resize: debounce, or use `SplitText`'s autoSplit.
|
|
82
|
+
- Counters: animate with `useSpring`/gsap `snap: 1`, `tabular-nums` mandatory,
|
|
83
|
+
duration ≤ 1.2s, trigger once in view.
|
|
84
|
+
|
|
85
|
+
## 5. Layout & presence
|
|
86
|
+
|
|
87
|
+
- Motion `layout` prop for reflow (filtering grids, expanding cards, reordering) —
|
|
88
|
+
it's FLIP under the hood, transform-only. `layoutId` for shared-element morphs
|
|
89
|
+
(card → modal). Wrap conditional trees in `AnimatePresence mode="popLayout"`.
|
|
90
|
+
- Accordion/height: `grid-template-rows: 0fr → 1fr` (CSS, cheap) or Motion
|
|
91
|
+
`height: "auto"` (it measures for you). Never `max-height: 9999px`.
|
|
92
|
+
- **View Transitions API** for page-level transitions (`document.startViewTransition`,
|
|
93
|
+
or the framework wrapper e.g. Next `<ViewTransition>`): name persistent elements
|
|
94
|
+
with `view-transition-name` so they morph across routes. Feature-gate; fall back
|
|
95
|
+
to instant navigation, not a JS fade.
|
|
96
|
+
|
|
97
|
+
## 6. Performance and accessibility (non-negotiable)
|
|
98
|
+
|
|
99
|
+
- Transform/opacity only in hot paths; promote animating layers sparingly
|
|
100
|
+
(`will-change` set just-in-time, removed after). > 6 simultaneously-animating
|
|
101
|
+
elements = re-choreograph.
|
|
102
|
+
- All scroll work driven by IntersectionObserver / ScrollTrigger / `useScroll`
|
|
103
|
+
(rAF-batched) — a raw `scroll` listener is a hard ban (DESIGN.md).
|
|
104
|
+
- Test the tab hidden→visible case: loops must not accumulate; `whileInView` must
|
|
105
|
+
not re-fire (`once: true`).
|
|
106
|
+
- `prefers-reduced-motion`: scrubbed narratives get a static end-state, entrances
|
|
107
|
+
become plain fades ≤ 200ms, marquees stop. In Motion use `useReducedMotion()`
|
|
108
|
+
and branch the variants — don't just shorten durations.
|
|
109
|
+
- Content exists without JS: initial styles are the VISIBLE state; animation
|
|
110
|
+
libraries then hide/offset from JS before first paint (`gsap.set`, Motion
|
|
111
|
+
`initial`). An SSR'd page must never flash-then-hide.
|
|
112
|
+
|
|
113
|
+
## 7. Recipes (copy the shape, restyle the skin)
|
|
114
|
+
|
|
115
|
+
- **Hero sequence**: nav fades in (150ms) → headline lines mask-slide up, 80ms
|
|
116
|
+
stagger → subtext + CTA fade-rise together → hero media scales 1.04→1 with 6px
|
|
117
|
+
blur→0. Total 900ms, expo-out.
|
|
118
|
+
- **Sticky product walkthrough**: `sticky` media column + scrolling step copy;
|
|
119
|
+
step index from IntersectionObserver on copy blocks; media crossfades/transforms
|
|
120
|
+
per step (springy, 300ms).
|
|
121
|
+
- **Marquee**: duplicated track, `animation: scroll Xs linear infinite`,
|
|
122
|
+
`width: max-content`, pause on hover, speed from content width (px/s constant,
|
|
123
|
+
~60-90px/s), masked edges with `mask-image: linear-gradient`.
|
|
124
|
+
- **Scrub showcase**: sticky canvas/image sequence, `useScroll` on the wrapper,
|
|
125
|
+
map progress to frame index / rotation; preload frames; ≤ 2.5 viewports tall.
|