fix(web): honor reduced-motion preferences across product UI - #4855
fix(web): honor reduced-motion preferences across product UI#4855FaisalMisbah23 wants to merge 3 commits into
Conversation
| <div className="h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" /> | ||
| <div className="h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" /> | ||
| <div className="h-2 w-2 animate-bounce rounded-full bg-gray-400" /> | ||
| <div className="motion-reduce:bg-gray-400 motion-reduce:opacity-40 h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" /> |
There was a problem hiding this comment.
WARNING: motion-reduce:opacity-40 does not stop the bounce animation, and motion-reduce:bg-gray-400 is a no-op
Two issues with the added utility classes on these three dots:
motion-reduce:bg-gray-400sets the same background color that's already applied unconditionally (bg-gray-400), so it has no effect under reduced motion.animate-bounceitself is never disabled here (nomotion-reduce:animate-none), so this change alone does not stop the animation. The bounce is only neutralized because of the unrelated global reset inapps/web/src/app/globals.css(animation-iteration-count: 1 !important/animation-duration: 0.01ms !important). If that global rule is ever scoped down or removed, these dots will keep bouncing despite this "fix". Consider addingmotion-reduce:animate-nonedirectly on these elements so the intent is explicit and self-contained.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| @@ -0,0 +1,18 @@ | |||
| import type { StoryObj } from '@storybook/react'; | |||
There was a problem hiding this comment.
CRITICAL: New story file is in the wrong directory and missing the required default export - it will not be picked up by Storybook
Two problems:
- Storybook's config (
apps/storybook/.storybook/main.ts) only globs../stories/**/*.stories.@(js|jsx|mjs|ts|tsx), i.e.apps/storybook/stories/. This file lives inapps/web/src/stories/instead, so Storybook will never discover it. - Even if it were in the right location, Storybook's Component Story Format requires a default export with at least a
title(see other files inapps/storybook/stories/*.stories.tsx, e.g.export default { title: '...' }). This file has no default export, so it would fail to register as a story module.
Move this file to apps/storybook/stories/ReducedMotion.stories.tsx and add a default export with a title (and typically a Meta type import), following the pattern used by the other story files in that directory.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe final commit removes the duplicate, non-functional Files Reviewed (1 file)
Previous Review Summaries (2 snapshots, latest commit 946c350)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 946c350)Status: 1 Issue Found | Recommendation: Address before merge Executive SummaryThe follow-up commit correctly fixes the Rules-of-Hooks violation, the typing-indicator animation, and the global CSS reset, but it leaves a duplicate, still-broken story file behind at Overview
Issue Details (click to expand)CRITICAL
Resolved Since Last Review
Files Reviewed in This Update (5 files)
Fix these issues in Kilo Cloud Previous review (commit 79b4d28)Status: 4 Issues Found | Recommendation: Address before merge Executive SummaryHighest-risk issue: Overview
Issue Details (click to expand)CRITICAL
WARNING
SUGGESTION
Files Reviewed (7 files)
Reviewed by claude-sonnet-5 · Input: 26 · Output: 4.8K · Cached: 515.8K Review guidance: REVIEW.md from base branch |
| style={{ top: '40%', left: '40%' }} | ||
| /> | ||
| {/* Drifting blobs — decorative, skip under reduced motion */} | ||
| {!shouldReduce && ( |
There was a problem hiding this comment.
Would it be better to use the automatic method for respecting user motion preferences?
There was a problem hiding this comment.
I think a hybrid approach works best here.
We can use <MotionConfig reducedMotion="user"> at the root level to reduce boilerplate and automatically respect the user preference for motion across standard Motion components. Motion will disable transform and layout animations for reduced motion users while other supported animations can still run.
However useReducedMotion is still useful for components where we explicitly want to change the rendering logic. For example in FrontierGlow (OnboardingStepModel.tsx) the drifting blobs are purely decorative so using !shouldReduce lets us avoid rendering them entirely instead of leaving them as static background elements.
Proposed solution:
Add <MotionConfig reducedMotion="user"> around the root providers so standard Motion components respect reduced motion automatically
Keep useReducedMotion only in components where reduced motion requires conditional rendering or other custom logic such as FrontierGlow
Summary
Adds a global @media (prefers-reduced-motion: reduce) CSS reset that kills all CSS animations and transitions when users enable reduced motion in their OS settings. Additionally, apply useReducedMotion() to Framer Motion animated components (HeaderLogo, BeadBoard, and OnboardingStepModel) to conditionally skip JS-driven animations.
Closes #4174
Reviewer Notes
The global CSS reset (*{ animation-duration: 0.01ms !important }) covers all CSS-based animations product-wide. Framer Motion components use the library's useReducedMotion() hook to conditionally skip animated props. Per-component changes are minimal the safety net does most of the work.