Conversation
commit: |
Bundle size
PerformanceTotal duration: 1,157.27 ms -100.30 ms(-8.0%) | Renders: 76 (+0) | Paint: 1,877.23 ms -144.80 ms(-7.2%)
13 tests within noise — details Check out the code infra dashboard for more information about this PR. |
✅ Deploy Preview for base-ui ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
PR reviewTwo merge-blocking SSR regressions affect disabled-radio focus and React 18 Strict Mode hydration. Both were reproduced on PR head Bugs (2)1. 🔴 Keep disabled radios out of the initial tab orderLocation: guessEnabling index guessing assigns the first radio Failure scenario: Server-rendering Fix: Account for disabled status when resolving the initial tab stop: choose the first enabled radio and leave fully disabled groups untabbable. Add SSR coverage for disabled-first and fully disabled groups. 2. 🔴 Make index guessing stable during React 18 Strict Mode hydrationLocation: guessIndex guessing increments the shared Failure scenario: Hydrating a two-radio group under React 18.3.1 Strict Mode produces Fix: Make initial index assignment stable across discarded Strict Mode renders. Extend SSR coverage to hydrate under React 18 Strict Mode and assert both warning-free hydration and the resulting tab order. VerdictRequest changes - disabled-radio focus and React 18 Strict Mode hydration regressions should be fixed before merging. 🤖 Review generated with Codex |
|
Fixed the first one in 3ccb929. Disabled radios now sit out index guessing, so the server-rendered tab stop lands on the first enabled radio and a fully disabled group stays out of the tab order, with SSR tests for both. The Strict Mode one looks pre-existing rather than new here. I do not think it can be closed at the item level. Memoising the guess by Allocating from a shared counter during render is what Strict Mode is built to catch, so closing it means changing what Separate PR for that, or here? |
Radio items skipped index guessing, so SSR markup left every radio at tabindex=-1 until layout effects ran.
Index guessing gave the first radio tabindex=0 in the server markup without checking whether it was disabled, so a disabled first radio, or a fully disabled group, rendered a tab stop on an element that should not hold one. Disabled radios now sit out the guess, so the server-rendered tab stop lands on the first enabled radio and a fully disabled group stays out of the tab order.
3ccb929 to
df9987c
Compare
Server-rendered
RadioGroupmarkup now putstabindex="0"on the first radio andtabindex="-1"on the rest, so the group is in the tab order before hydration.Radio.RootpassesguessintoCompositeItem, which forwards it throughuseCompositeItem(destructured so it does not leak onto the DOM). ArenderToStringassertion checks that the first radio istabindex="0"and the rest-1.Related to #5597 (the
tabindexhalf). This should not close the issue: server markup still omitsaria-labelledbyon the radiogroup.Composite highlight starts at index
0during render, but Radio items did not guess their index from render order, so every item stayed at-1and none matched.MenuItem,SelectItem,ComboboxItem, andOTPFieldInputalready passguess: trueintouseCompositeListItemso the index exists during SSR. Radio went throughCompositeItem, which did not accept or forwardguess.After hydration, the existing map flush still moves the tab stop onto the checked or first enabled radio. Arrow-key roving and the client "checked radio starts with
tabIndex=0" test are unchanged.Decision
Use the index-guess already used by other composites, scoped to Radio.
Alternative: invert
Fieldsetlegend-id generation so SSR also emitsaria-labelledby, and/or defaultguesson Tabs, Toolbar, Toggle, and Navigation Menu.The labelled-by half is a child-to-parent registration that current Fieldset SSR tests explicitly keep unset until hydration. Changing it needs a design for missing legends and caller-supplied legend ids. Can follow up on the labelled-by half.
The reporter already confirmed locally (against 1.7.0) that forwarding
guessemitstabindex="0"/tabindex="-1", hydration does not mismatch, and tabbing back into the group still lands on the checked radio.Test plan
Fixes #5597