Repository navigation
fix(react-headless-components-preview): remove keyborg and react-tabster from the headless bundle - #36897
Open
Dmytro Kirpa (dmytrokirpa) wants to merge 8 commits into
Conversation
…thin/tooltip paths Move `useFocusWithin` (react-tabster/keyborg focus-within ponyfill) out of the base hooks of Checkbox, Switch, Radio, Slider, RatingItem and ColorArea into their styled hooks, following the existing `useCard_unstable` pattern. The headless package consumes the base hooks and styles focus with native `:focus-within` / `:focus-visible`, so it no longer pulls keyborg for these. Headless Tooltip now detects keyboard-initiated focus with `:focus-visible` instead of keyborg's `keyborg:focusin` + `useIsNavigatingWithKeyboard`. This keeps the microsoft#27576 behavior (no tooltip when focus is restored programmatically after a mouse interaction) and drops the direct react-tabster dependency. Standalone subpath savings (min / gzip): checkbox, switch, radio-group, slider, rating, rating-display ~-4.9 kB / ~-1.85 kB; tooltip -4.4 / -1.65 kB; color-picker -4.7 / -1.7 kB; avatar-group -4.4 / -1.5 kB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGheL1F6Pnu6xDkHvH6XKw
…ve descendant / combobox family keyborg (keyboard-vs-pointer modality tracking, plus a global HTMLElement.prototype.focus patch) was still pulled into the headless bundle by Combobox, Dropdown and TagPicker through react-aria and react-combobox. - react-aria `useActiveDescendant`: replace the keyborg subscription with a small document-level tracker. Keys pressed on the active parent (or Tab) switch to keyboard mode; real mouse/touch presses switch to pointer mode; synthetic screen reader clicks are ignored, matching keyborg's semantics. - react-combobox `useListbox_unstable`: use native `:focus-visible` on the focused listbox instead of keyborg state to decide whether to scroll the active option into view. - react-combobox `useTriggerSlot`: drop `useSetKeyboardNavigation`. The styled hooks with an input trigger (`useComboboxBase_unstable`, `useTagPickerInput_unstable`) keep marking keyborg's global state, since keyborg ignores keys typed in inputs. Button triggers are tracked by keyborg natively. - Forbid `keyborg` in the headless bundle-isolation check. Verified in Chromium for styled v9 and headless Combobox/Dropdown: the active option shows focus-visible styles after keyboard navigation (incl. Tab + Arrow), and not after opening with the mouse or pressing the pointer in the listbox. entire library: 243.61 kB -> 240.02 kB min, 68.82 kB -> 67.46 kB gzip tag-picker: 54.10 kB -> 50.52 kB min, 17.74 kB -> 16.56 kB gzip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGheL1F6Pnu6xDkHvH6XKw
Preserve Tabster's never-hide marker without importing react-tabster, allowing the headless bundle isolation guard to forbid the package. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 92742ce8-99c7-4f9d-b2e2-a6fd46e82d38
📊 Bundle size reportUnchanged fixtures
|
|
Pull request demo site: URL |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 92742ce8-99c7-4f9d-b2e2-a6fd46e82d38
Dmytro Kirpa (dmytrokirpa)
marked this pull request as ready for review
October 16, 2026 10:07
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The replacement modality tracking leaves keyboard focus indicators missing during mounting, focus transfers, and shadow-root navigation.
3 open findings
What changed in this PR
Removes keyborg and react-tabster from the headless bundle while retaining styled-component focus behavior.
Changes:
- Moves focus-within tracking from base hooks to styled hooks.
- Replaces dependency-backed modality tracking and decouples live announcements.
- Strengthens bundle-isolation checks and adds patch release entries.
| File | Description |
|---|---|
| yarn.lock | Removes dependency entries. |
| packages/react-components/react-tag-picker/library/src/components/TagPickerInput/useTagPickerInput.tsx | Synchronizes styled keyboard modality. |
| packages/react-components/react-switch/library/src/components/Switch/useSwitch.tsx | Moves focus tracking to styled hook. |
| packages/react-components/react-slider/library/src/components/Slider/useSlider.ts | Moves focus tracking to styled hook. |
| packages/react-components/react-rating/library/src/components/RatingItem/useRatingItem.tsx | Moves focus tracking to styled hook. |
| packages/react-components/react-radio/library/src/components/Radio/useRadio.tsx | Moves focus tracking to styled hook. |
| packages/react-components/react-headless-components-preview/library/src/components/Tooltip/useTooltip.ts | Uses native focus-visible detection. |
| packages/react-components/react-headless-components-preview/library/package.json | Removes direct react-tabster dependency. |
| packages/react-components/react-headless-components-preview/library/bundle-isolation.config.json | Forbids keyborg and react-tabster. |
| packages/react-components/react-combobox/library/src/utils/useTriggerSlot.ts | Removes shared keyborg synchronization. |
| packages/react-components/react-combobox/library/src/components/Listbox/useListbox.ts | Uses focus-visible for focus-time scrolling. |
| packages/react-components/react-combobox/library/src/components/Combobox/useCombobox.tsx | Relocates keyboard-mode synchronization. |
| packages/react-components/react-color-picker/library/src/components/ColorArea/useColorArea.ts | Moves focus tracking to styled hook. |
| packages/react-components/react-checkbox/library/src/components/Checkbox/useCheckbox.tsx | Moves focus tracking to styled hook. |
| packages/react-components/react-aria/library/src/AriaLiveAnnouncer/useDomAnnounce.ts | Preserves never-hide marker without react-tabster. |
| packages/react-components/react-aria/library/src/AriaLiveAnnouncer/useAriaLiveAnnouncer.test.tsx | Checks never-hide marker. |
| packages/react-components/react-aria/library/src/activedescendant/useActiveDescendant.ts | Adds dependency-free modality listeners. |
| packages/react-components/react-aria/library/package.json | Removes react-tabster dependency. |
| change/@fluentui-react-tag-picker-5df75776-65f2-4016-bb85-1ef306d88ea9.json | Records TagPicker patch. |
| change/@fluentui-react-switch-952e972e-2e02-4ca1-be18-15f5886b8ce4.json | Records Switch patch. |
| change/@fluentui-react-slider-b0854ab1-3aaf-40ab-9ec8-00216538440c.json | Records Slider patch. |
| change/@fluentui-react-rating-35e36343-a399-437c-94c2-6278b0a29447.json | Records Rating patch. |
| change/@fluentui-react-radio-b5245f08-cf3a-4021-9047-61dda565d5c5.json | Records Radio patch. |
| change/@fluentui-react-headless-components-preview-c66e9254-9984-4caf-9bfa-fbe38f9bc106.json | Records headless bundle patch. |
| change/@fluentui-react-combobox-91683066-6dea-43e4-991f-b5e7430b3c86.json | Records Combobox patch. |
| change/@fluentui-react-color-picker-86eb9ff2-4ef8-405a-be5a-48fbd435599c.json | Records ColorPicker patch. |
| change/@fluentui-react-checkbox-9bee5379-d944-413e-b97b-17e75e63d81d.json | Records Checkbox patch. |
| change/@fluentui-react-aria-4058fc9d-5bf6-483f-b4e1-3bc2b98193e5.json | Records ARIA dependency-removal patch. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
Author
|
Copilot resolve the merge conflicts in this pull request |
…ixes # Conflicts: # packages/react-components/react-slider/library/src/components/Slider/useSlider.ts
This branch has not been deployed
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.



Previous Behavior
The headless bundle retained
keyborgand@fluentui/react-tabsterthrough focus-within hooks, Tooltip, active-descendant navigation, and the ARIA live announcer.New Behavior
:focus-visible, and track active-descendant keyboard/pointer modality without keyborg.react-tabsterdependency and decouple the ARIA live announcer while preserving Tabster's never-hide marker.keyborgand@fluentui/react-tabsterto the headless bundle-isolation forbidden packages.Validation:
yarn nx run react-headless-components-preview:verify-bundle-isolation— passed; 1 fixture has no forbidden package modules.yarn nx run-many -t test -p react-aria,react-combobox,react-checkbox,react-color-picker,react-radio,react-rating,react-slider,react-switch,react-tag-picker,react-headless-components-preview --parallel=3— passed; 10 projects, 1,180 tests.Related Issue(s)