A pill you pick. Interactive chips filter or choose; readonly chips label a session's tags.
Light
Interactive
KataSelected
KataPressed · not yet drawn
KataFocus · not yet drawn
KataReadonly
SparringFilter · removable
Green stripeDark
Interactive
KataSelected
KataPressed · not yet drawn
KataFocus · not yet drawn
KataReadonly
SparringFilter · removable
Green stripeSource:
TagChip.tsx, exported from the barrel asTagChip. Filter rules: search-filter. Chips vs. a sheet: sheet-picker.
When to use it
- Interactive: two to six options that stay visible while you choose (a session's tags), or the active filters under a search field.
- Readonly: tags shown inside something else that is already pressable, like a session row.
Not as a checkbox — a chip is a value you are picking, a checkbox is a decision about something already there. Not for seven or more options — use a sheet. Not for a rank: rank is always rows, because a chip cannot carry a swatch and its ring.
Anatomy
| Part | Interactive | Selected | Readonly |
|---|---|---|---|
| Fill | surface.card |
brand.red |
surface.tag |
| Edge | 1px border.control |
1px brand.red |
none |
| Label | 13/400, text.primary |
13/400, brand.onRed |
12/400, text.secondary |
| Padding | spacing.s8 × spacing.s12 |
same, so selecting never resizes | spacing.s6 × spacing.s12 |
| Shape | radius.pill |
radius.pill |
radius.pill |
A row of chips wraps with spacing.s8 between them.
States
- Default and selected as drawn. Selected is red because red is Kata's selected state.
- Pressed:
opacity.pressed0.85 is the target; the component does not draw a press state today. - Focus: a 2px
border.focusring, drawn on the page behind the chip. - Removable filter: an active filter chip carries its own × and removes itself on tap.
- Disabled: none. An option that cannot be chosen is not shown.
Content
- One or two words, sentence case.
- Chips wrap. They never scroll sideways, because a filter you cannot see is a filter you forget is on.
- More than two active filters get a "Clear all" text link after them.
Accessibility
- An interactive chip should announce role
buttonandaccessibilityState={{ selected }}. - The chip's drawn height is about 34pt, under the 44pt target; give it
hitSlopor a 44pt row around it. brand.onRedonbrand.redis 5.00:1 in both themes, so the selected label is legible.- A readonly chip's
surface.tagfill is nearly the page's colour. That is why it is never a control: it has no visible edge.
Known gaps in the shipped component. TagChip sets no accessibilityRole or
accessibilityState, draws no pressed or focus state, and its labels use literal 13/12 sizes
with weight 400 rather than typography.label. The search-filter
chapter still describes the pre-Kata ink fill for a selected filter; the code and Kata use red.
Do and don't
Do
Chips wrap. Every filter stays in sight.
Don’t
A sideways scroll hides exactly the filters at the end.
Code
import { TagChip } from '../../components/ui';
{tags.map((tag) => (
<TagChip
key={tag.id}
variant="interactive"
label={tag.label}
selected={chosen.has(tag.id)}
onPress={() => toggle(tag.id)}
testID={`session-tag-${tag.id}`}
/>
))}