Tag Group
Hue-tinted label chips on the 10-hue tag palette — bordered tints, +N overflow collapsing, removable tags.
- Status
- Since
0.3.0- Accessibility pattern
- list and listitem semantics
Last updated
Install
Add Tag Group from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/tag-groupThe same command installs the registry items it composes: @vegastack/chip.
Usage
import { Tag, TagGroup } from "@/components/ui/tag-group";
<TagGroup max={3} aria-label="Categories">
<Tag hue="yellow">Information Technology</Tag>
<Tag hue="blue">B2B</Tag>
<Tag hue="green">SaaS</Tag>
<Tag hue="purple">Enterprise</Tag>
</TagGroup>;Anatomy
Tag Group is a compound component. Every exported part, with the
data-slot it renders (generated from the canonical source):
Examples
The 10-hue palette
Tags ride the --tag-{hue} token trios (-subtle fill, -text ink, base accent) — 10
chromatic hues plus the neutral chip on the surface ladder's rest fill, each AA-gated in both
themes by the token contrast gate. Hue is a decorative label color: it never signals status
(that's Badge's five semantic intents).
Removable tags
Overflow
max collapses the group behind a +N chip; activating it expands the group inline — the
"View all values" pattern from record panes. The group renders role="list", so tag counts
are announced; the overflow control lives inside its own listitem.
Badge vs Tag
A Tag is the Chip primitive at the inline (sm) tier — the hue
formula, the geometry and the 24×24 remove control all live there, and this component adds the
group: the role="list" semantics, the +N overflow disclosure and the focus move that follows
it. Reach for Chip directly only when you need a pill that is not one of a group.
Badge | Tag | |
|---|---|---|
| Voice | status (5 semantic intents) | label (10 decorative hues) |
| Shape | pill (rounded-full) | pill (rounded-full) |
| Extras | dot, loading, animate-in | remove button, group overflow |
API Reference
Tag
| Prop | Type | Default | Description |
|---|---|---|---|
hue | ChipHue | 'neutral' | Chip hue from the tag palette. Decorative label color — never a status
signal (that's Badge's job). |
onRemove | (() => void) | — | Render a remove affordance and call this when it is activated. The button
is labelled "Remove {label}" from the tag's text content via removeLabel. |
removeLabel | string | 'Remove tag' | Accessible label for the remove button. Provide a specific label when onRemove is set. |
Data attributes and CSS variables on Tag
| Attribute | Values |
|---|---|
data-slot | "tag" |
TagGroup
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | — | Accessible label for the group list. |
expandLabel | string | `Show ${hiddenCount} more tags` | Accessible label for the expand chip. |
max | number | — | Collapse the group past this many tags behind a +N chip; activating it
expands the group inline (the "View all values" pattern). Omit to always
show every tag. |
Data attributes and CSS variables on TagGroup
| Attribute | Values |
|---|---|
data-expanded | "" |
data-slot | "tag-group" | "tag-group-overflow" |
Accessibility
- Give each
TagGroupanaria-labelor another accessible name; it exposes list/listitem semantics. - Supply a specific
removeLabelwhenever a tag is removable, such as “Remove SaaS”. - The remove control's REAL border box is 24×24 (a round ghost
IconButton), and the+Noverflow control is itself a 28px chip — neither relies on an invisible pseudo-element a nested<button>would clip. - Hue is decorative. Do not rely on a tag color alone to communicate status or meaning.
| Contract | States tested |
|---|---|
| Behaviour | default, expanded |
| Accessibility | expanded, labeled, semantic-html |
| Visual | default, hover |