# Selectable cell

A quiet cell for picking items in dense grids and rows. Selection reads through its stroke and a check chip, protection through a dashed frame and a text badge.

![Selected cells take the accent stroke and a check chip, so the selection never depends on colour alone.](https://ui.terratcg.app/previews/selectable/grid/ios-light.png)

```tsx
import { SelectableCell, Text } from "@terratcg/ui";
import { useState } from "react";
import { View } from "react-native";

const CARDS = ["Red fox", "Barn owl", "Hedgehog", "Grey heron"];

export default function SelectableGrid() {
  const [picked, setPicked] = useState(() => new Set(["Barn owl", "Grey heron"]));
  const toggle = (card: string) =>
    setPicked((current) => {
      const next = new Set(current);
      if (next.has(card)) next.delete(card);
      else next.add(card);
      return next;
    });
  return (
    <View style={{ flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: 12 }}>
      {CARDS.map((card) => (
        <SelectableCell
          key={card}
          accessibilityLabel={card}
          selected={picked.has(card)}
          onPress={() => toggle(card)}
          style={{ width: 140 }}
        >
          <View style={{ height: 88, padding: 12, justifyContent: "flex-end" }}>
            <Text variant="label">{card}</Text>
          </View>
        </SelectableCell>
      ))}
    </View>
  );
}
```

## Import

```tsx
import { SelectableCell } from "@terratcg/ui";
```

## Usage

```tsx
<SelectableCell accessibilityLabel={card.name} selected={picked.has(card.id)} onPress={() => toggle(card.id)}>
  <CardThumbnail card={card} />
</SelectableCell>
```

The cell draws the frame and the selection cue around your content, clipped to its corners, and reports a press. The selection rules are yours: toggle `selected` in `onPress`, and set `protected` on an item your rules keep out of the selection, such as the last copy of a card.

## Examples

### Protected and disabled

A protected cell wears a dashed frame and a text badge and never toggles; a disabled one fades but keeps its cue.

![A protected cell wears a dashed frame and a text badge and never toggles; a disabled one fades but keeps its cue.](https://ui.terratcg.app/previews/selectable/protected/ios-light.png)

```tsx
import { SelectableCell, Text } from "@terratcg/ui";
import { View } from "react-native";

export default function SelectableProtected() {
  return (
    <View style={{ flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: 12 }}>
      <SelectableCell selected={false} protected protectedLabel="Last one" accessibilityLabel="Red fox, kept" style={{ width: 140 }}>
        <View style={{ height: 88, padding: 12, justifyContent: "flex-end" }}>
          <Text variant="label">Red fox</Text>
        </View>
      </SelectableCell>
      <SelectableCell selected disabled accessibilityLabel="Barn owl" style={{ width: 140 }}>
        <View style={{ height: 88, padding: 12, justifyContent: "flex-end" }}>
          <Text variant="label">Barn owl</Text>
        </View>
      </SelectableCell>
    </View>
  );
}
```

## Props

### SelectableCellProps

| Prop              | Type                   | Description                                                                                                                                                    |
| ----------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selected`        | `boolean`              | Whether the cell is part of the current selection.                                                                                                             |
| `protected?`      | `boolean`              | Kept out of selection by the host's rules (for example the one item a user must keep). Shows a labelled badge, stays readable, never toggles. Default: `false` |
| `protectedLabel?` | `string`               | Badge text for the protected state. Default: `"Kept"`                                                                                                          |
| `disabled?`       | `boolean`              | Inactive: no action and no feedback; the selection cue stays visible. Default: `false`                                                                         |
| `children?`       | `ReactNode`            | Host content, clipped to the cell's corners: a thumbnail, a label, a row.                                                                                      |
| `style?`          | `StyleProp<ViewStyle>` | Outer style: width, margins. Colors come from the theme.                                                                                                       |

SelectableCell also accepts React Native's `Pressable` props, such as `accessibilityLabel` and `testID`, and `style` for its width and margins. `SELECTABLE_CELL_BORDER` exports the stroke width, 2 points, by which the content is narrower than the cell on each side.

## Accessibility

- **Role:** `checkbox`, with `accessibilityState` keeping `checked` and `disabled` (while `disabled` or `protected`) in sync. Your own `accessibilityState` is merged on top.
- **Label:** pass `accessibilityLabel` with the item's name. For a protected cell, say why in the label too: the badge is not read on its own.
- **Not by colour alone:** selected adds a check chip, protected a dashed frame and a text badge.
- **Touch target:** the cell is as large as you make it. Keep it at least 44 pt on iOS and 48 dp on Android.
- **Reduce Motion:** the cell no longer shrinks when pressed; the press dim stays.

## Platform differences

- **iOS and Android:** the same frame, chip and press on both, with no ripple.
- **Feedback:** a press calls ThemeProvider's `feedback` handler with `"select"`. A protected or disabled cell reports nothing.
- **Web:** SelectableCell renders through react-native-web for these docs, but the web is not a supported target.

## Rules

- Use it for picking several items among many. For one setting, use a [Switch](/docs/switch).
- Keep the content quiet: a thumbnail and a short label. The cell carries the selection cue; the content shouldn't add another.
- Use `protected` for items your rules keep, with a short `protectedLabel`, and `disabled` only for items that can't be acted on for now.
- Show what the selection will do in an [Action tray](/docs/action-tray) under the grid.
