TerraUI v0.1.1

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.

iPhone 17 Pro · iOS 27
Selected cells take the accent stroke and a check chip, so the selection never depends on colour alone.Selected cells take the accent stroke and a check chip, so the selection never depends on colour alone.
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

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

Usage

<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.

iPhone 17 Pro · iOS 27
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.
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

Prop

Type

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.
  • 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 under the grid.

On this page