Tag

A short label for a category, a status or a keyword, optionally closable and optionally something the reader can pick.

Accessibility

What Tag does for you, and what it leaves to the page around it.

What a Tag is, and is not

A tag renders as a plain <span>, or as an <a> when you pass href. It has no role, no tab stop and no state, so a screen reader reads the words inside it and nothing else: not the fill and not the color.
  • The label has to make sense without the tag around it. Draft in a row of twelve is read as the word “Draft”, so put the category in the text (“Status: Draft”) or in a visible heading above the group.
  • A Tag accepts role, tabIndex, aria-* and key handlers and passes them to its element, for the rare tag that has to be more than text.
  • A tag that runs an action should be a Button. A Tag with an onClick gets a click ripple but no focus ring, no Enter or Space and nothing announced.
  • A tag that is only a label is fine as it is. Do not give it tabIndex={0}: a tab stop that does nothing costs every keyboard user a keypress per tag.

Naming the close button

The close button is the one part of a Tag that is a control. It is a <span role="button" tabIndex={0}> that answers Enter and Space, named by the locale's close string: “Close” in English.
  • “Close” is rarely the right name in a row of tags. Ten tags make ten buttons called “Close”, and a reader listing the buttons learns nothing.
  • Pass closable an object for a better name: its aria-* keys reach the button, so closable={{ "aria-label": "Remove filter: React" }} names the action and its target. A closeIcon node changes the glyph but not the name.
  • A disabled tag's close button is aria-disabled and out of the tab order. It stays visible but does nothing, so leave it out unless you want that.
  • Removing a tag removes the element that had focus. Move focus to the next tag or the container, and say what happened in a live region.
  • onClose runs before the tag hides, so put a confirmation there, behind preventDefault().

Color is not the signal

A tag's color is only a fill and a text color. A red tag does not say “error” to a screen reader, or to a reader who cannot tell red from green. The word has to say it.
  • Put the state in the text: “Failed”, not a red tag carrying the build number.
  • status adds a dot, which is one more color. The five dots differ only by hue.
  • An icon-only tag has no text. title is a weak name, so give the tag role="img" and an aria-label, since a label on a span with no role is not reliably read. A visually hidden word does not work here, because any children make the tag stop being icon-only.
  • An icon beside a label is decoration and needs no name of its own.

Contrast across the ramps

Preset tags render their text one shade darker than Ant Design's, so most presets meet WCAG AA. Status tags (success, processing, warning, error) use the theme's status text color. Solid and disabled tags keep their own colors.
  • yellow and gold still fall just short of 4.5:1. Pair them with a clear word, or pick another color for text that has to be read.
  • variant="solid" puts light text on the color, and most presets miss AA that way, warm ones most. Prefer filled on a warm hue.
  • Solid gray palettes fill two steps darker, so their white text stays readable in both modes.
  • emphasis="subtle" swaps the fill for a neutral one, so check the combinations you ship.
  • size="small" text still needs 4.5:1. The 3:1 allowance is only for large text.
  • Nobody checks a raw CSS color for you, and its fill does not change in dark mode. Check a hex tag in both modes.
  • Check both modes again after any token change.

Reaching a checkable tag

Tag.CheckableTag is a control: it has role="checkbox", aria-checked and a tab stop, which it drops when disabled.
  • Space toggles it and Enter does not. Readers will try Enter, so where that matters use a Checkbox or a Button.
  • Tag.CheckableTagGroup renders a <div> with no role or name. Give it role="group" and an aria-label that says what the set is for.
  • A single-choice group still announces each option as a checkbox, with no hint that picking one clears the last. Use a Radio group when only one may be picked.
  • Every option is a tab stop, so a long set is slow to get past. Switch to a Select once it grows.

Motion

  • The tag's transitions and click ripple all turn off with motion: false in your token file's seed.
  • status="processing" pulses for as long as the tag is on screen. The word beside it says the same thing, so the pulse is safe to turn off.
  • Put any motion you add behind motion-safe:, as motion-safe:shimmer-text does.
  • A tag that appears or disappears as a filter changes is new content. Announce it in a live region.