Dot Group
A positioning container that pins indicator dots to the corners of content elements, with optional overflow handling and standalone stacking mode.
Installation
npx shadcn@latest add @redpanda/dot-grouppnpm dlx shadcn@latest add @redpanda/dot-groupyarn dlx shadcn@latest add @redpanda/dot-groupbunx shadcn@latest add @redpanda/dot-groupnubx shadcn@latest add @redpanda/dot-groupaube dlx shadcn@latest add @redpanda/dot-groupUsage
import { DotGroup } from '@/components/redpanda-ui/dot-group';import { Badge } from '@/components/redpanda-ui/badge';
{ /* Position dots on content */}<DotGroup content={ <Badge tone="default" variant="subtle"> kafka </Badge> } size="sm" items={[ { id: 'errors', kind: 'status', variant: 'destructive', }, { id: 'warnings', kind: 'status', variant: 'warning', }, ]}/>;
{ /* Standalone stacking (no content) */}<DotGroup size="sm" items={[ { id: 'errors', kind: 'status', variant: 'destructive', }, { id: 'warnings', kind: 'status', variant: 'warning', }, ]}/>;Descriptors use caller-owned, nonempty, unique IDs. Editing a count or changing order retains that indicator’s DOM node; changing its ID intentionally replaces it. label supplies an optional complete accessible name, including the count when relevant. The group owns size, overlap, and stack order; standalone StatusDot and CountDot remain unchanged.
When to use
Use DotGroup when:
- Positioning indicator dots on a badge, avatar, or other content element
- Displaying multiple status or count indicators that need compact stacking
- You need to truncate many indicators to a visible subset (
maxVisibleprop)
Don’t use DotGroup when:
- You only have a single indicator without content to attach it to (use StatusDot or CountDot directly)
- You need a label alongside the indicator (use StatusBadge)
- On a StatusBadge — it already composes StatusDot internally
Modes
Content positioning mode
When content is provided, DotGroup wraps the content in a relative container and positions the dots at the specified corner. This is the primary use case.
<DotGroup content={ <Badge tone="default" variant="subtle"> Service </Badge> } size="sm" items={[ { id: 'errors', kind: 'status', variant: 'destructive', }, ]}/>Standalone mode
Without content, DotGroup renders dots in a horizontal stack with overlapping negative margins. This is useful for inline indicator groups.
<DotGroup size="sm" items={[ { id: 'errors', kind: 'status', variant: 'destructive', }, { id: 'warnings', kind: 'status', variant: 'warning', }, ]}/>Pattern: Same-type grouping
Prefer same-type indicators for consistent visual weight. Items are explicit status or count descriptors with stable IDs. Status-only groups support every dot size; any count indicator requires sm, md, or lg. Arbitrary JSX children and implicit prop injection are no longer supported.
// StatusDot group<DotGroup content={<Badge tone="default" variant="subtle">kafka</Badge>} size="sm" items={[ { id: "errors", kind: "status", variant: "destructive" }, { id: "warnings", kind: "status", variant: "warning" }]}/>
// CountDot group<DotGroup content={<Badge tone="default" variant="subtle">broker-0</Badge>} size="sm" items={[ { id: "warnings", kind: "count", count: 5, variant: "warning" }, { id: "errors", kind: "count", count: 2, variant: "destructive" }]}/>Pattern: Overflow
When maxVisible is set and items exceed it, DotGroup renders the first N dots followed by a + icon signaling that more indicators exist. The icon sizes and contrasts automatically with the dots.
<DotGroup content={ <Badge tone="default" variant="subtle"> events </Badge> } maxVisible={3} size="sm" items={[ { id: 'errors', kind: 'status', variant: 'destructive', }, { id: 'warnings', kind: 'status', variant: 'warning', }, { id: 'information', kind: 'status', variant: 'informative', }, { id: 'healthy', kind: 'status', variant: 'success', }, { id: 'disabled', kind: 'status', variant: 'disabled', }, ]}/>;{ /* Shows the first 3 dots + overflow indicator */}Anatomy
Content positioning mode┌──────────────────────────────────┐│ [●][●] │ ← absolute positioned at corner│ Badge Label ││ │└──────────────────────────────────┘
Standalone mode[●][●][●] ← overlapping inline indicators with ring separationAPI Reference
DotGroup
A status descriptor has id, kind: 'status', optional variant, pulsing, and label. A count descriptor has id, kind: 'count', required count, and optional variant, max, and label. label is the complete accessible indicator name; unlabelled dots remain decorative. Group IDs must be nonempty and unique. maxVisible must be a nonnegative integer.
| Prop | Type | Default | Description |
|---|---|---|---|
items |
readonly DotGroupItem[] |
— | Explicit status/count descriptors with stable IDs |
content |
ReactNode? |
— | Element to position dots relative to |
position |
"top-right" | "top-left" | "bottom-right" | "bottom-left" | "top" | "bottom" | "left" | "right" |
"top-right" |
Position for dots when using content mode (corners or edge-centers) |
size |
"xxs" | "xs" | "sm" | "md" | "lg" |
"md" |
Controls owned dot size and overlap; count items require sm or larger |
maxVisible |
number? |
— | Maximum dots to show; overflow renders a + icon |
testId |
string? |
— | Test ID for visual regression testing |
className |
string? |
— | Additional CSS classes |
Credits
- Follows the overlapping stack pattern from AvatarGroup but simplified for indicator use cases.
- Designed to compose with StatusDot and CountDot.