feat(ui): motion primitives — keyframes, hover overlays, reduced-motion (slice 03, issue #440)#450
Conversation
…on (slice #3, issue #440) Add three PawWork keyframes (pw-spin / pw-rail / pw-shimmer) with matching --animate-pw-* shorthand variables, two hover-overlay tokens that canonicalise the 0.04 / 0.06 overlay values from STANDARDS L22, and a global prefers-reduced-motion kill-switch that floors animations to 0.001ms and caps transitions at 80ms. All 137 existing parity + colors + undefined-token tests pass unchanged.
📝 WalkthroughWalkthroughThis PR introduces motion design tokens and keyframes for PawWork animations (spin, rail, shimmer) in the animation system, and adds hover overlay color tokens plus a global reduced-motion media query to the theme for accessibility support. ChangesAnimation System Enhancements
Theme & Accessibility Updates
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces new motion primitives and hover overlay tokens to the UI package, along with a global reduced-motion media query. Feedback suggests adjusting the pw-rail animation to ensure a complete exit from the container, providing dark mode overrides for the new hover tokens to maintain visibility, and refining the reduced-motion transition duration to prevent perceived lag and improve override flexibility.
…il exit point - Change transition-duration in the prefers-reduced-motion kill-switch from 80ms to 0.001ms. Four existing components (animated-number, tool-count-label, tool-status-title, text-shimmer) explicitly set transition-duration:0ms inside their own reduced-motion blocks; the prior 80ms !important would have overridden those to a visible 80ms transition for users with the system preference enabled. Using 0.001ms is consistent with animation-duration and functionally instant. - Fix pw-rail keyframe endpoint: translateX(400%) leaves a highlight element touching the container right edge rather than exiting it. 500% ensures clean off-screen exit for highlight elements up to the container's full width.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ui/src/styles/theme.css`:
- Around line 529-537: The reduced-motion media query block for the global
selectors (*, ::before, ::after) currently resets animation-duration and
transition-duration but not delays; update the `@media` (prefers-reduced-motion:
reduce) rule (the block targeting *, ::before, ::after) to also set
animation-delay: 0s !important and transition-delay: 0s !important so any
staggered animations or transitions are effectively disabled when users request
reduced motion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2d1a337b-5a57-4708-b4ba-d3cf8704f7ce
📒 Files selected for processing (2)
packages/ui/src/styles/animations.csspackages/ui/src/styles/theme.css
… block Staggered animations (fade-up-text up to 3s, text-shimmer calc delay) keep elements at their start state while waiting for the delay even though animation-duration is already 0.001ms. Adding delay resets ensures no element is left stuck in its initial opacity/transform state under reduced-motion.
Summary
Slice 03 of eleven in issue #440 (PawWork UI carve-out). Scope: motion layer primitives only — no component changes, no token renames, no layout impact.
packages/ui/src/styles/animations.css: adds three PawWork keyframe definitions (pw-spin,pw-rail,pw-shimmer) plus matching--animate-pw-*shorthand variables in:root.packages/ui/src/styles/theme.css: adds--hover-overlay(rgba 4%) and--hover-overlay-warm(rgba 6%) as regulated tokens under the Motion section, and appends a global@media (prefers-reduced-motion: reduce)kill-switch (animations → 0.001ms, transitions → 0.001ms, scroll-behavior → auto).Why
Provides the motion vocabulary that slices 04–11 consume. Without these keyframe primitives, component slices would embed literal animation values inline and drift from each other. The hover overlay tokens canonicalise the 0.04 / 0.06 pair so sidebar/cream-surface hover in slice 08 reads
var(--hover-overlay-warm)rather than a raw rgba literal. The reduced-motion rule is a one-time global gate required by STANDARDS L22; individual component@mediaoverrides added in slices 04–11 will coexist correctly since the kill-switch uses!important.Related Issue
Refs #440 (umbrella). This PR is slice 03 of eleven.
Human Review Status
Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.
Review Focus
pw-spin: rotation 0→360, 700ms linear infinite. Must only be applied to circular geometry (spinner, thinking ring) per the keyframe comment. No call sites yet — future slices wire it in.pw-rail: translateX −100% → +500%. Highlight sweeps across the rail container in ~1400ms. The 500% endpoint ensures clean off-screen exit for elements up to container width.pw-shimmer: background-position 200% → −200%, intended forbackground-clip: textgradient sweeps on tool-name text. Consumer sets the gradient on the element; this keyframe animates the position.--hover-overlay/--hover-overlay-warm: purely additive, no existing callers. Not included inpawwork.json(not opencode loader tokens;hoverprefix is outsideREGULATED_PREFIXES).!importantis load-bearing here. Without it, componenttransition-durationdeclarations win the cascade and the kill-switch does nothing.Risk Notes
!importanton the reduced-motion rule is intentional and documented in the Review Focus above.--hover-overlay/--hover-overlay-warmare light-only tokens. Dark mode hover on warm surfaces (sidebar) has a different effective surface color; dark-mode callers will inline the appropriate alpha or use a dark-specific override. No dark-override is needed in this slice because there are no call sites yet.Changed-file boundary check
Only two files changed, both inside
packages/ui/src/styles/:animations.css— in scope (motion primitives)theme.css— in scope (token layer)No files in
packages/app/,packages/opencode/,packages/desktop-electron/, orpackages/ui/src/components/are touched.How To Verify
Screenshots or Recordings
No visible changes in this slice. Keyframes are defined but have no call sites yet; hover overlay tokens and reduced-motion rule have no runtime effect until component slices consume them.
Checklist
dev, and my PR title and commit messages use Conventional Commits in EnglishSummary by CodeRabbit