fix(Range): bind form aria attributes on thumbs instead of root - #431
Merged
Conversation
Port of nuxt/ui@f3c2ac21 (#6768). `useFormField`'s `ariaAttrs` — `aria-invalid` and `aria-describedby` — were spread onto `SliderRoot`, which renders no `role`. Assistive technology therefore read an invalid slider as valid and never reached the error text. The widget is the thumb, and the thumb is what carries `role="slider"`, so the attributes move there — both sites, the tooltip-wrapped one and the bare one. Upstream calls this component `Slider`; here it is `Range`, recorded in PORTING.md §1 since #423 — which exists because this very commit was first reported as a no-op on the grounds that no `Slider` component exists. Only the wrapper is renamed: every reka-ui export inside the file keeps its own name, so `SliderRoot` and `SliderThumb` needed no rewriting and the diff is upstream's line for line apart from `ui` -> `b24ui`. The defect was ours in full. The test is upstream's with the guard renamed. `FormField.spec.ts` keys `describe.each` off `__name`, so the block runs only if that value is exactly `Range` — checked in the reporter output rather than assumed, since a guard that never matches is a test that passes without running. Two mutations, each failing a different assertion. Reverting fully fails the role check (`expected undefined to be 'slider'`); leaving the attributes on the root as well as the thumbs fails the count (`length of 1 but got 2`). Both halves are load-bearing. No snapshot moved, which here means the opposite of the usual. `Range`'s own spec renders the component standalone, outside a `FormField`, so `ariaAttrs` is empty and moving an empty object changes no rendered attribute — the `role="slider"` occurrences in those snapshots come from reka-ui and never carried `aria-invalid`. The snapshots could not have caught this defect, which is why it is pinned by a FormField-level test. Also refreshes `.sync/dep-parity.json` and corrects §6, which this port caught contradicting itself. The rule said to refresh the snapshot "when a port touches a manifest"; the guard requires the snapshot's cursor to equal the ledger's, always. The guard is right and the prose was wrong: a snapshot one commit behind has not been compared against current upstream, so if a bump landed in between, this fork still matches the stale file, the guard stays green, and the drift is real — the exact failure the mechanism exists to catch. §6 now says to refresh on every port that advances the cursor. Here that rewrote one line and left all 148 versions identical, which is itself the evidence that nothing drifted. Verify (CI=true): lint · typecheck · test (6708 passed, 6 skipped, 294 files) · build — all green. No docs:generate; the commit touches no docs/.
IgorShevchik
force-pushed
the
sync/nuxt-f3c2ac2
branch
from
August 18, 2026 06:07
824e86d to
7028d96
Compare
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.
Port of
nuxt/ui@f3c2ac21(nuxt/ui#6768). Fourth of six in the queue, after #428.The defect
useFormField'sariaAttrs—aria-invalidandaria-describedby— were spread ontoSliderRoot, which renders norole. Assistive technology therefore read an invalid slider as valid, and never reached the error text. The widget is the thumb, and the thumb is what carriesrole="slider".Both thumb sites — the tooltip-wrapped one and the bare one. The defect was ours in full:
Range.vue:120carried the identical spread.This is the commit that was nearly dropped
Upstream calls the component
Slider; here it isRange. That rename is recorded inPORTING.md§1 as of #423 — which exists because this commit was first reported as a no-op, on the grounds that noSlidercomponent exists anywhere insrc/and 249 ledger entries never mention one. Both true; the search was by name, and the name is the one thing that changed.Only the wrapper is renamed. Every reka-ui export inside the file keeps its own name, so
SliderRootandSliderThumbneeded no rewriting and the diff is upstream's line for line,ui→b24uiaside.Tests
Upstream's case, with the guard renamed to
name === 'Range'.FormField.spec.tskeysdescribe.eachoff__name, so the block is reachable only if that value is exactlyRange. A guard that never matches is a test that passes without running, so this was checked in the reporter output rather than assumed:Two mutations, each failing a different assertion — which is what shows both are load-bearing rather than one covering for the other:
expected undefined to be 'slider'— the invalid element is the rootto have a length of 1 but got 2— duplicated rather than movedWhy no snapshot moved
Worth stating, because a silent snapshot suite normally means a change did nothing. Here it means the opposite.
Range.spec.tsrenders the component standalone, outside aFormField, souseFormFieldyields an emptyariaAttrs— moving an empty object from root to thumb changes no rendered attribute. The 20role="slider"occurrences in those snapshots come from reka-ui and never carriedaria-invalidat all.The snapshots could not have caught this defect. That is precisely why the fix is pinned by a
FormField-level test instead.Verify (
CI=true)lint·typecheck·test·build— all green. Tests 6708 passed | 6 skipped across 294 files.No
docs:generate— the commit touches nodocs/, per §6.Ledger
f065438dreconciled with #428 and squashc602ea06.cursor→f3c2ac21, entry added.Remaining:
cf5f15e3andf6d188bd— both showcase entries for sites built with nuxt/ui, so the content half is a no-op here;f6d188bdalso widens thescreenshotOptionsschema indocs/content.config.ts, which is worth taking.Generated by Claude Code