@hegemonart/get-design-done 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.claude-plugin/marketplace.json +7 -5
  2. package/.claude-plugin/plugin.json +17 -5
  3. package/CHANGELOG.md +84 -0
  4. package/README.md +20 -2
  5. package/agents/design-auditor.md +60 -1
  6. package/agents/design-doc-writer.md +21 -0
  7. package/agents/design-executor.md +22 -4
  8. package/agents/design-pattern-mapper.md +61 -0
  9. package/agents/motion-mapper.md +74 -9
  10. package/agents/token-mapper.md +8 -0
  11. package/package.json +10 -2
  12. package/reference/components/README.md +27 -23
  13. package/reference/components/alert.md +198 -0
  14. package/reference/components/badge.md +202 -0
  15. package/reference/components/breadcrumbs.md +198 -0
  16. package/reference/components/chip.md +209 -0
  17. package/reference/components/command-palette.md +228 -0
  18. package/reference/components/date-picker.md +227 -0
  19. package/reference/components/file-upload.md +219 -0
  20. package/reference/components/list.md +217 -0
  21. package/reference/components/menu.md +212 -0
  22. package/reference/components/navbar.md +211 -0
  23. package/reference/components/pagination.md +205 -0
  24. package/reference/components/progress.md +210 -0
  25. package/reference/components/rich-text-editor.md +226 -0
  26. package/reference/components/sidebar.md +211 -0
  27. package/reference/components/skeleton.md +197 -0
  28. package/reference/components/slider.md +208 -0
  29. package/reference/components/stepper.md +220 -0
  30. package/reference/components/table.md +229 -0
  31. package/reference/components/toast.md +200 -0
  32. package/reference/components/tree.md +225 -0
  33. package/reference/css-grid-layout.md +835 -0
  34. package/reference/external/NOTICE.hyperframes +28 -0
  35. package/reference/image-optimization.md +582 -0
  36. package/reference/motion-advanced.md +754 -0
  37. package/reference/motion-easings.md +381 -0
  38. package/reference/motion-interpolate.md +282 -0
  39. package/reference/motion-spring.md +234 -0
  40. package/reference/motion-transition-taxonomy.md +155 -0
  41. package/reference/motion.md +20 -0
  42. package/reference/output-contracts/motion-map.schema.json +135 -0
  43. package/reference/registry.json +183 -0
  44. package/reference/registry.schema.json +4 -0
  45. package/reference/variable-fonts-loading.md +532 -0
  46. package/scripts/lib/easings.cjs +280 -0
  47. package/scripts/lib/parse-contract.cjs +220 -0
  48. package/scripts/lib/spring.cjs +160 -0
  49. package/scripts/tests/test-motion-provenance.sh +64 -0
@@ -0,0 +1,381 @@
1
+ <!-- Source: React Native — Libraries/Animated/Easing.js (MIT License) -->
2
+ <!-- Attribution: Facebook, Inc. and its affiliates — see https://github.com/facebook/react-native/blob/main/Libraries/Animated/Easing.js -->
3
+
4
+ # Motion Easings
5
+
6
+ Canonical easing curve presets derived from React Native's `Easing` module, adapted for CSS and web animation contexts.
7
+
8
+ ## Quick Reference
9
+
10
+ | Token | CSS `cubic-bezier` | Character | Settle (spring/bounce) |
11
+ |---|---|---|---|
12
+ | `--ease-linear` | `cubic-bezier(0,0,1,1)` | Constant rate | — |
13
+ | `--ease-quad-in` | `cubic-bezier(0.55,0,1,1)` | Slow start | — |
14
+ | `--ease-quad-out` | `cubic-bezier(0,0,0.45,1)` | Slow end | — |
15
+ | `--ease-quad-in-out` | `cubic-bezier(0.455,0.03,0.515,0.955)` | Slow both | — |
16
+ | `--ease-cubic-in` | `cubic-bezier(0.55,0.055,0.675,0.19)` | Aggressive start | — |
17
+ | `--ease-cubic-out` | `cubic-bezier(0.215,0.61,0.355,1)` | Aggressive end | — |
18
+ | `--ease-cubic-in-out` | `cubic-bezier(0.645,0.045,0.355,1)` | Strong S-curve | — |
19
+ | `--ease-sin-in` | `cubic-bezier(0.47,0,0.745,0.715)` | Gentle start | — |
20
+ | `--ease-sin-out` | `cubic-bezier(0.39,0.575,0.565,1)` | Gentle end | — |
21
+ | `--ease-sin-in-out` | `cubic-bezier(0.445,0.05,0.55,0.95)` | Smooth S-curve | — |
22
+ | `--ease-circle-in` | `cubic-bezier(0.6,0.04,0.98,0.335)` | Circular arc start | — |
23
+ | `--ease-circle-out` | `cubic-bezier(0.075,0.82,0.165,1)` | Circular arc end | — |
24
+ | `--ease-exp-in` | `cubic-bezier(0.95,0.05,0.795,0.035)` | Explosive start | — |
25
+ | `--ease-exp-out` | `cubic-bezier(0.19,1,0.22,1)` | Explosive end | — |
26
+ | `--ease-elastic` | `linear(...)` baked | Overshoot + settle | ~500ms |
27
+ | `--ease-back-in` | `cubic-bezier(0.6,-0.28,0.735,0.045)` | Anticipation | — |
28
+ | `--ease-back-out` | `cubic-bezier(0.175,0.885,0.32,1.275)` | Overshoot | — |
29
+ | `--ease-bounce-out` | `linear(...)` baked | Bounces at end | ~420ms |
30
+
31
+ ---
32
+
33
+ ## Preset Groups
34
+
35
+ ### `linear`
36
+
37
+ Motion at a constant rate. No acceleration or deceleration.
38
+
39
+ - **Human name:** Linear
40
+ - **Character:** Mechanical, robotic. Use only for opacity fades or loading bars where constant rate is intentional.
41
+ - **CSS custom property:** `--ease-linear`
42
+ - **CSS equivalent:** `cubic-bezier(0, 0, 1, 1)` (also the CSS keyword `linear`)
43
+
44
+ ```css
45
+ :root {
46
+ --ease-linear: cubic-bezier(0, 0, 1, 1);
47
+ }
48
+
49
+ .fade {
50
+ transition: opacity 200ms var(--ease-linear);
51
+ }
52
+ ```
53
+
54
+ ```js
55
+ // React Native Easing
56
+ Easing.linear
57
+ ```
58
+
59
+ ---
60
+
61
+ ### `quad`
62
+
63
+ Quadratic — acceleration proportional to `t²`.
64
+
65
+ - **Human name:** Quad (ease-in-out)
66
+ - **Character:** Subtle, polished. Most common choice for UI transitions.
67
+ - **CSS custom property:** `--ease-quad-in-out`
68
+
69
+ ```css
70
+ :root {
71
+ --ease-quad-in: cubic-bezier(0.55, 0, 1, 1);
72
+ --ease-quad-out: cubic-bezier(0, 0, 0.45, 1);
73
+ --ease-quad-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955);
74
+ }
75
+ ```
76
+
77
+ ```js
78
+ Easing.quad // base: t * t
79
+ Easing.in(Easing.quad)
80
+ Easing.out(Easing.quad)
81
+ Easing.inOut(Easing.quad)
82
+ ```
83
+
84
+ ---
85
+
86
+ ### `cubic`
87
+
88
+ Cubic — acceleration proportional to `t³`. More pronounced than quad.
89
+
90
+ - **Human name:** Cubic (ease-in-out)
91
+ - **Character:** Snappy, decisive. Good for panels, drawers, menus.
92
+ - **CSS custom property:** `--ease-cubic-in-out`
93
+
94
+ ```css
95
+ :root {
96
+ --ease-cubic-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
97
+ --ease-cubic-out: cubic-bezier(0.215, 0.61, 0.355, 1);
98
+ --ease-cubic-out: cubic-bezier(0.215, 0.61, 0.355, 1); /* "ease-out-cubic" */
99
+ --ease-cubic-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
100
+ }
101
+ ```
102
+
103
+ ```js
104
+ Easing.cubic // base: t * t * t
105
+ Easing.inOut(Easing.cubic)
106
+ ```
107
+
108
+ ---
109
+
110
+ ### `poly(n)`
111
+
112
+ Generalised polynomial — `t^n`. Quad is `poly(2)`, cubic is `poly(3)`.
113
+
114
+ - **Human name:** Poly-n
115
+ - **Character:** Tunable. Higher `n` = harder snap.
116
+ - **CSS custom property:** No single token; bake to `cubic-bezier` per n.
117
+
118
+ ```js
119
+ Easing.poly(4) // quartic
120
+ Easing.poly(5) // quintic
121
+ Easing.in(Easing.poly(4))
122
+ ```
123
+
124
+ No direct CSS equivalent — approximate with a `cubic-bezier` or bake to `linear()`.
125
+
126
+ ---
127
+
128
+ ### `sin`
129
+
130
+ Sinusoidal — easing shaped by the sine function. Smooth and natural.
131
+
132
+ - **Human name:** Sine
133
+ - **Character:** Organic, gentle. Works well for ambient or breathing animations.
134
+ - **CSS custom property:** `--ease-sin-in-out`
135
+
136
+ ```css
137
+ :root {
138
+ --ease-sin-in: cubic-bezier(0.47, 0, 0.745, 0.715);
139
+ --ease-sin-out: cubic-bezier(0.39, 0.575, 0.565, 1);
140
+ --ease-sin-in-out: cubic-bezier(0.445, 0.05, 0.55, 0.95);
141
+ }
142
+ ```
143
+
144
+ ```js
145
+ Easing.sin
146
+ // Internally: 1 - Math.cos(t * Math.PI / 2)
147
+ ```
148
+
149
+ ---
150
+
151
+ ### `circle`
152
+
153
+ Circular arc — based on `sqrt(1 - t²)`. Sharp acceleration at the end of its range.
154
+
155
+ - **Human name:** Circ
156
+ - **Character:** Abrupt, dramatic. Use sparingly for emphasis.
157
+ - **CSS custom property:** `--ease-circle-out`
158
+
159
+ ```css
160
+ :root {
161
+ --ease-circle-in: cubic-bezier(0.6, 0.04, 0.98, 0.335);
162
+ --ease-circle-out: cubic-bezier(0.075, 0.82, 0.165, 1);
163
+ }
164
+ ```
165
+
166
+ ```js
167
+ Easing.circle
168
+ // Internally: 1 - Math.sqrt(1 - t * t)
169
+ ```
170
+
171
+ ---
172
+
173
+ ### `exp`
174
+
175
+ Exponential — `2^(10 * (t - 1))`. Starts near-zero, ends explosively.
176
+
177
+ - **Human name:** Expo
178
+ - **Character:** High-impact. Good for reveal animations, hero entrances.
179
+ - **CSS custom property:** `--ease-exp-out`
180
+
181
+ ```css
182
+ :root {
183
+ --ease-exp-in: cubic-bezier(0.95, 0.05, 0.795, 0.035);
184
+ --ease-exp-out: cubic-bezier(0.19, 1, 0.22, 1);
185
+ }
186
+ ```
187
+
188
+ ```js
189
+ Easing.exp
190
+ // Internally: Math.pow(2, 10 * (t - 1))
191
+ ```
192
+
193
+ ---
194
+
195
+ ### `elastic(bounciness, speed)`
196
+
197
+ Spring-like oscillation past the target before settling.
198
+
199
+ - **Human name:** Elastic
200
+ - **Character:** Playful, bouncy. Overshoots final value one or more times.
201
+ - **Default params:** `bounciness = 1`, `speed = 1`
202
+ - **60fps settle time:** ~500ms at defaults
203
+ - **CSS custom property:** `--ease-elastic` (must be baked to `linear()`)
204
+
205
+ ```css
206
+ /* Baked approximation for CSS linear() — default elastic */
207
+ :root {
208
+ --ease-elastic-out: linear(
209
+ 0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%, 0.723 12.9%,
210
+ 0.938, 1.017, 1.048, 1.056, 1.046 20.1%, 0.999 24.3%,
211
+ 0.984, 0.983, 0.988 29.9%, 1.001 35.5%, 1.004 40.1%, 1 100%
212
+ );
213
+ }
214
+ ```
215
+
216
+ ```js
217
+ Easing.elastic(1, 1) // default
218
+ Easing.out(Easing.elastic(1, 1))
219
+ ```
220
+
221
+ **Parameter guide:**
222
+ - `bounciness`: amplitude of overshoot (0 = no overshoot, higher = more)
223
+ - `speed`: controls oscillation frequency (higher = faster settle)
224
+
225
+ ---
226
+
227
+ ### `back(s)`
228
+
229
+ Anticipation curve — slightly retracts before moving forward (or overshoots then settles).
230
+
231
+ - **Human name:** Back
232
+ - **Character:** Mechanical delight. Communicates intentionality.
233
+ - **Default param:** `s = 1.70158`
234
+ - **CSS custom property:** `--ease-back-out`
235
+
236
+ ```css
237
+ :root {
238
+ /* s = 1.70158 (default) */
239
+ --ease-back-in: cubic-bezier(0.6, -0.28, 0.735, 0.045);
240
+ --ease-back-out: cubic-bezier(0.175, 0.885, 0.32, 1.275);
241
+ }
242
+ ```
243
+
244
+ ```js
245
+ Easing.back(1.70158) // default overshoot
246
+ Easing.out(Easing.back(2.5)) // stronger overshoot
247
+ ```
248
+
249
+ ---
250
+
251
+ ### `bounce`
252
+
253
+ Simulates a physical bounce — the value bounces off the endpoint.
254
+
255
+ - **Human name:** Bounce
256
+ - **Character:** Playful, energetic. Use for game-like or informal UIs.
257
+ - **60fps settle time:** ~420ms
258
+ - **CSS custom property:** `--ease-bounce-out` (must be baked to `linear()`)
259
+
260
+ ```css
261
+ :root {
262
+ /* Baked CSS linear() approximation */
263
+ --ease-bounce-out: linear(
264
+ 0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141, 0.191, 0.25,
265
+ 0.316, 0.391, 0.469, 0.563, 0.656, 0.765, 0.875, 0.891,
266
+ 0.906 45.7%, 0.922, 0.938, 0.953 50%, 0.984, 1.016, 1.031,
267
+ 1.047, 1.063, 1.016, 1 100%
268
+ );
269
+ }
270
+ ```
271
+
272
+ ```js
273
+ Easing.bounce
274
+ // Internally: piecewise quadratic with 4 bounces
275
+ ```
276
+
277
+ ---
278
+
279
+ ### `bezier(x1, y1, x2, y2)`
280
+
281
+ Raw cubic Bézier — the same primitive that powers all CSS `cubic-bezier()` calls.
282
+
283
+ - **Human name:** Custom Bézier
284
+ - **Character:** Whatever you specify.
285
+
286
+ ```js
287
+ Easing.bezier(0.25, 0.1, 0.25, 1.0) // CSS ease
288
+ Easing.bezier(0.42, 0, 1, 1) // CSS ease-in
289
+ Easing.bezier(0, 0, 0.58, 1) // CSS ease-out
290
+ Easing.bezier(0.42, 0, 0.58, 1) // CSS ease-in-out
291
+ ```
292
+
293
+ ```css
294
+ /* Direct mapping */
295
+ transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1.0);
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Higher-Order Wrappers: `in`, `out`, `inOut`
301
+
302
+ React Native's `Easing` module exposes three composition functions that transform any base curve into its directional variant. This is the correct pattern for producing all six canonical easing directions from any single base function.
303
+
304
+ ### How it works
305
+
306
+ Given a base easing function `f(t)` where `t ∈ [0, 1]`:
307
+
308
+ | Wrapper | Formula | Effect |
309
+ |---|---|---|
310
+ | `in(f)` | `f(t)` | Acceleration at start — slow → fast |
311
+ | `out(f)` | `1 - f(1 - t)` | Deceleration at end — fast → slow |
312
+ | `inOut(f)` | `t < 0.5 ? f(2t)/2 : 1 - f(2(1-t))/2` | Slow start and end |
313
+
314
+ ### Examples
315
+
316
+ ```js
317
+ import { in as easeIn, out as easeOut, inOut, cubic } from './lib/easings.cjs';
318
+
319
+ const easeInCubic = easeIn(cubic); // t => t³
320
+ const easeOutCubic = easeOut(cubic); // t => 1 - (1-t)³
321
+ const easeInOutCubic = inOut(cubic); // smooth S-curve
322
+
323
+ // Works identically with any base:
324
+ const easeInBounce = easeIn(bounce);
325
+ const easeInElastic = easeIn(elastic(1, 1));
326
+ ```
327
+
328
+ ```css
329
+ /* CSS does the same thing natively for cubic-bezier: */
330
+ /* ease-in = cubic-bezier(x1,y1, x2,y2) with control points in lower-left */
331
+ /* ease-out = cubic-bezier(x1,y1, x2,y2) with control points in upper-right */
332
+ /* inOut = symmetric control points */
333
+ ```
334
+
335
+ ### Design rule
336
+
337
+ - Use `out` for **enter** transitions (element arrives, decelerates into place).
338
+ - Use `in` for **exit** transitions (element departs, accelerates away).
339
+ - Use `inOut` for **state changes** (element moves from one state to another, both ends cushioned).
340
+
341
+ ---
342
+
343
+ ## CSS Custom Properties — Full Token Set
344
+
345
+ ```css
346
+ :root {
347
+ /* Linear */
348
+ --ease-linear: cubic-bezier(0, 0, 1, 1);
349
+
350
+ /* Sine */
351
+ --ease-sin-in: cubic-bezier(0.47, 0, 0.745, 0.715);
352
+ --ease-sin-out: cubic-bezier(0.39, 0.575, 0.565, 1);
353
+ --ease-sin-in-out: cubic-bezier(0.445, 0.05, 0.55, 0.95);
354
+
355
+ /* Quad */
356
+ --ease-quad-in: cubic-bezier(0.55, 0, 1, 1);
357
+ --ease-quad-out: cubic-bezier(0, 0, 0.45, 1);
358
+ --ease-quad-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955);
359
+
360
+ /* Cubic */
361
+ --ease-cubic-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
362
+ --ease-cubic-out: cubic-bezier(0.215, 0.61, 0.355, 1);
363
+ --ease-cubic-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
364
+
365
+ /* Circ */
366
+ --ease-circle-in: cubic-bezier(0.6, 0.04, 0.98, 0.335);
367
+ --ease-circle-out: cubic-bezier(0.075, 0.82, 0.165, 1);
368
+
369
+ /* Expo */
370
+ --ease-exp-in: cubic-bezier(0.95, 0.05, 0.795, 0.035);
371
+ --ease-exp-out: cubic-bezier(0.19, 1, 0.22, 1);
372
+
373
+ /* Back (default s=1.70158) */
374
+ --ease-back-in: cubic-bezier(0.6, -0.28, 0.735, 0.045);
375
+ --ease-back-out: cubic-bezier(0.175, 0.885, 0.32, 1.275);
376
+
377
+ /* Elastic and Bounce — baked linear() — see above for full values */
378
+ --ease-elastic-out: linear(0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%, 0.723 12.9%, 0.938, 1.017, 1.048, 1.056, 1.046 20.1%, 0.999 24.3%, 0.984, 0.983, 0.988 29.9%, 1.001 35.5%, 1.004 40.1%, 1 100%);
379
+ --ease-bounce-out: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141, 0.191, 0.250, 0.316, 0.391, 0.469, 0.563, 0.656, 0.765, 0.875, 0.891, 0.906 45.7%, 0.922, 0.938, 0.953 50%, 0.984, 1.016, 1.031, 1.047, 1.063, 1.016, 1 100%);
380
+ }
381
+ ```
@@ -0,0 +1,282 @@
1
+ <!-- Source: React Native — Libraries/Animated/AnimatedInterpolation.js (MIT License) -->
2
+ <!-- Attribution: Facebook, Inc. and its affiliates -->
3
+
4
+ # Motion Interpolation
5
+
6
+ Interpolation is the mechanism that maps any changing value — progress, scroll position, pointer delta, elapsed time — into any animated output range. Every animation in a system can be decomposed into a single canonical form.
7
+
8
+ ---
9
+
10
+ ## Core Concept
11
+
12
+ Any animation decomposes into four parameters:
13
+
14
+ ```
15
+ interpolate(inputValue, inputRange, outputRange, extrapolationConfig?)
16
+ ```
17
+
18
+ | Parameter | Type | Description |
19
+ |---|---|---|
20
+ | `inputValue` | `number` | The driver value (a scalar that changes over time) |
21
+ | `inputRange` | `number[]` | Ordered list of input breakpoints |
22
+ | `outputRange` | `number \| string[]` | Corresponding output values at each breakpoint |
23
+ | `extrapolationConfig` | `object?` | What to do when input is outside `inputRange` |
24
+
25
+ Between any two adjacent breakpoints, the output is linearly interpolated by default. An `easing` function can be applied per-segment to shape the curve. See [motion-easings.md](./motion-easings.md) for the `easing` parameter.
26
+
27
+ **Principle:** The driver value is always a plain number. The animation is a pure function of that number. This separates *what drives the animation* from *what the animation does*, making it composable and testable.
28
+
29
+ ---
30
+
31
+ ## Extrapolation Modes
32
+
33
+ When `inputValue` falls outside `inputRange`, the behavior is controlled by the extrapolation config. React Native defines four named modes:
34
+
35
+ ### `extend` (default)
36
+
37
+ Continues the linear trend of the first/last segment past the range boundary. Output keeps growing (or shrinking) indefinitely.
38
+
39
+ ```js
40
+ interpolate(scrollY, [0, 100], [0, 1], { extrapolate: 'extend' })
41
+ // scrollY = 200 → output = 2.0
42
+ ```
43
+
44
+ **Use when:** The animation should track the input continuously beyond the defined range (e.g., parallax that keeps moving as you scroll past a section).
45
+
46
+ ---
47
+
48
+ ### `clamp`
49
+
50
+ Pins the output to the boundary value once the input leaves the range.
51
+
52
+ ```js
53
+ interpolate(scrollY, [0, 100], [0, 1], { extrapolate: 'clamp' })
54
+ // scrollY = 200 → output = 1.0 (clamped at top)
55
+ // scrollY = -50 → output = 0.0 (clamped at bottom)
56
+ ```
57
+
58
+ **Use when:** An element should be fully visible/hidden after a threshold and not go further (e.g., a header that fades in and stays visible after scrolling 100px).
59
+
60
+ ---
61
+
62
+ ### `identity`
63
+
64
+ Returns the raw input value itself once outside the range, ignoring the output mapping.
65
+
66
+ ```js
67
+ interpolate(t, [0, 1], [0, 100], { extrapolate: 'identity' })
68
+ // t = 1.5 → output = 1.5 (not 150 — raw input echoed)
69
+ ```
70
+
71
+ **Use when:** You need the value to revert to unscaled behavior outside a zone. Uncommon; most interpolation uses `clamp` or `extend`.
72
+
73
+ ---
74
+
75
+ ### `wrap`
76
+
77
+ Wraps the input cyclically around the input range before applying the mapping. Produces looping output.
78
+
79
+ ```js
80
+ interpolate(t, [0, 1], [0, 360], { extrapolate: 'wrap' })
81
+ // t = 1.5 → maps to t=0.5 → output = 180
82
+ // t = 2.0 → maps to t=0.0 → output = 0
83
+ ```
84
+
85
+ **Use when:** Rotation, looping progress indicators, repeating carousel positions.
86
+
87
+ ---
88
+
89
+ ## Taxonomy of Animation Types
90
+
91
+ ### Progress-linked (`0 → 1`)
92
+
93
+ The driver is a normalized progress value from 0 to 1. Most explicit animations and transitions fall here.
94
+
95
+ ```
96
+ inputRange: [0, 1]
97
+ outputRange: [startValue, endValue]
98
+ ```
99
+
100
+ **Examples:** mount/unmount transition, tab indicator position, wizard step progress.
101
+
102
+ ```js
103
+ // Framer Motion
104
+ const opacity = useTransform(progress, [0, 1], [0, 1]);
105
+ const x = useTransform(progress, [0, 1], ['-100%', '0%']);
106
+ ```
107
+
108
+ ---
109
+
110
+ ### Scroll-linked (px scroll position)
111
+
112
+ The driver is the raw pixel scroll offset from a scroll container. Common range is `[0, containerHeight]` or a sub-range for reveal effects.
113
+
114
+ ```
115
+ inputRange: [0, 300] // px from top of scroll container
116
+ outputRange: [1, 0] // opacity fades out as user scrolls 300px
117
+ ```
118
+
119
+ ```js
120
+ // Framer Motion — scroll-linked opacity
121
+ const { scrollY } = useScroll();
122
+ const opacity = useTransform(scrollY, [0, 300], [1, 0], { clamp: true });
123
+ ```
124
+
125
+ ```css
126
+ /* CSS Scroll Timeline equivalent */
127
+ @keyframes fade-header {
128
+ from { opacity: 1; }
129
+ to { opacity: 0; }
130
+ }
131
+
132
+ .header {
133
+ animation: fade-header linear both;
134
+ animation-timeline: scroll(root);
135
+ animation-range: 0px 300px;
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ### Gesture-linked (pointer delta)
142
+
143
+ The driver is the cumulative pointer displacement in pixels (e.g., drag distance). Input range is typically anchored at 0 with a resistance model at the extremes.
144
+
145
+ ```
146
+ inputRange: [-200, 0, 200]
147
+ outputRange: ['-100%', '0%', '100%']
148
+ ```
149
+
150
+ **Key difference from scroll-linked:** gesture-linked animations are often bidirectional and involve velocity projection for throw/release behavior.
151
+
152
+ ```js
153
+ // Framer Motion — drag with spring release
154
+ <motion.div
155
+ drag="x"
156
+ dragConstraints={{ left: -200, right: 200 }}
157
+ style={{ x: dragX }}
158
+ />
159
+
160
+ // Manual lerp pattern for custom gesture handling
161
+ function lerp(a, b, t) {
162
+ return a + (b - a) * t;
163
+ }
164
+
165
+ function interpolateGesture(delta, inputRange, outputRange) {
166
+ const [inMin, inMax] = inputRange;
167
+ const [outMin, outMax] = outputRange;
168
+ const t = Math.max(0, Math.min(1, (delta - inMin) / (inMax - inMin)));
169
+ return lerp(outMin, outMax, t);
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ### Time-linked (`Date.now()`)
176
+
177
+ The driver is wall-clock time in milliseconds. Used for ambient animations, looping effects, and time-based sequencing where a spring or tween is not driving the value.
178
+
179
+ ```
180
+ inputRange: [startTime, startTime + durationMs]
181
+ outputRange: [0, 1]
182
+ extrapolate: 'clamp'
183
+ ```
184
+
185
+ ```js
186
+ // Manual time-linked animation loop
187
+ function useTimeLinked(durationMs) {
188
+ const [progress, setProgress] = useState(0);
189
+ const startTime = useRef(Date.now());
190
+
191
+ useAnimationFrame(() => {
192
+ const elapsed = Date.now() - startTime.current;
193
+ const t = Math.min(1, elapsed / durationMs);
194
+ setProgress(t);
195
+ });
196
+
197
+ return progress;
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Using Easing with Interpolation
204
+
205
+ The `easing` parameter shapes the interpolation curve within a segment. It accepts any `t → value` function from [motion-easings.md](./motion-easings.md).
206
+
207
+ ```js
208
+ import { inOut, cubic } from '../scripts/lib/easings.cjs';
209
+
210
+ // Framer Motion with easing
211
+ const y = useTransform(scrollY, [0, 400], [0, -100], {
212
+ ease: inOut(cubic), // or any easing function
213
+ });
214
+ ```
215
+
216
+ ```css
217
+ /* CSS animation-timing-function applies per-keyframe segment */
218
+ @keyframes slide-up {
219
+ from { transform: translateY(0); animation-timing-function: var(--ease-cubic-out); }
220
+ to { transform: translateY(-100px); }
221
+ }
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Manual `lerp` Pattern
227
+
228
+ When you need to interpolate outside a framework, the bare pattern is:
229
+
230
+ ```js
231
+ /**
232
+ * Linear interpolation between two values.
233
+ * @param {number} a - Start value (at t=0)
234
+ * @param {number} b - End value (at t=1)
235
+ * @param {number} t - Progress in [0, 1]
236
+ * @returns {number}
237
+ */
238
+ function lerp(a, b, t) {
239
+ return a + (b - a) * t;
240
+ }
241
+
242
+ /**
243
+ * Map inputValue from inputRange to outputRange with optional easing and clamping.
244
+ */
245
+ function interpolate(inputValue, [inMin, inMax], [outMin, outMax], {
246
+ easing = (t) => t,
247
+ extrapolate = 'clamp',
248
+ } = {}) {
249
+ let t = (inputValue - inMin) / (inMax - inMin);
250
+
251
+ if (extrapolate === 'clamp') t = Math.max(0, Math.min(1, t));
252
+ // extend: t is unbounded
253
+ // identity: return inputValue if out of range
254
+ // wrap: t = ((t % 1) + 1) % 1
255
+
256
+ return lerp(outMin, outMax, easing(t));
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Multi-segment Interpolation
263
+
264
+ Passing more than two breakpoints produces piecewise interpolation. Each adjacent pair is a separate segment.
265
+
266
+ ```js
267
+ // Framer Motion multi-segment
268
+ const background = useTransform(
269
+ scrollY,
270
+ [0, 200, 400, 600],
271
+ ['#ffffff', '#f0f0f0', '#e0e0e0', '#000000']
272
+ );
273
+ ```
274
+
275
+ This is equivalent to three separate interpolations chained by range.
276
+
277
+ ---
278
+
279
+ ## Cross-references
280
+
281
+ - Easing functions for the `easing` / `ease` parameter: [motion-easings.md](./motion-easings.md)
282
+ - Spring-based animation (an alternative driver to progress/scroll): [motion-spring.md](./motion-spring.md)