From df5adc99a8aebbdb32ab9cdbbb6e3c1324b0c865 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 11 Sep 2026 10:26:47 -0700 Subject: [PATCH 01/11] restore system focus behavior --- .changeset/native-focus-visuals.md | 10 +++ .../references/sources/flex-x3.md | 14 ++-- package.json | 1 + packages/agentic/components/package.json | 2 +- .../src/common/focusVisualPolicy.test.tsx | 82 +++++++++++++++++++ .../src/common/focusVisualPolicy.ts | 18 ++++ .../components/src/components/AGENTS.md | 42 +++++++++- .../src/components/accordion/SPEC.md | 10 ++- .../components/accordion/accordion.test.tsx | 7 +- .../accordion/spec/accessibility.md | 2 +- .../components/accordion/spec/interaction.md | 2 + .../src/components/accordion/spec/source.json | 8 +- .../src/components/accordion/useAccordion.ts | 2 + .../accordion/useAccordionStyles.ts | 4 +- .../components/src/components/button/SPEC.md | 33 ++++---- .../src/components/button/button.test.tsx | 8 +- .../components/button/spec/accessibility.md | 8 +- .../src/components/button/spec/interaction.md | 9 +- .../src/components/button/spec/source.json | 8 +- .../src/components/button/useButton.ts | 4 +- .../src/components/button/useButtonStyles.ts | 4 +- .../components/src/components/card/SPEC.md | 10 ++- .../src/components/card/card.test.tsx | 5 +- .../src/components/card/spec/accessibility.md | 2 +- .../src/components/card/spec/interaction.md | 2 +- .../src/components/card/spec/source.json | 8 +- .../src/components/card/spec/tokens.yaml | 2 +- .../components/src/components/card/useCard.ts | 2 + .../src/components/card/useCardStyles.ts | 4 +- .../src/components/checkbox/SPEC.md | 40 +++++---- .../src/components/checkbox/checkbox.test.tsx | 8 +- .../components/checkbox/spec/accessibility.md | 19 +++-- .../components/checkbox/spec/interaction.md | 17 ++-- .../src/components/checkbox/spec/source.json | 10 ++- .../src/components/checkbox/useCheckbox.ts | 3 +- .../components/checkbox/useCheckboxStyles.ts | 4 +- .../src/components/list-item/SPEC.md | 10 ++- .../components/list-item/list-item.test.tsx | 7 +- .../list-item/spec/accessibility.md | 6 +- .../components/list-item/spec/interaction.md | 7 +- .../src/components/list-item/spec/source.json | 6 +- .../src/components/list-item/useListItem.ts | 2 + .../components/list-item/useListItemStyles.ts | 4 +- .../src/components/listbox-item/SPEC.md | 29 ++++--- .../listbox-item/listbox-item.test.tsx | 5 +- .../listbox-item/spec/accessibility.md | 6 +- .../listbox-item/spec/interaction.md | 6 +- .../components/listbox-item/spec/source.json | 8 +- .../components/listbox-item/useListboxItem.ts | 2 + .../listbox-item/useListboxItemStyles.ts | 4 +- .../src/components/menu-item/SPEC.md | 28 ++++--- .../components/menu-item/menu-item.test.tsx | 7 +- .../menu-item/spec/accessibility.md | 8 +- .../components/menu-item/spec/interaction.md | 8 +- .../src/components/menu-item/spec/source.json | 8 +- .../src/components/menu-item/useMenuItem.ts | 2 + .../components/menu-item/useMenuItemStyles.ts | 4 +- .../components/src/components/radio/SPEC.md | 14 ++-- .../src/components/radio/radio.test.tsx | 7 +- .../components/radio/spec/accessibility.md | 6 +- .../src/components/radio/spec/interaction.md | 7 +- .../src/components/radio/spec/source.json | 6 +- .../src/components/radio/useRadio.ts | 2 + .../src/components/radio/useRadioStyles.ts | 4 +- .../components/src/components/switch/SPEC.md | 11 ++- .../components/switch/spec/accessibility.md | 7 +- .../src/components/switch/spec/interaction.md | 14 ++-- .../src/components/switch/spec/source.json | 8 +- .../src/components/switch/spec/tokens.yaml | 2 +- .../src/components/switch/switch.test.tsx | 7 +- .../src/components/switch/useSwitch.ts | 2 + .../src/components/switch/useSwitchStyles.ts | 4 +- .../components/src/components/tab/SPEC.md | 14 ++-- .../src/components/tab/spec/accessibility.md | 6 +- .../src/components/tab/spec/interaction.md | 13 +-- .../src/components/tab/spec/source.json | 8 +- .../src/components/tab/spec/tokens.yaml | 2 +- .../src/components/tab/tab.test.tsx | 7 +- .../components/src/components/tab/useTab.ts | 2 + .../src/components/tab/useTabStyles.ts | 4 +- .../components/src/components/tag/SPEC.md | 12 ++- .../src/components/tag/spec/accessibility.md | 7 +- .../src/components/tag/spec/interaction.md | 11 ++- .../src/components/tag/spec/source.json | 8 +- .../src/components/tag/spec/tokens.yaml | 2 +- .../src/components/tag/tag.test.tsx | 7 +- .../components/src/components/tag/useTag.ts | 2 + .../src/components/tag/useTagStyles.ts | 4 +- .../windows/FRNFocusZone/packages.lock.json | 14 ++-- .../windows/Callout/packages.lock.json | 14 ++-- yarn.lock | 12 +-- 91 files changed, 554 insertions(+), 277 deletions(-) create mode 100644 .changeset/native-focus-visuals.md create mode 100644 packages/agentic/components/src/common/focusVisualPolicy.test.tsx create mode 100644 packages/agentic/components/src/common/focusVisualPolicy.ts diff --git a/.changeset/native-focus-visuals.md b/.changeset/native-focus-visuals.md new file mode 100644 index 0000000000..df6aa2d86f --- /dev/null +++ b/.changeset/native-focus-visuals.md @@ -0,0 +1,10 @@ +--- +"@fluentui-react-native/components": patch +"@fluentui-react-native/callout": patch +"@fluentui-react-native/focus-zone": patch +--- + +Use native focus visuals on all platforms by default while retaining the custom +FocusVisual implementation behind a shared evaluation switch. Require React +Native Windows 0.81.35 or newer for the native focus visual crash fix. +Refresh the Windows Callout and FocusZone NuGet locks to the same runtime version. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md b/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md index 1a4180acd8..8828ff5621 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md @@ -33,13 +33,13 @@ cross-platform canonical reference unless the work targets iOS. Translate web concepts deliberately: -| Flex evidence | React Native contract | -| ------------------------------------ | ---------------------------------------------------------------------------------- | -| ARIA attributes or native elements | `accessibilityRole`, `accessibilityState`, labels, and UIA/AX expectations | -| CSS pseudo-classes | explicit interaction state and precedence | -| browser focus selectors and outlines | persistent `FocusVisual`; never conditional `outline*` or RNW native focus visuals | -| CSS pixel or layout rules | React Native units, minimum targets, and platform behavior | -| unsupported browser capability | explicit divergence, dependency, or blocker | +| Flex evidence | React Native contract | +| ------------------------------------ | ----------------------------------------------------------------------------------------- | +| ARIA attributes or native elements | `accessibilityRole`, `accessibilityState`, labels, and UIA/AX expectations | +| CSS pseudo-classes | explicit interaction state and precedence | +| browser focus selectors and outlines | shared native focus policy, with persistent `FocusVisual` retained as the custom fallback | +| CSS pixel or layout rules | React Native units, minimum targets, and platform behavior | +| unsupported browser capability | explicit divergence, dependency, or blocker | Do not copy Flex prose or token tables into the public package. A mutable Marketplace or X3 HEAD change is candidate drift and does not invalidate a diff --git a/package.json b/package.json index 260f993a45..bcfc5da3c8 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "launch-editor@npm:^2.9.1": "2.14.1", "lodash": "^4.18.1", "path-to-regexp@npm:8.3.0": "^8.4.2", + "react-native-windows": "0.81.35", "shell-quote": "^1.9.0", "socks@npm:^2.8.3": "2.8.9", "unicorn-magic@npm:^0.3.0": "^0.4.0", diff --git a/packages/agentic/components/package.json b/packages/agentic/components/package.json index 089a38cac4..1b113a0a8f 100644 --- a/packages/agentic/components/package.json +++ b/packages/agentic/components/package.json @@ -70,7 +70,7 @@ "react-native": "^0.81.6", "react-native-macos": "^0.81.0", "react-native-svg": "^15.12.1", - "react-native-windows": "^0.81.0" + "react-native-windows": "^0.81.35" }, "peerDependenciesMeta": { "@office-iss/react-native-win32": { diff --git a/packages/agentic/components/src/common/focusVisualPolicy.test.tsx b/packages/agentic/components/src/common/focusVisualPolicy.test.tsx new file mode 100644 index 0000000000..71d521f41a --- /dev/null +++ b/packages/agentic/components/src/common/focusVisualPolicy.test.tsx @@ -0,0 +1,82 @@ +/** @jsxImportSource @fluentui-react-native/framework-base */ +import type { ReactElement } from 'react'; +import { StyleSheet } from 'react-native'; +import type { AccessibilityRole } from 'react-native'; + +import { fireEvent, render } from '@testing-library/react-native'; + +import { Accordion } from '../components/accordion/accordion'; +import { Button } from '../components/button/button'; +import { Card } from '../components/card/card'; +import { Checkbox } from '../components/checkbox/checkbox'; +import { ListItem } from '../components/list-item/list-item'; +import { ListboxItem } from '../components/listbox-item/listbox-item'; +import { MenuItem } from '../components/menu-item/menu-item'; +import { Radio } from '../components/radio/radio'; +import { Switch } from '../components/switch/switch'; +import { Tab } from '../components/tab/tab'; +import { Tag } from '../components/tag/tag'; +import { createFocusVisualProps, focusVisualPolicy, getNativeFocusVisualProps } from './focusVisualPolicy'; + +const cases: { name: string; element: ReactElement; role: AccessibilityRole }[] = [ + { name: 'Accordion', element: , role: 'button' }, + { name: 'Button', element: