callstack / callstack/react-native-paper

No keyboard focus indicator on most components, and the three that have one each did it differently

未关闭
#5,083 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
主要语言
TypeScript
星标
14.5k
派生
2.2k
平均合并
5 天 23 小时
30 天内合并 PR
12

描述

### Current behaviour

Most interactive components have no MD3 keyboard focus indicator.

| has a ring | no ring |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `FAB`, `FAB.Menu`, `Checkbox`, `Switch` | `RadioButton`, `Chip`, `SegmentedButtons`, `List.Item`, pressable `Card`, `Button`, `IconButton`, `Menu.Item`, `Drawer.Item`, `DataTable` rows |

On Android a keyboard user gets nothing. On web you get the browser's default
outline, so not invisible, just not MD3 and different per browser.

The three that do have one each did it their own way. `FAB` has a local
`useFocusRing` hook with a reanimated `SharedValue` and checks
`document.activeElement`. `Checkbox` uses `useState` plus
`src/utils/isKeyboardFocusEvent.ts`. `Switch` uses a `SharedValue` plus the same
util. All three also carry their own copy of

```ts
const webNoOutline = { outline: "none" } as unknown as ViewStyle;
```

`src/theme/tokens/sys/state.ts` already has `focusIndicator: { thickness: 3,
outerOffset: 2 }`. It is just not used in most places.

One thing to know before testing on iOS: `onFocus` never fires there for a `View` or
`Pressable`. `RCTViewComponentView.mm` emits it from `becomeFirstResponder` only
behind the `enableImperativeFocus` flag, which defaults off. Android has no such
gate. So the FAB/Checkbox/Switch rings are already inert on iOS, and this is a web
and Android fix.

### Expected behaviour

A focus indicator on every interactive component, from one shared implementation and
the existing tokens.

Easy to get wrong:

- Keyboard only. A mouse click must not light it. `Pressable`'s own `focused` state
fires for clicks too, see necolas/react-native-web#1849.
- Placement. MD3 puts the ring outside, hence `outerOffset`. But outside gets
trimmed by a clipping ancestor sized to its content, and lands on the neighbour for
things that sit flush. Chips in a horizontal `ScrollView` are the obvious case.
material-web hits the same thing and has an `inward` variant for it.
- Colour. MD3 tonal palettes are luminance matched by tone, so a `secondary` ring
drawn on top of another role at the same tone is about 1:1 and disappears. It needs
to land somewhere with real contrast.

### How to reproduce?

1. `cd example && yarn start`
2. Android, hardware keyboard or `adb shell input keyevent KEYCODE_TAB`. Tab through
the Chip screen. Nothing. Same on List.Item, Segmented Buttons, Radio Button, a
pressable Card.
3. FAB and Checkbox do show a ring, so focus itself works.
4. Web, same screens, you get the browser outline instead.

Android drops keyboard focus the moment you touch the screen. Tab again to get back.

### Preview

Nothing to show as a still, it only exists while something is focused.

### What have you tried so far?

- Read every component for existing focus handling to get the table, rather than
trusting the ticket this came from.
- RN has `outlineWidth` / `outlineColor` / `outlineStyle` / `outlineOffset` since
0.77, implemented on both platforms. Measured on iOS sim, android-35 emulator and
expo web: paints on all three, costs no layout, takes its radius from the view it
sits on, survives that view's own `overflow: 'hidden'`, negative offset draws
inside. An ancestor's `overflow: 'hidden'` does clip it.
- Version floor is fine. The `react-native-reanimated >= 4.3.0` peer dep already
declares `react-native: "0.81 - 0.85"`.

### Your Environment

| software | version |
| ------------------ | ---------------------- |
| ios | 18.3 (simulator) |
| android | 15 / API 35 (emulator) |
| react-native | 0.85.3 |
| react-native-web | 0.21.2 |
| react-native-paper | 6.0.0-alpha.0 (`main`) |
| node | 24.13.0 |

贡献指南

打开贡献指南

调研方向

首先阅读 src/theme/tokens/sys/state.ts、src/utils/isKeyboardFocusEvent.ts 以及 FAB、Checkbox 和 Switch 中现有的焦点处理;使用 cd example && yarn start 重现该行为。在决定共享行为应如何工作之前,检查列出的交互式组件及其当前的焦点处理。完成的标准是:在 Android 和 Web 上,键盘导航会在受影响的组件中显示一致的 MD3 指示器,同时不存在由鼠标触发的光环,也不存在文档中记录的裁剪和对比度问题。

由索引模型根据 Issue 内容生成。

评估

技术栈
react-native, typescript
领域
accessibility, frontend, mobile
Issue 类型
缺陷
难度
5/5
预计耗时
一周以上
活跃度
活跃
描述清晰度
基本清楚
新手友好度
45/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。