Chip
Inline filter and selection chips with optional icon and dismiss.
Chip is the soft, rounded label for categories, filters, and selected values. Corners
are fixed at the catalog --radius-scale-md step (8px) in the component — not rounded-control
or rounded-md — so ICS pill buttons and xl boxes do not reshape chips.
By default chips read bg-secondary-subtle (--brand-secondary-subtle). Filled chips use text-fg (chrome ink).
Outline uses border-primary-subtle text-primary — the soft / emphasis pair on the brand ramp.
For per-value categorical hues that must not follow the brand, see Category-colored
tags below (or category colors in the data layer).
Badge is the sharper status mark (seven role variants, asChild). CircleBadge is the
8px dot. See Badge for those.
Chip is presentational — the root has no hover or focus ring. Only the dismiss control
is interactive. For filter dropdowns with selected / open states, use
Filter chip.
Variants
Three visual weights — default, outline, and ghost. All accept an optional leading
icon via iconBefore (same slot pattern as Button). Switch Color in the sidebar
(Washi / ICS) to see the brand remap.
Brand coloring
Soft and emphasis are the chip pair. Soft fill (--brand-secondary-subtle) seats in chrome
via --sys-soft so ink picks up paper temperature; chromatic prefabs pass soft
through unchanged. Emphasis (--brand-primary-subtle) binds direct — lighter primary tint on
the outline border.
| Look | Soft (fill) | Emphasis (border) |
|---|---|---|
| Ink / dry | --neutral-200 / --neutral-800 | --neutral-300 / --neutral-600 |
| Washi | --washi-blossom-soft | --washi-wave-emphasis |
| ICS light | --ics-orange-100 | --ics-violet-200 |
| ICS dark | --ics-orange-600 | --ics-violet-300 |
Atlas maps the same job to --secondary under [data-theme="ics"]. Dry Kit keeps
secondary for the fill wash and puts soft on brand instead.
Dismissible (combobox / multi-select)
Pass onRemove to render a trailing dismiss control. The parent owns the list — typical
pattern: map selected values to chips, filter state in onRemove.
Combobox-style selected values — each chip removes itself from the parent list via onRemove.
- Design
- Engineering
- Product
import { Chip } from '@/registry/dry/ui/badge';
const [values, setValues] = useState(['Design', 'Engineering']);
<div className="flex flex-wrap gap-2">
{values.map((value) => (
<Chip
key={value}
onRemove={() => setValues((v) => v.filter((x) => x !== value))}
removeLabel={`Remove ${value}`}
>
{value}
</Chip>
))}
</div>removeLabel overrides the default accessible name (Remove {children} when children is
a string, otherwise Remove).
Category-colored tags
Grid select/status values use PropertyTag — same chip shape,
categorical hue from the data layer (Badge tag variant + catChipStyle). A High option
stays orange whether the brand primary is ink, wave, or violet.
Role Chip
Category
Palette
catChipStyle in registry/data/lib/cat-colors.ts mixes --sys-cat-* into chroma-stripped
--background and --foreground. Primitives (--cat-orange, etc.) keep their hue; system
adds --chrome-chroma-tag so Washi warms tags slightly without recoloring them with the brand.
| Token on the tag | Mix |
|---|---|
| fill | 30% --sys-cat-* into chroma-stripped --background |
| ink | 45% --sys-cat-* into chroma-stripped --foreground |
| hairline | 38% --sys-cat-* into chroma-stripped --background |
Weights are tuned for WCAG AA (4.5:1) label contrast on the fill across every hue in light and dark, base/warm/cool chrome. Heavier anchor mixes (52 / 82 / 58) read fine by eye but land around ~2:1 on amber in the browser.
<PropertyTag color={option.color}>{option.label}</PropertyTag>Disabled
disabled dims the chip and blocks interaction, including dismiss.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Chip label. |
variant | "default" | "outline" | "ghost" | "default" | default = soft + ink fg; outline = emphasis border + primary text; ghost = primary text only. No root hover. |
iconBefore | ComponentType<{ className?: string }> | — | Leading icon (e.g. Phosphor). Sized to 14px. |
onRemove | () => void | — | When set, renders a dismiss button. Parent removes the chip from its collection. |
removeLabel | string | derived | aria-label for the dismiss button. |
disabled | boolean | false | Disables the chip and dismiss control. |
className | string | — | Extra classes on the root. |
...props | HTMLAttributes<HTMLDivElement> | — | Forwarded to the root div (data-*, etc.). |
Chip renders a <div> (not a <span>) so a dismiss <button> is valid inside the root.
Install
Ships with the badge registry item:
shadcn add @gesso/badgeimport { Chip } from '@/components/ui/badge';