cmux-next: Reduce Motion stops every loop; Status indicators setting (Animated / Static) (cx-sya4n)
Reduce Motion now stops every repeating animation in cmux NEXT, native and web. A new setting, Status indicators: Animated / Static, swaps pulsing and blinking status lights for steady shapes (cx-sya4n). Austin gets headaches from the pulsing lights.
What changed
-
Setting:
appearance.statusIndicator.animationtakesanimated(the default, unchanged look) orstatic. It is under Settings > Appearance > Loading Indicator > Status Indicators. Reduce Motion forces Static.- Which default NEXT should ship is a needs-a-call: #20403 proposes Static.
-
Static, native: each state has a steady shape, so states differ without color:
- working: solid dot
- busy: arc
- paused: outlined circle
- waiting: !
- permission: hand
- question: question bubble
- sign-in: key
- error: circled โ
- done: โ
Short labels are tooltip and VoiceOver text only; sidebar rows get no new visible text. The attention ring uses the steady style, which matches Leo's call to make steady the attention default.
-
Static, web (agent pane): these stop: the mic, model-picker and harness spinners, the floating breathe, the Thinking sweep (its text stays muted), the reply caret, the jump-to-latest dots and the writing marker. Color-only marks get their own shapes (triangle, โ, โ, dot, ring) and accessible labels in every mode.
-
Reduce Motion also stills Home's caret and typing dots, the update and import spinners, and the diff base-picker spinner.
-
Blink cap in every mode: nothing flashes faster than about 2 Hz (
MotionLoop.minimumPeriod). The flash is 1.0 s and the pulse 0.5 s. -
Motion kit for late content (asked for by cx-next-handoff):
- Web:
webviews/src/ui/motion.ts+motion.cssprovide spring tokens (linear()), reveal (a fade plus a 3 px rise), crossfade, and stagger (25 ms steps, at most 4 items). - Native:
Motion.stagger(index:)andMotion.reveal(_:). - Under Reduce Motion both are a plain fade with no stagger. The "Late content" section of
plans/cmux-next/motion.mddocuments them.
- Web:
-
Classic is unchanged. Its audit and a needs-a-call note for Austin are in #20403.
Testing
Red, then green: the failing tests were committed first.
- Web:
webviews/test/static-status-loops.test.tsasserts that under Static or Reduce Motion,document.getAnimations()finds no running infinite animation.motion-kit.test.tscovers the kit.- Red: run 38524527894 (1fb2d2d), with 4 static-status-loops failures and 3 motion-kit failures. Green: run 38528233410 (395e046).
- Swift:
- On the final head c98fa577:
- Fleet Swift suites: 293 passed, 0 failed.
- Release compile: fleet step 22fd4288, BUILD SUCCEEDED.
- Settings exports were regenerated on the merged head. gen-strings --check is clean. Catalogs and exports only add lines; none were deleted.
- Locally: check-motion, crash_ratchet, system-blue, type-scale and text-focus pass. God files: none of these files grew.
- Captures: real cmux NEXT DEV builds on a fleet capture slot, base on the left and head on the right. Host names are blurred. I watched them.
- Chat after a turn in Animated, Static and Reduce Motion. In Static and Reduce Motion, nothing on the head side loops except the system text caret in the composer (see Left over).
- The sidebar and attention ring in Animated vs Static.
Captures
In each clip, base is on the left and head on the right. In order: chat in Static, chat under Reduce Motion, the attention ring in Static, then a still of the Static shapes.
plan gif 20511/chat-static.gif (from chat-static.mp4)
plan video 20511/chat-static.mp4
plan gif 20511/chat-reduce.gif (from chat-reduce.mp4)
plan video 20511/chat-reduce.mp4
plan gif 20511/clip-attention-static.gif (from clip-attention-static.mp4)
plan video 20511/clip-attention-static.mp4
plan image 20511/still-static.png
uploaded 20511/chat-static.gif
uploaded 20511/chat-static.mp4
uploaded 20511/chat-reduce.gif
uploaded 20511/chat-reduce.mp4
uploaded 20511/clip-attention-static.gif
uploaded 20511/clip-attention-static.mp4
uploaded 20511/still-static.png

Full quality:

Full quality:

Full quality:

Left over
- The composer's text-input caret still blinks, at about 1 Hz. That is the system caret.
- The floating, summary and jump dots don't appear in the live captures. The browser test covers them.
- Base reds this PR does not touch: react-apps-check
check:layersonsummary.css(from d77476fd), main-thread concurrency inControlWorkspaceInfoCache.swift, andPaneAnimationsSettingin the package conventions lint.
Changelog
Added: a Status indicators setting (Animated / Static); Reduce Motion now stops every looping animation, and nothing blinks faster than 2 Hz.
Light and dark, base vs head (real cmux NEXT DEV builds of the merge-base and this head, on a capture mini; host name blurred). Each sheet has three columns: base (animated), Status indicators = Static, and Reduce Motion. The rows are the sidebar status rows at 2x, the tab strip and the attention-ring corner. The clips put the sidebar and tabs side by side: on base the spinners turn, and on head with Static or Reduce Motion they are a still dot. In the attention clip a notification arrives: base flashes the ring and head shows a steady one. Pixel diffs of the head recordings show nothing moving except the composer's system caret. The "Release notes" row's subtitle differs only because of when the tabs were seeded. The set has no running agent turn; the chat clips above cover the web Thinking sweep and the reply caret.
Showcase








๐ค Generated with Claude Code