Repository navigation
Checkbox: respect prefers-reduced-motion for the checkmark animation - #8647
Open
adityalenova wants to merge 1 commit into
Open
adityalenova wants to merge 1 commit into
adityalenova wants to merge 1 commit into
Conversation
The clip-path wipe on the checkmark is decorative - whether the mark is shown is driven by visibility - so move the animation and the 230ms hide delay behind @media (prefers-reduced-motion: no-preference), the same way Skeleton and SegmentedControl opt in to motion. With motion reduced the mark is simply present when checked or indeterminate. The animated path keeps the declarations it uses today, including the clipped start value that covers the 80ms delay on checkmarkIn, so the rendered end state is unchanged.
adityalenova
requested review from
siddharthkp
and
a balanced review from Copilot
and removed request for
Copilot
October 31, 2026 10:33
🦋 Changeset detectedLatest commit: 513d61d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This was referenced Oct 31, 2026
This branch was successfully deployed
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.
Closes #8646
The Checkbox checkmark is a
::beforeon the input that wipes in and out with aclip-pathanimation on every toggle, and nothing in the component looked atprefers-reduced-motion, so the wipe played for everyone.Whether the mark is shown is decided by
visibility; theclip-pathanimation only adds motion on top of that. So I moved the decorative parts behind@media (prefers-reduced-motion: no-preference), which is howSkeleton(SkeletonBox.module.css, lines 18-24) andSegmentedControl(SegmentedControl.module.css, line 119) already opt in to motion, and gave the checked/indeterminate mark a static un-clippedclip-path.In
packages/react/src/Checkbox/Checkbox.module.css:&::beforekeepsvisibility: hiddenandclip-path: inset(var(--base-size-16) 0 0 0);transition: visibility 0s linear 230msandanimation: checkmarkOut ...moved into theno-preferenceblock.&:checked::before, &:indeterminate::beforenow setclip-path: inset(0 0 0 0)unconditionally, and keepclip-path: inset(var(--base-size-16) 0 0 0)together withanimation: checkmarkIn ... forwards 80msinside theno-preferenceblock.The clipped start value has to stay next to the
checkmarkInanimation instead of being replaced outright: that animation isforwardswith an 80ms delay, so during the delay the mark is concealed byclip-pathalone. Anyone without a preference therefore gets exactly the declarations it uses today, and the wipe is unchanged. With motion reduced, the mark is present when checked or indeterminate and hidden when unchecked, with no wipe and no 230ms hide delay.The overrides are written on the
&:checked::before/&:indeterminate::beforecompound selectors rather than on the base&::before, because.Checkbox:checked::before(0-2-1) beats.Checkbox::before(0-1-1) and an override on the base selector would not reach the checked state.I left the other animated components alone.
Spinnerand the deprecatedTooltipaside,ActionBarandUnderlineNavanimate withanimation-timeline: scroll(), which is overflow detection doing real work rather than decoration, soprefers-reduced-motionshould not apply there.Changelog
New
Changed
Checkbox: the checkmark'sclip-pathanimation and the delayedvisibilitytransition now only apply when the user has no preference for reduced motion. No API changes.Removed
Rollout strategy
Testing & Reviewing
npx stylelint packages/react/src/Checkbox/Checkbox.module.css— clean.npx vitest run packages/react/src/Checkbox/Checkbox.test.tsx— 16 passed.npx eslint e2e/components/Checkbox.test.tsandnpx tsc --noEmit(root config, which coverse2e/) — clean.e2e/components/Checkbox.test.ts.toHaveCSScannot target a pseudo-element, so they readgetComputedStyle(input, '::before')on[data-component="Checkbox"]. UnderreducedMotion: 'reduce'the checked and indeterminate marks reportanimation-name: none,clip-path: inset(0px),visibility: visibleand a0stransition delay, and clearing the mark hides it immediately. UnderreducedMotion: 'no-preference'the same reads report thecheckmarkIn/checkmarkOutanimations and the0.23shide delay, so the animated behaviour is intact.main: the reduced-motion test fails there and the no-preference test passes, which is what you would want to see.prefers-reduced-motion: reduceemulated and screenshotted the checked, unchecked and indeterminate states. The marks render as they do today.No screenshots are attached, because the change is invisible in a still image: the wipe is gone, the end state is not. I expect the existing
@vrtsnapshots to pass unchanged — CI captures them withcontextOptions.reducedMotion: 'reduce'andanimations: 'disabled', and under both the old and the new CSS the mark ends up fully un-clipped and visible. I could not run the snapshot comparisons locally, so if a Checkbox snapshot does move, that is the thing to look at first.One note on the test tags: the new tests are tagged
@vrtbecause the VRT and AAT shards are the only jobs that run Playwright here (--grep @vrt/--grep @aat), and an untagged test would never run. They assert computed styles rather than taking screenshots, following the forced-colors test ine2e/components/SelectPanel.test.ts.