callstack / callstack/react-native-paper

Touch targets fall below the 48dp minimum, and IconButton's hitSlop never applies

Đang mở
#5,079 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
TypeScript
Star
14.5k
Fork
2.2k
Merge trung bình
5 ngày 23 giờ
Pull request đã merge (30 ngày)
12

Mô tả

### Current behaviour

Touch targets are smaller than the 48dp MD3 asks for. The touch area is just the
box the component draws, nothing more.

Measured on iOS 18.3, Android 15 and web. Same numbers on all three.

| component | size and touch area |
| --- | --- |
| `Checkbox` | 40 x 40 |
| `RadioButton` | 32 x 36 |
| `IconButton` (default `size={24}`) | 40 x 40 |
| `Chip` close icon | 26 x 18 |

Tap 1dp outside any of them and nothing happens.

`IconButton` already tries to fix this and it does not work:

```ts
// src/components/IconButton/IconButton.tsx
hitSlop={
TouchableRipple.supported
? { top: 10, left: 10, bottom: 10, right: 10 }
: { top: 6, left: 6, bottom: 6, right: 6 }
}
```

Its `Surface` has `overflow: 'hidden'`, and a parent that clips also clips the
hitSlop. So tapping 3dp outside an `IconButton` misses on both iOS and Android,
even though it asks for 6 or 10. The prop does nothing.

On web there is no hitSlop at all. react-native-web dropped it in 0.13.0:

> The `hitSlop` prop has been removed. This caused incorrect hit targets for mouse
> interactions.

`Pressable` landed in the same release and never had it. So anything using hitSlop
to reach 48dp gets nothing on web.

Also there is no token for this. `src/theme/tokens/sys/state.ts` has
`focusIndicator` but no size values, so 40dp is written out per component and 48dp
does not exist anywhere.

### Expected behaviour

Targets should be at least 48dp.

Two things that are easy to get wrong:

- It is an invisible expansion outside the component, not a resize. The 40dp state
layer on `Checkbox` and `Switch` is correct and should stay. Compose does the same
thing, it grows the target past the component's bounds.
- Only when the thing is actually interactive. No handler means it is not a control
and should get nothing. Same split as #5070.

### How to reproduce?

1. `cd example && yarn start`
2. Open the Checkbox screen and tap 3dp outside the box. Nothing. Tap the middle, it
toggles.
3. Same on Radio Button and Icon Button.
4. Icon Button is the interesting one: 3dp outside is well inside the hitSlop it
passes, and it still misses.
5. On web, inspect an `IconButton`. No hitSlop anywhere in the DOM.

### Preview

Nothing to show. A good build and a bad one look identical, only the tap areas
differ. That is probably why this went unnoticed.

### What have you tried so far?

- Tapped just outside and in the middle of each one, on all three platforms, so a
miss means the area is small and not that the handler is broken.
- Checked it is the `Surface` clipping that kills `IconButton`'s hitSlop. Putting
`overflow: 'hidden'` on the touchable itself is fine, on a parent it is not.
`Checkbox` is ok because its `overflow: 'hidden'` is on a child.
- CSS pseudo elements do not help on web, `::before` gets clipped the same as a real
child. The clipping has to move instead.
- material-web solves web with an invisible absolutely positioned child at
`max(48px, 100%)`. Worth noting it does not give a chip's close icon 48dp, that
stays 24 x 24.

### Your Environment

| software | version |
| ------------------ | ---------------------- |
| ios | 18.3 (simulator) |
| android | 15 / API 35 (emulator) |
| react-native | 0.85.3 |
| react-native-paper | 6.0.0-alpha.0 (`main`) |
| node | 24.13.0 |
| npm or yarn | yarn 4.9.1 |
| expo sdk | 56.0.0 |

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu với src/components/IconButton/IconButton.tsx và src/theme/tokens/sys/state.ts, sau đó kiểm tra việc xử lý chạm vào close-icon của Checkbox, RadioButton, Switch và Chip trên native và web. Chạy example bằng cd example && yarn start và tái hiện các lần bỏ sót bên ngoài từng control. Hoàn thành có nghĩa là các control tương tác có target vô hình ít nhất 48dp mà không thay đổi kích thước phần hiển thị của chúng, các instance không tương tác vẫn giữ nguyên và IconButton hoạt động dù bị clipping trên iOS, Android và web.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
react-native, typescript
Lĩnh vực
frontend, mobile, web-dev
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
55/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.