Skip to content
cennge-ui
GitHubv0.3.1, changelog

Indicator

A dot or a count pinned to a corner of an icon button, an avatar or anything else.

cennge-ui
128 unread messages3 unread notificationsCEOnline
DeploysPCLiveASBuildingSSFailed
  1. Click the bell: the count drops to zero and the indicator shrinks away. Send a notification brings it back.
  2. A count above max shows as max+: with the default of 99, the inbox's 128 reads 99+.
  3. On the round avatar, shape="circle" moves the dot in from the corner onto the rim; on the square deploy tiles it stays on the corner.
Live: click the bell.

Field marks

Import
import { Indicator } from "cennge-ui";
Size
1.1 KB gzip for this import, its dependencies aside
Pattern
A <span> around your element; hidden from screen readers unless you pass label
State
None: pass content, and isInvisible to hide it
SSR
Rendered on the server in the Next.js check; hydrates without a warning
Outline
A 2 px ring in the page colour parts it from the element under it
Tests
Unit tests

When to use#

Use an Indicator to flag something about one control: unread notifications on a bell, a person's status on their avatar, a new entry on a tab. It sits on the corner without taking space, so the layout around the control does not change when the count appears or goes.

Similar#

  • Badge is a label in the flow of the text, with room for words; an Indicator is pinned on top of another element.
  • Avatar is often what the Indicator sits on; status dots belong on the avatar, not beside it.

Counts and dots#

content takes a number or a short word. Numbers above max show as max+. Without content it is a plain dot, sized by size. The colour is danger unless you choose another.

4 open pull requests12 failing checksNew releaseUnread changes
counts.tsx
import { Button, Indicator } from "cennge-ui";

export default function IndicatorCounts() {
  return (
    <div className="flex flex-wrap items-center gap-8">
      <Indicator content={4} label="4 open pull requests">
        <Button variant="flat">Pull requests</Button>
      </Indicator>
      <Indicator content={12} max={9} color="warning" label="12 failing checks">
        <Button variant="flat">Checks</Button>
      </Indicator>
      <Indicator content="new" color="primary" size="lg" label="New release">
        <Button variant="flat">Releases</Button>
      </Indicator>
      <Indicator color="info" size="sm" label="Unread changes">
        <Button variant="flat">Changelog</Button>
      </Indicator>
    </div>
  );
}

Status on an avatar#

On a circle the corner of the box is empty space. shape="circle" moves the indicator onto the rim; placement picks the corner. Write the status beside the avatar as well, as here, so it does not rest on colour alone.

  • ALAnn LeeOnline
  • MHMax HartAway
  • ICIra ColeBusy
avatar-status.tsx
import { Avatar, Indicator, type CenngeColor } from "cennge-ui";

const people: { name: string; status: string; color: CenngeColor }[] = [
  { name: "Ann Lee", status: "Online", color: "success" },
  { name: "Max Hart", status: "Away", color: "warning" },
  { name: "Ira Cole", status: "Busy", color: "danger" },
];

export default function AvatarStatus() {
  return (
    <ul className="flex flex-wrap gap-x-8 gap-y-4">
      {people.map((person) => (
        <li key={person.name} className="flex items-center gap-3">
          {/* The status is written beside the avatar, so the dot needs no label of its own. */}
          <Indicator color={person.color} shape="circle" placement="bottom-right" size="lg">
            <Avatar name={person.name} size="lg" />
          </Indicator>
          <span className="flex flex-col text-sm">
            <span className="font-medium">{person.name}</span>
            <span className="text-muted">{person.status}</span>
          </span>
        </li>
      ))}
    </ul>
  );
}

Accessibility#

An Indicator is decorative until you give it a label: then screen readers read the label, "3 unread notifications", instead of a bare "3". While isInvisible is on, the label is not read either. Keep the name of the control itself, such as "Notifications", on the control.

Styling#

The ring that parts the indicator from what it sits on is ring-background, the page colour. On a panel or a card, pass the panel's colour in className, such as ring-surface-3, as the bar on the plate does; showOutline={false} removes the ring.

Props#

PropTypeDefault
childrenrequired
React.ReactNodeThe element the indicator sits on: an Avatar, an icon Button.
–
content
React.ReactNodeA count or short text. Omit for a plain dot.
–
max
numberNumbers above this show as "99+".
99
color
"default""primary""secondary""success""warning""danger""info"
"danger"
size
"sm""md""lg"
"md"
placement
"top-right""top-left""bottom-right""bottom-left"
"top-right"
shape
"rectangle""circle"Use "circle" on round anchors such as avatars.
"rectangle"
isInvisible
booleanHide with a scale animation, e.g. when the count drops to zero.
–
showOutline
boolean
true
label
stringWhat the indicator means, for screen readers: "3 unread messages", "Online". Without it the indicator is decorative.
–

Also takes every attribute of <span>, except content, color, which mean something else here.