feat(roots): expressive motion tokens (spring easings + pop/shake/fade presets)#45
Merged
Conversation
…e presets) Adds curated motion from transitions.dev, re-fitted to Canopy's minimal scale: - easings: ease.spring (0.34,1.36,0.64,1), ease.spring-strong (0.34,3.85,0.64,1) - Canopy's first overshoot curves; every existing easing decelerates. - durations: micro (80ms) and slower (480ms) extend fast/base/slow without collision. - presets (preset-motion.css keyframes + token-composed animate-*): pop-in/out (spring), shake (error/invalid states), fade-in/out (toasts/content). Each composes --duration-*/ --ease-* and is gated by consumers with motion-reduce:animate-none. Existing tokens and animate-dialog/drawer/bottom-sheet utilities are untouched (purely additive). success-check and distance/scale/blur helpers deferred (see spec 0033). Spec: docs/specs/0033-expressive-motion-tokens.md Tests: tokens.test.ts asserts the new tokens across all three outputs and that the animate-* utilities ship token-composed. pnpm build/test/lint green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHabPujNkgKUUNJjaBQCgT
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
mattmaynes
commented
Jul 13, 2026
Persona review of PR #45 (designer/engineer/tester/architect): - tests (tester+engineer, major): assert BOTH halves of every preset pair (pop-out, fade-out were unverified) and that each --animate-* composes its tokens, via a per-variant loop; add an occurrence-count guard proving the fold stays idempotent (spec 0033 acceptance item that had no test). fade-in now asserts composition, not just presence. - shake a11y (designer, major): reduced-motion gating is now documented as MANDATORY (not optional) for animate-shake in the preset header + README - it is a vestibular trigger. - doc drift (architect, minor): reframe the preset-motion.css header and architecture.md from "overlay-motion" to keyframed motion (overlay + expressive), now that generic pop/shake/fade presets live there too. Learning captured: docs/feedback/0015-assert-every-shipped-variant-and-acceptance-property.md + overview/learnings.md (second coverage-gap review after 0014). Open for developer decision: spring-strong (designer major) - kept as opted-in; see PR reply. pnpm build/test/lint green (50/50). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHabPujNkgKUUNJjaBQCgT
Developer decision on the designer's major (PR #45): keep ease.spring-strong but dial the overshoot from 3.85 to cubic-bezier(0.34, 2.2, 0.64, 1) - still a pronounced bounce, but on-brand for a calm system. Token, tests, and spec 0033 updated in lockstep. pnpm build/test/lint green (50/50). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHabPujNkgKUUNJjaBQCgT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements spec 0033 - curated expressive motion from transitions.dev, re-fitted to Canopy's minimal, opinionated scale (no wholesale import).
What's added (purely additive)
ease.springcubic-bezier(0.34,1.36,0.64,1),ease.spring-strongcubic-bezier(0.34,3.85,0.64,1)duration.micro80ms,duration.slower480msanimate-*)pop-in/pop-out(spring scale+fade),shake(error/invalid states),fade-in/fade-out(toasts/content)spring/spring-strongfill that gap.fast/base/sloware unchanged (transitions.dev's ownfast=250/slow=400 would have collided, so we mapped intent, not literals).preset-motion.cssfollowing the existinganimate-dialog-*/-drawer-*/-bottom-sheet-*pattern exactly:@keyframes+ a@themeblock whose--animate-*values compose the--duration-*/--ease-*tokens (never hardcoded ms/easing), folded intodist/tailwind-preset.css. Consumers gate them withmotion-reduce:animate-none.Out of scope (deferred)
success-checkand other multi-element composites (fade+rotate+blur+bob+SVG path-draw) - a single--animate-*is one keyframe timeline on one element; the check's path-draw needsstroke-dashoffseton a nested SVG path. That's component motion, not a token. Noted as future Branches work in the spec.distance/scale/blurhelper token families - separate decision.Verification
pnpm build- new tokens land indist/tokens.css(:root),dist/tokens.ts(typed),dist/tailwind-preset.css(@theme inline); the fiveanimate-*utilities ship folded in.pnpm test- 50/50 green;tokens.test.tsasserts the four tokens across all three outputs and thatanimate-pop-incomposesvar(--duration-base)+var(--ease-spring),animate-shakeusesvar(--ease-standard),animate-fade-inships.pnpm lint- clean (only the pre-existing eslint-plugin-react warning).Docs: README motion section +
docs/overview/features.md/architecture.mdupdated to match.🤖 Generated with Claude Code