Description
Description
V2's AnimatedNumber still exposes animation cells to assistive technology and gives multi-digit numbers the wrong reading order.
With the unmodified production component and CSS rendered in Chromium / Solid 1.9.15:
- Value
12looks correct in LTR, but the accessibility snapshot contains text2, then1. - During a
5to6update, the accessibility snapshot exposes0123456789three times instead of the actual count. The existing motion-duration CSS variable was increased only to capture this transient state.
The component reverses digits in DOM order and renders 30 cells per animated strip, without aria-hidden on the visual digits. The parent span's aria-label does not replace those children in the observed accessibility tree: production component.
CSS hides the strip at rest, but exposes it while animating; visual row-reverse does not repair accessibility reading order: production CSS. A current consumer is ToolCountLabel.
OpenCode version
2.0.26 source build, latest v2 commit 4617210822bdbb31e5749751afbfb4584ccedac8 (audited 2026-10-12). Both component and CSS are unchanged at current dev commit 707de5832656e89cc0f04436aa1cd5f4f91c5763.
Steps to reproduce
Render <AnimatedNumber value={12} /> with its production CSS, inspect its accessibility tree, then increment a single-digit value during animation.
Expected: one correctly ordered, current numeric value. Expose stable text for that value and hide the visual animation from assistive technology regardless of motion state.
Related reports
V2 continuation of Problem 2 in #41408, closed during V1 migration triage. #43643 concerns visual RTL ordering in the same component. Streaming-content exposure is tracked separately in #54652 and addressed by PR #51084, which does not fix this component.
Activity
opencode-agent commentedon Oct 12, 2026
Thanks for the detailed report. I was able to reproduce this. I rendered the production
AnimatedNumberwith its CSS in headless Chromium and read the Playwright accessibility snapshot:value={12}is exposed as2 1.5→6update (with--tool-motion-odometer-msraised so the snapshot lands mid-animation), it is exposed as0 1 2 3 4 5 6 7 8 9three times.The component and CSS are the same in v2.0.26 and on the latest
dev(707de58), so it still happens there. The script below reproduces it.Related: #43643 (wrong visual digit order in RTL locales, caused by the same DOM reversal), #41408 (the v1 report of this problem).
Reproduction script (repro.ts)
Run it:
bun repro.tsargszero commentedon Oct 12, 2026
I'd like to work on this.
I read the component at
packages/ui/src/components/animated-number.tsxand the report matches the code:digits()reverses the label (digits = [...label].reverse()), so the DOM exposes2before1for12, and eachDigitrenders a 30-cellTRACKstrip that stays in the accessibility tree whether or not it is animating —data-animatingis only a visual hook andaria-labelon the root does not replace its children.My plan, kept to the a11y contract the issue names:
aria-hidden="true"covers the whole visual tree12and not as2 1No visual change: the strip stays in the DOM and the CSS keeps driving the odometer, and the reversed DOM order plus
row-reversestays as it is — the fix is about what assistive technology sees, not about what the eye sees.I will verify with a component test in the same Storybook harness the rest of the repo's browser tests use (
packages/ui/src/**/*.stories.tsxis already picked up by the shared Storybook config): it renders the production component and asserts the accessibility snapshot is the current value in order at rest and mid-animation, and that noanimated-number-cellis exposed.Opening the PR against
devas soon as today's output cap allows.fix(ui): read AnimatedNumber once, in order, in any direction
fix(ui): read AnimatedNumber once, in order, in any direction
fix(ui): read AnimatedNumber once, in order, in any direction