> Markdown version of [Badge](https://vaadin.com/docs/v24/components/badge). Section index: [llms.txt](https://vaadin.com/docs/v24/components/llms.txt)

# Badge

Badges are colored text elements containing small bits of information. They’re used for labeling content, displaying metadata and/or highlighting information.

**Lit** — `badge-basic.ts`

```html
<span theme="badge">Pending</span>
<span theme="badge success">Confirmed</span>
<span theme="badge warning">Warning</span>
<span theme="badge error">Denied</span>
<span theme="badge contrast">On hold</span>
```

**Flow** — `BadgeBasic.java`

```java
Span pending = new Span("Pending");
pending.getElement().getThemeList().add("badge");

Span confirmed = new Span("Confirmed");
confirmed.getElement().getThemeList().add("badge success");

Span warning = new Span("Warning");
warning.getElement().getThemeList().add("badge warning");

Span denied = new Span("Denied");
denied.getElement().getThemeList().add("badge error");

Span onHold = new Span("On hold");
onHold.getElement().getThemeList().add("badge contrast");
```

**React** — `badge-basic.tsx`

```tsx
{/* Since a native span element does not know about the theme attribute, as a workaround, you
  can use the spread operator to pass the theme attribute to the span element. */}
<span {...{ theme: 'badge' }}>Pending</span>
<span {...{ theme: 'badge success' }}>Confirmed</span>
<span {...{ theme: 'badge warning' }}>Warning</span>
<span {...{ theme: 'badge error' }}>Denied</span>
<span {...{ theme: 'badge contrast' }}>On hold</span>
```

> **Note: Import styles**
>
> Badge is a set of CSS classes rather than a web / Flow component. The Badge-specific CSS classes are available as part of the Lumo theme. To use these classes in your application, enable them in your theme’s `theme.json`:
>
> ```json
> {
>   "lumoImports": [<...>, "badge"]
> }
> ```
>
> The `theme.json` is located in the theme folder at `/src/main/frontend/themes/<my-theme>/theme.json`.

## <a id="label"></a>Label

Badges should contain a label. Labels should be clear, concise, and written using sentence case. Aim for 1 to 2 words.

## <a id="icons"></a>Icons

Badges can contain icons as well as text. Icons can be placed on either side of the text.

**Lit** — `badge-icons.ts`

```html
<vaadin-vertical-layout theme="spacing">
  <vaadin-horizontal-layout theme="spacing">
    <span theme="badge">
      <vaadin-icon icon="vaadin:clock" style="padding: var(--lumo-space-xs)"></vaadin-icon>
      <span>Pending</span>
    </span>
    <span theme="badge success">
      <vaadin-icon icon="vaadin:check" style="padding: var(--lumo-space-xs)"></vaadin-icon>
      <span>Confirmed</span>
    </span>
    <span theme="badge warning">
      <vaadin-icon icon="vaadin:warning" style="padding: var(--lumo-space-xs)"></vaadin-icon>
      <span>Warning</span>
    </span>
    <span theme="badge error">
      <vaadin-icon
        icon="vaadin:exclamation-circle-o"
        style="padding: var(--lumo-space-xs)"
      ></vaadin-icon>
      <span>Denied</span>
    </span>
    <span theme="badge contrast">
      <vaadin-icon icon="vaadin:hand" style="padding: var(--lumo-space-xs)"></vaadin-icon>
      <span>On hold</span>
    </span>
  </vaadin-horizontal-layout>
  <vaadin-horizontal-layout theme="spacing">
    <span theme="badge">
      <span>Pending</span>
      <vaadin-icon icon="vaadin:clock" style="padding: var(--lumo-space-xs)"></vaadin-icon>
    </span>
    <span theme="badge success">
      <span>Confirmed</span>
      <vaadin-icon icon="vaadin:check" style="padding: var(--lumo-space-xs)"></vaadin-icon>
    </span>
    <span theme="badge warning">
      <span>Warning</span>
      <vaadin-icon icon="vaadin:warning" style="padding: var(--lumo-space-xs)"></vaadin-icon>
    </span>
    <span theme="badge error">
      <span>Denied</span>
      <vaadin-icon
        icon="vaadin:exclamation-circle-o"
        style="padding: var(--lumo-space-xs)"
      ></vaadin-icon>
    </span>
    <span theme="badge contrast">
      <span>On hold</span>
      <vaadin-icon icon="vaadin:hand" style="padding: var(--lumo-space-xs)"></vaadin-icon>
    </span>
  </vaadin-horizontal-layout>
</vaadin-vertical-layout>
```

**Flow** — `BadgeIcons.java`

```java
// Icon before text
Span pending1 = new Span(createIcon(VaadinIcon.CLOCK),
        new Span("Pending"));
pending1.getElement().getThemeList().add("badge");

// Icon after text
Span pending2 = new Span(new Span("Pending"),
        createIcon(VaadinIcon.CLOCK));
pending2.getElement().getThemeList().add("badge");

...

private Icon createIcon(VaadinIcon vaadinIcon) {
    Icon icon = vaadinIcon.create();
    icon.getStyle().set("padding", "var(--lumo-space-xs)");
    return icon;
}
```

**React** — `badge-icons.tsx`

```tsx
<VerticalLayout theme="spacing">
  <HorizontalLayout theme="spacing">
    <span {...{ theme: 'badge' }}>
      <Icon icon="vaadin:clock" style={{ padding: 'var(--lumo-space-xs)' }} />
      <span>Pending</span>
    </span>
    <span {...{ theme: 'badge success' }}>
      <Icon icon="vaadin:check" style={{ padding: 'var(--lumo-space-xs)' }} />
      <span>Confirmed</span>
    </span>
    <span {...{ theme: 'badge warning' }}>
      <Icon icon="vaadin:warning" style={{ padding: 'var(--lumo-space-xs)' }} />
      <span>Warning</span>
    </span>
    <span {...{ theme: 'badge error' }}>
      <Icon icon="vaadin:exclamation-circle-o" style={{ padding: 'var(--lumo-space-xs)' }} />
      <span>Denied</span>
    </span>
    <span {...{ theme: 'badge contrast' }}>
      <Icon icon="vaadin:hand" style={{ padding: 'var(--lumo-space-xs)' }} />
      <span>On hold</span>
    </span>
  </HorizontalLayout>
  <HorizontalLayout theme="spacing">
    <span {...{ theme: 'badge' }}>
      <span>Pending</span>
      <Icon icon="vaadin:clock" style={{ padding: 'var(--lumo-space-xs)' }} />
    </span>
    <span {...{ theme: 'badge success' }}>
      <span>Confirmed</span>
      <Icon icon="vaadin:check" style={{ padding: 'var(--lumo-space-xs)' }} />
    </span>
    <span {...{ theme: 'badge warning' }}>
      <span>Warning</span>
      <Icon icon="vaadin:warning" style={{ padding: 'var(--lumo-space-xs)' }} />
    </span>
    <span {...{ theme: 'badge error' }}>
      <span>Denied</span>
      <Icon icon="vaadin:exclamation-circle-o" style={{ padding: 'var(--lumo-space-xs)' }} />
    </span>
    <span {...{ theme: 'badge contrast' }}>
      <span>On hold</span>
      <Icon icon="vaadin:hand" style={{ padding: 'var(--lumo-space-xs)' }} />
    </span>
  </HorizontalLayout>
</VerticalLayout>
```

> **Note: Use icons sparingly**
>
> The benefit of using icons should be weighed against the visual noise it adds.

### <a id="icon-only"></a>Icon-Only

Badges can also be used with icons without a label. For accessibility, a tooltip and `aria-label` attribute is recommended to ensure that all users can identify their meaning.

**Lit** — `badge-icons-only.ts`

```html
<vaadin-icon
  aria-label="Confirmed"
  icon="vaadin:check"
  style="padding: var(--lumo-space-xs)"
  theme="badge success"
  title="Confirmed"
></vaadin-icon>
<vaadin-icon
  aria-label="Cancelled"
  icon="vaadin:close-small"
  style="padding: var(--lumo-space-xs)"
  theme="badge error"
  title="Cancelled"
></vaadin-icon>
```

**Flow** — `BadgeIconsOnly.java`

```java
Icon confirmed = createIcon(VaadinIcon.CHECK, "Confirmed");
confirmed.getElement().getThemeList().add("badge success");

Icon cancelled = createIcon(VaadinIcon.CLOSE_SMALL, "Cancelled");
cancelled.getElement().getThemeList().add("badge error");

...

private Icon createIcon(VaadinIcon vaadinIcon, String label) {
    Icon icon = vaadinIcon.create();
    icon.getStyle().set("padding", "var(--lumo-space-xs");
    // Accessible label
    icon.getElement().setAttribute("aria-label", label);
    // Tooltip
    icon.getElement().setAttribute("title", label);
    return icon;
}
```

**React** — `badge-icons-only.tsx`

```tsx
<Icon
  aria-label="Confirmed"
  icon="vaadin:check"
  style={{ padding: 'var(--lumo-space-xs)' }}
  theme="badge success"
  title="Confirmed"
/>

<Icon
  aria-label="Cancelled"
  icon="vaadin:close-small"
  style={{ padding: 'var(--lumo-space-xs)' }}
  theme="badge error"
  title="Cancelled"
/>
```

Icon-only badges should primarily be used for common recurring content with highly standardized, universally understood icons (such as a checkmark for "*yes*"), and for content that’s repeated, for example in lists and tables.

**Lit** — `badge-icons-only-table.ts`

```typescript
const renderBoolean: GridColumnBodyLitRenderer<UserPermissions> = (item, _model, column) => {
  let icon: string;
  let title: string;
  let theme: string;

  if (item[column.id as keyof UserPermissions]) {
    icon = 'vaadin:check';
    title = 'Yes';
    theme = 'success';
  } else {
    icon = 'vaadin:close-small';
    title = 'No';
    theme = 'error';
  }

  return html`
    <vaadin-icon
      aria-label="${title}"
      icon="${icon}"
      style="padding: var(--lumo-space-xs)"
      theme="badge ${theme}"
      title="${title}"
    ></vaadin-icon>
  `;
};

return html`
  <vaadin-grid .items="${this.items}">
    <vaadin-grid-column path="name" header="Name"></vaadin-grid-column>
    <vaadin-grid-column
      id="view"
      header="View"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
    <vaadin-grid-column
      id="comment"
      header="Comment"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
    <vaadin-grid-column
      id="edit"
      header="Edit"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
  </vaadin-grid>
`;
```

**Flow** — `BadgeIconsOnlyTable.java`

```java
grid.addColumn(UserPermissions::getName).setHeader("Name");
grid.addComponentColumn(userPermissions -> createPermissionIcon(
        userPermissions.getView())).setHeader("View");
grid.addComponentColumn(userPermissions -> createPermissionIcon(
        userPermissions.getComment())).setHeader("Comment");
grid.addComponentColumn(userPermissions -> createPermissionIcon(
        userPermissions.getEdit())).setHeader("Edit");

...

private Icon createPermissionIcon(boolean hasPermission) {
    Icon icon;
    if (hasPermission) {
        icon = createIcon(VaadinIcon.CHECK, "Yes");
        icon.getElement().getThemeList().add("badge success");
    } else {
        icon = createIcon(VaadinIcon.CLOSE_SMALL, "No");
        icon.getElement().getThemeList().add("badge error");
    }
    return icon;
}

private Icon createIcon(VaadinIcon vaadinIcon, String label) {
    Icon icon = vaadinIcon.create();
    icon.getStyle().set("padding", "var(--lumo-space-xs");
    // Accessible label
    icon.getElement().setAttribute("aria-label", label);
    // Tooltip
    icon.getElement().setAttribute("title", label);
    return icon;
}
```

**React** — `badge-icons-only-table.tsx`

```tsx
const renderBadge = ({
  item,
  original: column,
}: {
  item: any;
  original: GridColumnElement<UserPermissions>;
}) => {
  let icon;
  let title;
  let theme;

  if (item[column.id]) {
    icon = 'vaadin:check';
    title = 'Yes';
    theme = 'success';
  } else {
    icon = 'vaadin:close-small';
    title = 'No';
    theme = 'error';
  }

  return (
    <Icon
      aria-label={title}
      icon={icon}
      style={{ padding: 'var(--lumo-space-xs)' }}
      theme={`badge ${theme}`}
      title={title}
    />
  );
};

return (
  <Grid items={items.value}>
    <GridColumn path="name" header="Name" />
    <GridColumn id="view" header="View" renderer={renderBadge} />
    <GridColumn id="comment" header="Comment" renderer={renderBadge} />
    <GridColumn id="edit" header="Edit" renderer={renderBadge} />
  </Grid>
);
```

## <a id="theme-variants"></a>Theme Variants

Badge features theme variants for different sizes, colors, and shapes. You can combine any theme variants together.

### <a id="size"></a>Size

Badges have two different sizes you can use: the default (normal) and `small`. Use the `small` theme variant to make a badge smaller, for example when space is limited or for compact parts of the UI.

**Lit** — `badge-size.ts`

```html
<span theme="badge small">Pending</span>
<span theme="badge success small">Confirmed</span>
<span theme="badge warning small">Warning</span>
<span theme="badge error small">Denied</span>
<span theme="badge contrast small">On hold</span>
```

**Flow** — `BadgeSize.java`

```java
Span pending = new Span("Pending");
pending.getElement().getThemeList().add("badge small");

Span confirmed = new Span("Confirmed");
confirmed.getElement().getThemeList().add("badge success small");

Span warning = new Span("Warning");
warning.getElement().getThemeList().add("badge warning small");

Span denied = new Span("Denied");
denied.getElement().getThemeList().add("badge error small");

Span onHold = new Span("On hold");
onHold.getElement().getThemeList().add("badge contrast small");
```

**React** — `badge-size.tsx`

```tsx
<span {...{ theme: 'badge small' }}>Pending</span>
<span {...{ theme: 'badge success small' }}>Confirmed</span>
<span {...{ theme: 'badge warning small' }}>Warning</span>
<span {...{ theme: 'badge error small' }}>Denied</span>
<span {...{ theme: 'badge contrast small' }}>On hold</span>
```

### <a id="color"></a>Color

Badges have four different color variants: default, `success`, `error`, and `contrast`. The color variants can be paired with the `primary` theme variant for additional emphasis.

**Lit** — `badge-color.ts`

```html
<vaadin-vertical-layout theme="spacing">
  <vaadin-horizontal-layout theme="spacing">
    <span theme="badge">Pending</span>
    <span theme="badge success">Confirmed</span>
    <span theme="badge warning">Warning</span>
    <span theme="badge error">Denied</span>
    <span theme="badge contrast">On hold</span>
  </vaadin-horizontal-layout>
  <vaadin-horizontal-layout theme="spacing">
    <span theme="badge primary">Pending</span>
    <span theme="badge success primary">Confirmed</span>
    <span theme="badge warning primary">Warning</span>
    <span theme="badge error primary">Denied</span>
    <span theme="badge contrast primary">On hold</span>
  </vaadin-horizontal-layout>
</vaadin-vertical-layout>
```

**Flow** — `BadgeColor.java`

```java
// Default variant
Span pending = new Span("Pending");
pending.getElement().getThemeList().add("badge");

// Primary variant
Span pendingPrimary = new Span("Pending");
pendingPrimary.getElement().getThemeList().add("badge primary");
```

**React** — `badge-color.tsx`

```tsx
<VerticalLayout theme="spacing">
  <HorizontalLayout theme="spacing">
    <span {...{ theme: 'badge ' }}>Pending</span>
    <span {...{ theme: 'badge success ' }}>Confirmed</span>
    <span {...{ theme: 'badge warning ' }}>Warning</span>
    <span {...{ theme: 'badge error ' }}>Denied</span>
    <span {...{ theme: 'badge contrast ' }}>On hold</span>
  </HorizontalLayout>
  <HorizontalLayout theme="spacing">
    <span {...{ theme: 'badge primary ' }}>Pending</span>
    <span {...{ theme: 'badge success primary ' }}>Confirmed</span>
    <span {...{ theme: 'badge warning primary ' }}>Warning</span>
    <span {...{ theme: 'badge error primary ' }}>Denied</span>
    <span {...{ theme: 'badge contrast primary ' }}>On hold</span>
  </HorizontalLayout>
</VerticalLayout>
```

| Variant  | Theme name | Usage recommendations                                                                                                                                                            |
| -------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Normal   |            | Default style. Recommended for informational messages. This style may be confused with a Button or link.                                                                         |
| Success  | `success`  | Highlight positive outcomes, such as when a task or operation is completed.                                                                                                      |
| Warning  | `warning`  | Communicates caution or potential issues that require user attention.                                                                                                            |
| Error    | `error`    | Use the error theme variant to communicate alerts and failures.                                                                                                                  |
| Contrast | `contrast` | A high-contrast version that improves legibility and distinguishes the badge from the rest of the UI. Recommended for neutral badges (that don’t communicate success or errors). |
| Primary  | `primary`  | Used for important information and/or to draw more attention to your badge. Can be combined with all other theme variants.                                                       |

> **Note: Accessibility**
>
> Assistive technologies, such as screen readers, interpret badges solely based on their content. Without proper context, they may end up confusing the user. If you are using colors and icons to convey information, provide the same info via `aria-label` to ensure that screen readers can interpret the information.

### <a id="shape"></a>Shape

Applying the `pill` theme variant produces a badge with rounded corners. It can aid in making badges and buttons more distinct from one another.

**Lit** — `badge-shape.ts`

```html
<span theme="badge pill">Pending</span>
<span theme="badge success pill">Confirmed</span>
<span theme="badge warning pill">Warning</span>
<span theme="badge error pill">Denied</span>
<span theme="badge contrast pill">On hold</span>
```

**Flow** — `BadgeShape.java`

```java
Span pending = new Span("Pending");
pending.getElement().getThemeList().add("badge pill");

Span confirmed = new Span("Confirmed");
confirmed.getElement().getThemeList().add("badge success pill");

Span warning = new Span("Warning");
warning.getElement().getThemeList().add("badge warning pill");

Span denied = new Span("Denied");
denied.getElement().getThemeList().add("badge error pill");

Span onHold = new Span("On hold");
onHold.getElement().getThemeList().add("badge contrast pill");
```

**React** — `badge-shape.tsx`

```tsx
<span {...{ theme: 'badge pill' }}>Pending</span>
<span {...{ theme: 'badge success pill' }}>Confirmed</span>
<span {...{ theme: 'badge warning pill' }}>Warning</span>
<span {...{ theme: 'badge error pill' }}>Denied</span>
<span {...{ theme: 'badge contrast pill' }}>On hold</span>
```

## <a id="use-cases"></a>Use Cases

### <a id="highlighting-and-distinguishing-information"></a>Highlighting and Distinguishing Information

A typical use case for badges is to highlight an item’s status, for example in a Grid.

**Lit** — `badge-highlight.ts`

```typescript
return html`
  <vaadin-grid .items="${this.items}">
    <vaadin-grid-column path="report" header="Report"></vaadin-grid-column>
    <vaadin-grid-column
      header="Due date"
      ${columnBodyRenderer<Report>(
        ({ due }) => html`${dateFormatter.format(new Date(due))}`,
        []
      )}
    ></vaadin-grid-column>
    <vaadin-grid-column path="assignee" header="Assignee"></vaadin-grid-column>
    <vaadin-grid-column
      header="Status"
      ${columnBodyRenderer<Report>(({ status }) => {
        let title: string;
        let theme: string;

        switch (status) {
          case ReportStatus.COMPLETED:
            title = 'Completed';
            theme = 'success';
            break;
          case ReportStatus.IN_PROGRESS:
            title = 'In progress';
            theme = '';
            break;
          case ReportStatus.CANCELLED:
            title = 'Cancelled';
            theme = 'error';
            break;
          default:
            title = 'On hold';
            theme = 'contrast';
            break;
        }

        return html`<span theme="badge ${theme} primary">${title}</span>`;
      }, [])}
    ></vaadin-grid-column>
  </vaadin-grid>
`;
```

**Flow** — `BadgeHighlight.java`

```java
grid.addComponentColumn(report -> createStatusBadge(report.getStatus()))
        .setHeader("Status");

...

private Span createStatusBadge(String status) {
    String theme;
    switch (status) {
    case "In progress":
        theme = "badge primary";
        break;
    case "Completed":
        theme = "badge success primary";
        break;
    case "Cancelled":
        theme = "badge error primary";
        break;
    default:
        theme = "badge contrast primary";
        break;
    }
    Span badge = new Span(status);
    badge.getElement().getThemeList().add(theme);
    return badge;
}
```

**React** — `badge-highlight.tsx`

```tsx
function renderDueDate({ item: report }: { item: Report }) {
  return <span>{dateFormatter.format(new Date(report.due))}</span>;
}

function renderStatus({ item: report }: { item: Report }) {
  let title: string;
  let theme: string;

  switch (report.status) {
    case ReportStatus.COMPLETED:
      title = 'Completed';
      theme = 'success';
      break;
    case ReportStatus.IN_PROGRESS:
      title = 'In progress';
      theme = '';
      break;
    case ReportStatus.CANCELLED:
      title = 'Cancelled';
      theme = 'error';
      break;
    default:
      title = 'On hold';
      theme = 'contrast';
      break;
  }

  return <span {...{ theme: `badge ${theme} primary` }}>{title}</span>;
}

function Example() {
  const items = useSignal<Report[]>([]);
  useEffect(() => {
    getReports().then((reports) => {
      items.value = reports as Report[];
    });
  }, []);

  return (
    <Grid items={items.value}>
      <GridColumn path="report" header="Report" />
      <GridColumn header="Due date" renderer={renderDueDate} />
      <GridColumn path="assignee" header="Assignee" />
      <GridColumn header="Status" renderer={renderStatus} />
    </Grid>
  );
}
```

They are also often used for displaying metadata tags.

### <a id="interactive-content"></a>Interactive Content

Badges can house interactive content such as Anchors and Buttons. For example, Badges that highlight active filters might contain a "Clear" Button which removes the associated filter.

**Lit** — `badge-interactive.ts`

```typescript
return html`
  <vaadin-vertical-layout theme="spacing">
    <vaadin-combo-box
      label="Profession"
      .items="${this.items}"
      @change="${this.onChange}"
    ></vaadin-combo-box>
    <vaadin-horizontal-layout style="flex-wrap: wrap" theme="spacing">
      ${repeat(
        this.selectedProfessions,
        (profession) => profession,
        (profession) => html`
          <span theme="badge pill contrast">
            <span>${profession}</span>
            <vaadin-button
              aria-label="Clear filter: ${profession}"
              data-profession="${profession}"
              theme="contrast tertiary-inline"
              title="Clear filter: ${profession}"
              style="margin-inline-start: var(--lumo-space-xs)"
              @click="${this.onClick}"
            >
              <vaadin-icon icon="vaadin:close-small"></vaadin-icon>
            </vaadin-button>
          </span>
        `
      )}
    </vaadin-horizontal-layout>
  </vaadin-vertical-layout>
`;
```

**Flow** — `BadgeInteractive.java`

```java
HorizontalLayout badges = new HorizontalLayout();
badges.getStyle().set("flex-wrap", "wrap");

ComboBox<String> comboBox = new ComboBox<>("Profession");
comboBox.setItems(DataService.getProfessions());
comboBox.addValueChangeListener(e -> {
    Span filterBadge = createFilterBadge(e.getValue());
    badges.add(filterBadge);
});

...

private Span createFilterBadge(String profession) {
    Button clearButton = new Button(VaadinIcon.CLOSE_SMALL.create());
    clearButton.addThemeVariants(ButtonVariant.LUMO_CONTRAST,
            ButtonVariant.LUMO_TERTIARY_INLINE);
    clearButton.getStyle().set("margin-inline-start",
            "var(--lumo-space-xs)");
    // Accessible button name
    clearButton.getElement().setAttribute("aria-label",
            "Clear filter: " + profession);
    // Tooltip
    clearButton.getElement().setAttribute("title",
            "Clear filter: " + profession);

    Span badge = new Span(new Span(profession), clearButton);
    badge.getElement().getThemeList().add("badge contrast pill");

    // Add handler for removing the badge
    clearButton.addClickListener(event -> {
        badge.getElement().removeFromParent();
    });

    return badge;
}
```

**React** — `badge-interactive.tsx`

```tsx
return (
  <VerticalLayout theme="spacing">
    <ComboBox label="Profession" items={items.value} onChange={onChange} />

    <HorizontalLayout style={{ flexWrap: 'wrap' }} theme="spacing">
      {selectedProfessions.value.map((profession) => (
        <span key={profession} {...{ theme: 'badge pill contrast' }}>
          <span>{profession}</span>
          <Button
            aria-label={`Clear filter: ${profession}`}
            data-profession={profession}
            theme="contrast tertiary-inline"
            title={`Clear filter: ${profession}`}
            style={{ marginInlineStart: 'var(--lumo-space-xs)' }}
            onClick={onClick}
          >
            <Icon icon="vaadin:close-small" />
          </Button>
        </span>
      ))}
    </HorizontalLayout>
  </VerticalLayout>
);
```

### <a id="counter"></a>Counter

Badges can be used as counters, for example to show the number of unread/new messages, selection count, etc.

**Lit** — `badge-counter.ts`

```html
<vaadin-tabs>
  <vaadin-tab>
    <span>Inbox</span>
    <span
      theme="badge contrast pill small"
      aria-label="12 unread messages"
      title="12 unread messages"
    >
      12
    </span>
  </vaadin-tab>
  <vaadin-tab>
    <span>Important</span>
    <span
      theme="badge contrast pill small"
      aria-label="3 unread messages"
      title="3 unread messages"
    >
      3
    </span>
  </vaadin-tab>
  <vaadin-tab>
    <span>Spam</span>
    <span
      theme="badge contrast pill small"
      aria-label="45 unread messages"
      title="45 unread messages"
    >
      45
    </span>
  </vaadin-tab>
  <vaadin-tab>
    <span>Archive</span>
    <span
      theme="badge contrast pill small"
      aria-label="23 unread messages"
      title="23 unread messages"
    >
      23
    </span>
  </vaadin-tab>
</vaadin-tabs>
```

**Flow** — `BadgeCounter.java`

```java
Tabs tabs = new Tabs(createTab("Inbox", 12), createTab("Important", 3),
        createTab("Spam", 45), createTab("Archive", 23));

...

private static Tab createTab(String labelText, int messageCount) {
    Span label = new Span(labelText);
    Span counter = new Span(String.valueOf(messageCount));
    counter.getElement().getThemeList().add("badge pill small contrast");
    counter.getStyle().set("margin-inline-start", "var(--lumo-space-s)");
    // Accessible badge label
    String counterLabel = String.format("%d unread messages", messageCount);
    counter.getElement().setAttribute("aria-label", counterLabel);
    counter.getElement().setAttribute("title", counterLabel);

    return new Tab(label, counter);
}
```

**React** — `badge-counter.tsx`

```tsx
<Tabs>
  <Tab>
    <span>Inbox</span>
    <span
      {...{ theme: 'badge contrast pill small' }}
      style={badgeStyle}
      aria-label="12 unread messages"
      title="12 unread messages"
    >
      12
    </span>
  </Tab>
  <Tab>
    <span>Important</span>
    <span
      {...{ theme: 'badge contrast pill small' }}
      style={badgeStyle}
      aria-label="3 unread messages"
      title="3 unread messages"
    >
      3
    </span>
  </Tab>
  <Tab>
    <span>Spam</span>
    <span
      {...{ theme: 'badge contrast pill small' }}
      style={badgeStyle}
      aria-label="45 unread messages"
      title="45 unread messages"
    >
      45
    </span>
  </Tab>
  <Tab>
    <span>Archive</span>
    <span
      {...{ theme: 'badge contrast pill small' }}
      style={badgeStyle}
      aria-label="23 unread messages"
      title="23 unread messages"
    >
      23
    </span>
  </Tab>
</Tabs>
```

Assistive technologies, such as screen readers, interpret badges solely based on their content. Without proper context, they may end up confusing the user. To provide context for people using screen readers, set the badge’s `aria-label` attribute.

## <a id="best-practices"></a>Best Practices

### <a id="badge-vs-button"></a>Badge vs Button

Badges and Buttons are similar in appearance. This might lead users to think that badges are interactive.

Placement, language, shape, and color can all help mitigate any confusion. First, badges shouldn’t be labeled with active verbs. They aren’t actions, but rather static text/content. Second, avoid placing badges directly next to Buttons, in particular if they are using similar themes. The `pill` theme variant may aid in making badges and Buttons more distinct from one another.

`69AF8882-CA67-4AB8-BF9F-69A1EA6CE84C`
