Skip to main content

Chip

Chip is a compact inline label used for filters, entered values, and short metadata. It can optionally expose a built-in remove action that dismisses the chip from the DOM.

Static chips are not keyboard-focusable. A clickable chip makes its root focusable and button-like. A removable chip exposes its remove button as the tab stop. A chip that is both clickable and removable keeps its root as a plain container and moves the click action onto the label, so the label and the remove button are two sibling controls in the tab sequence. Disabled chips are removed from the tab order.

Usage​

import { Chip } from '@takeoff-ui/react-spar';
<Chip removable>Business class</Chip>

Playground​


function PlaygroundDemo() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <Chip>Economy</Chip>
      <Chip variant="success" removable>Confirmed</Chip>
      <Chip variant="danger" appearance="outlined">Delayed</Chip>
    </div>
  );
}

render(<PlaygroundDemo />);

Variants​


function VariantsDemo() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <Chip variant="primary">Primary</Chip>
      <Chip variant="secondary">Secondary</Chip>
      <Chip variant="neutral">Neutral</Chip>
      <Chip variant="info">Info</Chip>
      <Chip variant="success">Success</Chip>
      <Chip variant="danger">Danger</Chip>
      <Chip variant="warning">Warning</Chip>
      <Chip variant="verified">Verified</Chip>
      <Chip variant="purple">Purple</Chip>
      <Chip variant="cyan">Cyan</Chip>
      <Chip variant="business">Business</Chip>
      <Chip variant="teal">Teal</Chip>
      <Chip variant="white">White</Chip>
      <Chip variant="dark">Dark</Chip>
    </div>
  );
}

render(<VariantsDemo />);

Appearances​


function AppearancesDemo() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <Chip appearance="filled">Filled</Chip>
      <Chip appearance="filledLight">Filled Light</Chip>
      <Chip appearance="outlined">Outlined</Chip>
    </div>
  );
}

render(<AppearancesDemo />);

Sizes​


function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      <Chip size="small">Small</Chip>
      <Chip size="base">Base</Chip>
      <Chip size="large">Large</Chip>
    </div>
  );
}

render(<SizesDemo />);

Remove​

The remove button is a real, labeled <button> in the tab sequence, so keyboard and assistive-tech users can reach it directly and activate it with Enter or Space. Its click reports through onRemove only — it never doubles as the chip's onClick. A chip that is also clickable additionally accepts Backspace or Delete while its click action (the label, in that layout) is focused to request removal; those keys do nothing on the remove button itself.

Use onRemove to sync removal with application state. By default, a removable chip dismisses itself after a remove action. Set autoDismiss={false} when the parent owns the chip list — otherwise the chip's internal dismissed state and the parent's list become two sources of truth.


function RemoveDemo() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <Chip removable>Removable</Chip>
      <Chip variant="primary" removable>Cabin bag</Chip>
      <Chip disabled removable>Disabled</Chip>
    </div>
  );
}

render(<RemoveDemo />);

Clickable​

Use clickable when the chip performs an action, such as selecting a filter. Clickable chips are keyboard-focusable and trigger their onClick handler with Enter or Space. When a clickable chip is also removable, the label carries the action (role="button") next to the remove button — a remove <button> nested inside a role="button" root would be hidden from assistive tech.


function ClickableDemo() {
  const [selected, setSelected] = React.useState('Economy');

  return (
    <div className="flex flex-wrap justify-center gap-3">
      {['Economy', 'Business', 'Miles'].map(option => (
        <Chip
          appearance={selected === option ? 'filled' : 'outlined'}
          clickable
          key={option}
          onClick={() => setSelected(option)}
          variant={selected === option ? 'primary' : 'secondary'}
        >
          {option}
        </Chip>
      ))}
    </div>
  );
}

render(<ClickableDemo />);

Accessibility​

  • Static chip roots are not focusable.
  • Clickable chips use a button-like root and support Enter and Space.
  • The remove button is a labeled control in the tab sequence and is activated with Enter or Space.
  • Clickable removable chips place the click action on the label, beside the remove button, so both controls are reachable; they also support Delete and Backspace while that action is focused.
  • Disabled chips do not receive focus (an explicit tabIndex resolves to -1) and do not respond to remove actions.

API Reference​

Chip​

Props​

NameTypeDefaultDescription
childrenReact.ReactNode-Chip content.
variantChipVariant'primary'Color variant.
appearanceChipAppearance'filled'Visual appearance.
sizeChipSize'base'Size scale.
disabledbooleanfalseDisables interactive affordances and remove actions, and keeps the chip out of the tab order (a tabIndex given to a disabled chip resolves to -1).
removablebooleanfalseRenders a remove button that dismisses the chip from the DOM when pressed.
clickablebooleanfalseMakes the chip keyboard-focusable and button-like for click actions. The root carries the action; when the chip is also removable, the action moves onto the label slot so the remove button stays a sibling control that keyboard and assistive-tech users can reach separately.
autoDismissbooleantrueWhether the chip removes itself from the DOM after a remove action. Removal is tracked in internal state, so set autoDismiss={false} when the parent owns the chip list (e.g. renders chips from an array) and let onRemove drive the parent's state — otherwise the chip and the parent's list become two sources of truth.
classNamesPartial<Record<ChipSlot, string>>-Per-slot extra classes.
slotPropsPartial<Record<ChipSlot, React.HTMLAttributes<HTMLElement>>>-Per-slot HTML-attribute overrides. root.role / root.tabIndex describe the click action and follow it onto whichever node carries it (the root, or the label of a clickable removable chip); they resolve as instance role / tabIndex → slotProps.root → wrapper default.
classNamestring-Appends custom classes to the root slot.

Events​

NameTypeDefaultDescription
onRemove() => void-Called when the remove button is pressed or a focused clickable chip receives Backspace/Delete. The remove button's click never doubles as the chip's onClick.

Data attributes​

AttributeApplied whenPurpose
data-slot="root"AlwaysStable selector for wrapper styling on the root slot.
data-variantAlwaysReflects the resolved variant prop for theme recipe scoping.
data-typeAlwaysReflects the resolved appearance prop for theme recipe scoping.
data-sizeAlwaysReflects the resolved size prop for theme recipe scoping.
data-clickableWhen clickable is trueStyling hook for chips with a click action.
data-disabledWhen disabled is trueStyling hook for the disabled state.
data-removableWhen removable is trueStyling hook for chip with a remove action.

Type Definitions​

NameDefinition
ChipVariant'primary' | 'secondary' | 'neutral' | 'info' | 'success' | 'danger' | 'warning' | 'verified' | 'purple' | 'cyan' | 'business' | 'teal' | 'white' | 'dark'
ChipAppearance'filled' | 'filledLight' | 'outlined'
ChipSize'small' | 'base' | 'large'
ChipSlot'root' | 'label' | 'remove'