# Badge

A small status marker: a soft or flat pill with a label and optional icons.

Block: `smbk-badge`. 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 |
|---|---|---|---|
| `look` | `soft`, `flat` | `soft` | Soft, the default, is the subtle surface inside an edge of the tone's color; flat is the tone's full surface. The default tone looks the same in soft and flat. |
| `tone` | `default`, `success`, `info`, `warning`, `danger` | `default` | A see-through gray that shows on every surface, or one of the four feedback families. |
| `size` | `lg`, `md`, `sm` | `lg` | The pill scale shared with the chip and the tag: 32, 24 and 20 tall. |

Parts: `smbk-badge__icon`, `smbk-badge__label`

## Markup

```html
<span class="smbk-badge smbk-badge--look-soft smbk-badge--tone-success smbk-badge--size-lg">
  <i
    class="ph ph-check-circle smbk-icon smbk-icon--size-md smbk-badge__icon"
    aria-hidden="true"
  ></i>
  <span class="smbk-badge__label">Active</span>
  <i
    class="ph ph-arrow-up-right smbk-icon smbk-icon--size-md smbk-badge__icon"
    aria-hidden="true"
  ></i>
</span>
```

## React

```tsx
import { Badge } from './design-system/components/badge/badge.js';

<Badge look="soft" tone="success" size="lg" leadingIcon="CheckCircle" trailingIcon="ArrowUpRight">Active</Badge>
```

## Stylesheet

```css
/* smbk-badge — Badge. Figma "Badge" 4126:20020.
 * One pill scale with the chip and the tag: heights on the mark sizes, Label MD
 * at lg and Label SM below it, padding and gap one step per size.
 * Soft is the default look, so a badge with no look class draws soft; flat
 * and the dot draw the tone's full surface. The default tone has one look, a
 * see-through gray with no edge, so it shows on every surface.
 * Every badge has an edge, clear unless its look draws one (the soft badges of
 * the four feedback tones). Figma draws the edge inside the badge, so each
 * padding gives the edge's width back and a badge with an edge is the same
 * size as one without.
 * Counts take equal-width digits, so a number that changes does not move its
 * neighbors. */

.smbk-badge {
  /* The default tone; each feedback tone's soft look 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-default);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--smbk-space-inline-sm);
  height: var(--smbk-size-mark-xl);
  padding: 0 calc(var(--smbk-space-inset-lg) - var(--smbk-border-width-hairline));
  border: var(--smbk-border-width-hairline) solid var(--smbk-tone-border);
  border-radius: var(--smbk-radius-full);
  background: var(--smbk-tone-surface);
  color: var(--smbk-tone-text);
  font-family: var(--smbk-typography-label-md-font-family);
  font-size: var(--smbk-typography-label-md-font-size);
  font-weight: var(--smbk-typography-label-md-font-weight);
  line-height: var(--smbk-typography-label-md-line-height);
  letter-spacing: var(--smbk-typography-label-md-letter-spacing);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.smbk-badge__icon {
  font-size: var(--smbk-size-icon-sm);
}

/* ---- size ---- */

.smbk-badge--size-md,
.smbk-badge--size-sm {
  gap: var(--smbk-space-inline-xs);
  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);
}

.smbk-badge--size-md {
  height: var(--smbk-size-mark-lg);
  padding: 0 calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline));
}

.smbk-badge--size-sm {
  height: var(--smbk-size-mark-md);
  padding: 0 calc(var(--smbk-space-inset-sm) - var(--smbk-border-width-hairline));
}

:is(.smbk-badge--size-md, .smbk-badge--size-sm) .smbk-badge__icon {
  font-size: var(--smbk-size-icon-xs);
}

/* The default tone is the block's own look above. */

/* ---- soft, the default: the tone recipe, the tone's subtle surface inside its pale edge ---- */

.smbk-badge--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-badge--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-badge--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); }
.smbk-badge--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); }

/* ---- flat and dot: the tone's full surface, with no edge ---- */

.smbk-badge--look-flat.smbk-badge--tone-success {
  border-color: transparent;
  background: var(--smbk-color-feedback-success-surface);
  color: var(--smbk-color-feedback-success-text-on-surface);
}

.smbk-badge--look-flat.smbk-badge--tone-info {
  border-color: transparent;
  background: var(--smbk-color-feedback-info-surface);
  color: var(--smbk-color-feedback-info-text-on-surface);
}

.smbk-badge--look-flat.smbk-badge--tone-warning {
  border-color: transparent;
  background: var(--smbk-color-feedback-warning-surface);
  color: var(--smbk-color-feedback-warning-text-on-surface);
}

.smbk-badge--look-flat.smbk-badge--tone-danger {
  border-color: transparent;
  background: var(--smbk-color-feedback-danger-surface);
  color: var(--smbk-color-feedback-danger-text-on-surface);
}

/* ---- dot: a bare mark, no label. Its words, if markup gives it any, stay hidden over the
   tone's text color above. ---- */

/* ---- high contrast (forced colors) ---- */
/* In Windows high-contrast mode the browser drops fills and shadows; this puts the meaning back with the
   user's own colors. Nothing here applies outside that mode. */

@media (forced-colors: active) {
  /* The dot is a fill and nothing else, so it would vanish. It takes the palette's text color. */
}
```

## Tokens it reads

```json
{
  "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."
      }
    },
    "text": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Main text."
      }
    },
    "feedback": {
      "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."
        },
        "surface": {
          "$type": "color",
          "$value": "{{color.green.600}}",
          "$description": "Success messages: strong fill."
        },
        "text-on-surface": {
          "$type": "color",
          "$value": "{{color.grey.50}}",
          "$description": "Success messages: text on the strong fill."
        }
      },
      "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."
        },
        "surface": {
          "$type": "color",
          "$value": "{{color.blue.600}}",
          "$description": "Info messages: strong fill."
        },
        "text-on-surface": {
          "$type": "color",
          "$value": "{{color.grey.50}}",
          "$description": "Info messages: text on the strong fill."
        }
      },
      "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."
        },
        "surface": {
          "$type": "color",
          "$value": "{{color.orange.600}}",
          "$description": "Warning messages: strong fill."
        },
        "text-on-surface": {
          "$type": "color",
          "$value": "{{color.grey.50}}",
          "$description": "Warning messages: text on the strong fill."
        }
      },
      "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."
        },
        "surface": {
          "$type": "color",
          "$value": "{{color.red.600}}",
          "$description": "Danger messages: strong fill."
        },
        "text-on-surface": {
          "$type": "color",
          "$value": "{{color.grey.50}}",
          "$description": "Danger messages: text on the strong fill."
        }
      }
    }
  },
  "space": {
    "inline": {
      "sm": {
        "$type": "dimension",
        "$value": "{{space.6}}",
        "$description": "Gap between things side by side, small."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Gap between things side by side, extra small."
      }
    },
    "inset": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Padding inside a component, large."
      },
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Padding inside a component, medium."
      },
      "sm": {
        "$type": "dimension",
        "$value": "{{space.6}}",
        "$description": "Padding inside a component, small."
      }
    }
  },
  "size": {
    "mark": {
      "xl": {
        "$type": "dimension",
        "$value": "{{size.32}}",
        "$description": "Small fixed marks inside components (dots, bars, keys), extra large."
      },
      "lg": {
        "$type": "dimension",
        "$value": "{{size.24}}",
        "$description": "Small fixed marks inside components (dots, bars, keys), large."
      },
      "md": {
        "$type": "dimension",
        "$value": "{{size.20}}",
        "$description": "Small fixed marks inside components (dots, bars, keys), medium."
      }
    },
    "icon": {
      "sm": {
        "$type": "dimension",
        "$value": "{{size.16}}",
        "$description": "Icon box, small."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{size.12}}",
        "$description": "Icon box, extra small."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  },
  "radius": {
    "full": {
      "$type": "dimension",
      "$value": "{{radius.9999}}",
      "$description": "Capsule: pills, chips, switches."
    }
  },
  "typography": {
    "label-md": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label MD: 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."
    },
    "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."
    }
  }
}
```

## Purpose

A small, inert marker that states a status or a count beside the thing it describes: published, three unread, warning. It is not pressed and not removed, which is what separates it from a chip and a tag.

## Anatomy

- The block, a pill that fixes its height by size, on the same scale as the chip and the tag. The default tone is a see-through gray with no edge, so it shows on any surface. A soft feedback badge draws a thin edge in its tone's own border color; a flat badge has none.
- The label part, the text, in the label style. Numbers take equal-width digits, so a count that changes does not shift what is beside it.
- The icon part, before or after the label. Optional.

## When to use

- A status word on a row, a card or a heading.
- A count on a navigation item.

## When not to use

- For something a person can press or dismiss. Use a [chip](chip.md) or a [tag](tag.md).
- For a category that is not a state. Use a tag.
- For a dot that marks a status beside a name. Use the [status dot](status-dot.md).
- As the only carrier of meaning by color. Give it a text or shape cue as well (WCAG 1.4.1 Use of Color).

## Behavior

None. It renders and stays put.

## Accessibility

### What it does for you

- A labeled badge is read as its text.
- The text of both looks on its surface, soft and flat, is measured on the color foundation's Contrast section, in both themes. A pair that falls short there is corrected in Figma, not here.

### What you must do

- Say the status in the badge's words, or beside it with an icon, so it never rests on color alone.

### How to test

- With a screen reader, the badge is read as its text.
- Tab past it: a badge takes no focus.

## Usage rules

### Do

- Keep the label to a word or a number.
- Use the soft look, the default, for status almost everywhere; it stays quiet in dense lists. Use the flat look when the status should stand out.

### Don't

- Put a badge inside a button. The button's own label is the place for its state.
- Rely on the tone's color alone; say the status in words.
