Chip

A small, dense label for a tag, a count or a status. Never shrinks, never wraps.

API

Props

proptypedefault
as?ElementType"div"the element or component to render
active?booleanfalseinverts the chip: foreground becomes the fill
className?stringmerged over the defaults, so it wins
…restprops of astyped against the chosen element

Variables

token
--margo-shadow-chipits elevation, declared for both themes

Examples

Usage

tsx
<Chip>typescript</Chip>
<Chip>tailwind</Chip>
<Chip active>react</Chip>
typescript
tailwind
react

Details

Dense by construction

The chip is built to survive tight rows: it never wraps, it never shrinks, and it trims the leading above the capitals so the text sits optically centred instead of floating on its line box. Text is rendered lowercase by the component, which keeps a row of tags even regardless of how the data was capitalised.

tsx
<div className="flex flex-wrap gap-2">
  <List array={tags} itemExtractor={({ row }) => <Chip key={row}>{row}</Chip>} />
</div>

Spacing between chips belongs to the container, not to the chip — a chip with a margin of its own would be wrong in every layout but the one it was tuned for.

The lowercase is a default and not a constraint: content that carries its own casing — a country code, a currency, a proper noun — says so with a className.

tsx
<Chip className="normal-case">{country}</Chip>

<Chip className="uppercase">{currency}</Chip>

Active

active inverts the chip, painting it in the foreground colour. The contrast is deliberately strong: a chip is small, and a subtler distinction would be lost at that size.

It is a visual state and nothing more. When the chip is a filter, render it as a button and say so with aria-pressed; otherwise the change is invisible to anyone not looking at the screen.

tsx
const [selected, setSelected] = useState("all");

<div className="flex flex-wrap gap-2">
  <List
    array={tags}
    itemExtractor={({ row }) => (
      <Chip
        key={row}
        as="button"
        active={selected === row}
        aria-pressed={selected === row}
        onClick={() => setSelected(row)}
      >
        {row}
      </Chip>
    )}
  />
</div>