# Tag

A small label naming a category, with an optional leading icon or picture and remove control; pressable, it opens something about what it names.

Block: `smbk-tag`. Status: candidate. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `tone` | `default`, `brand`, `outline`, `info`, `success`, `danger`, `warning` | `default` | A see-through gray that shows on every surface, brand, outlined, or one of the four feedback families on their subtle surfaces. |
| `pressable` | on / off | off | The tag is a button that opens something about what it names, such as a card, with the hover, pressed and focus looks in the tag's own shape. The wrapper sets it when given onClick, as a popover's opener is; plain markup sets it on a button. |

Parts: `smbk-tag__icon`, `smbk-tag__leading-content`, `smbk-tag__label`, `smbk-tag__remove`

## Markup

```html
<span class="smbk-tag smbk-tag--tone-default">
  <i class="ph ph-palette smbk-icon smbk-icon--size-md smbk-tag__icon" aria-hidden="true"></i>
  <span class="smbk-tag__label">Design</span>
  <button type="button" class="smbk-tag__remove" aria-label="Remove Design">
    <i class="ph ph-x smbk-icon smbk-icon--size-md" aria-hidden="true"></i>
  </button>
</span>
```

## React

```tsx
import { Tag } from './design-system/components/tag/tag.js';

<Tag tone="default" icon="Palette" onRemove={() => {}} removeLabel="Remove Design">Design</Tag>
```

## Stylesheet

```css
/* smbk-tag — Tag. Figma "Tag" 4088:12242.
 * 24 tall on the pill scale, Label SM in sentence case, a 12 icon and cross.
 * The default tone is a see-through gray with no edge and no shadow, so it
 * shows on every surface. Figma draws the edge inside the tag, so the padding
 * gives the edge's width back, and every tag draws one, clear where its tone
 * has none, so all are the same size. A pressable tag is a button drawn the
 * same, with a control's hover, pressed and focus looks (the end of this file). */

.smbk-tag {
  /* The default tone; each feedback tone sets these again below (the tone recipe). */
  --smbk-tone-surface: var(--smbk-color-surface-neutral-subtle);
  --smbk-tone-border: transparent;
  --smbk-tone-text: var(--smbk-color-text-subtle);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--smbk-space-inline-xs);
  height: var(--smbk-size-mark-lg);
  padding: 0 calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline));
  border: var(--smbk-border-width-hairline) solid var(--smbk-tone-border);
  border-radius: var(--smbk-radius-control-sm);
  background: var(--smbk-tone-surface);
  color: var(--smbk-tone-text);
  font-family: var(--smbk-typography-label-sm-font-family);
  font-size: var(--smbk-typography-label-sm-font-size);
  font-weight: var(--smbk-typography-label-sm-font-weight);
  line-height: var(--smbk-typography-label-sm-line-height);
  letter-spacing: var(--smbk-typography-label-sm-letter-spacing);
  text-transform: var(--smbk-typography-label-sm-text-transform);
  white-space: nowrap;
}

.smbk-tag__icon {
  font-size: var(--smbk-size-icon-xs);
}

/* The page's own picture in the icon's place: a box the icon's size, an svg or an img filling it, and any other element
   of the page's own held to it however large it is drawn elsewhere. An svg that fills or strokes with currentColor
   takes the tag's text color, as the icon does. */
.smbk-tag__leading-content {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: var(--smbk-size-icon-xs);
  height: var(--smbk-size-icon-xs);
}

.smbk-tag__leading-content > * {
  max-width: 100%;
  max-height: 100%;
}

.smbk-tag__leading-content > :is(svg, img) {
  display: block;
  width: 100%;
  height: 100%;
}

/* The cross sits inside a 24 px box, the least a press needs; the box reaches past the cross into the gap
   and the padding, so it adds no width and the cross sits where Figma draws it. The focus ring is drawn
   on the cross's own edge.
   The box is the small press box, with its press area on a touch screen, shared in press-box/press-box.css. */
.smbk-tag__remove {
  margin: calc((var(--smbk-size-icon-xs) - var(--smbk-size-target-min)) / 2);
}

.smbk-tag__remove > .smbk-icon {
  font-size: var(--smbk-size-icon-xs);
  border-radius: var(--smbk-radius-full);
}

.smbk-tag__remove:focus-visible {
  outline: none;
}

.smbk-tag__remove:focus-visible > .smbk-icon {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: 0;
}

/* ---- tone ---- */

.smbk-tag--tone-brand {
  background: var(--smbk-color-surface-brand);
  border-color: var(--smbk-color-border-rim-subtle);
  color: var(--smbk-color-text-on-brand);
  box-shadow: var(--smbk-elevation-raised);
}

/* The focus color would not show on the brand fill, so the ring there takes the inverse focus color. */
.smbk-tag--tone-brand .smbk-tag__remove:focus-visible > .smbk-icon {
  outline-color: var(--smbk-color-border-focus-inverse);
}

.smbk-tag--tone-outline {
  background: transparent;
  border-color: var(--smbk-color-border-strong);
}

/* The feedback tones: the tone recipe, each family's subtle surface inside its pale edge. */

.smbk-tag--tone-info { --smbk-tone-surface: var(--smbk-color-feedback-info-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-info-border-subtle); --smbk-tone-text: var(--smbk-color-feedback-info-text-on-subtle); }
.smbk-tag--tone-success { --smbk-tone-surface: var(--smbk-color-feedback-success-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-success-border-subtle); --smbk-tone-text: var(--smbk-color-feedback-success-text-on-subtle); }
.smbk-tag--tone-danger { --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-danger-border-subtle); --smbk-tone-text: var(--smbk-color-feedback-danger-text-on-subtle); }
.smbk-tag--tone-warning { --smbk-tone-surface: var(--smbk-color-feedback-warning-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-warning-border-subtle); --smbk-tone-text: var(--smbk-color-feedback-warning-text-on-subtle); }

/* ---- pressable: a button that opens something about what it names ----
   The tag's own shape, height, corner and type, never the chip's pill, with the chip's pointer, hover, pressed and
   focus looks. The button's own look is reset, so a button and a span draw the same tag. The hover and pressed looks
   go through the tone recipe: the default tone takes the hover and pressed fills, a feedback tone keeps its surface and
   takes its family's full edge for its pale one; brand and outline set their colors directly, as they do at rest.
   Pressed, every tone sinks in. On a touch screen the tag takes an invisible press area round it, out to the
   comfortable target (press-box/press-box.css). */

.smbk-tag--pressable {
  margin: 0;
  cursor: pointer;
  text-align: start;
  text-decoration: none;
  -webkit-user-select: none;
  user-select: none;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

.smbk-tag--pressable:focus-visible {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: var(--smbk-focus-ring-offset);
}

@media (hover: hover) {
  .smbk-tag--pressable:hover { --smbk-tone-surface: var(--smbk-color-surface-hover); }
  .smbk-tag--pressable.smbk-tag--tone-info:hover { --smbk-tone-surface: var(--smbk-color-feedback-info-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-info-border); }
  .smbk-tag--pressable.smbk-tag--tone-success:hover { --smbk-tone-surface: var(--smbk-color-feedback-success-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-success-border); }
  .smbk-tag--pressable.smbk-tag--tone-danger:hover { --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-danger-border); }
  .smbk-tag--pressable.smbk-tag--tone-warning:hover { --smbk-tone-surface: var(--smbk-color-feedback-warning-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-warning-border); }

  .smbk-tag--pressable.smbk-tag--tone-brand:hover {
    background: var(--smbk-color-action-brand-background-hover);
    border-color: var(--smbk-color-action-brand-border-hover);
  }

  .smbk-tag--pressable.smbk-tag--tone-outline:hover {
    background: var(--smbk-color-action-outline-background-hover);
    border-color: var(--smbk-color-action-outline-border-hover);
  }
}

.smbk-tag--pressable:active {
  --smbk-tone-surface: var(--smbk-color-action-default-background-pressed);
  box-shadow: var(--smbk-elevation-inset-strong);
}

.smbk-tag--pressable.smbk-tag--tone-info:active { --smbk-tone-surface: var(--smbk-color-feedback-info-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-info-border); }
.smbk-tag--pressable.smbk-tag--tone-success:active { --smbk-tone-surface: var(--smbk-color-feedback-success-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-success-border); }
.smbk-tag--pressable.smbk-tag--tone-danger:active { --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-danger-border); }
.smbk-tag--pressable.smbk-tag--tone-warning:active { --smbk-tone-surface: var(--smbk-color-feedback-warning-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-warning-border); }

.smbk-tag--pressable.smbk-tag--tone-brand:active {
  background: var(--smbk-color-action-brand-background-pressed);
  border-color: var(--smbk-color-action-brand-border-hover);
}

.smbk-tag--pressable.smbk-tag--tone-outline:active {
  background: var(--smbk-color-action-outline-background-pressed);
  border-color: var(--smbk-color-action-outline-border-hover);
}
```

## Tokens it reads

```json
{
  "size": {
    "target": {
      "min": {
        "$type": "dimension",
        "$value": "{{size.24}}",
        "$description": "Smallest tap area allowed (24, WCAG 2.2)."
      },
      "comfortable": {
        "$type": "dimension",
        "$value": "{{size.44}}",
        "$description": "Comfortable touch target (44)."
      }
    },
    "mark": {
      "lg": {
        "$type": "dimension",
        "$value": "{{size.24}}",
        "$description": "Small fixed marks inside components (dots, bars, keys), large."
      }
    },
    "icon": {
      "xs": {
        "$type": "dimension",
        "$value": "{{size.12}}",
        "$description": "Icon box, extra small."
      }
    }
  },
  "color": {
    "surface": {
      "neutral-subtle": {
        "$type": "color",
        "$value": "{{color.alpha.grey-800.10}}",
        "$description": "A quiet neutral fill for small marks and their resting state (default badge, tag, chip): translucent, so it shows by the same step on the default, raised and sunken surfaces. Hover steps up to color/surface/hover."
      },
      "brand": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Brand-red surface: closers, highlights."
      },
      "hover": {
        "$type": "color",
        "$value": "{{color.alpha.grey-800.15}}",
        "$description": "A row or option under the pointer or the keyboard highlight: menu options, table rows. Translucent, so it works on any surface."
      }
    },
    "text": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      },
      "on-brand": {
        "$type": "color",
        "$value": "{{color.grey.50}}",
        "$description": "Text on the brand surface."
      }
    },
    "border": {
      "focus": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Keyboard focus ring on controls."
      },
      "rim-subtle": {
        "$type": "color",
        "$value": "{{color.alpha.white.20}}",
        "$description": "A fainter light rim."
      },
      "focus-inverse": {
        "$type": "color",
        "$value": "{{color.grey.50}}",
        "$description": "The focus ring on brand surfaces (a brand chip or tag’s remove button), where the brand-red ring would vanish. Decision F."
      },
      "strong": {
        "$type": "color",
        "$value": "{{color.grey.400}}",
        "$description": "Stronger edges and separators."
      }
    },
    "feedback": {
      "info": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.blue.200}}",
          "$description": "Info messages: pale fill."
        },
        "border-subtle": {
          "$type": "color",
          "$value": "{{color.blue.300}}",
          "$description": "Info messages: pale edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.blue.700}}",
          "$description": "Info messages: text on the pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.blue.500}}",
          "$description": "Info messages: edge."
        }
      },
      "success": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.green.100}}",
          "$description": "Success messages: pale fill."
        },
        "border-subtle": {
          "$type": "color",
          "$value": "{{color.green.300}}",
          "$description": "Success messages: pale edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.green.700}}",
          "$description": "Success messages: text on the pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.green.500}}",
          "$description": "Success messages: edge."
        }
      },
      "danger": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.red.200}}",
          "$description": "Danger messages: pale fill."
        },
        "border-subtle": {
          "$type": "color",
          "$value": "{{color.red.300}}",
          "$description": "Danger messages: pale edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.red.700}}",
          "$description": "Danger messages: text on the pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.red.500}}",
          "$description": "Danger messages: edge; the edge of a field in error (3:1)."
        }
      },
      "warning": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.orange.200}}",
          "$description": "Warning messages: pale fill."
        },
        "border-subtle": {
          "$type": "color",
          "$value": "{{color.orange.300}}",
          "$description": "Warning messages: pale edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.orange.700}}",
          "$description": "Warning messages: text on the pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.orange.500}}",
          "$description": "Warning messages: edge."
        }
      }
    },
    "action": {
      "brand": {
        "background-hover": {
          "$type": "color",
          "$value": "{{color.red.700}}",
          "$description": "Brand button and chip: fill on hover."
        },
        "border-hover": {
          "$type": "color",
          "$value": "{{color.alpha.black.5}}",
          "$description": "Brand button and chip: edge on hover."
        },
        "background-pressed": {
          "$type": "color",
          "$value": "{{color.red.800}}",
          "$description": "Brand button and chip: fill while pressed, one step past hover."
        }
      },
      "outline": {
        "background-hover": {
          "$type": "color",
          "$value": "{{color.alpha.grey-800.10}}",
          "$description": "Outline button: fill on hover."
        },
        "border-hover": {
          "$type": "color",
          "$value": "{{color.grey.400}}",
          "$description": "Outline button: edge on hover."
        },
        "background-pressed": {
          "$type": "color",
          "$value": "{{color.grey.300}}",
          "$description": "Outline button: fill while pressed, one step past hover."
        }
      },
      "default": {
        "background-pressed": {
          "$type": "color",
          "$value": "{{color.grey.300}}",
          "$description": "Neutral chip: fill while pressed or selected, one step past hover."
        }
      }
    }
  },
  "space": {
    "inline": {
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Gap between things side by side, extra small."
      }
    },
    "inset": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Padding inside a component, medium."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  },
  "radius": {
    "control": {
      "sm": {
        "$type": "dimension",
        "$value": "{{radius.4}}",
        "$description": "Corners of controls, small."
      }
    },
    "full": {
      "$type": "dimension",
      "$value": "{{radius.9999}}",
      "$description": "Capsule: pills, chips, switches."
    }
  },
  "typography": {
    "label-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "12px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label SM: labels on controls and short UI text (buttons, chips, tabs, menu headings). Geist Medium, sentence case. Code and keys use Code SM and Code MD."
    }
  },
  "focus": {
    "ring": {
      "width": {
        "$type": "dimension",
        "$value": "{{border-width.2}}",
        "$description": "Thickness of the focus ring."
      },
      "offset": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Gap between a control and its focus ring."
      }
    }
  },
  "elevation": {
    "raised": {
      "$type": "shadow",
      "$value": {
        "light": "0px 2px 8px 1px #373d431a, 0px 1px 2px 0px #373d431a",
        "dark": "0px 2px 8px 1px #00000026, 0px 1px 2px 0px #00000026"
      }
    },
    "inset-strong": {
      "$type": "shadow",
      "$value": {
        "light": "inset -1px 2px 2px 0px #373d4333",
        "dark": "inset -1px 2px 2px 0px #0000004d"
      }
    }
  }
}
```

## Purpose

A small label that names a category, a topic or a keyword on the thing it sits on. It reads as metadata, which is what separates it from a badge (a state) and from a chip (a choice). A pressable tag opens something about what it names, such as a small card about a person or a place.

## Anatomy

- The block, a short rounded rectangle of fixed height, on the same scale as the badge and the chip. The default tone is a see-through gray with no edge, so it shows on any surface. A pressable tag is a button drawn the same.
- The icon part, an optional small glyph before the label.
- The leading content part, the page's own small picture in the icon's place, at the icon's size. An inline SVG or an image fills the icon's box, and any other element is held to it. A tag has the icon or the picture, not both.
- The label part, text in sentence case in the small label style.
- The remove part, an optional small cross control, inside a box large enough to press that adds no width.

## When to use

- Topics on an article or a card.
- Keywords a person has attached to something and may take off again.
- A row of things that each open a small card about themselves when pressed, such as the members of a team or the stops on a route.

## When not to use

- For status. Use a [badge](badge.md); the feedback tones here are for categories that happen to be colored, not for state.
- For a choice a person toggles, or a filter. Use a [chip](chip.md).
- For a press that goes to another page or does something. Use a [link](link.md) or a [button](button.md).

## Behavior

None of its own. The remove control, when present, calls back.

Given a press handler, the tag is a button, and as a popover's opener it gets one from the popover. It keeps its shape, height and type, and takes the pointer, a hover look, a pressed look that sinks in and a focus ring round it. A pressable tag is never removable: given both, it is drawn removable and warns in development, because its remove control would be a button inside a button.

On a touch screen the remove cross keeps its box, and a pressable tag keeps its size, each taking an invisible press area round it, out to the comfortable touch target.

## Accessibility
### What it does for you

- The label is read as text.
- The remove control is a real button. It is at least 24 by 24 pixels, the least a press needs, though its cross is small.
- The remove control's focus ring is drawn round the cross, and on a brand tag in the inverse focus color, so it shows on the fill.
- A pressable tag is a real button, named by its words, and its focus ring is drawn round the tag. As a popover's opener it says that it opens a dialog and whether that is open.
- The icon is decorative.
- On a touch screen the remove cross and a pressable tag each take an invisible press area round them, out to the comfortable touch target.

### What you must do

- Give the remove control an accessible name saying what it removes. Without one the wrapper warns in development and names it "Remove" and the tag's words.
- Hide a picture of your own that only repeats the tag's words (`aria-hidden="true"` on an SVG, an empty `alt` on an image). Give it words of its own when it says something the label does not.
- Never put a pressable tag inside a link or another button.

### How to test

- Tab to the remove control: the focus ring shows round the cross, on a brand tag too, in both themes and in a forced-color mode. Press Enter, then Space: each calls back.
- Tab to a pressable tag: the focus ring shows round it. Press Enter, then Space: each opens what it names.
- With a screen reader, the label is read as text and the remove control as a button by what it removes; a pressable tag is read as a button by its words.
- On a touch screen, a press just outside the cross still removes the tag, and a press just outside a pressable tag still presses it.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to a pressable tag, or to the remove control when the tag has one. Each pressable tag in a row is a stop of its own. |
| Enter or Space | Presses the pressable tag, or the remove control, which calls back. |

## Usage rules

### Do

- Keep the label to one or two words, so the tag stays a glance.
- Make a tag pressable when a press opens something about what it names, such as a card about a person or a place.

### Don't

- Make a tag pressable to filter a list or choose an option. That is a chip.
- Make a tag both pressable and removable. Open what it names from the tag, and remove it somewhere else.
