---
title: Indicator
description: Anchor a count badge, status dot, or ribbon to any corner of any target
visibility: guest
draft: false
---

# Indicator

Pins a small thing — an unread count, a status dot, a "NEW" ribbon — to the
corner of **any** target: a bell icon, an avatar, a card, a button.

It exists so you stop hand-rolling absolute positioning with negative corner
offsets on every one of them. That pattern is exactly the raw-`div` scaffolding a
component library is supposed to replace — and it silently breaks in RTL.

## Basic Usage

:::preview{title="An unread count on a bell"}
<x-wirekit::indicator>
    <x-wirekit::button intent="neutral" surface="ghost" aria-label="Notifications">
        <x-wirekit::icon name="bell" size="md" />
    </x-wirekit::button>
    <x-slot:badge>
        <x-wirekit::badge intent="danger" size="sm">3</x-wirekit::badge>
    </x-slot:badge>
</x-wirekit::indicator>
:::

## Corners

`position` is **logical**: `end` is the right corner in LTR and the *left*
corner in RTL, so the badge follows the reading direction on its own.

:::preview{title="Every corner"}
<x-wirekit::row gap="2xl" align="center" wrap>
    <x-wirekit::indicator position="top-end">
        <x-wirekit::avatar alt="Ada Lovelace" size="md" />
        <x-slot:badge><x-wirekit::badge intent="success" size="sm">1</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
    <x-wirekit::indicator position="top-start">
        <x-wirekit::avatar alt="Grace Hopper" size="md" />
        <x-slot:badge><x-wirekit::badge intent="info" size="sm">2</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
    <x-wirekit::indicator position="bottom-end">
        <x-wirekit::avatar alt="Alan Turing" size="md" />
        <x-slot:badge><x-wirekit::badge intent="warning" size="sm">3</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
    <x-wirekit::indicator position="bottom-start">
        <x-wirekit::avatar alt="Katherine Johnson" size="md" />
        <x-slot:badge><x-wirekit::badge intent="neutral" size="sm">4</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
</x-wirekit::row>
:::

## Offset

By default the badge is centered on the corner. `offset` nudges it further out.

:::preview{title="Nudged further out"}
<x-wirekit::row gap="lg" align="center">
    <x-wirekit::indicator>
        <x-wirekit::avatar alt="Centered on the corner" size="md" />
        <x-slot:badge><x-wirekit::badge intent="danger" size="sm">9</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
    <x-wirekit::indicator offset="4px">
        <x-wirekit::avatar alt="Nudged out by 4px" size="md" />
        <x-slot:badge><x-wirekit::badge intent="danger" size="sm">9</x-wirekit::badge></x-slot:badge>
    </x-wirekit::indicator>
</x-wirekit::row>
:::

## Naming the badge

**The badge must say what it means.** A bare "3" next to a bell tells a
screen-reader user nothing.

```blade
{{-- 1. A count: name it, and hide the raw number from assistive tech --}}
<x-wirekit::indicator>
    <x-wirekit::button aria-label="Notifications">
        <x-wirekit::icon name="bell" size="md" />
    </x-wirekit::button>
    <x-slot:badge>
        <x-wirekit::badge intent="danger" size="sm">
            <span aria-hidden="true">{{ $unread }}</span>
            <span class="sr-only">{{ $unread }} unread notifications</span>
        </x-wirekit::badge>
    </x-slot:badge>
</x-wirekit::indicator>
```

```blade
{{-- 2. A purely decorative presence dot: hide it, and say it elsewhere --}}
<x-wirekit::indicator position="bottom-end">
    <x-wirekit::avatar :alt="$user->name" />
    <x-slot:badge>
        <span aria-hidden="true" style="width:.625rem;height:.625rem;border-radius:9999px;background:var(--color-wk-success);display:block"></span>
    </x-slot:badge>
</x-wirekit::indicator>
<span class="sr-only">{{ $user->name }} is online</span>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `position` | string | `'top-end'` | `top-end`, `top-start`, `bottom-end`, `bottom-start` — logical, so `end` follows the reading direction |
| `offset` | string\|null | `null` | Nudge the badge further out (any CSS length); default centers it on the corner |
| `scope` | string\|null | `null` | Scoped personalization name |

## Slots

| Slot | Description |
| --- | --- |
| default | The target being decorated |
| `badge` | The thing anchored to the corner |

## Accessibility

- **The badge carries its own meaning.** Indicator only positions it. A count
  needs a real name (`3 unread notifications`), and a decorative dot needs
  `aria-hidden` plus the state stated elsewhere — see the examples above.
- The badge is `pointer-events: none`, so it never swallows a click meant for the
  target underneath. Anything genuinely interactive inside it (a link, a button)
  gets pointer events back automatically.
- The badge is out of flow, so decorating a target never resizes it — no layout
  shift, no touch target shrinking.
- **RTL is automatic**: placement uses logical insets, so `end` moves to the left
  corner under `dir="rtl"` with no extra rules.

## Keyboard Interaction

This component is presentational and does not respond to keyboard input. The
target and any control inside the badge keep their own keyboard behavior.

## Pitfalls

- **Do not leave a bare number as the whole badge.** "3" is not an accessible
  name — say "3 unread notifications".
- **Do not put your main action in the badge.** It is a decoration on the
  target, not a second control competing for the same corner.
- **Do not hand-roll the absolute-corner-offset pattern.** That is what this
  replaces — and it does not mirror in RTL.

## Design Tokens

| Element | Token |
| --- | --- |
| Corner nudge | `--indicator-wk-offset` (default `0px`) |

The badge's own colors and shape come from whatever you place in the slot —
usually [Badge](/components/badge).

## Further Reading

- [MDN: CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
- [WCAG 1.4.1: Use of Color](https://www.w3.org/WAI/WCAG21/Understanding/use-of-color.html)
