Skip to content

Tag

A labelled group of tags: removable with a 44px remove button or Backspace (announced), or selectable as filter chips.

  • Free core, MIT
  • Version 2.0.0-rc.1
  • APG pattern: Grid of tags (removable, selectable)

Examples

These are the real React components. Use them with a mouse, touch or keyboard, and switch the theme and density for every example at once.

Default

Skills
Design
Research
Writing
Prototyping
Press Backspace or Delete to remove the focused tag.
Show the code

Default, code

function Removable() {
  const [tags, setTags] = useState(['Design', 'Research', 'Writing', 'Prototyping']);
  return (
    <TagGroup label="Skills" hint="Press Backspace or Delete to remove the focused tag." onRemove={(keys) => setTags((t) => t.filter((x) => !keys.has(x)))}>
      {tags.map((t) => <Tag key={t} id={t}>{t}</Tag>)}
    </TagGroup>
  );
}

<Removable />

Static

Labels
Design
Research
Writing
Show the code

Static, code

<TagGroup label="Labels"><Tag id="a">Design</Tag><Tag id="b">Research</Tag><Tag id="c">Writing</Tag></TagGroup>

Selectable

Filter by status
Open
In review
Done
Show the code

Selectable, code

<TagGroup label="Filter by status" selectionMode="multiple" defaultSelectedKeys={['open']}>
  <Tag id="open">Open</Tag><Tag id="review">In review</Tag><Tag id="done">Done</Tag>
</TagGroup>

Small

Labels
Design
Research
Writing
Accessibility
Prototyping
Content
Show the code

Small, code

function SmallRemovable() {
  const [tags, setTags] = useState(['Design', 'Research', 'Writing', 'Accessibility', 'Prototyping', 'Content']);
  return (
    <div className="max-w-xs">
      <TagGroup label="Labels" onRemove={(keys) => setTags((t) => t.filter((x) => !keys.has(x)))}>
        {tags.map((t) => <Tag key={t} id={t} size="sm">{t}</Tag>)}
      </TagGroup>
    </div>
  );
}

<SmallRemovable />

Overflow

Skills
Design
Research
Writing
Labels
Bug
Docs
Design
Show the code

Overflow, code

<div className="flex flex-col gap-gap-lg">
  <TagGroup label="Skills" maxVisible={3}>
    {['Design', 'Research', 'Writing', 'Accessibility', 'Prototyping', 'Content'].map((t) => <Tag key={t} id={t}>{t}</Tag>)}
  </TagGroup>
  <TagGroup label="Labels" maxVisible={3}>
    {['Bug', 'Docs', 'Design', 'Help wanted', 'Good first issue'].map((t) => <Tag key={t} id={t} size="sm">{t}</Tag>)}
  </TagGroup>
</div>

Empty

Skills
No skills added yet
Show the code

Empty, code

<TagGroup label="Skills" emptyText="No skills added yet">{[]}</TagGroup>

Tokens

The 18 design tokens Tag reads, with the value each has in the theme and density chosen above. Change a token and the component follows, in Figma and in code.

Show all 18 tokens
Tokens Tag reads
TokenValue nowCSS variable
chips.target…--sd-chips-target
color.bg.brand-subtle…--sd-color-bg-brand-subtle
color.bg.canvas…--sd-color-bg-canvas
color.bg.muted…--sd-color-bg-muted
color.bg.subtle…--sd-color-bg-subtle
color.border.brand…--sd-color-border-brand
color.border.control…--sd-color-border-control
color.icon.default…--sd-color-icon-default
color.text.default…--sd-color-text-default
color.text.disabled…--sd-color-text-disabled
color.text.secondary…--sd-color-text-secondary
radius.full…--sd-radius-full
spacing.gap.sm…--sd-spacing-gap-sm
spacing.gap.xs…--sd-spacing-gap-xs
spacing.inset.md…--sd-spacing-inset-md
spacing.inset.sm…--sd-spacing-inset-sm
spacing.stack.xs…--sd-spacing-stack-xs
target.min…--sd-target-min

Install

The npm package isn’t published yet. When it is, this is the install line; until then, this page runs the same build of the package (from commit fa3abf8).

Install

npm i @superduperui/react @superduperui/tokens

Import

import { TagGroup, Tag } from "@superduperui/react"

Props

Tag also takes the props of the React Aria component it’s built on.

Tag props
PropTypeWhat it does
childrenrequiredstringThe tag's text.
size'default' | 'sm'Default 'default'default: target.min tall (44px Comfortable). sm: 24px, for dense desktop tables and filters; its remove button is 24px with a 44px hit area outside it.

When to use it

Use it for

  • Chosen values (skills, labels, recipients)
  • Quick filters (selectionMode="multiple")

Use something else for

  • Statuses that nobody acts on: use Badge
  • Adding tags by typing: pair with Combobox (TagInput, Tier 3)

Accessibility

Screen readers announce "Skills, grid"; on a tag "Design, row"; on remove "Remove Design, button"; after removal "Removed Design"; the overflow button "+2 more, button, collapsed".

WCAG 2.2 AAA criteria the component meets in Comfortable density: 1.4.6, 1.4.9, 2.1.3, 2.3.2, 2.4.13, 2.5.5, 2.5.6.

Compact density meets AA (2.5.8, 24px targets), not AAA.

What your product still has to do

  • Confirm removals that cannot be undone

This release’s test results

Test results for Tag in this release
axe-core (WCAG A, AA, AAA and best practice)140 runs across every story, theme, density and width: 0 violations
Rendered text contrast300 text runs: 0 below 7:1, lowest 11.42:1
Target size24 targets: 0 under 44px in Comfortable
Focus appearance (2.4.13)60 focus stops in 3 browser engines: all pass
Keyboard scriptChromium 4 of 4, Firefox 4 of 4, WebKit 4 of 4

Manual passes with VoiceOver and NVDA haven’t been done yet.

Keyboard

Each step is scripted and run in Chromium, Firefox and WebKit.

Tag keyboard steps
KeyWhat happens
TabTab enters the tag list on the first tag
ArrowRightRight moves to the next tag
BackspaceBackspace removes the focused tag, announces it and focus moves on
TabTab leaves the list

In Figma

The Figma component is TagGroup, with Selection (None, Single, Multiple). The Figma library isn’t published yet.

Figma to code: Selection → selectionMode (None = "none", Single = "single", Multiple = "multiple"); Removable → onRemove; Tags → children; Overflow → (generated: +n from children and maxVisible) when Show overflow; nested Label → label; nested Hint → hint.