Skip to content

feat(react-progress): migrate ProgressBar from CSS to motion components#35883

Open
robertpenner wants to merge 7 commits intomicrosoft:masterfrom
robertpenner:refactor/progressbar-motion
Open

feat(react-progress): migrate ProgressBar from CSS to motion components#35883
robertpenner wants to merge 7 commits intomicrosoft:masterfrom
robertpenner:refactor/progressbar-motion

Conversation

@robertpenner
Copy link
Collaborator

@robertpenner robertpenner commented Mar 19, 2026

image

Previous Behavior

  • The indeterminate ProgressBar sliding animation was driven by CSS @keyframes that animated the left style property.
  • Because left animation triggers layout recalc, the animation ran on the main thread and was susceptible to jank.
  • Reduced motion (opacity pulse) was handled via @keyframes and @media (prefers-reduced-motion: reduce).

New Behavior

  • The indeterminate animation is now powered by a custom motion component.
  • It animates translate instead of left, making the animation run on the compositor thread and jank-proof.
  • Reduced motion is handled via the motion component's reducedMotion option.
  • The indeterminate animation is exposed as an indeterminateMotion slot, allowing consumers to customize, replace, or disable it (pass null).

Code Changes

Motion Migration

  • Created progressBarMotions.ts with a ProgressBarIndeterminateMotion motion component.
  • Updated renderProgressBar.tsx to wrap the bar slot with the motion component when value is undefined (indeterminate).
  • Removed CSS @keyframes animations and related styles from useProgressBarStyles.styles.ts.
  • Added @fluentui/react-motion as a dependency.

Motion Slot

  • Added motionSlot() utility and MotionSlotProps type to @fluentui/react-motion, like the existing presenceMotionSlot() but for one-way animations. (NOTE: this react-motion change will be moved to another PR)
  • Added indeterminateMotion slot to ProgressBarSlots, resolved via motionSlot() in useProgressBar_unstable.
  • Added a MotionCustom story demonstrating how to use the slot to override the indeterminate animation.
image

Testing

  • Performance tested using the Jank Simulator Edge extension.
    • The previous left-based CSS animation becomes choppy when the main thread is busy.
    • But the new translate-based motion component runs smoothly without disruption.
  • Tested reduced motion by toggling animations via OS setting and DevTool's Rendering tab.
  • The new motion slot is tested in the MotionCustom Storybook example.

@robertpenner robertpenner self-assigned this Mar 19, 2026
@robertpenner robertpenner force-pushed the refactor/progressbar-motion branch from 2b62711 to 9e32153 Compare March 19, 2026 17:03
@robertpenner robertpenner changed the title feat(react-progress): migrate ProgressBar root rotation to motion components feat(react-progress): migrate ProgressBar from CSS to motion components Mar 19, 2026
@github-actions
Copy link

github-actions bot commented Mar 19, 2026

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-components
react-components: entire library
1.297 MB
323.993 kB
1.296 MB
323.963 kB
-296 B
-30 B
react-progress
ProgressBar
15.7 kB
6.214 kB
20.097 kB
7.834 kB
4.397 kB
1.62 kB
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-accordion
Accordion (including children components)
103.471 kB
31.341 kB
react-avatar
Avatar
48.367 kB
15.336 kB
react-avatar
AvatarGroup
17.45 kB
6.995 kB
react-avatar
AvatarGroupItem
61.608 kB
19.326 kB
react-breadcrumb
@fluentui/react-breadcrumb - package
115.355 kB
31.542 kB
react-charts
AreaChart
412.369 kB
126.436 kB
react-charts
DeclarativeChart
763.093 kB
220.471 kB
react-charts
DonutChart
322.798 kB
96.987 kB
react-charts
FunnelChart
314.351 kB
94.048 kB
react-charts
GanttChart
395.488 kB
119.947 kB
react-charts
GaugeChart
322.229 kB
96.435 kB
react-charts
GroupedVerticalBarChart
403.358 kB
122.516 kB
react-charts
HeatMapChart
397.559 kB
121.802 kB
react-charts
HorizontalBarChart
302.525 kB
89.127 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
Legends
242.471 kB
71.602 kB
react-charts
LineChart
423.71 kB
128.491 kB
react-charts
PolarChart
351.416 kB
107.38 kB
react-charts
SankeyChart
220.381 kB
67.866 kB
react-charts
ScatterChart
403.085 kB
122.621 kB
react-charts
Sparkline
91.393 kB
28.708 kB
react-charts
VerticalBarChart
439.829 kB
128.225 kB
react-charts
VerticalStackedBarChart
409.354 kB
124.017 kB
react-components
react-components: Button, FluentProvider & webLightTheme
70.397 kB
19.96 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
236.65 kB
68.725 kB
react-components
react-components: FluentProvider & webLightTheme
43.612 kB
14.022 kB
react-datepicker-compat
DatePicker Compat
225.158 kB
63.633 kB
react-dialog
Dialog (including children components)
102.12 kB
30.394 kB
react-menu
Menu (including children components)
170.313 kB
51.895 kB
react-menu
Menu (including selectable components)
173.491 kB
52.475 kB
react-message-bar
MessageBar (all components)
23.294 kB
8.63 kB
react-motion
@fluentui/react-motion - createMotionComponent()
4.156 kB
1.818 kB
react-motion
@fluentui/react-motion - createPresenceComponent()
5.908 kB
2.442 kB
react-motion
@fluentui/react-motion - PresenceGroup
1.727 kB
823 B
react-overflow
hooks only
12.117 kB
4.627 kB
react-persona
Persona
55.322 kB
17.279 kB
react-popover
Popover
133.719 kB
41.451 kB
react-portal-compat
PortalCompatProvider
8.386 kB
2.624 kB
react-table
DataGrid
159.346 kB
44.935 kB
react-table
Table (Primitives only)
40.997 kB
13.172 kB
react-table
Table as DataGrid
130.566 kB
35.939 kB
react-table
Table (Selection only)
68.954 kB
19.303 kB
react-table
Table (Sort only)
67.597 kB
18.923 kB
react-tag-picker
@fluentui/react-tag-picker - package
186.596 kB
55.849 kB
react-tags
InteractionTag
13.666 kB
5.459 kB
react-tags
Tag
29.521 kB
9.389 kB
react-tags
TagGroup
82.211 kB
24.143 kB
react-teaching-popover
TeachingPopover
112.414 kB
34.219 kB
react-timepicker-compat
TimePicker
108.174 kB
35.695 kB
react-toast
Toast (including Toaster)
102.56 kB
30.608 kB
react-tree
FlatTree
147.668 kB
42.127 kB
react-tree
PersonaFlatTree
149.496 kB
42.513 kB
react-tree
PersonaTree
145.556 kB
41.337 kB
react-tree
Tree
143.734 kB
40.964 kB
🤖 This report was generated against d2d48440be57395a523dfd901621d99b58340ef4

@github-actions
Copy link

Pull request demo site: URL

@robertpenner robertpenner marked this pull request as ready for review March 19, 2026 18:15
@robertpenner robertpenner requested review from a team and dmytrokirpa as code owners March 19, 2026 18:15
@robertpenner robertpenner force-pushed the refactor/progressbar-motion branch from b5c7568 to 5019491 Compare March 20, 2026 14:22
@@ -0,0 +1,7 @@
{
Copy link

@github-actions github-actions bot Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/CalendarCompat 4 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/CalendarCompat.multiDayView.default.chromium_1.png 478 Changed
vr-tests-react-components/CalendarCompat.multiDayView - High Contrast.default.chromium.png 1194 Changed
vr-tests-react-components/CalendarCompat.multiDayView - Dark Mode.default.chromium.png 1088 Changed
vr-tests-react-components/CalendarCompat.multiDayView - RTL.default.chromium.png 478 Changed
vr-tests-react-components/Charts-DonutChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Charts-DonutChart.Dynamic - RTL.default.chromium.png 5570 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 599 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 160 Changed
vr-tests-react-components/Positioning.Positioning end.chromium.png 732 Changed
vr-tests-react-components/ProgressBar converged 4 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 208 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 405 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - RTL.default.chromium.png 162 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 326 Changed
vr-tests-react-components/TagPicker 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled - RTL.disabled input hover.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled - High Contrast.disabled input hover.chromium.png 1319 Changed

There were 4 duplicate changes discarded. Check the build logs for more information.

…slots

Adds motionSlot() and MotionSlotProps, parallel to presenceMotionSlot(),
for components that use continuous/looping animations (createMotionComponent)
rather than enter/exit animations (createPresenceComponent).
Replace hardcoded ProgressBarIndeterminateMotion with a customizable
indeterminateMotion slot using motionSlot(). Adds a MotionCustom story
demonstrating how to override the indeterminate animation.
@robertpenner robertpenner force-pushed the refactor/progressbar-motion branch from 864edca to 7b676e9 Compare March 25, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant