Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1a774eb
fix: paint the MD3 state layer on checkbox
likevy Sep 7, 2026
1ddbd87
fix: ripple the checkbox press inside the state layer
likevy Sep 7, 2026
6ebdc86
fix: meet the 48dp touch target on checkbox
likevy Sep 7, 2026
1f0e89c
fix: offset the checkbox focus ring by 2dp
likevy Sep 7, 2026
37ab78a
feat: add tapTargetStyle to the checkbox
likevy Sep 7, 2026
d7205a3
feat: require an accessible name on a standalone checkbox
likevy Sep 7, 2026
314cb5e
test(checkbox): adapt tests after rebase onto main
likevy Sep 22, 2026
c060f2e
fix(checkbox): skip the accessible name warning for hidden checkboxes
likevy Oct 30, 2026
a5c403e
refactor(checkbox): drop the tapTargetStyle prop
likevy Oct 30, 2026
8fb45fe
refactor(checkbox): remove internal test IDs
likevy Oct 30, 2026
05ae4ce
chore(example): name the example checkboxes
likevy Oct 30, 2026
d9ba88d
fix(checkbox): toggle on space key on web
likevy Oct 30, 2026
64590ec
fix(checkbox): size the outer container to the touch target
likevy Oct 30, 2026
17d078b
fix(checkbox): judge the accessible name by what the platform supports
likevy Oct 30, 2026
ec21e74
fix(checkbox): press in and out on space like enter
likevy Oct 30, 2026
df3fe38
test(checkbox): cover a row disabled in the middle of a space press
likevy Oct 30, 2026
cfa49af
refactor(checkbox): rename the checkbox state layer resolver
likevy Oct 30, 2026
afb4c40
docs(checkbox): describe the checkbox changes against 5.x
likevy Oct 30, 2026
71c7a4d
docs(checkbox): name the checkbox in the usage example
likevy Oct 30, 2026
b6de531
fix(checkbox): press out when a space press loses onPress
likevy Oct 30, 2026
3797c68
test(checkbox): cover the hiding props on the platforms that ignore them
likevy Oct 30, 2026
c79fe23
fix(checkbox): warn about a missing accessible name only in development
likevy Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/6.x/docs/guides/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,3 +573,43 @@ mutually exclusive options, use `SegmentedButtons`.
/>
</>
```

### Checkbox

#### Interaction states

Hover, focus and press now paint a Material Design 3 state layer, a 40dp circle around the box, on every platform. Hover and focus use `primary` when the checkbox is selected and `onSurface` when it is not, a press ripples in the opposite color, and an error checkbox uses `error` throughout. `color` and `uncheckedColor` also set the matching state layer color. Keyboard focus draws a 50dp ring with a 2dp gap around the state layer.

To use the platform ripple instead of the state layer press, pass `rippleColor` on any platform, `background` on Android or `underlayColor` on iOS. Setting `rippleEffectEnabled: false` on the `settings` prop of `PaperProvider` turns off the press ripple either way.

#### Touch target size

`Checkbox` now takes up 48dp instead of 36dp to meet the minimum touch target, so rows with a checkbox can grow up to 12dp taller.

#### Style

`Checkbox` now accepts `style` and applies it to its outer container, so layout styles such as `margin`, `position` and `transform` move the whole checkbox, focus ring included. `width` and `height` do not resize the 48dp touch target.

To keep the 36dp footprint of 5.x, use a negative margin. The touch target stays 48dp and reaches 6dp into the space around the checkbox:

```tsx
// Before (v5)
<Checkbox status="checked" onPress={onPress} aria-label="Notify me" />

// After (v6)
<Checkbox
status="checked"
onPress={onPress}
aria-label="Notify me"
style={{ margin: -6 }}
/>
```

#### Accessible name

A standalone `Checkbox` has no visible label, so it now warns in development when it has no accessible name. Pass `aria-label` or `accessibilityLabel`, or use `Checkbox.Item`, which names the whole row. On Android and web, `aria-labelledby` names it too. A checkbox hidden from assistive technology, for example with `aria-hidden`, does not warn.

```diff
-<Checkbox status="checked" onPress={onPress} />
+<Checkbox status="checked" onPress={onPress} aria-label="Notify me" />
```
40 changes: 32 additions & 8 deletions example/src/Examples/CheckboxExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,51 @@ const STATUSES: Status[] = ['unchecked', 'checked', 'indeterminate'];

type Row = {
label: string;
render: (status: Status) => React.ReactNode;
render: (status: Status, label: string) => React.ReactNode;
};

const ROWS: Row[] = [
{
label: 'Default',
render: (status) => <Checkbox status={status} onPress={() => {}} />,
render: (status, label) => (
<Checkbox status={status} onPress={() => {}} aria-label={label} />
),
},
{
label: 'Disabled',
render: (status) => <Checkbox status={status} disabled />,
render: (status, label) => (
<Checkbox status={status} disabled aria-label={label} />
),
},
{
label: 'Custom color (tertiary)',
render: (status) => <CustomColorCheckbox status={status} />,
render: (status, label) => (
<CustomColorCheckbox status={status} label={label} />
),
},
{
label: 'Error',
render: (status) => <Checkbox status={status} error onPress={() => {}} />,
render: (status, label) => (
<Checkbox status={status} error onPress={() => {}} aria-label={label} />
),
},
];

const CustomColorCheckbox = ({ status }: { status: Status }) => {
const CustomColorCheckbox = ({
status,
label,
}: {
status: Status;
label: string;
}) => {
const theme = useTheme();
return (
<Checkbox
status={status}
color={theme.colors.tertiary}
uncheckedColor={theme.colors.tertiary}
onPress={() => {}}
aria-label={label}
/>
);
};
Expand All @@ -59,6 +74,7 @@ const Interactive = () => {
<Checkbox
status={aChecked ? 'checked' : 'unchecked'}
onPress={() => setAChecked((v) => !v)}
aria-label="Unchecked or checked"
/>
</View>
<Text
Expand All @@ -75,6 +91,7 @@ const Interactive = () => {
<Checkbox
status={bIndeterminate ? 'indeterminate' : 'unchecked'}
onPress={() => setBIndeterminate((v) => !v)}
aria-label="Unchecked or indeterminate"
/>
</View>
<Text
Expand Down Expand Up @@ -114,7 +131,11 @@ const IndeterminateParent = () => {
Parent / children (indeterminate)
</Text>
<View style={styles.parentRow}>
<Checkbox status={parentStatus} onPress={toggleAll} />
<Checkbox
status={parentStatus}
onPress={toggleAll}
aria-label="Select all"
/>
<Text
style={[styles.interactiveLabel, { color: theme.colors.onSurface }]}
>
Expand All @@ -125,6 +146,7 @@ const IndeterminateParent = () => {
<Checkbox
status={child1 ? 'checked' : 'unchecked'}
onPress={() => setChild1((v) => !v)}
aria-label="Apples"
/>
<Text
style={[styles.interactiveLabel, { color: theme.colors.onSurface }]}
Expand All @@ -136,6 +158,7 @@ const IndeterminateParent = () => {
<Checkbox
status={child2 ? 'checked' : 'unchecked'}
onPress={() => setChild2((v) => !v)}
aria-label="Bananas"
/>
<Text
style={[styles.interactiveLabel, { color: theme.colors.onSurface }]}
Expand All @@ -147,6 +170,7 @@ const IndeterminateParent = () => {
<Checkbox
status={child3 ? 'checked' : 'unchecked'}
onPress={() => setChild3((v) => !v)}
aria-label="Cherries"
/>
<Text
style={[styles.interactiveLabel, { color: theme.colors.onSurface }]}
Expand All @@ -173,7 +197,7 @@ const CheckboxExample = () => {
<View style={styles.cells}>
{STATUSES.map((status) => (
<View key={status} style={styles.cell}>
{row.render(status)}
{row.render(status, row.label)}
</View>
))}
</View>
Expand Down
12 changes: 6 additions & 6 deletions example/src/Examples/ListItemExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ScreenWrapper from '../ScreenWrapper';

const CenteredCheckbox = () => (
<View style={styles.centered}>
<Checkbox status="checked" />
<Checkbox status="checked" aria-label="Headline" />
</View>
);

Expand All @@ -30,7 +30,7 @@ const ListItemExample = () => {
<List.Item
title="Headline"
description="Supporting text that is long enough to fill up multiple lines in the item"
right={() => <Checkbox status="checked" />}
right={() => <Checkbox status="checked" aria-label="Headline" />}
/>
<Divider />
</List.Section>
Expand Down Expand Up @@ -66,7 +66,7 @@ const ListItemExample = () => {
title="Headline"
description="Supporting text that is long enough to fill up multiple lines in the item"
left={(props) => <List.Icon {...props} icon="account-outline" />}
right={() => <Checkbox status="checked" />}
right={() => <Checkbox status="checked" aria-label="Headline" />}
/>
<Divider />
</List.Section>
Expand Down Expand Up @@ -114,7 +114,7 @@ const ListItemExample = () => {
left={(props) => (
<Avatar.Text style={props.style} label="A" size={40} />
)}
right={() => <Checkbox status="checked" />}
right={() => <Checkbox status="checked" aria-label="Headline" />}
/>
<Divider />
</List.Section>
Expand Down Expand Up @@ -180,7 +180,7 @@ const ListItemExample = () => {
source={require('../../../example/assets/images/strawberries.jpg')}
/>
)}
right={() => <Checkbox status="checked" />}
right={() => <Checkbox status="checked" aria-label="Headline" />}
/>
<Divider />
</List.Section>
Expand Down Expand Up @@ -252,7 +252,7 @@ const ListItemExample = () => {
source={require('../../../example/assets/images/strawberries.jpg')}
/>
)}
right={() => <Checkbox status="checked" />}
right={() => <Checkbox status="checked" aria-label="Headline" />}
/>
<Divider />
</List.Section>
Expand Down
Loading
Loading