Chip
A compact pill in one of four shapes: selectable, command, removable, or readonly. Never more than one.
Implementation previewEvery example. In one place.
Explore every named example, including states, compositions, and theme variants.
Example source
packages/design-system/src/components/controls/Chip/Chip.stories.tsxExport PlaygroundImplementation preview Local Storybook build 0.2.0-preview.1. Source specification status does not approve this runtime.
Stories + source JSON ↗Local specification source status: preview. Implementation preview pending sign-off.
A compact pill in one of four shapes: selectable, command, removable, or readonly. Never more than one.
Implementation API 1 entry point · source-derived preview
Read the actual callable API and preserve its composition constraints. Each union branch is a complete alternative; props marked forbidden cannot be supplied.
import { Chip } from '@cloakedinc/design-system';ChipChipProps
packages/design-system/src/components/controls/Chip/Chip.tsx:121
Props must satisfy one complete branch; a forbidden prop cannot be supplied.
SelectableChipProps
| Prop | Type | Presence | Notes |
|---|---|---|---|
onPress | () => void | Required | Fires on tap. Its presence is what makes the root a button. |
selected | boolean | Required | Selected state, exposed as `aria-pressed`. |
onRemove | never | Forbidden | Unavailable here: a selectable chip cannot also be removable. |
removeLabel | never | Forbidden | Unavailable here: there is no remove affordance to name. |
ariaLabel | never | Forbidden | Unavailable here: the visible label names a toggle whose state `aria-pressed` carries. |
busy | never | Forbidden | Unavailable here: only a removable chip has an in-flight removal to report. |
children | React.ReactNode | Required | |
size | ChipSize | Required | Height and padding tier. `sm` suits inline value tokens, `md` filter rows. |
disabled | boolean | undefined | Optional | Blocks press and remove alike, and dims the chip. |
leading | React.ReactNode | Optional | Optional leading visual, usually an `Icon`, `Avatar`, or `SFIcon`. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`selection`). |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the chip. A removable chip's × derives `${testId}-remove` from it. |
CommandChipProps
| Prop | Type | Presence | Notes |
|---|---|---|---|
onPress | () => void | Required | Fires on tap. Its presence is what makes the root a button. |
ariaLabel | string | undefined | Optional | Replaces the visible label as the accessible name when the label alone lacks context. |
selected | never | Forbidden | Unavailable here: a command has no selected state. Pass `selected` to get a toggle. |
onRemove | never | Forbidden | Unavailable here: a command chip cannot also be removable. |
removeLabel | never | Forbidden | Unavailable here: there is no remove affordance to name. |
busy | never | Forbidden | Unavailable here: only a removable chip has an in-flight removal to report. |
children | React.ReactNode | Required | |
size | ChipSize | Required | Height and padding tier. `sm` suits inline value tokens, `md` filter rows. |
disabled | boolean | undefined | Optional | Blocks press and remove alike, and dims the chip. |
leading | React.ReactNode | Optional | Optional leading visual, usually an `Icon`, `Avatar`, or `SFIcon`. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`selection`). |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the chip. A removable chip's × derives `${testId}-remove` from it. |
RemovableChipProps
| Prop | Type | Presence | Notes |
|---|---|---|---|
onRemove | () => void | Required | Fires from the remove button only, never from the chip body. |
removeLabel | string | Required | Accessible name for the remove button. Name the value, as in `Remove sriram@example.com`. |
busy | boolean | undefined | Optional | Shows a spinner in place of the remove icon and blocks the button while its removal is in flight. |
onPress | never | Forbidden | Unavailable here: the remove button owns the only focus stop. |
selected | never | Forbidden | Unavailable here: a removable value has no selected state. |
ariaLabel | never | Forbidden | Unavailable here: the remove button carries the accessible name, via `removeLabel`. |
children | React.ReactNode | Required | |
size | ChipSize | Required | Height and padding tier. `sm` suits inline value tokens, `md` filter rows. |
disabled | boolean | undefined | Optional | Blocks press and remove alike, and dims the chip. |
leading | React.ReactNode | Optional | Optional leading visual, usually an `Icon`, `Avatar`, or `SFIcon`. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`selection`). |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the chip. A removable chip's × derives `${testId}-remove` from it. |
ReadonlyChipProps
| Prop | Type | Presence | Notes |
|---|---|---|---|
onPress | never | Forbidden | Unavailable here: a readonly chip has no press. Add `onPress` to make it interactive. |
selected | never | Forbidden | Unavailable here: a readonly chip has no selected state. |
onRemove | never | Forbidden | Unavailable here: a readonly chip cannot be removed. Add `onRemove` to make it removable. |
removeLabel | never | Forbidden | Unavailable here: there is no remove affordance to name. |
ariaLabel | never | Forbidden | Unavailable here: the visible label is the whole accessible name. |
busy | never | Forbidden | Unavailable here: only a removable chip has an in-flight removal to report. |
children | React.ReactNode | Required | |
size | ChipSize | Required | Height and padding tier. `sm` suits inline value tokens, `md` filter rows. |
disabled | boolean | undefined | Optional | Blocks press and remove alike, and dims the chip. |
leading | React.ReactNode | Optional | Optional leading visual, usually an `Icon`, `Avatar`, or `SFIcon`. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`selection`). |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the chip. A removable chip's × derives `${testId}-remove` from it. |
Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source.
Overview
A compact pill in one of four shapes. A selectable chip takes onPress with selected. A command
chip takes onPress alone. A removable chip takes onRemove. A readonly chip takes neither, for a
locked member of a chip set. Reach for Tag when the pill is a standalone marker or status label,
and Dropdown for a trigger that opens a menu.
The shapes are different elements
This is the reason Chip and Tag are separate components, and the reason press and remove cannot be combined:
| Shape | Element | Focus stops |
|---|---|---|
| selectable | <button aria-pressed> | 1 |
| command | <button> | 1 |
| removable | <span> with a nested remove <button> | 1 |
| readonly | <span> with no focus stop | 0 |
| press+remove | would need a <button> inside a <button> | 2 |
ChipProps is a discriminated union, so the last row cannot be written. Passing onPress and
onRemove together is a type error rather than a runtime surprise.
selected is what separates the first two rows. A chip that reports aria-pressed="false" forever
tells assistive technology it is an unselected member of a selection set — wrong for a chip that
just opens an explainer. Omit selected and the attribute goes away with it.
Use when
size is sm for inline value tokens and md for filter rows. disabled sets the native disabled
attribute and dims the chip. removeLabel is required on a removable chip: the × carries no visible
text, so a generic default would ship a row of buttons all announcing the same thing.
Put a set in a ChipGroup
A bare row of chips reads to assistive technology as loose controls with no relationship. ChipGroup
gives the set an accessible name and owns the row layout, including the wrap and scroll behavior.
Usage by variant
| Choice | Use when | Avoid when |
|---|---|---|
Job: onPress + selected | A filter toggle where selected marks the active option — website filters, feedback options. | Combining with onRemove. A trigger that opens a menu is a Dropdown, not a Chip. |
Job: onPress alone | A one-off command — a risk chip that opens an explainer. ariaLabel names it when the visible label is not a complete phrase. | A member of a selection set. Pass selected there so the toggle state is announced. |
Job: onRemove | A removable value token — entered emails, phones, and addresses in the data-removal profile, where the × selector suffixes the chip testId with -remove. | A filter — a removable chip is an entered value, not a toggle. |
Job: no handler | A readonly, locked member of a chip set — the protected primary name sitting inline with removable name chips. | A standalone marker or status label. Use a Tag there. |
Do and don’t
| Guidance | Example |
|---|---|
| Do | Give a chip onPress, or give it onRemove, or leave both off for a readonly member. |
| Do | Pass selected only when the chip really is a toggle. |
| Do | Use a readonly chip for a locked value inside a chip set, like the primary name in a value list. |
| Don’t | Reach for a readonly Chip as a standalone marker or status label. That is a Tag. |
Component specification
$schema
../../../specs/component-spec.schema.jsonParts
- Id
- container
- Label
- Container
- Description
- A button for a selectable or command chip, a span for a removable or readonly one. Owns size, selected, and disabled.
- Required
- true
- Id
- leading
- Label
- Leading visual
- Description
- Optional icon or avatar before the value.
- Id
- content
- Label
- Content
- Description
- Visible value or filter label.
- Id
- remove-action
- Label
- Remove action
- Description
- The independently named button on a removable chip, and its only focus stop.
Allowed Design Choices
- Variants
- Selectable
- Label
- Selectable
- Description
- Toggles its own state. Renders a button carrying aria-pressed.
- Command
- Label
- Command
- Description
- Runs a one-off action. Renders a button with no aria-pressed.
- Removable
- Label
- Removable
- Description
- Holds a value the user can dismiss. Renders a span with a nested remove button.
- Readonly
- Label
- Readonly
- Description
- A locked member of a chip set. Renders a span with no remove button and no focus stop.
- Sizes
- Sm
- Label
- Small
- Md
- Label
- Medium
Variants
- Id
- selectable
- Label
- Selectable
- Description
onPresswithselected. One focus stop, on the chip itself.- Platforms
- ios
- android
- web
- Id
- command
- Label
- Command
- Description
onPresswithoutselected, plus an optionalariaLabel. One focus stop, on the chip itself.- Platforms
- ios
- android
- web
- Id
- removable
- Label
- Removable
- Description
onRemove. One focus stop, on the remove affordance.- Platforms
- ios
- android
- web
- Id
- readonly
- Label
- Readonly
- Description
- Neither
onPressnoronRemove. Renders a span with no focus stop. - Platforms
- ios
- android
- web
Sizes
- Id
- sm
- Label
- Small
- Description
- 24-unit inline value token.
- Id
- md
- Label
- Medium
- Description
- 32-unit filter or standalone value chip.
Measurements
- Id
- small-min-height
- Label
- Small chip minimum height
- Kind
- exact
- Token
- --cds-size-24
- Value
- 24
- Unit
- px
- Applies To
- container
- sm
- Id
- medium-min-height
- Label
- Medium chip minimum height
- Kind
- exact
- Token
- --cds-size-32
- Value
- 32
- Unit
- px
- Applies To
- container
- md
- Id
- medium-content-gap
- Label
- Medium content gap
- Kind
- exact
- Token
- --cds-space-8
- Value
- 8
- Unit
- px
- Applies To
- leading
- content
- trailing
- remove-action
- md
Layout
- Alignment
- All visible parts align on the pill center line and the container hugs its content.
- Flow
- Optional leading visual, content, then the remove action on a removable chip.
- Notes
- Assign exactly one shape: selectable, command, removable, or readonly.
- Use readonly for a locked member of a chip set, and Tag for a standalone marker or status label.
- Use Dropdown for a trigger that opens a menu.
- Put a set in a ChipGroup rather than a bare row.
Typography And Color
- Typography
- Label
- Chip content
- Description
- Use body-small text at both chip sizes.
- Token
- --cds-type-body-small-size
- Colors
- Label
- Default surface
- Description
- Use the semantic input container.
- Token
- --cds-input-container
- Label
- Selected surface
- Description
- Use the inverse background for selected filters.
- Token
- --cds-bg-inverse
- Label
- Selected content
- Description
- Use inverse text on the selected inverse surface.
- Token
- --cds-text-inverse
- Label
- Content
- Description
- Use primary text emphasis for the represented value.
- Token
- --cds-text-primary
- Label
- Remove action
- Description
- Use secondary text emphasis for the remove icon.
- Token
- --cds-text-secondary
States
- Id
- default
- Label
- Default
- Description
- Shows the neutral pill treatment.
- Id
- selected
- Label
- Selected
- Description
- Inverse fill and inverse text on a selectable chip, exposed as
aria-pressed.
- Id
- pressed
- Label
- Pressed
- Description
- Temporary press feedback on a selectable or command chip.
- Id
- disabled
- Label
- Disabled
- Description
- Sets the native disabled attribute and dims the chip.
- Id
- removing
- Label
- Removing
- Description
- On a removable chip, shows a spinner in place of the remove icon and blocks the remove button while its removal is in flight, exposed as
aria-busy.
Behavior
- Label
- Selectable press
- Description
- Invoke onPress on tap and expose the pressed state. The root is a real button, so activation is native.
- Label
- Command press
- Description
- Invoke onPress on tap without reporting any selection state. Use this shape when the chip runs a one-off action, such as opening an explainer.
- Label
- Remove
- Description
- Invoke onRemove only from the independently labelled remove action.
- Label
- One shape per chip
- Description
- A chip is exactly one of selectable, command, removable, or readonly. The props are a discriminated union, so onPress and onRemove cannot be combined (two focus stops would require a button inside a button), selected is what picks selectable over command, and a chip with neither handler is readonly.
- Label
- Readonly member versus a standalone marker
- Description
- A readonly chip is a locked member of a chip set, so it stays in the chip family alongside its removable siblings. A standalone marker or status label is a Tag, and a pill that opens a menu is a Dropdown.
Accessibility
- Label
- Selectable semantics
- Description
- A selectable chip is a button carrying aria-pressed and the native disabled attribute.
- Label
- Command semantics
- Description
- A command chip is a button with no aria-pressed, because a permanent unpressed toggle state misreports a one-off action. Give it ariaLabel when the visible label is not a complete name.
- Label
- Removable semantics
- Description
- The body of a removable chip takes no focus. Its remove button is the only focus stop.
- Label
- Readonly semantics
- Description
- A readonly chip is a span with no role and no focus stop, so it is announced as text alongside its interactive siblings.
- Label
- Remove name
- Description
- Every remove action carries a value-specific accessible label and a stable test identifier. removeLabel is required, so there is no generic fallback to fall back to.
- Label
- Grouping
- Description
- Put a set of chips in a ChipGroup so they are announced as one group.
Content Adaptation
- Label
- Concise values
- Description
- Keep chip labels short enough to remain scannable in wrapping rows.
- Label
- Value-specific removal
- Description
- Name remove actions with the represented value or category when context is not obvious.
Motion Haptics
- Motion
- Label
- Press feedback
- Description
- Use subtle opacity and scale feedback and remove scale under reduced-motion preferences.
- Haptics
- Label
- Successful interaction
- Description
- Request the selection haptic after an enabled press or removal.
Platform Differences
- Platforms
- web
- Description
- Web renders no focus ring and no keyboard activation handler for either shape.
- Rationale
- The only consumers are a touch iOS WKWebView and a harness previewing that same UI, so a keyboard affordance here would be dead code.