Tag

User Experience (UX)

How a tag behaves for every person, input and setting, from what screen readers hear to removing it, following it and every state in between.

Now let's look at how a tag behaves for every person, input and setting. A good tag is accessible, predictable, designed in every state and adaptive.

Accessible

Keep a plain tag plain text

A tag that people can't remove or follow is a word on the page, so the Tag renders it as text in a <span>, with no role and no place in the tab order. A screen reader reads it where it sits, like the words around it, and keyboard users don't stop on something that does nothing when they get there. Carbon's read-only tags work the same way: they "are not in the tab order, are not interactive, and do not receive focus".

Only the parts people act on are interactive, and each is a real element: a link when the tag goes somewhere, and a button when it can be removed. A tag never gets tabindex, role="button" or a click handler on its root, since none of them would tell people what pressing it does.

Name the remove button

A removable tag has a native <button type="button"> after its label, so it's in the tab order, Enter and Space press it, and it never submits a form by accident. Its icon is hidden, so the button needs a name of its own, and the Tag builds it from the label, like "Remove Bug". That name is what people hear and say:

  • A screen reader says "Remove Bug, button", rather than "button" or "×", so people know which tag it removes (WCAG 2.4.6, 4.1.2).
  • Every remove button in a row has a name of its own, so voice control users can say "click Remove Bug", and people who list a page's buttons can tell them apart.
  • removeLabel changes the wording, like for another language. Keep the tag's text in it, since that's what people see and say (WCAG 2.5.3).

The name comes from label, so a removable tag always needs one, even when its default slot shows something else, like a link. Our reference implementation warns you in the console when it's missing.

A tag can take people to a page about what it names, like the other posts on a topic. With href, the label holds a real <a href>, so a screen reader says "Accessibility, link", people can open it in a new tab, and they can see where it goes before they press it.

<p>
  Topics:
  {topics.map((topic) => (
    <Tag key={topic.slug} label={topic.name} href={`/topics/${topic.slug}`} />
  ))}
</p>
  • A link tag looks different from a plain one, with a border, so people can tell which tags go somewhere without relying on colour (WCAG 1.4.1).
  • Its link takes a press anywhere on the tag, and its name is still the label alone, since the leading icon is hidden.
  • We recommend linking only to a page about what the tag names, like a topic's posts or a label's issues. Carbon says not to use tags "as links that direct you to an entirely different page", and a tag that leads somewhere else surprises people. Atlassian's tags can be links too.
  • A link for your router, or one that needs target, rel or download, goes in the default slot, which Links and routers covers.

Contrast and size

  • The label needs at least 4.5:1 against the tag's background, and the focus ring at least 3:1 against what's behind it (WCAG 1.4.3, 1.4.11). Our tokens come to 12.3:1 on white and 10.9:1 on zinc-900, as Tokens shows.
  • The remove button and a link each take a press over at least 24 × 24px, at every size, so people with less precise hands can hit them (WCAG 2.5.8). At xs and sm, a tag is shorter than that, so the hit area reaches past it, the way the Select's tags do.
  • On touch screens, both reach at least 44px tall, which WCAG 2.5.5 asks for at level AAA.

Show focus

When its link has focus, the whole tag shows an outline in the same focus colour as every other component, 2px thick and 2px outside its edge (WCAG 2.4.7). The remove button draws its outline inside its edge, as the Select's tags do, so the ring never covers the tag next to it. Only one of them can have focus at a time, so only one thing on the page ever looks focused.

Predictable

Keyboard

KeyTag
Tab, Shift+TabMoves focus to the tag's link, then to its remove button. A plain or disabled tag isn't a stop
EnterFollows the link, or presses the remove button
SpacePresses the remove button

A tag has no keys of its own. Its link and its remove button are a link and a button like any other, which Tab reaches in the order they come in. Enter follows the link, and Enter or Space press the remove button, as they dismiss Carbon's dismissible tags.

Keys like Backspace or Delete on a focused tag, or the arrow keys between tags, only make sense in a group of tags that has a keyboard model of its own. The Tag Group doesn't add one: it keeps each tag's own tab stops, so these keys do nothing there either.

Move focus when a tag goes

The Tag doesn't remove itself. Pressing its remove button fires remove, and the page decides whether the tag goes, since it's the page that knows what the tag stands for, like a filter or a value in a form. Whatever removes it then has two jobs:

  • Open
  • Bug
  • Assigned to me
const [filters, setFilters] = useState(ALL);
const list = useRef<HTMLUListElement>(null);
const reset = useRef<HTMLButtonElement>(null);

function remove(filter: string) {
  const index = filters.indexOf(filter);
  flushSync(() => setFilters(filters.filter((other) => other !== filter)));
  const buttons = list.current!.querySelectorAll<HTMLElement>("[data-slot=remove]");
  (buttons[index] ?? buttons[index - 1] ?? reset.current)?.focus();
  announce(`${filter} removed`); // through a polite live region
}

<ul ref={list} className="filters" aria-label="Filters">
  {filters.map((filter) => (
    <li key={filter}><Tag label={filter} removable onRemove={() => remove(filter)} /></li>
  ))}
</ul>
<Button ref={reset} size="xs" variant="ghost" onClick={() => setFilters(ALL)}>Reset</Button>
  • Move focus. The remove button had focus, and it's gone with its tag, so focus would drop to the top of the page, and keyboard users would lose their place (WCAG 2.4.3). Move it to the next tag's remove button, or the previous one when it was the last, or to something nearby when no tags are left.
  • Say what went. A screen reader announces where focus lands, but not that a tag disappeared, so announce it through a polite live region, like "Bug removed" (WCAG 4.1.3).

The Select does both for its own tags, which you'll find in select/focus-after-remove and select/announce-removal. The Tag Group does both for any group of tags, in tag-group/focus-after-remove and tag-group/announce-removal, so these aren't rules of the Tag's own.

Don't nest interactive elements

The link and the remove button sit side by side in the tag, never one inside the other. HTML doesn't allow a button inside a link, or a link inside a button (interactive content), so nothing defines what browsers and screen readers should do with one, and pressing the remove button might follow the link too. Carbon keeps it the same way, with the close icon as the only part of a dismissible tag to press.

Keep labels short

A tag is a word or two, like a topic or a label's name, so people can take in a row of them at a glance. Atlassian says to keep labels short, since its tags cut long ones off at 200px. Anything longer belongs in the text around the tags, or on the page the tag links to.

Keep status out of tags

A tag says what something is about, while a status says what state it's in, like "Draft", "Overdue" or "3 unread". Atlassian says the same of its tags: for "workflow status, system state, priority", it points you to its lozenge. A status goes in a Badge, which nobody removes or follows, so the two never look like they mean the same thing. The Badge is on our Roadmap.

Every state

Removable

A removable tag shows its remove button after its label, at the end edge, as wide as the tag is tall. Pressing it fires remove and nothing else, so the tag stays until whatever owns it takes it away, and it's that code that moves focus and announces it, as Move focus when a tag goes explains.

Linked

A link tag has a border, and on hover its background gets stronger and its label is underlined, which tells people it goes somewhere before they press it. On focus, it shows its focus ring around the whole tag. A link tag can be removable too, and its remove button sits above the link, so pressing it removes the tag rather than following the link.

Disabled

A disabled tag is dimmed to 50%, drops its remove button and turns its link off: the <a> loses its href, so it can't be followed, and takes role="link" and aria-disabled="true", so a screen reader still says it's a link that's turned off, as the Button does with a link. It leaves the tab order, like a plain tag. WCAG doesn't ask for contrast on disabled controls (WCAG 1.4.3), but it's best to say somewhere nearby why a tag can't be removed, since people can't find out from the tag.

Read-only

A read-only tag keeps its usual look and drops its remove button, so people can read it but not remove it, like a label someone else added. Its link still works, since reading a tag includes following it. The Select does the same with its tags when it's read-only.

Long labels

A long label stays on one line and ends in an ellipsis, so a row of tags keeps its height, as the Select's tags do. The whole text stays in the DOM, so screen readers read all of it. Sighted people can't, though, so try to keep labels short enough not to be cut off, and give a tag that might be cut off a link to a page that shows its whole name.

Adaptive

Pointer, touch and pen

  • Hover styles only apply to pointers that can actually hover (@media (hover: hover)), otherwise a tap would leave the tag looking hovered.
  • On touch screens, the remove button and a link reach at least 44px tall, as the Select's tags do on coarse pointers (@media (any-pointer: coarse)).
  • At xs and sm, those hit areas reach past the tag, so they can cover a tag on the next row. Leave room between rows of tags, which the Select and the Tag Group do for you.

Motion

The Tag doesn't move. If you'd like a tag to fade out when it's removed, wrap the animation in @media (prefers-reduced-motion: no-preference), so it only runs for people who haven't asked for less motion (WCAG 2.3.3), and move focus once it's gone.

More contrast

When someone asks for more contrast (prefers-contrast: more), as with Increase contrast on macOS and iOS, a link tag's border is drawn in the text colour at full strength, rather than at 55%. The label already passes 4.5:1, so it stays as it is.

Forced colours

Windows contrast themes replace every colour with the user's own, and drop backgrounds and shadows. Every tag gets a real border in CanvasText, as the Select's tags do, and a link tag one in LinkText, so its boundary stays visible without its background. Focus is drawn with outline, in Highlight, and a disabled tag uses GrayText rather than opacity.

Colour scheme

The tag's background and its border mix the text colour, which --color--neutral-text sets with light-dark(), so the Tag follows color-scheme and adapts to your light and dark themes on its own, as long as they set it.

Text size and spacing

The tag's height is a minimum in rem, so it grows with its text instead of clipping it, at 200% text size (WCAG 1.4.4) and with wider letter and word spacing (WCAG 1.4.12). It never gets wider than what holds it, so a long label ends in an ellipsis rather than overflowing it.

Languages and direction

The Tag uses logical properties, so it mirrors itself in right-to-left languages, with its icon on the right and its remove button on the left:

خطأميزة
<div dir="rtl" lang="ar">
  <Tag label="خطأ" leading={<BugIcon />} removable removeLabel={(label) => `إزالة ${label}`} onRemove={remove} />
</div>
  • In right-to-left layouts, it's usually the page that sets dir and lang, on <html>, and the Tag follows it. Set lang on a tag when its language differs from the page's, like a tag for a language's name (WCAG 3.1.2).
  • Translate the remove button's name with removeLabel, keeping the tag's text in it, since screen readers read it in the page's language.

UX Checklist

RuleLevelScopeRequirementBasisCheck
tag/plain-textMustComponentA tag without a link or a remove button has no role and isn't focusableWAI-ARIAUnit test, ARIA snapshot
tag/linkMustComponentWith href, the label holds an <a href>HTMLUnit test
tag/links-and-routersShouldComponentA link in the default slot, like a router's, sits in the label, takes the label's look and hit area, and keeps its own attributesOpen ComponentsUnit test, Visual regression test
tag/remove-buttonMustComponentRemoval is a native <button type="button">, which Enter and Space pressHTML, WCAG 2.1.1 (A)Unit test, Keyboard
tag/remove-nameMustBothThe remove button's name includes the tag's text, like "Remove Bug"WCAG 2.4.6 (AA), 4.1.2 (A)Unit test, axe button-name
tag/no-nestingMustComponentThe link and the remove button are siblings, with neither inside the otherHTMLUnit test, axe nested-interactive
tag/disabledMustComponentdisabled drops the remove button, and a link loses its href and takes aria-disabled="true"HTML, WAI-ARIAUnit test
tag/read-onlyMustComponentreadonly drops the remove button, and a link still worksOpen ComponentsUnit test
tag/not-statusShouldUsageA status or a count, like "Draft" or "3", is a Badge rather than a tagOpen ComponentsReview
tag/not-selectableShouldUsageA tag that toggles is a toggle button, and picking from a set is a Checkbox GroupOpen ComponentsReview
tag/short-labelsShouldUsageLabels are a word or twoOpen ComponentsReview
tag/mirrors-in-rtlShouldBothIt uses logical properties, so the remove button sits at the end edge in right-to-left layouts tooOpen ComponentsReview

FAQ

Tag or something else?

A tag says what something is about, and people can remove it or follow it. For anything else, another component will do a better job:

When you need to…Use
Label what something is about, like a topic or a picked valueTag
Mark a status or a count, like "New" or "3"Badge
Turn one thing on or off, like a filterA toggle button
Pick any number of options out of a fewCheckbox Group
Take people somewhere, from inside a sentenceLink

The Badge and the Link are on our Roadmap. If you're coming from the GOV.UK Design System, keep in mind that its tag is what we call a Badge: it shows "the status of something", and GOV.UK says not to make it a link or a button.

Copyright © 2026