Skip to content
Brok UI

Loading…

No results

Metrics

Open source

Dashboard analytics cards — a traffic trend card with a range control, a KPI performance row with sparklines, a sales-by-channel distribution, a weekly-traffic heatmap and a spending breakdown with a segmented bar and table.

Preview

5 of 5

resources/views/blocks/metrics-breakdown.blade.php Blade
@props([
    'title' => null,
    'total' => null,
    'delta' => null,
    'categories' => null,
    'actionHref' => null,
])

{{--
    Metrics Breakdown — a "Spending breakdown" style panel: a tray frame
    holding a headline stat (total + delta), a thin segmented bar of the
    categories with a dot/label/value legend, and a small table listing
    each category with a tinted round icon, its share, and its amount.

    `categories` rows: {name, percent, amount, icon?, tone?}. `percent` and
    `amount` should add up to the headline `total`. `icon` selects a built-in
    glyph (home, utensils, car, shopping, health — falls back to a plain
    dot). `tone` pins a category to a chart token (chart-1..5, primary, info,
    success, warning, destructive) instead of the auto-assigned palette
    order; the same colour drives the bar segment, the legend dot, and the
    table row's icon tint.
--}}

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');

    $title ??= __('Spending breakdown');
    $total ??= '$4,250';
    $delta ??= '+8.4%';
    $actionHref ??= '#';

    $categories ??= [
        ['name' => __('Housing'), 'percent' => 45, 'amount' => '$1,912', 'icon' => 'home'],
        ['name' => __('Food & Dining'), 'percent' => 30, 'amount' => '$1,275', 'icon' => 'utensils'],
        ['name' => __('Transport'), 'percent' => 25, 'amount' => '$1,063', 'icon' => 'car'],
    ];

    // Fixed inline icon set for the category chips, keyed by the `icon` field.
    // Hand-authored, trusted markup — not user data — so it is safe to print
    // unescaped below.
    $icons = [
        'home' => '<path d="M3 9.5 12 3l9 6.5"/><path d="M5 9.5V21h14V9.5"/><path d="M9 21v-6h6v6"/>',
        'utensils' => '<path d="M7 3v7a2 2 0 0 0 4 0V3"/><path d="M9 12v9"/><path d="M17 3c-1.1 0-2 1.57-2 4.5S15.9 12 17 12s2-1.57 2-4.5S18.1 3 17 3Z"/><path d="M17 12v9"/>',
        'car' => '<path d="M3 17V11l2-5h14l2 5v6"/><path d="M3 11h18"/><circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/>',
        'shopping' => '<path d="m6 2-3 4v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
        'health' => '<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>',
    ];

    // Same palette order partition-bar assigns by default, so an unspecified
    // category still lands on chart-1, chart-2, … in row order; `tone` opts a
    // category into a fixed token instead.
    $chartPalette = $styles['chart']['palette'];
    $tints = [
        'chart-1' => 'bg-chart-1/10 text-chart-1',
        'chart-2' => 'bg-chart-2/10 text-chart-2',
        'chart-3' => 'bg-chart-3/10 text-chart-3',
        'chart-4' => 'bg-chart-4/10 text-chart-4',
        'chart-5' => 'bg-chart-5/10 text-chart-5',
        'primary' => 'bg-primary/10 text-primary',
        'info' => 'bg-info/10 text-info',
        'success' => 'bg-success/10 text-success-text',
        'warning' => 'bg-warning/10 text-warning-text',
        'destructive' => 'bg-destructive/10 text-destructive-text',
    ];

    $rows = [];
    foreach (array_values($categories) as $i => $cat) {
        $variant = array_key_exists($cat['tone'] ?? null, $tints) ? $cat['tone'] : $chartPalette[$i % count($chartPalette)];
        $rows[] = [
            'name' => $cat['name'] ?? '',
            'percent' => (float) ($cat['percent'] ?? 0),
            'amount' => $cat['amount'] ?? null,
            'iconKey' => $cat['icon'] ?? null,
            'variant' => $variant,
        ];
    }

    $segments = array_map(fn ($row) => [
        'label' => $row['name'],
        'value' => rtrim(rtrim(number_format($row['percent'], 1), '0'), '.').'%',
        'num' => $row['percent'],
        'variant' => $row['variant'],
    ], $rows);
@endphp

<x-ui.tray
    :title="$title"
    :action-label="__('View all')"
    :action-href="$actionHref"
    {{ $attributes->merge(['class' => 'w-full']) }}
>
    <x-slot:icon>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M3 3v18h18" />
            <path d="M8 17V9" />
            <path d="M13 17V5" />
            <path d="M18 17v-7" />
        </svg>
    </x-slot:icon>

    <div class="flex flex-col gap-6">
        <x-ui.stat variant="plain" inverse :value="$total" :delta="$delta" />

        <x-ui.partition-bar :segments="$segments" size="xs" />

        <x-ui.table>
            <x-ui.table.header>
                <x-ui.table.row>
                    <x-ui.table.head>{{ __('Category') }}</x-ui.table.head>
                    <x-ui.table.head align="end">{{ __('Percent') }}</x-ui.table.head>
                    <x-ui.table.head align="end">{{ __('Total') }}</x-ui.table.head>
                </x-ui.table.row>
            </x-ui.table.header>
            <x-ui.table.body>
                @foreach ($rows as $row)
                    <x-ui.table.row>
                        <x-ui.table.cell>
                            <div class="flex min-w-0 items-center gap-2">
                                <span aria-hidden="true" class="inline-flex size-8 shrink-0 items-center justify-center rounded-full {{ $tints[$row['variant']] ?? $tints['primary'] }}">
                                    @if ($row['iconKey'] !== null && isset($icons[$row['iconKey']]))
                                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">{!! $icons[$row['iconKey']] !!}</svg>
                                    @else
                                        <span class="size-2 rounded-full bg-current"></span>
                                    @endif
                                </span>
                                <span class="min-w-0 truncate text-sm font-medium text-foreground">{{ $row['name'] }}</span>
                            </div>
                        </x-ui.table.cell>
                        <x-ui.table.cell align="end" class="tabular-nums text-muted-foreground">{{ rtrim(rtrim(number_format($row['percent'], 1), '0'), '.') }}%</x-ui.table.cell>
                        <x-ui.table.cell align="end" class="font-medium tabular-nums text-foreground">{{ $row['amount'] }}</x-ui.table.cell>
                    </x-ui.table.row>
                @endforeach
            </x-ui.table.body>
        </x-ui.table>
    </div>
</x-ui.tray>

Installation

terminal
php artisan ui:add blocks/metrics-breakdown

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/metrics-breakdown writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.0.1
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/metrics-breakdown.blade.php

Registry dependencies

tray stat partition-bar table

Package dependencies

composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

metrics-breakdown.md
# Brok UI block: Metrics Breakdown (`metrics-breakdown`)

A spending-breakdown panel: a tray frame holding a headline total with a delta, a segmented bar of categories with a dot/label legend, and a small table listing each category's share and amount.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/metrics-breakdown
```

## Render it

```blade
<x-blocks.metrics-breakdown />
```

## Props

- `title` (string, default `Spending breakdown`) — Tray header title.
- `total` (string, default `$4,250`) — Headline total shown as the stat value.
- `delta` (string, default `+8.4%`) — Change shown next to the total; the stat is toned negative because a spending increase is bad news regardless of the arithmetic sign.
- `categories` (mixed|null, default `null`) — Rows of {name, percent, amount, icon?, tone?}; null falls back to a 3-category sample (Housing 45% $1,912, Food & Dining 30% $1,275, Transport 25% $1,063). icon selects a built-in glyph (home, utensils, car, shopping, health); tone pins the row to a chart token instead of the auto-assigned palette order.
- `action-href` (string, default `#`) — Href for the tray's 'View all' action.

## Use when

- Summarising a total (spend, storage, time) across a handful of categories with a headline delta, a proportional segmented bar, and a per-category table of share and amount.
- Framing a KPI + chart + table trio inside one tinted panel with a header action such as 'View all'.

## Avoid when

- Only the channel list with per-row bars is needed, with no headline stat or segmented bar; use metrics-channels instead.
- The breakdown needs a time-series trend rather than a static category split; use metrics-traffic or metrics-kpis.

## Rules

- Render the installed block with `<x-blocks.metrics-breakdown />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/metrics-breakdown
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/metrics-breakdown.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Summarising a total (spend, storage, time) across a handful of categories with a headline delta, a proportional segmented bar, and a per-category table of share and amount.
  • Framing a KPI + chart + table trio inside one tinted panel with a header action such as 'View all'.

Avoid when

  • Only the channel list with per-row bars is needed, with no headline stat or segmented bar; use metrics-channels instead.
  • The breakdown needs a time-series trend rather than a static category split; use metrics-traffic or metrics-kpis.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
tray with icon, title, and a 'View all' action well: headline stat (total + delta), segmented partition bar with legend, category table

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.metrics-breakdown />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/metrics-breakdown.blade.php Blade
@props([
    'title' => null,
    'total' => null,
    'delta' => null,
    'categories' => null,
    'actionHref' => null,
])

{{--
    Metrics Breakdown — a "Spending breakdown" style panel: a tray frame
    holding a headline stat (total + delta), a thin segmented bar of the
    categories with a dot/label/value legend, and a small table listing
    each category with a tinted round icon, its share, and its amount.

    `categories` rows: {name, percent, amount, icon?, tone?}. `percent` and
    `amount` should add up to the headline `total`. `icon` selects a built-in
    glyph (home, utensils, car, shopping, health — falls back to a plain
    dot). `tone` pins a category to a chart token (chart-1..5, primary, info,
    success, warning, destructive) instead of the auto-assigned palette
    order; the same colour drives the bar segment, the legend dot, and the
    table row's icon tint.
--}}

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');

    $title ??= __('Spending breakdown');
    $total ??= '$4,250';
    $delta ??= '+8.4%';
    $actionHref ??= '#';

    $categories ??= [
        ['name' => __('Housing'), 'percent' => 45, 'amount' => '$1,912', 'icon' => 'home'],
        ['name' => __('Food & Dining'), 'percent' => 30, 'amount' => '$1,275', 'icon' => 'utensils'],
        ['name' => __('Transport'), 'percent' => 25, 'amount' => '$1,063', 'icon' => 'car'],
    ];

    // Fixed inline icon set for the category chips, keyed by the `icon` field.
    // Hand-authored, trusted markup — not user data — so it is safe to print
    // unescaped below.
    $icons = [
        'home' => '<path d="M3 9.5 12 3l9 6.5"/><path d="M5 9.5V21h14V9.5"/><path d="M9 21v-6h6v6"/>',
        'utensils' => '<path d="M7 3v7a2 2 0 0 0 4 0V3"/><path d="M9 12v9"/><path d="M17 3c-1.1 0-2 1.57-2 4.5S15.9 12 17 12s2-1.57 2-4.5S18.1 3 17 3Z"/><path d="M17 12v9"/>',
        'car' => '<path d="M3 17V11l2-5h14l2 5v6"/><path d="M3 11h18"/><circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/>',
        'shopping' => '<path d="m6 2-3 4v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
        'health' => '<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>',
    ];

    // Same palette order partition-bar assigns by default, so an unspecified
    // category still lands on chart-1, chart-2, … in row order; `tone` opts a
    // category into a fixed token instead.
    $chartPalette = $styles['chart']['palette'];
    $tints = [
        'chart-1' => 'bg-chart-1/10 text-chart-1',
        'chart-2' => 'bg-chart-2/10 text-chart-2',
        'chart-3' => 'bg-chart-3/10 text-chart-3',
        'chart-4' => 'bg-chart-4/10 text-chart-4',
        'chart-5' => 'bg-chart-5/10 text-chart-5',
        'primary' => 'bg-primary/10 text-primary',
        'info' => 'bg-info/10 text-info',
        'success' => 'bg-success/10 text-success-text',
        'warning' => 'bg-warning/10 text-warning-text',
        'destructive' => 'bg-destructive/10 text-destructive-text',
    ];

    $rows = [];
    foreach (array_values($categories) as $i => $cat) {
        $variant = array_key_exists($cat['tone'] ?? null, $tints) ? $cat['tone'] : $chartPalette[$i % count($chartPalette)];
        $rows[] = [
            'name' => $cat['name'] ?? '',
            'percent' => (float) ($cat['percent'] ?? 0),
            'amount' => $cat['amount'] ?? null,
            'iconKey' => $cat['icon'] ?? null,
            'variant' => $variant,
        ];
    }

    $segments = array_map(fn ($row) => [
        'label' => $row['name'],
        'value' => rtrim(rtrim(number_format($row['percent'], 1), '0'), '.').'%',
        'num' => $row['percent'],
        'variant' => $row['variant'],
    ], $rows);
@endphp

<x-ui.tray
    :title="$title"
    :action-label="__('View all')"
    :action-href="$actionHref"
    {{ $attributes->merge(['class' => 'w-full']) }}
>
    <x-slot:icon>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M3 3v18h18" />
            <path d="M8 17V9" />
            <path d="M13 17V5" />
            <path d="M18 17v-7" />
        </svg>
    </x-slot:icon>

    <div class="flex flex-col gap-6">
        <x-ui.stat variant="plain" inverse :value="$total" :delta="$delta" />

        <x-ui.partition-bar :segments="$segments" size="xs" />

        <x-ui.table>
            <x-ui.table.header>
                <x-ui.table.row>
                    <x-ui.table.head>{{ __('Category') }}</x-ui.table.head>
                    <x-ui.table.head align="end">{{ __('Percent') }}</x-ui.table.head>
                    <x-ui.table.head align="end">{{ __('Total') }}</x-ui.table.head>
                </x-ui.table.row>
            </x-ui.table.header>
            <x-ui.table.body>
                @foreach ($rows as $row)
                    <x-ui.table.row>
                        <x-ui.table.cell>
                            <div class="flex min-w-0 items-center gap-2">
                                <span aria-hidden="true" class="inline-flex size-8 shrink-0 items-center justify-center rounded-full {{ $tints[$row['variant']] ?? $tints['primary'] }}">
                                    @if ($row['iconKey'] !== null && isset($icons[$row['iconKey']]))
                                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">{!! $icons[$row['iconKey']] !!}</svg>
                                    @else
                                        <span class="size-2 rounded-full bg-current"></span>
                                    @endif
                                </span>
                                <span class="min-w-0 truncate text-sm font-medium text-foreground">{{ $row['name'] }}</span>
                            </div>
                        </x-ui.table.cell>
                        <x-ui.table.cell align="end" class="tabular-nums text-muted-foreground">{{ rtrim(rtrim(number_format($row['percent'], 1), '0'), '.') }}%</x-ui.table.cell>
                        <x-ui.table.cell align="end" class="font-medium tabular-nums text-foreground">{{ $row['amount'] }}</x-ui.table.cell>
                    </x-ui.table.row>
                @endforeach
            </x-ui.table.body>
        </x-ui.table>
    </div>
</x-ui.tray>

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
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0