Skip to content
Brok UI

Loading…

No results

Kpi Strip

Open source

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.

Version
v1.0.1
Stability
stable
License
MIT
Related
Stat
Saved Views
List Toolbar

Preview

previews.components.admin-kpi-strip.default.blade.php Blade
<div class="w-full">
    <x-ui.admin.kpi-strip />
</div>

Installation

terminal
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:

resources/js/ui/index.js JS
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.

  • blade resources/views/components/ui/admin/kpi-strip.blade.php
Registry dependencies
stat
Packages
composer: jml/brok:^0.2
npm: alpinejs

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.

admin-kpi-strip.md
# 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

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
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.

kpi-strip

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

Scannable data display

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
kpi-strip
Theming hooks
kpi-strip

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
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-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, 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 under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Needs wire:key

Add a stable wire:key when Livewire can reorder this interactive component.

livewire-component.blade.php Blade
<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.

resources/views/components/ui/admin/kpi-strip.blade.php Blade
{{--
    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