86 lines
2.5 KiB
Markdown
86 lines
2.5 KiB
Markdown
# Motion
|
|||
|
|
|
||
|
|
Motion should clarify hierarchy and presence—not decorate every pixel.
|
||
|
|
|
||
|
|
Canonical scroll motion: `lib/block-motion.ts` + `components/motion-reveal.tsx`.
|
||
|
|
Header / micro-interactions use CSS transitions documented below.
|
||
|
|
|
||
|
|
## Duration scale
|
||
|
|
|
||
|
|
| ms | Tailwind / CSS | Use |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| 150 | `duration-150` | Micro feedback (rarely needed) |
|
||
|
|
| 200 | `duration-200` | Buttons, inputs, cards, links (default UI) |
|
||
|
|
| 250 | `duration-250` | Base heading/paragraph color transitions |
|
||
|
|
| 300 | `duration-300` | Header chrome, nav, sheets, larger reveals |
|
||
|
|
| 500 | `duration-500` | Timeline / emphasis state changes, section reveals |
|
||
|
|
|
||
|
|
Do not invent `duration-[350ms]` or multi-second loops for marketing UI.
|
||
|
|
|
||
|
|
## Easing
|
||
|
|
|
||
|
|
| Token | Value | Use |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| UI default | `ease-out` | Hovers, chrome |
|
||
|
|
| Block reveal | `blockEase = [0.22, 1, 0.36, 1]` | Scroll entrances |
|
||
|
|
| Spring (hero) | `type: "spring", bounce: ~0.3` | Occasional hero text only |
|
||
|
|
|
||
|
|
## Hover transitions
|
||
|
|
|
||
|
|
Prefer transitioning:
|
||
|
|
|
||
|
|
- `color`, `background-color`, `border-color`
|
||
|
|
- `box-shadow`, `opacity`
|
||
|
|
- `transform` (`translate`, `scale`)
|
||
|
|
|
||
|
|
**Never animate layout** (`top`/`left`/`width`/`height`/`margin`) when `transform` or opacity can achieve the effect.
|
||
|
|
|
||
|
|
Buttons already include `active:scale-[0.98]` and `duration-200`.
|
||
|
|
|
||
|
|
## Scroll animations
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { MotionReveal, MotionRevealItem } from "@/components/motion-reveal";
|
||
|
|
|
||
|
|
<MotionReveal stagger>
|
||
|
|
<MotionRevealItem>…</MotionRevealItem>
|
||
|
|
<MotionRevealItem>…</MotionRevealItem>
|
||
|
|
</MotionReveal>
|
||
|
|
```
|
||
|
|
|
||
|
|
Defaults from `block-motion.ts`:
|
||
|
|
|
||
|
|
| Option | Value |
|
||
|
|
| --- | --- |
|
||
|
|
| Duration | 0.5s |
|
||
|
|
| Offset | `y: 24` → `0` |
|
||
|
|
| Stagger | 0.08s children |
|
||
|
|
| Viewport | `once: true`, `amount: 0.2`, bottom margin `-10%` |
|
||
|
|
|
||
|
|
Keep stagger counts small (≤ ~8).
|
||
|
|
|
||
|
|
## Reduced motion
|
||
|
|
|
||
|
|
- `MotionReveal` short-circuits to static markup when `useReducedMotion()` is true.
|
||
|
|
- Timeline and header animations disable under `@media (prefers-reduced-motion: reduce)` in `globals.css`.
|
||
|
|
- New animations **must** honor reduced motion.
|
||
|
|
|
||
|
|
## Performance rules
|
||
|
|
|
||
|
|
1. Prefer `transform` + `opacity`.
|
||
|
|
2. Avoid animating `blur` on large surfaces in loops (hero entrance blur is OK once).
|
||
|
|
3. Do not use `will-change` everywhere—only hot paths (e.g. timeline progress).
|
||
|
|
4. No infinite attention-grabbing pulses on primary CTAs.
|
||
|
|
|
||
|
|
## Forbidden
|
||
|
|
|
||
|
|
- Layout thrashing animations.
|
||
|
|
- Autoplaying large Lottie stacks on every section.
|
||
|
|
- Ignoring `prefers-reduced-motion`.
|
||
|
|
|
||
|
|
## Related
|
||
|
|
|
||
|
|
- [navbar.md](./navbar.md)
|
||
|
|
- [accessibility.md](./accessibility.md)
|
||
|
|
- [buttons.md](./buttons.md)
|