Status
Animated status dots — a person's presence and a system or service's state.
Status — A compact status dot with an optional label and ping/pulse motion. Presence (online, offline, busy, away, neutral) and system (active, fixing, down, idle) vocabularies share one dot.
Preview
{{-- One presence dot; the playground's variant and size controls drive it. --}}
<x-ui.status variant="online" label="{{ __('Online') }}" :pulse="true" />
Variant options
Size options
Installation
php artisan ui:add status
Registry contract
php artisan ui:add status
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/status.blade.php
- Registry dependencies
- None — installs on its own.
- Packages
-
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: Status (`status`)
A compact status dot with an optional label and ping/pulse motion. Presence (online, offline, busy, away, neutral) and system (active, fixing, down, idle) vocabularies share one dot.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add status
```
## Usage
```blade
{{-- One presence dot; the playground's variant and size controls drive it. --}}
<x-ui.status variant="online" label="{{ __('Online') }}" :pulse="true" />
```
## Props
- `variant` (online|busy|away|offline|neutral, default `online`) — Presence (online, busy, away, offline, neutral) or system (active, fixing, down, idle) state; unknown values fall back to neutral.
- `label` (mixed|null, default `null`) — Visible label text; when omitted, the variant name is announced to screen readers only.
- `pulse` (mixed|null, default `null`) — Motion override: true or 'ping' echoes a ring outward, 'pulse' breathes the dot, false stays still; unset lets the variant decide (active pings, fixing pulses).
- `size` (md|sm|lg, default `md`) — Dot and text size: sm, md, or lg.
- `appearance` (string, default `dot`) — dot renders a plain dot and label; pill renders a soft tinted chip with the dot inside.
## Use when
- Use to show state, status, notification, or lightweight social feedback near the relevant object.
- Showing a presence or system state (online, busy, away, offline, active, fixing, down, idle) as a compact dot with an optional label.
- Wanting a pill appearance for a table cell or card header instead of a bare dot and label.
## Avoid when
- Do not overload small status elements with explanations that should remain visible in text.
- Announcing a one-off outcome message rather than an ongoing state; use alert or a toast instead.
- Needing a numeric count on a trigger corner; use notification-badge instead.
## Anti-patterns
- Using color or icon alone to communicate state
## Rules
- Use the `<brok:status>` tag (or `<x-ui.status>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/status
- Registry JSON (files, props, contract): https://brokui.dev/r/open/status.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
<div class="flex flex-col gap-6">
{{-- Pill appearance: a soft chip with the dot inside --}}
<div class="flex flex-wrap items-center gap-3">
<x-ui.status appearance="pill" variant="online" label="{{ __('Online') }}" :pulse="true" />
<x-ui.status appearance="pill" variant="busy" label="{{ __('In a meeting') }}" />
<x-ui.status appearance="pill" variant="away" label="{{ __('Away') }}" />
<x-ui.status appearance="pill" variant="offline" label="{{ __('Offline') }}" />
</div>
{{-- Pulse on vs off --}}
<div class="flex flex-wrap items-center gap-6">
<x-ui.status variant="online" label="{{ __('Live') }}" :pulse="true" />
<x-ui.status variant="busy" label="{{ __('Recording') }}" :pulse="true" />
</div>
{{-- Sizes --}}
<div class="flex flex-wrap items-center gap-6">
<x-ui.status variant="online" label="{{ __('Small') }}" size="sm" />
<x-ui.status variant="online" label="{{ __('Medium') }}" size="md" />
<x-ui.status variant="online" label="{{ __('Large') }}" size="lg" />
</div>
{{-- No label (sr-only fallback), e.g. on an avatar --}}
<div class="flex flex-wrap items-center gap-4">
<x-ui.status variant="online" />
<x-ui.status variant="busy" />
<x-ui.status variant="away" />
<x-ui.status variant="offline" />
</div>
</div>
{{-- System vocabulary (the former status-indicator): active pings, fixing
pulses, down and idle stay still. --}}
<div class="flex flex-col gap-3">
<x-ui.status variant="active" size="lg" label="{{ __('All systems operational') }}" />
<x-ui.status variant="fixing" size="lg" label="{{ __('Degraded — recovering') }}" />
<x-ui.status variant="down" size="lg" label="{{ __('Outage') }}" />
<x-ui.status variant="idle" size="lg" label="{{ __('Idle') }}" />
</div>
<div class="flex flex-wrap items-center gap-6">
<x-ui.status variant="online" label="{{ __('Online') }}" :pulse="true" />
<x-ui.status variant="busy" label="{{ __('Busy') }}" />
<x-ui.status variant="away" label="{{ __('Away') }}" />
<x-ui.status variant="offline" label="{{ __('Offline') }}" />
<x-ui.status variant="neutral" label="{{ __('Unknown') }}" />
</div>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | online | busy | away | offline | neutral | online | Presence (online, busy, away, offline, neutral) or system (active, fixing, down, idle) state; unknown values fall back to neutral. |
| label | mixed | null | null | Visible label text; when omitted, the variant name is announced to screen readers only. |
| pulse | mixed | null | null | Motion override: true or 'ping' echoes a ring outward, 'pulse' breathes the dot, false stays still; unset lets the variant decide (active pings, fixing pulses). |
| size | md | sm | lg | md | Dot and text size: sm, md, or lg. |
| appearance | string | dot | dot renders a plain dot and label; pill renders a soft tinted chip with the dot inside. |
Slots
default— Unused; the component has no visible default slot content.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Unless pulse is set explicitly, motion follows the variant: active pings, fixing pulses, every other variant stays still.
- status-indicator reuses this component under its own data-slot name by passing a data-slot attribute.
- Exposes status text and an optional token-driven pulse.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Communicate concise object or system state.
Use when
- Use to show state, status, notification, or lightweight social feedback near the relevant object.
- Showing a presence or system state (online, busy, away, offline, active, fixing, down, idle) as a compact dot with an optional label.
- Wanting a pill appearance for a table cell or card header instead of a bare dot and label.
Avoid when
- Do not overload small status elements with explanations that should remain visible in text.
- Announcing a one-off outcome message rather than an ongoing state; use alert or a toast instead.
- Needing a numeric count on a trigger corner; use notification-badge instead.
Use instead
- Persistent explanatory text
Anti-patterns
- Using color or icon alone to communicate state
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- The root carries role="status"; when no visible label is set, the variant name is still announced via an sr-only span.
- The ping and pulse motion layers are purely decorative and marked aria-hidden.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Livewire can update this component through forwarded wire:* attributes.
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
// Presence: online | busy | away | offline | neutral.
// System: active | fixing | down | idle.
'variant' => 'online',
'label' => null,
// Motion on the dot: true|'ping' echoes a ring outward, 'pulse' breathes
// the dot itself, false is still. Unset, the variant decides — active
// pings and fixing pulses; every other variant is still.
'pulse' => null,
'size' => 'md',
// dot (default): a dot and a plain label. pill: a soft tinted chip with the
// dot inside — for a table cell or a card header.
'appearance' => 'dot',
])
@php
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$variant = $styles['normalizeVariant']($variant);
$variant = array_key_exists($variant, $styles['status']['variants']) ? $variant : 'neutral';
$size = in_array($size, ['sm', 'md', 'lg'], true) ? $size : 'md';
$appearance = $appearance === 'pill' ? 'pill' : 'dot';
$status = $styles['status'];
$motion = match (true) {
$pulse === null => $status['motion'][$variant] ?? false,
$pulse === true || $pulse === 'ping' => 'ping',
$pulse === 'pulse' => 'pulse',
default => false,
};
// status-indicator renders this component under its own name; the
// browser keeps only the first of two identical attributes, so read the
// override out of the bag and print exactly one.
$slotName = (string) ($attributes->get('data-slot') ?: 'status');
$dotColour = $status['variants'][$variant];
$sizeStyles = $status['sizes'][$size];
$dotSize = $sizeStyles['dot'];
// A hollow (offline) dot has no fill for the ping to echo; it pulses the ring colour.
$pingColour = $variant === 'offline' ? 'bg-muted-foreground/60' : $dotColour;
$rootClasses = $appearance === 'pill'
? 'inline-flex items-center gap-2 rounded-full font-medium '.$status['pills'][$variant].' '.$sizeStyles['pill']
: $status['base'];
@endphp
<span
data-slot="{{ $slotName }}"
data-variant="{{ $variant }}"
data-appearance="{{ $appearance }}"
@if ($motion) data-motion="{{ $motion }}" @endif
role="status"
{{ $attributes->except('data-slot')->merge(['class' => $rootClasses]) }}
>
{{-- Dot wrapper (relative so the ping layer can be absolute) --}}
<span class="relative inline-flex shrink-0 {{ $dotSize }}">
@if ($motion === 'ping')
<span data-slot="status-dot-ping" aria-hidden="true" class="{{ $status['pingBase'] }} {{ $pingColour }}"></span>
@endif
<span data-slot="status-dot" aria-hidden="true" class="{{ $status['dotBase'] }} {{ $dotSize }} {{ $dotColour }} {{ $motion === 'pulse' ? $status['pulseDot'] : '' }}"></span>
</span>
@if ($label !== null)
<span data-slot="status-label" class="{{ $appearance === 'pill' ? 'leading-none' : $status['labelBase'].' '.$sizeStyles['text'] }}">{{ $label }}</span>
@else
{{-- No visible label: give screen readers the variant name --}}
<span class="sr-only">{{ ucfirst($variant) }}</span>
@endif
</span>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.4.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0