Tag
A short label for a status, a category or a count. It can be read-only, dismissible, selectable or operational, as the kit draws it. If the status needs a sentence, it is a Notification.
Live preview
Rendered by the component itself from the same generated tokens as the rest of the site.
<Tag variant="primary">New</Tag>Installation
Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.
npx shadcn@latest add https://www.graphite-ui.com/r/tag.jsonimport { Tag, SelectableTag, OperationalTag } from '@/components/ui/tag'Anatomy
One slot: a short string or a number. The pill shape comes from the full radius, so the ends stay round at any label length.
- LabelRequiredShort text, or a number capped at `max`.
- IconOptionalThe kit's leading Icon, a 16px glyph before the label. Decorative.
Variants
The kit’s eight Read-only colours plus warning, three sizes, an optional leading icon and a dismiss button, and the kit’s two interactive forms: selectable and operational. Neutral and Medium are the defaults. A number over max (99 by default) shows as 99+.
API reference
Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.
| Prop | Type | Default | Notes |
|---|---|---|---|
| variant | 'neutral' | 'primary' | 'secondary' | 'info' | 'success' | 'danger' | 'warning' | 'high-contrast' | 'outline' | — | The kit's Tag - Read-only colours by role (Gray, Purple, Teal, Blue, Green, Red, High contrast, Outline), plus warning, which the kit does not draw. The operational form takes the first six. |
| size | 'sm' | 'md' | 'lg' | — | 18, 24 and 32px, the kit's Small, Medium and Large. The label is 12/16 Regular at every size, inset 8 (12 at Large). |
| disabled | boolean | — | |
| icon | ReactNode | — | |
| max | number | 99 | The cap for a numeric label. A number above it shows as the cap with a plus (99+), and the full number stays in the accessibility tree as visually hidden text. |
| onDismiss | — | — | The kit's Dismissible. A trailing close button, named "Remove <label>", that calls it. |
| dismissLabel | — | — | The close button's accessible name, for a dismissible tag whose label alone would not say what closing does (the multi-select's count tag, "Clear all selected items"). "Remove" and the label by default. |
| selected / onSelectedChange | — | — | The selectable form only, the kit's Tag - Selectable. A toggle button with aria-pressed. |
| onClick | — | — | The operational form only, the kit's Tag - Operational. A tag that opens or does something. |
Design tokens
Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.
surface-variantFill on the neutral tag and on an unselected or disabled selectable tag.on-surface-variantLabel on the neutral tag and an unselected selectable tag.primary-containerFill on the primary tag and a selected selectable tag.on-primary-containerTheir label.secondary-containerFill on the secondary (Teal) tag.on-secondary-containerIts label.info-containerFill on the info (Blue) tag.on-info-containerIts label.success-containerFill on the success variant.on-success-containerIts label.danger-containerFill on the danger variant.on-danger-containerIts label.warning-containerFill on the warning variant.on-warning-containerIts label.on-backgroundFill on the high-contrast tag, the inverse pair.backgroundLabel on the high-contrast tag.surfaceFill on the outline tag, and a disabled close button.on-surfaceLabel on the outline tag.outlineThe 1px edge on the outline tag, an unselected selectable tag and a neutral operational tag.primaryThe primary operational tag's edge and hover fill, the Tag focus ring (the kit binds primary here rather than the focus step), and the disabled fill, edge and label through its disabled steps.on-primaryThe label on a hovered primary or danger operational tag.secondaryThe secondary operational tag's edge.infoThe info operational tag's edge.successThe success operational tag's edge.dangerThe danger operational tag's edge and hover fill.spacingThe label inset, the icon inset and the close button's padding.radiusPill shape, via `full`.textThe label, Caption/1 12/16 Regular at every size.
Usage
The contract's prohibitions, written as the choices you will actually face.
Do
- Keep the label to a word or two. A tag names a state, it does not explain it.
- Say the status in the label text. "Failed" on danger reads without the colour; a red dot does not.
- Pass a count as a number, not a string, so it caps at 99+ instead of widening the row.
- Use neutral for categories that carry no status, so the status colours keep their meaning.
Don’t
- Wrap a read-only tag in a link or click handler. A tag that does something is the operational form, which is a real button with a focus ring.
- Pick a variant for its hue. With a red source colour, primary and danger look nearly the same.
- Reach for warning casually. It is the one variant the kit does not vouch for.
- Set a colour by hand for a status the variants do not cover. Use a generated role or use neutral.
Accessibility
What the component does for you, and what it leaves to you.
- Roles
- A read-only tag is a plain span, read inline as text, with no tab stop. The selectable form is a toggle button carrying aria-pressed; the operational form is a button; the dismiss control is a button named “Remove” and the label.
- Focus
- The interactive forms draw the kit’s Tag focus: a 2px primary ring 1px outside the pill, and a 1px primary ring inside the close button.
- Colour
- The variant colour is a second signal, never the only one. A source colour near a status hue collapses primary and danger, so the label text has to carry the meaning.
- Counts
- When a count is capped, the visible 99+ is aria-hidden and the full number is rendered as visually hidden text, so every screen reader reads the exact figure.
- Contrast
- Each variant pairs a container role with its own on-container role, and those pairs are measured at the theme’s target, AA or AAA.
Figma parity
The kit's Tag page ships 276 variants across 4 sets, one of them a private close button. The code exposes 9 props. This table is where those two facts are reconciled instead of quietly diverging.
| Kit axis | Values | Code | How it maps |
|---|---|---|---|
| Color | Blue · Teal · Green · Purple · Red · Gray · High contrast · Outline | variant | All eight: Gray is neutral, Purple primary, Teal secondary, Blue info, Green success, Red danger, plus high-contrast and outline. Warning has no kit colour. |
| Size | Small · Medium · Large | size | One to one: 18, 24 and 32px, the label 12/16 Regular at every size, inset 8 (12 at Large). |
| Icon · Dismissible | Booleans | icon · onDismiss | The leading 16px icon and the trailing close button, as the kit stacks them. |
| State | Enabled · Disabled · Skeleton | disabled | Disabled is a prop. Skeleton has no counterpart: nothing loads into a tag. |
| Tag - Selectable | Selected: False · True | SelectableTag | A toggle with aria-pressed. Hover is drawn identical to rest in the kit and is left that way. |
| Tag - Operational | Enabled · Hover · Focus · Disabled · Skeleton | OperationalTag | A button. The kit fills on hover only for Purple and Red; the other four are drawn identical to rest and left that way. The kit binds Purple’s hover label to the disabled tone, a slip; the code uses on-primary. |
