Skip to content
Brok UI

Loading…

No results

Chart Card

Open source

The card shell every chart and diagram sits in — a title or KPI value, a description, an optional actions slot and an auto-toned trend badge over a content well for the visualisation.

Version
v1.0.1
Stability
stable
License
MIT
Related
Card
Badge
Chart
Stat

Preview

Revenue

This year vs. last year

+12.5%
previews.components.chart-card.default.blade.php Blade
<x-ui.chart-card
    heading="Revenue"
    subheading="This year vs. last year"
    trend="+12.5%"
>
    <x-ui.chart
        type="area"
        :height="260"
        :series="[
            ['name' => __('This year'), 'data' => [18, 22, 21, 28, 31, 30, 35, 39]],
            ['name' => __('Last year'), 'data' => [14, 16, 18, 19, 24, 22, 27, 29]],
        ]"
        :options="[
            'stroke' => ['curve' => 'smooth', 'width' => 2],
            'fill' => ['type' => 'gradient', 'gradient' => ['opacityFrom' => 0.4, 'opacityTo' => 0.05, 'stops' => [0, 90, 100]]],
            'legend' => ['position' => 'bottom'],
            'xaxis' => ['categories' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug']],
        ]"
        label="{{ __('Revenue, this year against last year') }}"
    />
</x-ui.chart-card>

Installation

terminal
php artisan ui:add chart-card

Registry contract

php artisan ui:add chart-card 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/chart-card.blade.php
Registry dependencies
card badge
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.

chart-card.md
# Brok UI: Chart Card (`chart-card`)

The card shell every chart and diagram sits in — a title or KPI value, a description, an optional actions slot and an auto-toned trend badge over a content well for the visualisation.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add chart-card
```

## Usage

```blade
<x-ui.chart-card
    heading="Revenue"
    subheading="This year vs. last year"
    trend="+12.5%"
>
    <x-ui.chart
        type="area"
        :height="260"
        :series="[
            ['name' => __('This year'), 'data' => [18, 22, 21, 28, 31, 30, 35, 39]],
            ['name' => __('Last year'), 'data' => [14, 16, 18, 19, 24, 22, 27, 29]],
        ]"
        :options="[
            'stroke' => ['curve' => 'smooth', 'width' => 2],
            'fill' => ['type' => 'gradient', 'gradient' => ['opacityFrom' => 0.4, 'opacityTo' => 0.05, 'stops' => [0, 90, 100]]],
            'legend' => ['position' => 'bottom'],
            'xaxis' => ['categories' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug']],
        ]"
        label="{{ __('Revenue, this year against last year') }}"
    />
</x-ui.chart-card>
```

## Props

- `heading` (string|null, default `null`) — Card title, or the small KPI label when `value` is set. Translated with __().
- `subheading` (string|null, default `null`) — Supporting description under the heading. Translated with __().
- `value` (string|null, default `null`) — Headline KPI value. When set, the heading renders as a muted label above it instead of a card title.
- `trend` (string|null, default `null`) — Delta label shown as a badge at the end of the header. Blank or null hides the badge.
- `trendVariant` (auto|success|destructive|neutral|secondary|warning|info|outline, default `auto`) — Tone of the trend badge. 'auto' reads the sign off the trend label (a leading - or − is destructive, anything else success); 'neutral' maps to the secondary badge.
- `actions` (mixed|null, default `null`) — Declared by @props in the shipped Blade source.

## Use when

- Use when trend, distribution, flow, intensity, or relationship questions are easier to answer visually than in raw numbers.
- Framing any chart, gauge or diagram in a dashboard card with a title, a description and a delta badge.
- Showing a headline KPI value above the chart instead of a card title.
- Putting a range selector, menu or link beside the trend badge in a chart's header.

## Avoid when

- Do not use a chart when users mainly need exact lookup or when a single metric would be clearer as text or a stat.
- The surface carries no visualisation; compose card directly.
- A number needs no chart beside it; use stat.

## Anti-patterns

- Decorative charts without a meaningful question

## Rules

- Use the `<brok:chart-card>` tag (or `<x-ui.chart-card>`) 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/chart-card
- Registry JSON (files, props, contract): https://brokui.dev/r/open/chart-card.json

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

Examples

actions.blade.php Blade
{{-- The `actions` slot sits before the trend badge; `trend-variant` overrides
     the sign-derived tone. --}}
<x-ui.chart-card
    heading="Requests"
    subheading="Edge requests served"
    trend="Last 7 days"
    trend-variant="neutral"
>
    <x-slot:actions>
        <x-ui.button variant="outline" size="sm">{{ __('Export') }}</x-ui.button>
    </x-slot:actions>

    <x-ui.chart
        type="bar"
        :height="220"
        :series="[['name' => __('Requests'), 'data' => [420, 1180, 2240, 1560, 720, 310, 140]]]"
        :options="[
            'plotOptions' => ['bar' => ['borderRadius' => 6, 'borderRadiusApplication' => 'end']],
            'legend' => ['show' => false],
            'xaxis' => ['categories' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']],
        ]"
        label="{{ __('Edge requests served per day') }}"
    />
</x-ui.chart-card>
metric.blade.php Blade
{{-- `value` swaps the card title for a KPI label and a headline figure. --}}
<x-ui.chart-card
    heading="Monthly recurring revenue"
    value="$48,200"
    trend="-2.4%"
>
    <x-ui.chart
        type="line"
        :height="220"
        :series="[['name' => __('MRR'), 'data' => [42, 45, 44, 47, 46, 49, 48]]]"
        :options="[
            'stroke' => ['curve' => 'smooth', 'width' => 2],
            'legend' => ['show' => false],
            'xaxis' => ['categories' => ['Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep']],
        ]"
        label="{{ __('Monthly recurring revenue, last seven months') }}"
    />
</x-ui.chart-card>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
heading string | null null Card title, or the small KPI label when `value` is set. Translated with __().
subheading string | null null Supporting description under the heading. Translated with __().
value string | null null Headline KPI value. When set, the heading renders as a muted label above it instead of a card title.
trend string | null null Delta label shown as a badge at the end of the header. Blank or null hides the badge.
trendVariant auto | success | destructive | neutral | secondary | warning | info | outline auto Tone of the trend badge. 'auto' reads the sign off the trend label (a leading - or − is destructive, anything else success); 'neutral' maps to the secondary badge.
actions mixed | null null Declared by @props in the shipped Blade source.

Slots

  • default — The chart, gauge or diagram, rendered inside the card content well.
  • actions — Controls placed before the trend badge — a range selector, a menu trigger, a link.

Data slots

Stable hooks for CSS overrides and browser tests.

$slotName

Behavior

  • Uses server-rendered Blade and semantic HTML.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Chart and visual analytics

Reveal a visual trend, distribution, flow, or relationship.

Use when

  • Use when trend, distribution, flow, intensity, or relationship questions are easier to answer visually than in raw numbers.
  • Framing any chart, gauge or diagram in a dashboard card with a title, a description and a delta badge.
  • Showing a headline KPI value above the chart instead of a card title.
  • Putting a range selector, menu or link beside the trend badge in a chart's header.

Avoid when

  • Do not use a chart when users mainly need exact lookup or when a single metric would be clearer as text or a stat.
  • The surface carries no visualisation; compose card directly.
  • A number needs no chart beside it; use stat.

Use instead

  • Table for exact lookup
  • Stat for a single value

Anti-patterns

  • Decorative charts without a meaningful question
Anatomy
card card header heading or KPI label + value description actions slot trend badge card content (default slot)
Theming hooks
$slotName

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
No component-owned keyboard interaction; native element behavior applies.
Focus
none
  • The heading renders through card.title (an h3), so a page of chart cards keeps one heading level.
  • The trend badge is text, not colour alone: the delta reads the same to a screen reader and in a high-contrast theme.
  • 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

Safe

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.

resources/views/components/ui/chart-card.blade.php Blade
{{-- The one card shell every chart and diagram sits in: a header carrying a
     title (or a KPI label and value), a description, an optional actions slot
     and a trend badge, over a content well that holds the visualisation. --}}
@props([
    'heading' => null,
    'subheading' => null,
    'value' => null,
    'trend' => null,
    'trendVariant' => 'auto',
    'actions' => null,
])

@php
    $trendText = trim((string) $trend);

    // "auto" reads the sign off the label itself, so a card can hand through a
    // delta string without also computing its tone. Both the ASCII hyphen and
    // the typographic minus count as negative.
    $isNegative = $trendText !== '' && (str_starts_with($trendText, '-') || str_starts_with($trendText, '−'));

    $badgeVariant = match ($trendVariant) {
        'auto' => $isNegative ? 'destructive' : 'success',
        'neutral' => 'secondary',
        default => $trendVariant,
    };

    // A block built on this shell passes its own data-slot; the card primitive
    // prints exactly one, so read the override here and forward it.
    $slotName = (string) ($attributes->get('data-slot') ?: 'chart-card');

    $hasActions = $actions !== null && ! $actions->isEmpty();
    $hasHeader = $heading || $subheading || $value !== null || $trendText !== '' || $hasActions;
@endphp

<x-ui.card :data-slot="$slotName" {{ $attributes->except('data-slot')->merge(['class' => 'w-full']) }}>
    @if ($hasHeader)
        <x-ui.card.header>
            <div class="flex items-start justify-between gap-4">
                <div class="flex min-w-0 flex-col gap-2">
                    @if ($value !== null)
                        @if ($heading)
                            <span class="text-sm font-medium text-muted-foreground">{{ __($heading) }}</span>
                        @endif
                        <span class="text-2xl font-bold tracking-tight text-foreground">{{ $value }}</span>
                    @elseif ($heading)
                        <x-ui.card.title>{{ __($heading) }}</x-ui.card.title>
                    @endif

                    @if ($subheading)
                        <x-ui.card.description>{{ __($subheading) }}</x-ui.card.description>
                    @endif
                </div>

                @if ($hasActions || $trendText !== '')
                    <div class="flex shrink-0 items-center gap-2">
                        {{ $actions }}

                        @if ($trendText !== '')
                            <x-ui.badge :variant="$badgeVariant">{{ __($trendText) }}</x-ui.badge>
                        @endif
                    </div>
                @endif
            </div>
        </x-ui.card.header>
    @endif

    <x-ui.card.content>
        {{ $slot }}
    </x-ui.card.content>
</x-ui.card>

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