Kpi Strip
The operational figures above a list as a grid of stat tiles, each a link or a button that applies the saved view holding the rows it counts.
Preview
<div class="w-full">
<x-ui.admin.kpi-strip />
</div>
Installation
php artisan ui:add admin-kpi-strip
Note
This component ships an Alpine behavior module at
resources/js/ui/admin-kpi-strip.js. Import it once from your bundle so it registers on alpine:init:
import './admin-kpi-strip.js';
Registry contract
php artisan ui:add admin-kpi-strip
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/admin/kpi-strip.blade.php
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: Kpi Strip (`admin-kpi-strip`)
The operational figures above a list as a grid of stat tiles, each a link or a button that applies the saved view holding the rows it counts.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add admin-kpi-strip
```
## Usage
```blade
<div class="w-full">
<x-ui.admin.kpi-strip />
</div>
```
## Props
- `kpis` (array, default `[]`) — [['label','value','note' => null,'delta' => null,'tone' => null,'href' => null,'view' => null]]. `view` makes the tile a button dispatching `kpi-strip:apply-view` with `{ key }`; `href` makes it a link.
- `columns` (2|3|4, default `4`) — Tiles per row at the widest breakpoint; always two-up below `xl`.
- `label` (string|null, default `null`) — Accessible group name.
## Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- A list screen leads with three or four counts the operator triages by, and each count must open the rows behind it.
- A KPI is a filter into the list: pass `view` and listen for `kpi-strip:apply-view` on the list root.
## Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- Figures with trends and charts for a dashboard — use `metrics-*` or `stats-*` blocks.
- A single figure — use `stat` directly.
## Anti-patterns
- Adding display ornament without informational value
## Rules
- Use the `<brok:admin-kpi-strip>` tag (or `<x-ui.admin-kpi-strip>`) 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/admin-kpi-strip
- Registry JSON (files, props, contract): https://brokui.dev/r/open/admin-kpi-strip.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| kpis | array | [] | [['label','value','note' => null,'delta' => null,'tone' => null,'href' => null,'view' => null]]. `view` makes the tile a button dispatching `kpi-strip:apply-view` with `{ key }`; `href` makes it a link. |
| columns | 2 | 3 | 4 | 4 | Tiles per row at the widest breakpoint; always two-up below `xl`. |
| label | string | null | null | Accessible group name. |
Slots
Default Blade slot only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Tiles are uncoloured unless a `tone` is set, so contrast stays with the rows.
- A tile with `view` dispatches a bubbling `kpi-strip:apply-view` event from its own root; a tile with `href` navigates; a tile with neither is inert.
- Installs a JavaScript behavior module when the registry item includes resources/js/ui files.
- Declares registry capability flags: a11y, interactive, responsive, rtl, darkMode, localized, alpine.
Guidance
Present data for rapid scanning.
Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- A list screen leads with three or four counts the operator triages by, and each count must open the rows behind it.
- A KPI is a filter into the list: pass `view` and listen for `kpi-strip:apply-view` on the list root.
Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- Figures with trends and charts for a dashboard — use `metrics-*` or `stats-*` blocks.
- A single figure — use `stat` directly.
Use instead
- Table for exact comparison
- Plain text for a single value
Anti-patterns
- Adding display ornament without informational value
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- managed
- Focus
none
- Actionable tiles are real buttons or links with an accessible label naming the figure they open.
- 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
Add a stable wire:key when Livewire can reorder this interactive component.
<div wire:key="admin-kpi-strip-{{ $record->id }}">
<div class="w-full">
<x-ui.admin.kpi-strip />
</div>
</div>
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
{{--
KPI Strip — the operational figures above a list, each one a way into
the rows it counts.
A figure you cannot open is a dead end. Every tile is a link (`href`) or
a button that dispatches an event (`view`) the list listens for, so the
count and the list can never be two different questions: pressing
"Awaiting payment, 14" applies the view that holds those fourteen. The
tile is uncoloured by default; contrast is reserved for the rows and a
`tone` is set only where the figure itself is the warning.
The event is `kpi-strip:apply-view` with `{ key }` in `detail`, bubbling
from the tile so a list root anywhere above it can catch it with
`@kpi-strip:apply-view.window`. The name is fixed so one listener serves
every module; the `key` is whatever the host's saved-views vocabulary
uses.
Generalised from the Noord-C admin's `kpis.blade.php`, copied into
twenty-four modules with only the event name changed.
--}}
@props([
'kpis' => [],
'columns' => 4,
'label' => null,
])
@php
// Shapes documented in item.json knowledge.props:
// kpis: [['label', 'value', 'note' => null, 'delta' => null, 'tone' => null, 'href' => null, 'view' => null]]
// columns: 2|3|4 — the tile count at the widest breakpoint; the strip is two-up below xl.
$kpis = $kpis !== [] ? $kpis : [
['label' => __('Awaiting payment'), 'value' => '14', 'note' => __('Older than one hour'), 'tone' => 'attention', 'view' => 'awaiting_payment'],
['label' => __('Ready to ship'), 'value' => '23', 'note' => __('Paid, stock reserved'), 'view' => 'ready'],
['label' => __('Shipped today'), 'value' => '42', 'note' => __('Across every carrier'), 'delta' => '+8', 'view' => 'shipped_today'],
['label' => __('Refund requests'), 'value' => '5', 'note' => __('Waiting on a decision'), 'tone' => 'negative', 'view' => 'refunds'],
];
$kpis = array_values(array_filter((array) $kpis, 'is_array'));
$gridClass = match ((int) $columns) {
2 => 'grid-cols-2',
3 => 'grid-cols-2 xl:grid-cols-3',
default => 'grid-cols-2 xl:grid-cols-4',
};
$label ??= __('Key figures');
@endphp
<div
data-slot="kpi-strip"
data-surface="admin"
role="group"
aria-label="{{ $label }}"
{{ $attributes->merge(['class' => 'grid gap-4 '.$gridClass]) }}
>
@foreach ($kpis as $kpi)
@php
$view = $kpi['view'] ?? null;
$href = $kpi['href'] ?? null;
$as = $view !== null ? 'button' : ($href !== null ? 'a' : null);
// Built here rather than inline: a Blade directive inside a
// component-tag attribute is not compiled, a bound PHP string is.
$dispatch = $view !== null
? "\$dispatch('kpi-strip:apply-view', { key: ".json_encode($view, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT)." })"
: null;
$openLabel = $view !== null
? __('Open :label (:value)', ['label' => $kpi['label'] ?? '', 'value' => $kpi['value'] ?? ''])
: null;
@endphp
<x-ui.stat
:label="$kpi['label'] ?? ''"
:value="$kpi['value'] ?? null"
:delta="$kpi['delta'] ?? null"
:tone="$kpi['tone'] ?? null"
:description="$kpi['note'] ?? null"
:href="$href"
:as="$as"
:data-view="$view"
:x-data="$view !== null ? '' : null"
:x-on:click="$dispatch"
:aria-label="$openLabel"
/>
@endforeach
</div>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.1 - 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