Chip

A small pill you act on: a callsign that toggles its assignment, an assign-to choice, a frequency, a filter tag. The interactive sibling of Badge, with a free colour, a hover-swapped icon and its own remove button.

Call #2291 · 2 assigned

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add chip

Usage#

usage.tsxTSX
import { Chip, ChipIcon, ChipRemove } from "@/components/ui/chip"export function Example() {  return (    <Chip selected={on} onClick={() => setOn(!on)}>      1-ADAM-12    </Chip>  )}

A Chip is a <button type="button"> unless you give it another element through asChild. Reach for it when the pill does something: toggles, assigns, filters, links or drags. A pill that only shows a status or a count is a Badge.

Anatomy#

import { Chip, ChipIcon, ChipRemove } from "@/components/ui/chip"<Chip>  <ChipIcon />  Label</Chip><Chip asChild>  <span>    Label    <ChipRemove />  </span></Chip>

Assign to#

Extra-small outline chips in a call banner: one click hands the call to a unit, a second click takes it back. The department colour rides on the edge and the mark; the chosen unit takes the brand edge and wash.

10-50 Vehicle collision, Vinewood Blvd
Assign to

Frequencies#

A leading ChipIcon and a numeric label: chips are tabular-nums, so a row of frequencies lines up digit for digit. Wrap a set in role="group" with a name, so the pressed chip is announced in context.

Filter tags#

A tag whose only action is its × is a <span> (through asChild) holding a ChipRemove: a button cannot sit inside a button. The × has its own name, so say what it removes. A display-only chip does not answer hover or press; only the × does.

LSPDOpen
ReportUnitStatus
R-10421-ADAM-12Open
R-10451-ADAM-15Open

Variants and tones#

soft (default) fills the pill: the muted ground for neutral, the tone's own wash otherwise. outline keeps only the edge. Tones are neutral, brand, info, success, warning and destructive; the label takes the tone's legible -strong ink.

Sizes#

xs is 20px tall, sm (default) 24px. Both reach a 40px target (44px on touch) through an invisible extension, so a row of small chips stays easy to hit without growing.

Colour#

color takes any CSS colour: a department's hex from your config, a theme variable. It tints the edge and the ground and adds a leading mark in that colour; the label stays in the foreground ink, since an arbitrary hex as text carries no contrast guarantee. The tint is an opacity layer, not color-mix(), so it holds in old embedded browsers too.

Selected#

selected paints the brand edge and a brand wash over any variant, tone or colour (the colour mark stays). On the default button it also sets aria-pressed, so the chip is announced as a toggle; leave it undefined on a chip that is not one.

Icons#

ChipIcon goes before the label (leading) or after it (trailing) and tucks in toward the edge. Pass idle and hover instead of children to swap one glyph for another while the chip is hovered or focused: a check that becomes an × says “click to remove” before the click. Both glyphs stay mounted and cross-fade.

Remove#

ChipRemove is its own <button>, last in the chip, hugging the trailing edge at either size. Its target is 40px tall but never wider than itself, so it never takes a tap meant for the next chip. Name it with label.

Open Open LSPD Priority

asChild hands the chip's paint to your element: a link to a unit's record, or the element a sortable (dnd-kit's useSortable) puts its listeners on. Hover and press answer on a link and on anything with role="button"; pass static to a drag handle so the press scale does not fight the drag. Try the arrow keys on a focused chip below.

1-ADAM-12
1-ADAM-12
2-LINCOLN-30
7-MARY-3

Disabled#

disabled dims the chip in any state and takes it out of the pointer's way: the native disabled on the default button, aria-disabled with asChild.

API reference#

Chip

The pill. A <button type="button">, or your element with asChild (Radix Slot). Forwards every button prop; className is merged last.

PropTypeDefaultDescription
variant"soft" | "outline""soft"soft fills the pill (the muted ground for neutral, the tone's wash otherwise); outline keeps only the edge.
tone"neutral" | "brand" | "info" | "success" | "warning" | "destructive""neutral"The hue of the label, edge and wash. The label takes the tone's -strong ink on a light ground.
size"xs" | "sm""sm"xs is 20px tall, sm 24px. The hit area grows to 40px either way.
colorstring-Any CSS colour (a hex, var(--purple)): tints the edge and ground and adds a leading mark in that colour. Takes over the tone's hue; the label stays foreground.
selectedboolean-Chosen: the brand edge and wash. Sets aria-pressed on the default button; with asChild only data-selected is set. Leave undefined for a chip that is not a toggle.
disabledboolean-disabled on the default button, aria-disabled with asChild.
staticbooleanfalseOpts out of the press scale (a drag handle, a chip in a dense live list).
asChildbooleanfalseRenders the child element as the chip: a link, a <span> tag, a drag handle.
Data attributeDescription
data-slot="chip"On the root. The leading colour mark is data-slot="chip-mark".
data-sizexs or sm.
data-selectedPresent while selected.
data-disabledPresent while disabled.
CSS variableDescription
--chip-colorSet from color; drives the mark, the edge and the wash.

ChipIcon

A decorative <span> (aria-hidden) for a leading or trailing glyph. Forwards every span prop.

PropTypeDefaultDescription
childrenReact.ReactNode-A plain glyph, when the icon does not swap.
idleReact.ReactNode-The glyph at rest, when the icon swaps on hover.
hoverReact.ReactNode-The glyph on the chip's hover or keyboard focus (a check that becomes an ×). Both glyphs stay mounted and cross-fade.

ChipRemove

A <button type="button"> with an × glyph. Place it last, in a chip that is not a button itself (a <span> through asChild). Forwards every button prop.

PropTypeDefaultDescription
labelstring"Remove"The accessible name: say what goes ("Remove Patrol").
childrenReact.ReactNode-Another glyph in place of the ×.

chipVariants

The tv recipe, with the slots root, mark, icon, iconIdle, iconHover and remove.

FAQ#

A Badge shows something: a status, a count, a version. A Chip is acted on: pressed, toggled, removed, followed or dragged. If nothing happens when you click it, it is a Badge.