dreative 0.1.0 → 0.2.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.
@@ -1,126 +1,129 @@
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).
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
+ If an effect needs different markup (wrappers, layers, split spans), follow
9
+ DESIGN.md §11's transformation-depth ladder — offer the restructure and, once
10
+ confirmed, change the structure rather than approximating the effect in CSS.
11
+
12
+ ## 0. The feel system
13
+
14
+ Micro-interactions are a SYSTEM, not garnish. Before adding any, fix three global
15
+ constants and apply them everywhere: one hover response vocabulary, one press
16
+ response, one focus style. A page where every card hovers differently reads as
17
+ assembled, not designed.
18
+
19
+ - **Press** (all clickables): `active:scale-[0.98]` (buttons) or
20
+ `active:scale-[0.99]` (large cards) + 100ms ease-out. This single rule adds more
21
+ perceived quality per byte than anything else in this file.
22
+ - **Hover** (pick ONE per element class): background tint shift, border-color
23
+ lift, shadow deepen (tinted to bg hue), or underline draw. 150-200ms.
24
+ - **Focus**: `:focus-visible` ring, 2px, offset 2px, accent color — identical on
25
+ every interactive element, never removed.
26
+ - Everything targets the 80ms "feels instant" threshold for input feedback.
27
+
28
+ ## 1. Pointer-tracking effects (the premium tier)
29
+
30
+ All pointer tracking is springs/damping (Motion `useSpring` on `useMotionValue`,
31
+ or lerp in rAF: `x += (target - x) * 0.12`) — raw 1:1 tracking feels robotic.
32
+ Desktop-only: gate behind `@media (hover: hover) and (pointer: fine)`; touch gets
33
+ the static design, not a broken half-effect. All of these are decoration — cap at
34
+ 1-2 distinct pointer effects per page, on the signature element's territory.
35
+
36
+ - **Magnetic elements**: on pointer-move within a proximity zone, translate the
37
+ element toward the cursor at 15-25% of offset (buttons) with the LABEL moving
38
+ a further ~10% for parallax depth; spring back on leave
39
+ (`stiffness 150, damping 15`). Reserve for 1-2 primary CTAs or nav items.
40
+ - **Spotlight cards**: radial-gradient that follows the pointer
41
+ `background: radial-gradient(240px circle at var(--x) var(--y),
42
+ <accent 6-10% alpha>, transparent 65%)` on an overlay div; update CSS vars in
43
+ rAF. For grids, track on the CONTAINER and light borders of all cards from one
44
+ listener (the Aceternity/Linear look). Alpha 0.1; visible-but-subtle.
45
+ - **3D tilt**: perspective 800-1000px, max tilt 4-8° (10°+ is 2015 flash),
46
+ computed from pointer offset to card center, damped; add a moving specular
47
+ `linear-gradient` highlight at low alpha for material realism. Cards with text:
48
+ ≤ 5° or readability suffers.
49
+ - **Image distortion / lens effects** need WebGL — that's `skills/3d.md` §4
50
+ territory; don't fake them with CSS filters.
51
+
52
+ ## 2. Hover reveals and state morphs
53
+
54
+ - **Underline draw**: `background-image: linear-gradient(accent, accent)` sized
55
+ `0% 1.5px → 100% 1.5px` on hover, 250ms expo-out, origin left. The correct link
56
+ hover for editorial registers.
57
+ - **Text swap/scramble**: label slides up revealing a duplicate from below
58
+ (wrap in `overflow-hidden`, translateY both spans). Scramble/decode effects
59
+ only on dark-tech/playful briefs, 600ms, never on body copy.
60
+ - **Card reveals**: secondary info (arrow, meta row, CTA) slides/fades in ON TOP
61
+ of a stable layout the card must never change size on hover (layout shift =
62
+ amateur tell). Arrows translate 2-4px on hover (`group-hover:translate-x-1`),
63
+ they don't appear from nothing.
64
+ - **Icon morphs**: hamburger↔close, play↔pause as two-path SVG morphs or rotated
65
+ strokes, 200ms. Icons that rotate/bounce on every hover = noise; icons respond
66
+ only when the response means something (chevron rotates because the section
67
+ opened).
68
+ - Checkbox/toggle/radio: hand-styled with an SVG check that draws in
69
+ (`stroke-dashoffset` 200ms) the native-looking-but-smoother tier. Toggles
70
+ slide with a spring, thumb squashes slightly at rest ends.
71
+
72
+ ## 3. Surface effects (no pointer needed)
73
+
74
+ - **Glow borders**: `conic-gradient` rotating behind a masked 1px border
75
+ (`@property --angle` + animation, or padding-box/border-box double background).
76
+ One element per page, dark surfaces only, accent-hued this is a signature
77
+ move, not a card default.
78
+ - **Border beam / tracer**: an accent segment orbiting a card border via
79
+ `offset-path: border-box` (or rect()) same rationing as glow.
80
+ - **Gradient shift**: oversized `background-size: 200%` + position animation on
81
+ a CTA, ≤ 2 hues from the palette. Never on text (gradient text is banned).
82
+ - **Noise/grain**: a tiled 128px PNG/data-URI at 2-4% opacity as a fixed overlay
83
+ can kill flat-gradient banding on brand pages; `feTurbulence` inline SVG is
84
+ banned (DESIGN.md §7) use a pre-baked texture.
85
+ - **Blend modes**: `mix-blend-mode: difference` for text crossing light/dark
86
+ boundaries (fixed nav over sections); `multiply/screen` to sit imagery into
87
+ tinted surfaces. Test in both themes; blend modes are the #1 source of
88
+ invisible-text bugs.
89
+ - Shadows that feel physical: 2-3 stacked layers, tinted to the background hue,
90
+ y-offset blur/2 (`0 1px 2px, 0 4px 8px, 0 16px 32px` at descending alpha).
91
+ One elevation scale page-wide.
92
+
93
+ ## 4. Scroll-adjacent feel
94
+
95
+ (Choreography lives in `skills/motion.md`; these are ambient behaviors.)
96
+
97
+ - **Sticky nav transform**: past ~80px scroll, nav gains blur backdrop
98
+ (`backdrop-blur-md` + bg at 70-85% alpha + hairline border-b), height eases
99
+ 72→60px. Drive from IntersectionObserver on a sentinel, not scroll position.
100
+ - **Scroll progress**: thin accent bar (2px) via `scroll-timeline` /
101
+ `animation-timeline: scroll()`, CSS-only. Long-form articles only.
102
+ - **Hide-on-scroll-down nav**: product register only, translate ±100% with 300ms
103
+ ease, 8px hysteresis so it doesn't flicker.
104
+
105
+ ## 5. Feedback micro-moments (product register)
106
+
107
+ - Button loading: label crossfades to spinner INSIDE the button at fixed width
108
+ (measure or `min-w`), never a layout jump; success flashes a check 600ms before
109
+ reverting.
110
+ - Copy-to-clipboard: icon morphs to check + 1.2s revert; optional 150ms
111
+ background pulse. No toast for copy.
112
+ - Form errors: input border→error color + one 4px x-axis shake (3 oscillations,
113
+ 250ms total, reduced-motion: none) + message fades in below. Shake ONCE per
114
+ submit, never loop.
115
+ - Optimistic toggles (likes, stars): fill instantly with a 1→1.2→1 spring pop on
116
+ the icon; reconcile silently on failure with an inline note.
117
+ - Skeletons: shaped like the real layout, shimmer via one masked gradient sweep
118
+ (1.4s linear infinite), same radius system as the content it becomes.
119
+
120
+ ## 6. Guardrails
121
+
122
+ - Decorative pointer effects: max 1-2 distinct kinds per page, desktop-only,
123
+ `prefers-reduced-motion` disables tracking (element stays styled, static).
124
+ - Nothing in this file may cause layout shift, block scrolling, steal focus, or
125
+ attach a raw `scroll`/`mousemove` listener without rAF batching + cleanup.
126
+ - Every effect must survive keyboard use: whatever hover reveals, focus reveals.
127
+ - Effects use palette tokens (accent/neutral), never introduce new hues.
128
+ - If an effect can't be justified in one sentence of communication value
129
+ (affordance, state, hierarchy, brand voice), it's noise — cut it (DESIGN.md §6).
@@ -1,125 +1,137 @@
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.
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
+ If the requested motion needs structure the current markup can't express (pinned
9
+ sections, split text, persistent stages), use DESIGN.md §11's transformation-depth
10
+ ladder: offer restructure as an option and, once confirmed, rebuild the markup —
11
+ don't fake choreography onto the old skeleton.
12
+
13
+ ## 0. Pick the tool by the job, not by habit
14
+
15
+ - **CSS only** hover/focus/press states, one-shot entrances, marquees, simple
16
+ scroll reveals via `animation-timeline: view()`. Zero JS cost; always first choice.
17
+ - **Motion (`motion/react`, formerly Framer Motion)** React apps: presence
18
+ (`AnimatePresence`), layout animation (`layout` prop = free FLIP), springs,
19
+ `whileInView`, gesture props. The default for React product+brand pages.
20
+ - **GSAP + ScrollTrigger** timeline choreography: multiple elements coordinated
21
+ over one scroll range, scrubbed sequences, pinned sections, kinetic type.
22
+ Use when a Motion version would need 4+ manually-synced `useScroll` hooks.
23
+ - **Lenis** smooth scroll wrapper for scroll-driven brand pages. Only on brand
24
+ register with motion dial ≥7; never in product UI. Sync ScrollTrigger with
25
+ `lenis.on('scroll', ScrollTrigger.update)`.
26
+
27
+ One animation system per page. Motion AND GSAP on the same page = two spring
28
+ vocabularies fighting; pick one and commit.
29
+
30
+ ## 1. Springs beat durations for anything interactive
31
+
32
+ Duration+easing is for one-shot choreography (entrances, exits). Anything that
33
+ tracks input or can be interrupted (drag, hover follow, toggles, layout shifts)
34
+ uses springs they redirect mid-flight without a visible restart.
35
+
36
+ - Motion defaults: `{ type: "spring", stiffness: 300, damping: 30 }` = snappy UI;
37
+ `stiffness: 120, damping: 20` = soft brand movement. Overshoot (`damping < 15`)
38
+ only on playful briefs, and only on small elements — never on layout.
39
+ - `visualDuration` (Motion ≥11.13) sets spring feel by time: easier to reason about.
40
+ - Never spring opacity or color; springs are for transform. Crossfade those.
41
+
42
+ ## 2. Orchestration: one timeline, one story
43
+
44
+ The scattered-reveals look (every section fades up independently) is the #1 motion
45
+ tell. Instead:
46
+
47
+ - **Hero entrance** = ONE composed sequence: parent `staggerChildren: 0.08`,
48
+ `delayChildren` after nav settles; elements enter in reading order; total ≤ 900ms.
49
+ Nothing else on the page gets entrance choreography.
50
+ - **Below the fold**: at most 2-3 sections get `whileInView` reveals (`once: true`,
51
+ `margin: "-20% 0px"`), and they should differ (a clip reveal here, a stagger
52
+ there) — not the same fade-and-rise stamped on everything.
53
+ - Stagger direction carries meaning: top-down for lists, center-out for grids
54
+ (`delay: Math.abs(i - mid) * 0.05`), left-right only for horizontal sequences.
55
+ - Exits are faster than entrances (~75%) and simpler (fade + small offset; never
56
+ reverse the full entrance).
57
+
58
+ ## 3. Scroll choreography (motion dial 7)
59
+
60
+ - **Scrub vs trigger**: scrub (`scrub: 0.5–1` or Motion `useScroll`+`useTransform`)
61
+ for progress-bound narrative (a product rotating, a number counting, a diagram
62
+ assembling). Triggered one-shots for everything else. Scrubbed text reveals are
63
+ worn out; scrub things that MOVE, trigger things that APPEAR.
64
+ - **Pinning**: max ONE pinned sequence per page (`ScrollTrigger pin: true`, or
65
+ `position: sticky` + a tall parent — prefer sticky, it's free and un-janks).
66
+ Pin length 2.5 viewport heights; past that users feel trapped.
67
+ - **Parallax**: subtle = 8-15% differential (`useTransform(scrollYProgress, [0,1],
68
+ ["-8%","8%"])`); apply to media/decoration, never body text. 2-3 layers max.
69
+ - **CSS scroll-driven animations** (`animation-timeline: view(); animation-range:
70
+ entry 0% cover 40%`) handle simple reveals/parallax with zero JS — use for
71
+ progressive enhancement, gate with `@supports (animation-timeline: view())`.
72
+ - Progress-linked values snap to keyframes users can feel: use eased ranges
73
+ (`useTransform` with cubic arrays), not raw linear mapping.
74
+ - Never scroll-jack (hijacking wheel delta into slide-snapping). `scroll-snap` on
75
+ a horizontal strip is fine; taking over vertical page scroll is not.
76
+
77
+ ## 4. Kinetic type and text reveal
78
+
79
+ - Split text with `SplitText` (GSAP, now free) or manual word-`<span>` wrapping;
80
+ animate **words or lines, not characters** for readability (chars only for short
81
+ display words 12 chars). Wrap split parents in `overflow-hidden` line masks and
82
+ slide lines up `y: "110%" 0` the line-mask reveal reads as premium; per-char
83
+ fade reads as 2021 template.
84
+ - `aria-label` the original string on the split container; split spans get
85
+ `aria-hidden`. Re-splitting on resize: debounce, or use `SplitText`'s autoSplit.
86
+ - Counters: animate with `useSpring`/gsap `snap: 1`, `tabular-nums` mandatory,
87
+ duration 1.2s, trigger once in view.
88
+
89
+ ## 5. Layout & presence
90
+
91
+ - Motion `layout` prop for reflow (filtering grids, expanding cards, reordering)
92
+ it's FLIP under the hood, transform-only. `layoutId` for shared-element morphs
93
+ (card modal). Wrap conditional trees in `AnimatePresence mode="popLayout"`.
94
+ - Accordion/height: `grid-template-rows: 0fr 1fr` (CSS, cheap) or Motion
95
+ `height: "auto"` (it measures for you). Never `max-height: 9999px`.
96
+ - **View Transitions API** for page-level transitions (`document.startViewTransition`,
97
+ or the framework wrapper e.g. Next `<ViewTransition>`): name persistent elements
98
+ with `view-transition-name` so they morph across routes. Feature-gate; fall back
99
+ to instant navigation, not a JS fade.
100
+
101
+ ## 6. Performance and accessibility (non-negotiable)
102
+
103
+ - Transform/opacity only in hot paths; promote animating layers sparingly
104
+ (`will-change` set just-in-time, removed after). > 6 simultaneously-animating
105
+ elements = re-choreograph.
106
+ - All scroll work driven by IntersectionObserver / ScrollTrigger / `useScroll`
107
+ (rAF-batched) a raw `scroll` listener is a hard ban (DESIGN.md).
108
+ - Test the tab hidden→visible case: loops must not accumulate; `whileInView` must
109
+ not re-fire (`once: true`).
110
+ - `prefers-reduced-motion`: scrubbed narratives get a static end-state, entrances
111
+ become plain fades 200ms, marquees stop. In Motion use `useReducedMotion()`
112
+ and branch the variants — don't just shorten durations.
113
+ - Content exists without JS: initial styles are the VISIBLE state; animation
114
+ libraries then hide/offset from JS before first paint (`gsap.set`, Motion
115
+ `initial`). An SSR'd page must never flash-then-hide.
116
+
117
+ ## 7. Recipes (copy the shape, restyle the skin)
118
+
119
+ - **Hero sequence**: nav fades in (150ms) headline lines mask-slide up, 80ms
120
+ stagger subtext + CTA fade-rise together → hero media scales 1.04→1 with 6px
121
+ blur→0. Total 900ms, expo-out.
122
+ - **Sticky product walkthrough**: `sticky` media column + scrolling step copy;
123
+ step index from IntersectionObserver on copy blocks; media crossfades/transforms
124
+ per step (springy, 300ms).
125
+ - **Marquee**: duplicated track, `animation: scroll Xs linear infinite`,
126
+ `width: max-content`, pause on hover, speed from content width (px/s constant,
127
+ ~60-90px/s), masked edges with `mask-image: linear-gradient`.
128
+ - **Scrub showcase**: sticky canvas/image sequence, `useScroll` on the wrapper,
129
+ map progress to frame index / rotation; preload frames; ≤ 2.5 viewports tall.
130
+ - **Text on a path** (verified on epic.net): a manifesto paragraph flows along a
131
+ curved SVG path (`<textPath>` or per-word transforms sampled from a curve) and
132
+ relaxes toward straight lines as scroll progress advances — the curve
133
+ amplitude is the scrubbed value. One per site; body text elsewhere stays flat.
134
+ - **Orbiting cursor label** (epic.net's video CTA): a circular text ring
135
+ ("PLAY VIDEO ⟲") slowly rotates around the pointer while it's over the media
136
+ region — SVG `<textPath>` on a circle, damped position follow, constant
137
+ rotation. Replaces the play button; fine pointers only, real button fallback.