Skip to content

Indicator

An indicator pins a count bubble or a status dot to the corner of any element: unread counts on an inbox button, a “new” dot on a nav item, a mention count on an avatar. CSS-only. The inline badge stays the in-flow label; this is the positioned overlay.

Wrap the target in .re-indicator and add a .re-indicator__badge after it. A single digit renders as a circle; longer counts grow into a pill. Overflow (“99+”) is authored content, not CSS.

3 unread messages 99+ notifications
Live example
<div class="row">
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Inbox</button>
    <span class="re-indicator__badge">3<span class="re-sr-only"> unread messages</span></span>
  </span>
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Notifications</button>
    <span class="re-indicator__badge">99+<span class="re-sr-only"> notifications</span></span>
  </span>
</div>

data-dot renders a small dot with no visible text, for a binary “something new” signal.

New alerts
Live example
<span class="re-indicator">
  <button class="re-button" data-variant="ghost">Alerts</button>
  <span class="re-indicator__badge" data-dot><span class="re-sr-only">New alerts</span></span>
</span>

data-tone: danger (default, the notification convention), info, success, warning, neutral. Fills use the solid *-700 scale with --re-color-text-on-accent so the count text keeps AA contrast.

5 alerts 2 updates 4 completed 7 pending 9 archived
Live example
<div class="row">
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Danger</button>
    <span class="re-indicator__badge">5<span class="re-sr-only"> alerts</span></span>
  </span>
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Info</button>
    <span class="re-indicator__badge" data-tone="info"
      >2<span class="re-sr-only"> updates</span></span
    >
  </span>
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Success</button>
    <span class="re-indicator__badge" data-tone="success"
      >4<span class="re-sr-only"> completed</span></span
    >
  </span>
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Warning</button>
    <span class="re-indicator__badge" data-tone="warning"
      >7<span class="re-sr-only"> pending</span></span
    >
  </span>
  <span class="re-indicator">
    <button class="re-button" data-variant="secondary">Neutral</button>
    <span class="re-indicator__badge" data-tone="neutral"
      >9<span class="re-sr-only"> archived</span></span
    >
  </span>
</div>

data-position="bottom-end" moves the badge to the lower corner. Corners are logical, so both positions mirror automatically under RTL.

GH 2
Live example
<span class="re-indicator">
  <span class="re-avatar" role="img" aria-label="Grace Hopper, 2 mentions">GH</span>
  <span class="re-indicator__badge" data-position="bottom-end" data-tone="info">2</span>
</span>

The badge reads after its target in source order, and a bare number (or an empty dot) means nothing to assistive tech, so always provide real text:

  • Count on a normal target: append an .re-sr-only suffix inside the badge, e.g. 3<span class="re-sr-only"> unread messages</span>.
  • Dot: the sr-only text is the entire content, <span class="re-sr-only">New alerts</span>.
  • On a role="img" initials avatar: descendant text is presentational (same trap as the avatar presence dot), so fold the meaning into the avatar’s aria-label (“Grace Hopper, 2 mentions”) and leave the badge visual-only.
  • Forced colors: backgrounds flatten, which would erase the bubble and the dot, so both are re-established with system Highlight/HighlightText (tone variants included, at matched specificity).
  • Keep counts truthful and fresh: a stale visual count that contradicts the spoken text is worse than none.

See the accessibility guide for the project-wide stance.