Diagrams
Dependency-free, token-themed relationship diagrams — flow, network, org chart, indented tree, mind map and fishbone.
Fishbone Diagram — A dependency-free Ishikawa (cause-and-effect) diagram: a horizontal spine to an effect head, with category bones alternating above and below and sub-causes as labelled twigs.
Preview
A dependency-free SVG Ishikawa diagram. Pass `:effect` (the problem at the head) and `:categories` — each `[label, causes[]]` — to chart your own cause-and-effect breakdown; categories alternate above and below the spine.
<div class="space-y-4">
<x-ui.fishbone-diagram :height="340" label="{{ __('Missed-SLA cause and effect analysis') }}" />
<p class="text-sm text-muted-foreground">
{{ __('A dependency-free SVG Ishikawa diagram. Pass `:effect` (the problem at the head) and `:categories` — each `[label, causes[]]` — to chart your own cause-and-effect breakdown; categories alternate above and below the spine.') }}
</p>
</div>
Installation
php artisan ui:add fishbone-diagram
Registry contract
php artisan ui:add fishbone-diagram
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/fishbone-diagram.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: Fishbone Diagram (`fishbone-diagram`)
A dependency-free Ishikawa (cause-and-effect) diagram: a horizontal spine to an effect head, with category bones alternating above and below and sub-causes as labelled twigs.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add fishbone-diagram
```
## Usage
```blade
<div class="space-y-4">
<x-ui.fishbone-diagram :height="340" label="{{ __('Missed-SLA cause and effect analysis') }}" />
<p class="text-sm text-muted-foreground">
{{ __('A dependency-free SVG Ishikawa diagram. Pass `:effect` (the problem at the head) and `:categories` — each `[label, causes[]]` — to chart your own cause-and-effect breakdown; categories alternate above and below the spine.') }}
</p>
</div>
```
## Props
- `effect` (mixed|null, default `null`) — The problem or effect shown in the head box on the right. Defaults to a demo SLA-miss effect.
- `categories` (mixed|null, default `null`) — Cause categories: each an object with label and a list of cause strings; alternated above and below the spine with bones angled toward the head.
- `height` (int, default `340`) — SVG height in pixels; width fills the container.
- `label` (string, default `Fishbone diagram`) — Accessible name for the figure.
## Use when
- Use when hierarchy, causality, network structure, or process flow is the information itself.
- Running a root-cause analysis session where causes group into fixed categories (people, process, tools, and so on) feeding one effect.
- Documenting an incident postmortem's contributing factors in the classic Ishikawa layout.
## Avoid when
- Do not use specialized diagrams when a list, table, or short paragraph would be easier to interpret.
- The causes form a sequence of steps rather than parallel categories; use flow-diagram instead.
- The relationships are a general graph with no single effect or fixed categories; use mind-map or network-graph instead.
## Anti-patterns
- Using a specialized diagram when linear content is clearer
## Rules
- Use the `<brok:fishbone-diagram>` tag (or `<x-ui.fishbone-diagram>`) 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/fishbone-diagram
- Registry JSON (files, props, contract): https://brokui.dev/r/open/fishbone-diagram.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
Root Cause
{{-- The default cause-and-effect diagram framed by chart-card with a factor-count badge. --}}
<x-ui.chart-card heading="Root-cause analysis" subheading="Why we missed the SLA" trend="6 factors" trend-variant="neutral">
<x-ui.fishbone-diagram label="{{ __('Missed-SLA cause and effect analysis') }}" />
</x-ui.chart-card>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| effect | mixed | null | null | The problem or effect shown in the head box on the right. Defaults to a demo SLA-miss effect. |
| categories | mixed | null | null | Cause categories: each an object with label and a list of cause strings; alternated above and below the spine with bones angled toward the head. |
| height | int | 340 | SVG height in pixels; width fills the container. |
| label | string | Fishbone diagram | Accessible name for the figure. |
Slots
default— Unused; the diagram renders from the effect and categories props only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Renders without arguments using built-in demo data, so it previews immediately.
- Splits categories evenly above and below a horizontal spine that runs into the effect head box, with each category's causes as labelled twigs off its bone.
- Dependency-free SVG: no chart library, computed and laid out entirely in Blade.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Explain hierarchy, causality, network structure, or flow.
Use when
- Use when hierarchy, causality, network structure, or process flow is the information itself.
- Running a root-cause analysis session where causes group into fixed categories (people, process, tools, and so on) feeding one effect.
- Documenting an incident postmortem's contributing factors in the classic Ishikawa layout.
Avoid when
- Do not use specialized diagrams when a list, table, or short paragraph would be easier to interpret.
- The causes form a sequence of steps rather than parallel categories; use flow-diagram instead.
- The relationships are a general graph with no single effect or fixed categories; use mind-map or network-graph instead.
Use instead
- List, table, or prose explanation
Anti-patterns
- Using a specialized diagram when linear content is clearer
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- The figure carries role="img" with an aria-label from label, so a screen reader hears a named summary instead of parsing raw SVG.
- Every category and cause label renders as visible SVG text, never as a hover-only tooltip.
- 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.
@props([
// The problem/effect shown in the head box on the right.
'effect' => null,
// Categories: each ['label' => string, 'causes' => [string, ...]]. Alternated
// above/below the spine; bones angle toward the head.
'categories' => null,
'height' => 340,
'label' => 'Fishbone diagram',
])
@php
// ── Demo data (a classic Ishikawa cause/effect breakdown) ────────────────
$effect ??= __('Missed SLA');
$categories ??= [
['label' => __('People'), 'causes' => [__('Understaffed'), __('Skills gap'), __('Handoff delays')]],
['label' => __('Process'), 'causes' => [__('No runbook'), __('Manual steps')]],
['label' => __('Tools'), 'causes' => [__('Flaky CI'), __('Slow alerts'), __('Legacy stack')]],
['label' => __('Data'), 'causes' => [__('Stale metrics'), __('Bad inputs')]],
['label' => __('Environment'), 'causes' => [__('Infra outage'), __('Region lag')]],
['label' => __('Policy'), 'causes' => [__('Vague SLA'), __('No on-call'), __('Late sign-off')]],
];
$categories = array_values($categories);
$count = count($categories);
// ── Geometry ─────────────────────────────────────────────────────────────
$W = 760;
$H = max(220, (int) $height);
$spineY = (int) round($H / 2);
$headW = 132; // effect (head) box width
$headH = 56; // effect box height
$marginStart = 24; // left padding before the tail
$headX = $W - $headW; // head box left edge
$spineEndX = $headX - 6; // spine meets the head box
// Half the categories ride above the spine, half below.
$aboveCount = (int) ceil($count / 2);
$belowCount = $count - $aboveCount;
// Distribute each side's bone roots evenly along the usable spine length.
$usable = $spineEndX - $marginStart;
$rootGap = max(1, max($aboveCount, $belowCount));
// Diagonal rise/run for each bone (points up-and-right toward the head).
$boneRun = 86; // horizontal span of a bone
$boneRise = $spineY - 34; // vertical reach toward the chart edge
$bones = [];
$aboveIdx = 0;
$belowIdx = 0;
foreach ($categories as $i => $cat) {
$above = $i % 2 === 0;
$dir = $above ? -1 : 1; // -1 = up, +1 = down
$sideCount = $above ? $aboveCount : $belowCount;
$slot = $above ? $aboveIdx++ : $belowIdx++;
// Root sits on the spine; spread roots across the usable length, biased
// away from the head so bones never collide with the effect box.
$rootX = $marginStart + ($usable * ($slot + 0.75)) / ($sideCount + 0.6);
$rootY = $spineY;
// The bone angles up/down toward the head end.
$tipX = $rootX + $boneRun;
$tipY = $spineY + $dir * $boneRise;
// Place the category label just beyond the bone tip.
$labelX = $tipX + 4;
$labelY = $tipY + $dir * 4;
// Spread sub-causes as twigs along the bone, from root toward tip.
$causes = array_values($cat['causes'] ?? []);
$cTotal = max(1, count($causes));
$twigs = [];
foreach ($causes as $ci => $cause) {
$t = ($ci + 1) / ($cTotal + 1); // fraction along the bone
$cx = $rootX + ($tipX - $rootX) * $t;
$cy = $rootY + ($tipY - $rootY) * $t;
$twigs[] = [
'x' => $cx + 8,
'y' => $cy + $dir * 1,
'label' => $cause,
];
}
$bones[] = [
'label' => $cat['label'] ?? '',
'rootX' => $rootX,
'rootY' => $rootY,
'tipX' => $tipX,
'tipY' => $tipY,
'labelX' => $labelX,
'labelY' => $labelY,
'above' => $above,
'tone' => 'tone-' . ($i % 5),
'twigs' => $twigs,
];
}
// Cycle the categorical chart tokens for the category bones (stroke uses
// currentColor, so the tone class drives both the bone and its label).
// Categories are arbitrary, so they take the chart scale, not the status
// tokens — a bone is not "a warning" because it is fourth.
$palette = [
'tone-0' => 'text-chart-1',
'tone-1' => 'text-chart-2',
'tone-2' => 'text-chart-3',
'tone-3' => 'text-chart-4',
'tone-4' => 'text-chart-5',
];
@endphp
<div
data-slot="fishbone-diagram"
role="img"
aria-label="{{ $label }}"
{{ $attributes->merge(['class' => 'w-full text-foreground']) }}
>
<svg
viewBox="0 0 {{ $W }} {{ $H }}"
width="100%"
height="{{ $H }}"
preserveAspectRatio="xMidYMid meet"
class="h-auto w-full"
fill="none"
aria-hidden="true"
>
{{-- Spine: the central horizontal line running to the head. --}}
<line
data-slot="fishbone-diagram-spine"
class="stroke-muted-foreground"
x1="{{ $marginStart }}"
y1="{{ $spineY }}"
x2="{{ round($spineEndX, 2) }}"
y2="{{ $spineY }}"
stroke-width="2.5"
stroke-linecap="round"
/>
{{-- Category bones + their sub-cause twigs. --}}
@foreach ($bones as $bone)
@php($tone = $palette[$bone['tone']])
<g class="{{ $tone }}">
<line
data-slot="fishbone-diagram-bone"
x1="{{ round($bone['rootX'], 2) }}"
y1="{{ round($bone['rootY'], 2) }}"
x2="{{ round($bone['tipX'], 2) }}"
y2="{{ round($bone['tipY'], 2) }}"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
{{-- Category label at the outer end of the bone. --}}
<text
class="fill-foreground text-xs font-semibold"
x="{{ round($bone['labelX'], 2) }}"
y="{{ round($bone['labelY'], 2) }}"
text-anchor="start"
dominant-baseline="{{ $bone['above'] ? 'auto' : 'hanging' }}"
>{{ $bone['label'] }}</text>
{{-- Sub-cause twigs along the bone. --}}
@foreach ($bone['twigs'] as $twig)
<text
data-slot="fishbone-diagram-cause"
class="fill-muted-foreground text-2xs"
x="{{ round($twig['x'], 2) }}"
y="{{ round($twig['y'], 2) }}"
text-anchor="start"
dominant-baseline="middle"
>{{ $twig['label'] }}</text>
@endforeach
</g>
@endforeach
{{-- Head: the effect/problem box at the end of the spine. --}}
<g data-slot="fishbone-diagram-head">
<rect
class="fill-primary"
x="{{ $headX }}"
y="{{ round($spineY - $headH / 2, 2) }}"
width="{{ $headW }}"
height="{{ $headH }}"
rx="10"
/>
<text
class="fill-primary-foreground text-xs font-semibold"
x="{{ round($headX + $headW / 2, 2) }}"
y="{{ $spineY }}"
text-anchor="middle"
dominant-baseline="middle"
>{{ $effect }}</text>
</g>
</svg>
</div>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.1.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