IndiaCN UI

Indicator

A dot, count, or short label for drawing attention to state.

Introduction

Indicators mark something as needing attention — unread messages, pending items, a status. They attach to another element rather than standing alone.

This is UX4G's Badge component. IndiaCN's Badge is a label/tag, which is a different thing, so the notification form lives here under its own name to keep both usable.

VariantSizeUse
dot6pxSomething changed; no number needed
count16px circle, grows to a pillA number of pending items
textpill, 20px tallA one-word status

Installation

npx shadcn@latest add https://indiacn.in/r/indicator.json
import { Indicator } from '@/components/ui/indicator';

Usage

<Indicator variant="dot" />
<Indicator variant="count">3</Indicator>
<Indicator theme="success" variant="text">Resolved</Indicator>

Examples

Variants

332Primary
IndicatorDefault
import { Bell, Mail } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Indicator } from '@/components/ui/indicator';
import { Body3, Label2 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='flex items-center gap-6'>
      <Indicator variant='dot' />
      <Indicator variant='count'>3</Indicator>
      <Indicator variant='count'>32</Indicator>
      <Indicator variant='text'>Primary</Indicator>
    </div>
  );
}

Themes

UX4G defines three states for this component: primary, success and danger.

3Primary
3Success
3Danger
IndicatorThemes
import { Bell, Mail } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Indicator } from '@/components/ui/indicator';
import { Body3, Label2 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='grid gap-4'>
      <div className='flex items-center gap-4'>
        <Indicator theme='primary' variant='dot' />
        <Indicator theme='primary' variant='count'>
          3
        </Indicator>
        <Indicator theme='primary' variant='text'>
          Primary
        </Indicator>
      </div>
      <div className='flex items-center gap-4'>
        <Indicator theme='success' variant='dot' />
        <Indicator theme='success' variant='count'>
          3
        </Indicator>
        <Indicator theme='success' variant='text'>
          Success
        </Indicator>
      </div>
      <div className='flex items-center gap-4'>
        <Indicator theme='danger' variant='dot' />
        <Indicator theme='danger' variant='count'>
          3
        </Indicator>
        <Indicator theme='danger' variant='text'>
          Danger
        </Indicator>
      </div>
    </div>
  );
}

Attached to an Element

Position it with absolute on the indicator and relative on the wrapper.

5
Grievance statusResolved
IndicatorAttached
import { Bell, Mail } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Indicator } from '@/components/ui/indicator';
import { Body3, Label2 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='flex items-center gap-8'>
      <div className='relative inline-flex'>
        <Button theme='primary' variant='outlined' size='md' iconButton aria-label='Notifications'>
          <Bell />
        </Button>
        <Indicator theme='danger' variant='count' className='absolute -top-1 -right-1'>
          5
        </Indicator>
      </div>

      <div className='relative inline-flex'>
        <Button theme='primary' variant='outlined' size='md' iconButton aria-label='Inbox'>
          <Mail />
        </Button>
        <Indicator theme='danger' variant='dot' className='absolute top-0 right-0' />
      </div>

      <div className='flex items-center gap-2'>
        <Label2>Grievance status</Label2>
        <Indicator theme='success' variant='text'>
          Resolved
        </Indicator>
      </div>
    </div>
  );
}

Counts

11299+

A single digit renders a 16px circle; longer values grow into a pill.

IndicatorCounts
import { Bell, Mail } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Indicator } from '@/components/ui/indicator';
import { Body3, Label2 } from '@/components/ui/typography';

export default function Component() {
  return (
    <div className='grid gap-3'>
      <div className='flex items-center gap-4'>
        <Indicator variant='count'>1</Indicator>
        <Indicator variant='count'>12</Indicator>
        <Indicator variant='count'>99+</Indicator>
      </div>
      <Body3 className='text-neutral-500'>
        A single digit renders a 16px circle; longer values grow into a pill.
      </Body3>
    </div>
  );
}

Accessibility

  • A dot carries no text, so it is role="presentation" and invisible to screen readers. The meaning must come from its context — give the element it marks an accessible name that includes the state, e.g. aria-label="Inbox, unread messages".
  • A count announces its number, but not what the number means. Label the container: aria-label="5 unread notifications".
  • Do not rely on colour alone. danger and success look different but read identically to a screen reader, so the surrounding text must carry the distinction.

API Reference

Indicator

PropTypeDefaultDescription
variant'dot' | 'count' | 'text''count'Which form to render. dot ignores children
theme'primary' | 'success' | 'danger''primary'Colour, from UX4G's three states
childrenReactNodeThe number or label. Ignored when variant is dot
classNamestringExtra Tailwind classes, typically for positioning

On this page