Skip to content
Brok UI

Loading…

No results

Diagrams

Open source

Dependency-free, token-themed relationship diagrams — flow, network, org chart, indented tree, mind map and fishbone.

Version
v1.1.1
Stability
stable
License
MIT
Related
Mind Map
Flow Diagram
Organization Chart
6 of 6

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.

previews.components.fishbone-diagram.default.blade.php 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>

Installation

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

  • blade 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.

fishbone-diagram.md
# 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.blade.php Blade
{{-- 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

manifest knowledge + registry-derived coverage

Props

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

fishbone-diagram fishbone-diagram-bone fishbone-diagram-cause fishbone-diagram-head fishbone-diagram-spine

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

Diagram and relationship view

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
root fishbone-diagram-spine fishbone-diagram-head fishbone-diagram-bone fishbone-diagram-cause
Theming hooks
fishbone-diagram fishbone-diagram-bone fishbone-diagram-cause fishbone-diagram-head fishbone-diagram-spine

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 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-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/fishbone-diagram.blade.php Blade
@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