Sparkline
A compact inline SVG trend chart (line, area or bar) with an optional value, trend indicator and date range — no charting dependency.
Preview
@php
$series = [12, 18, 14, 22, 26, 20, 28, 24, 33, 30, 38, 35, 44, 48];
@endphp
<div class="flex flex-wrap items-end gap-8">
<x-ui.sparkline :data="$series" variant="line" :show-endpoint="true" />
<x-ui.sparkline :data="$series" variant="line" tone="primary" :show-endpoint="true" />
<x-ui.sparkline
:data="$series"
variant="area"
tone="success"
value="560.3M"
label="/30d"
:trend="12.4"
date-range="May 14 – Jun 12"
/>
<x-ui.sparkline :data="$series" variant="bar" tone="info" />
</div>
Variant options
Tone options
Installation
php artisan ui:add sparkline
Registry contract
php artisan ui:add sparkline
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/sparkline.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: Sparkline (`sparkline`)
A compact inline SVG trend chart (line, area or bar) with an optional value, trend indicator and date range — no charting dependency.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add sparkline
```
## Usage
```blade
@php
$series = [12, 18, 14, 22, 26, 20, 28, 24, 33, 30, 38, 35, 44, 48];
@endphp
<div class="flex flex-wrap items-end gap-8">
<x-ui.sparkline :data="$series" variant="line" :show-endpoint="true" />
<x-ui.sparkline :data="$series" variant="line" tone="primary" :show-endpoint="true" />
<x-ui.sparkline
:data="$series"
variant="area"
tone="success"
value="560.3M"
label="/30d"
:trend="12.4"
date-range="May 14 – Jun 12"
/>
<x-ui.sparkline :data="$series" variant="bar" tone="info" />
</div>
```
## Props
- `data` (array, default `[]`) — The series: a list of numbers, or rows with a `value` key.
- `variant` (line|area|bar, default `line`) — Line, filled area or bars.
- `width` (int, default `120`) — Drawing width in pixels.
- `height` (int, default `36`) — Drawing height in pixels.
- `strokeWidth` (int, default `2`) — Line width in pixels (line and area).
- `tone` (neutral|primary|success|destructive|warning|info|muted|foreground, default `neutral`) — Token colour of the drawing: neutral (the foreground colour, so primary stays reserved for the main action and focus), primary (the brand colour, opt in), a status tone, muted (muted-foreground) or foreground. An unknown tone falls back to neutral.
- `showEndpoint` (bool, default `false`) — Marks the last point with a dot.
- `showBaseline` (bool, default `false`) — Draws a dashed line at the lowest value.
- `label` (string|null, default `null`) — What the series plots. Printed beside the value and part of the SVG accessible name (role="img").
- `labelHidden` (bool, default `false`) — Keeps the label out of the visible header while it still names the SVG, for a sparkline in a table cell or under a heading that already says what it plots.
- `value` (mixed|null, default `null`) — The current value, printed before the trend.
- `trend` (mixed|null, default `null`) — A percentage change, printed with an up or down arrow in the success or destructive colour.
- `dateRange` (mixed|null, default `null`) — The period the series covers, printed under the drawing.
## Use when
- Use when trend, distribution, flow, intensity, or relationship questions are easier to answer visually than in raw numbers.
- Showing the shape of a trend beside a number, in a table row, stat or card.
- Giving a metric context without the weight of a full chart.
## 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.
- A reader needs to read values off the chart; chart gives axes, a legend and tooltips.
- Plotting more than one series, which a sparkline cannot separate.
## Anti-patterns
- Decorative charts without a meaningful question
## Rules
- Use the `<brok:sparkline>` tag (or `<x-ui.sparkline>`) 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/sparkline
- Registry JSON (files, props, contract): https://brokui.dev/r/open/sparkline.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| data | array | [] | The series: a list of numbers, or rows with a `value` key. |
| variant | line | area | bar | line | Line, filled area or bars. |
| width | int | 120 | Drawing width in pixels. |
| height | int | 36 | Drawing height in pixels. |
| strokeWidth | int | 2 | Line width in pixels (line and area). |
| tone | neutral | primary | success | destructive | warning | info | muted | foreground | neutral | Token colour of the drawing: neutral (the foreground colour, so primary stays reserved for the main action and focus), primary (the brand colour, opt in), a status tone, muted (muted-foreground) or foreground. An unknown tone falls back to neutral. |
| showEndpoint | bool | false | Marks the last point with a dot. |
| showBaseline | bool | false | Draws a dashed line at the lowest value. |
| label | string | null | null | What the series plots. Printed beside the value and part of the SVG accessible name (role="img"). |
| labelHidden | bool | false | Keeps the label out of the visible header while it still names the SVG, for a sparkline in a table cell or under a heading that already says what it plots. |
| value | mixed | null | null | The current value, printed before the trend. |
| trend | mixed | null | null | A percentage change, printed with an up or down arrow in the success or destructive colour. |
| dateRange | mixed | null | null | The period the series covers, printed under the drawing. |
Slots
default— Unused; the sparkline draws from the data prop only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- variant renders the same data as a line, a filled area or bars, sized by width and height rather than by its container.
- showEndpoint marks the last point and showBaseline draws the zero line; tone picks the token colour.
- The drawing is neutral (the foreground colour) by default; tone="primary" opts in to the brand colour. data-tone on the root reports the tone.
- The SVG is drawn server-side, so there is no charting dependency and no layout shift on load.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
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.
- Showing the shape of a trend beside a number, in a table row, stat or card.
- Giving a metric context without the weight of a full chart.
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.
- A reader needs to read values off the chart; chart gives axes, a legend and tooltips.
- Plotting more than one series, which a sparkline cannot separate.
Use instead
- Table for exact lookup
- Stat for a single value
Anti-patterns
- Decorative charts without a meaningful question
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- The chart is role="img" with an accessible name summarising the series, so it is announced rather than skipped.
- With `label-hidden` the label is not printed and is not duplicated: it reaches assistive technology once, as the SVG's aria-label (with the value in front when one is set). Without a label the name is the generic "Trend sparkline", so pass one.
- The value and trend are rendered as text beside the chart, so the trend does not depend on reading the drawing.
- 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.
{{--
Sparkline: a compact inline SVG trend.
- label: printed beside the value and part of the SVG's accessible name.
- labelHidden: keeps the label out of the visible header but still names
the SVG (role="img" aria-label), for a sparkline in a table cell or next
to a heading that already says what it plots.
- tone: neutral (the default) draws in the foreground colour, so a
product that keeps primary for its one main action and focus is not
diluted; primary opts in to the brand colour.
--}}
@props([
'data' => [],
'variant' => 'line',
'width' => 120,
'height' => 36,
'strokeWidth' => 2,
'tone' => 'neutral',
'showEndpoint' => false,
'showBaseline' => false,
'label' => null,
'labelHidden' => false,
'value' => null,
'trend' => null,
'dateRange' => null,
])
@php
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$variant = $styles['normalizeVariant']($variant);
$variant = in_array($variant, ['line', 'area', 'bar'], true) ? $variant : 'line';
// Tone resolves to a fixed text-* token class so the SVG paints with
// currentColor — no inline colour and JIT-visible (never spliced).
$tones = [
'neutral' => 'text-foreground',
'primary' => 'text-primary',
'success' => 'text-success',
'destructive' => 'text-destructive',
'warning' => 'text-warning',
'info' => 'text-info',
'muted' => 'text-muted-foreground',
'foreground' => 'text-foreground',
];
$tone = isset($tones[$tone]) ? $tone : 'neutral';
$toneClass = $tones[$tone];
$labelHidden = filter_var($labelHidden, FILTER_VALIDATE_BOOLEAN);
$showLabel = $label !== null && ! $labelHidden;
// Normalise data to a flat list of floats.
$points = array_values(array_map(
fn ($d) => is_array($d) ? (float) ($d['value'] ?? 0) : (float) $d,
$data,
));
$n = count($points);
$w = max(1, (int) $width);
$h = max(1, (int) $height);
$sw = (float) $strokeWidth;
$pad = $sw + 1;
$min = $n ? min($points) : 0.0;
$max = $n ? max($points) : 0.0;
$span = ($max - $min) ?: 1.0;
$fx = fn (int $i) => $n <= 1 ? $w / 2 : $pad + ($i / ($n - 1)) * ($w - 2 * $pad);
$fy = fn (float $v) => $h - $pad - (($v - $min) / $span) * ($h - 2 * $pad);
$fmt = fn (float $f) => rtrim(rtrim(number_format($f, 2, '.', ''), '0'), '.');
$linePath = '';
foreach ($points as $i => $v) {
$linePath .= ($i === 0 ? 'M' : 'L').$fmt($fx($i)).' '.$fmt($fy($v)).' ';
}
$linePath = trim($linePath);
$areaPath = $n
? 'M'.$fmt($fx(0)).' '.$fmt($h - $pad).' '
.implode('', array_map(fn ($i, $v) => 'L'.$fmt($fx($i)).' '.$fmt($fy($v)).' ', array_keys($points), $points))
.'L'.$fmt($fx($n - 1)).' '.$fmt($h - $pad).' Z'
: '';
$barW = $n > 0 ? max(1.0, ($w - 2 * $pad) / max(1, $n) * 0.7) : 1.0;
$trendValue = is_numeric($trend) ? (float) $trend : null;
$trendUp = $trendValue !== null && $trendValue >= 0;
$a11y = $label
? trim(($value ? $value.' ' : '').$label)
: __('Trend sparkline');
@endphp
<div
data-slot="sparkline"
data-variant="{{ $variant }}"
data-tone="{{ $tone }}"
{{ $attributes->merge(['class' => 'inline-flex flex-col gap-1 '.$toneClass]) }}
>
@if ($value !== null || $showLabel || $trendValue !== null)
<div class="flex items-baseline gap-2 text-foreground">
@if ($value !== null)<span class="text-sm font-semibold tabular-nums">{{ $value }}</span>@endif
@if ($trendValue !== null)
<span class="inline-flex items-center gap-0.5 text-xs font-medium tabular-nums {{ $trendUp ? 'text-success' : 'text-destructive' }}">
<svg class="size-3 {{ $trendUp ? '' : 'rotate-180' }}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 7-7 7 7M12 5v14" /></svg>
{{ ($trendUp ? '+' : '').$fmt($trendValue) }}%
</span>
@endif
@if ($showLabel)<span class="text-xs text-muted-foreground">{{ $label }}</span>@endif
</div>
@endif
<svg
viewBox="0 0 {{ $w }} {{ $h }}"
width="{{ $w }}"
height="{{ $h }}"
class="overflow-visible"
preserveAspectRatio="none"
role="img"
aria-label="{{ $a11y }}"
>
@if ($n > 0)
@if ($variant === 'bar')
@foreach ($points as $i => $v)
@php $bx = $fx($i) - $barW / 2; $by = $fy($v); @endphp
<rect x="{{ $fmt(max(0, $bx)) }}" y="{{ $fmt($by) }}" width="{{ $fmt($barW) }}" height="{{ $fmt(max(0.5, $h - $pad - $by)) }}" rx="1" fill="currentColor" />
@endforeach
@else
@if ($variant === 'area')
<path d="{{ $areaPath }}" fill="currentColor" fill-opacity="0.12" stroke="none" />
@endif
<path d="{{ $linePath }}" fill="none" stroke="currentColor" stroke-width="{{ $sw }}" stroke-linecap="round" stroke-linejoin="round" />
@if ($showBaseline)
<line x1="{{ $fmt($pad) }}" y1="{{ $fmt($fy($min)) }}" x2="{{ $fmt($w - $pad) }}" y2="{{ $fmt($fy($min)) }}" stroke="currentColor" stroke-opacity="0.2" stroke-width="1" stroke-dasharray="2 2" />
@endif
@if ($showEndpoint && $n > 0)
<circle cx="{{ $fmt($fx($n - 1)) }}" cy="{{ $fmt($fy($points[$n - 1])) }}" r="{{ $fmt($sw + 0.5) }}" fill="currentColor" />
@endif
@endif
@endif
</svg>
@if ($dateRange !== null)
<span class="text-xs text-muted-foreground">{{ $dateRange }}</span>
@endif
</div>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.3.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