Skip to main content

Badge

Compact status indicators, count bubbles, and dot markers.

Quick start
import { Badge } from "mates-ui";

Badge(3);
Badge(99, { max: 99, variant: "error" });
Badge(undefined, { dot: true, variant: "primary" });

Variants

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';

const App = () => () => html`
<x-row gap="var(--md-space-3)" align="center">
${Badge(3)}
${Badge(12, { variant: "error" })}
${Badge(5, { variant: "success" })}
${Badge(2, { variant: "warning" })}
${Badge(7, { variant: "neutral" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Sizes

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';

const App = () => () => html`
<x-row gap="var(--md-space-3)" align="center">
${Badge(3, { size: "sm" })}
${Badge(3, { size: "md" })}
${Badge(3, { size: "lg" })}
${Badge("New", { size: "sm", variant: "success" })}
${Badge(undefined, { dot: true, size: "sm" })}
${Badge(undefined, { dot: true, size: "md" })}
${Badge(undefined, { dot: true, size: "lg", variant: "error" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Text badges

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';

const App = () => () => html`
<x-row gap="var(--md-space-3)" align="center">
${Badge("New")}
${Badge("Beta")}
${Badge("Pro", { variant: "warning" })}
${Badge("Live", { variant: "success" })}
${Badge("Error", { variant: "error" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Count with max

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';

const App = () => () => html`
<x-row gap="var(--md-space-3)" align="center">
${Badge(5, { max: 99 })}
${Badge(99, { max: 99 })}
${Badge(100, { max: 99 })}
${Badge(999, { max: 99, variant: "error" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Dot indicators

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';

const App = () => () => html`
<x-row gap="var(--md-space-3)" align="center">
${Badge(undefined, { dot: true })}
${Badge(undefined, { dot: true, variant: "error" })}
${Badge(undefined, { dot: true, variant: "success" })}
${Badge(undefined, { dot: true, variant: "warning" })}
${Badge(undefined, { dot: true, variant: "neutral" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Composed with icon

import { html, renderApp } from 'mates';
import { Badge } from 'mates-ui';
import { MailIcon, NotificationsIcon, PersonIcon } from 'mates-icons';

const App = () => () => html`
<x-row gap="var(--md-space-6)" align="center">
<div style="position:relative;display:inline-flex;">
${NotificationsIcon({ size: 24 })}
<span style="position:absolute;top:-0.25rem;right:-0.38rem;">
${Badge(3, { variant: "error" })}
</span>
</div>
<div style="position:relative;display:inline-flex;">
${MailIcon({ size: 24 })}
<span style="position:absolute;top:-0.25rem;right:-0.38rem;">
${Badge(99, { max: 99, variant: "error" })}
</span>
</div>
<div style="position:relative;display:inline-flex;">
${PersonIcon({ size: 24 })}
<span style="position:absolute;top:-0.13rem;right:-0.13rem;">
${Badge(undefined, { dot: true, variant: "success" })}
</span>
</div>
</x-row>
`;

renderApp(App, document.getElementById('app'));

Skeleton (loading placeholder)

import { html, renderApp } from 'mates';
import { Skeleton } from 'mates-ui';

const App = () => () => html`
<x-col gap="var(--md-space-3)" style="max-width:22.5rem;width:100%;">
<x-row gap="var(--md-space-3)" align="center">
${Skeleton({ variant: "circle", width: "2.5rem" })}
<x-col gap="var(--md-space-2)" style="flex:1;">
${Skeleton({ variant: "text", width: "60%", height: "0.88rem" })}
${Skeleton({ variant: "text", width: "40%", height: "0.75rem" })}
</x-col>
</x-row>
${Skeleton({ variant: "rect", width: "100%", height: "5rem" })}
${Skeleton({ variant: "text", lines: 4, height: "0.88rem" })}
<x-row gap="var(--md-space-2)">
${Skeleton({ variant: "rect", width: "5rem", height: "2rem" })}
${Skeleton({ variant: "rect", width: "5rem", height: "2rem" })}
</x-row>
</x-col>
`;

renderApp(App, document.getElementById('app'));

Badge

import { Badge } from "mates-ui";
Prop Type Default Description
attr AttrMap Extra HTML attributes forwarded to the root element.
classes string | string[] Extra root classes — joined and appended after built-in classes.
content number | string | undefined Badge label. Omit (or pass undefined) to render a dot badge.
data DataAttrMap Root `data-*` map (`testId` → `data-testid`). `testId` prop / `attr` win over `data`.
dot boolean Renders a small filled circle with no content. Ignores the content prop.
id string Root `id` shorthand (`attr.id` wins). Form native-control `id` props are unchanged.
max number Caps numeric content — values above max display as "max+". Has no effect on string content.
on OnEventMap DOM event handlers on the root element.
size "sm" | "md" | "lg" "md" Visual size of the badge.
style StyleMap Inline CSS on the root element.
style StyleMap Root inline CSS (`style` prop; `style` wins if both set).
testId string Overrides default data-testid for testing.
variant "primary" | "error" | "success" | "warning" | "neutral" "primary" Color variant.

CSS tokens

Token Role
--md-color-error Error variant background
--md-color-on-primary Primary variant text
--md-color-primary Primary variant background
--md-color-success Success variant background
--md-color-warning Warning variant background
--md-shape-full Pill border-radius

Caveats

  • When `dot: true`, `content` is ignored.
  • `max` only applies to numeric content — string content is always shown as-is.
  • For accessible badge-on-icon patterns, wrap in a `position:relative` container and add `aria-label` to the icon element describing the count.