My Design System

Badge

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

Open in a
new tab
<span theme="badge">Pending</span>
<span theme="badge success">Confirmed</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:

{
  "lumoImports": [<...>, "badge"]
}

The theme.json is located in the theme folder at /frontend/themes/<my-theme>/theme.json.

Label

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

Icons

Badges can contain icons in addition to text. Icons can be placed on either side of the text.

Open in a
new tab
<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 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 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>
Note
Use icons sparingly
The benefit of icons should be weighed against the visual noise it adds.

Icon-Only

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

Open in a
new tab
<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>

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

Open in a
new tab

The inline .renderer function is encapsulated by the guard directive for performance reasons. See the official Lit documentation for more details.

const renderBoolean = guard(
  [],
  () =>
    (
      root: HTMLElement,
      column?: GridColumnElement,
      model?: GridItemModel<UserPermissions>
    ): void => {
      if (!column || !model) {
        return;
      }

      let icon: string;
      let title: string;
      let theme: string;

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

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

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

Theme Variants

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

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.

Open in a
new tab
<span theme="badge small">Pending</span>
<span theme="badge success small">Confirmed</span>
<span theme="badge error small">Denied</span>
<span theme="badge contrast small">On hold</span>

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.

Open in a
new tab
<vaadin-vertical-layout theme="spacing">
  <vaadin-horizontal-layout theme="spacing">
    <span theme="badge">Pending</span>
    <span theme="badge success">Confirmed</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 error primary">Denied</span>
    <span theme="badge contrast primary">On hold</span>
  </vaadin-horizontal-layout>
</vaadin-vertical-layout>
VariantTheme nameUsage recommendations

Normal

Default style. Recommended for informational messages. Note that this style may be confused with a Button or link.

Success

Success

Highlight positive outcomes, such as when a task or operation is completed.

Error

Error

Use the error theme variant to communicate alerts, failures, or warnings.

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 extra 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’re using colors and icons to convey information, provide the same info via aria-label to ensure screen readers can interpret the info.

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.

Open in a
new tab
<span theme="badge pill">Pending</span>
<span theme="badge success pill">Confirmed</span>
<span theme="badge error pill">Denied</span>
<span theme="badge contrast pill">On hold</span>

Use Cases

Highlighting and Distinguishing Information

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

Open in a
new tab

The inline .renderer function is encapsulated by the guard directive for performance reasons. See the official Lit documentation for more details.

return html`
  <vaadin-grid .items="${this.items}">
    <vaadin-grid-column path="report" header="Report"></vaadin-grid-column>
    <vaadin-grid-column
      header="Due date"
      .renderer="${guard(
        [],
        () => (root: HTMLElement, column?: GridColumnElement, model?: GridItemModel<Report>) => {
          if (!column || !model) {
            return;
          }

          render(html`${dateFormatter.format(new Date(model.item.due))}`, root);
        }
      )}"
    ></vaadin-grid-column>
    <vaadin-grid-column path="assignee" header="Assignee"></vaadin-grid-column>
    <vaadin-grid-column
      header="Status"
      .renderer="${guard(
        [],
        () => (root: HTMLElement, column?: GridColumnElement, model?: GridItemModel<Report>) => {
          if (!column || !model) {
            return;
          }

          const { status } = model.item;

          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;
          }

          render(html` <span theme="badge ${theme} primary"> ${title} </span> `, root);
        }
      )}"
    ></vaadin-grid-column>
  </vaadin-grid>
`;

They’re also often used for displaying metadata tags.

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.

Open in a
new tab
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>
`;

Counter

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

Open in a
new tab
<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>

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.

Best Practises

Badge vs Button

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

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