Components · Data display & feedback
Tag
A pill that labels or categorises — a keyword, a person, a filter. Tags can be plain, removable, or links. On its own a tag is neutral grey; hovering a removable or linked tag tints it toward Primary.
Preview
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Tag" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `tag` spec (variants: default, removable, linked) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `tag` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
╭──────────────╮ ╭──────────────╮
│ design │ │ frontend ✕ │ ← removable
╰──────────────╯ ╰──────────────╯
| State | Background | Border | Notes |
|---|---|---|---|
| Default | {colors.surface-muted} |
{colors.border-strong} |
— |
| Hover | {colors.surface-secondary} |
{colors.border-strong} |
— |
| Focus | {colors.surface-muted} + 2px solid {colors.border-focus} |
— | Keyboard only |
| Selected | {colors.primary-bg} |
{colors.primary} |
Filter-selected state |
| Disabled | {colors.disabled-bg} |
{colors.disabled-border} |
not-allowed |
| Remove hover | Remove button shows {colors.danger} on hover |
— | Dismissible variant |
When to use
- User-defined categories, labels, or keywords applied to content items.
- Filter chips in a search or filter bar.
When not to use
Do
- Keep tag labels concise — 1–3 words.
- Group related tags with Tag Group so they wrap consistently.
- Make the remove button a distinct focus target from the tag label.
Don't
- Don't use more than 5–6 tags per item before collapsing into a "+N more" overflow.
- Don't use tags as navigation — use links or breadcrumbs.
- Tag labels: noun or short noun phrase, 1–3 words. "Design", "In progress", "High priority".
- Title case for category tags, sentence case for status/content tags.
- Remove button
aria-label: "Remove [tag label] tag". - Never truncate tag text — if too long, reconsider the label.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Move focus to the tag or its remove button |
Enter / Space |
Activate the focused tag — follow link (linked) or select/deselect (filter context) |
Delete / Backspace |
Remove the focused tag (dismissible variant) |
Accessibility
- Remove button:
aria-label="Remove [tag label]"— not just "Remove". - Tag Group:
role="list"witharia-labeldescribing the collection. - Each tag:
role="listitem". - Linked tag:
role="link"with descriptivearia-labelif the visible text alone is insufficient.
Spec
Values and token references resolved straight from the Zetta spec.
tag
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
- borderColor
- {colors.hairline}
- borderWidth
- 1px
- borderRadius
- {rounded.full}
- fontFamily
- Geist
- fontSize
- 12px
- fontWeight
- 400
- lineHeight
- 1.5
- paddingX
- {spacing.sm}
- paddingY
- {spacing.xs}
- display
- inline-flex
- alignItems
- center
- gap
- {spacing.xs}
tag-hover
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
- borderColor
- {colors.primary}
tag-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- 2px
tag-remove
- iconSize
- {icons.sizeMap.sm}
- iconColor
- {colors.muted}
- cursor
- pointer
- borderRadius
- {rounded.full}
- padding
- {spacing.xs}
tag-remove-hover
- iconColor
- {colors.ink}
- backgroundColor
- {colors.surface-pressed}
tag-linked
- textColor
- {colors.primary}
- cursor
- pointer
- textDecoration
- none
tag-linked-hover
- textDecoration
- underline
tag-group
- display
- flex
- flexWrap
- wrap
- gap
- {spacing.xs}
- alignItems
- center