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
Organization Chart
Flow Diagram
Fishbone Diagram
5 of 6

Mind Map — Dependency-free static SVG mind map: a central topic with balanced left/right branches and stacked child nodes, connected by smooth token-coloured bezier links.

Preview

A dependency-free SVG mind map. Pass `:root` for the central topic and `:branches` — each `["label" => string, "children" => [string, …]]` — to map your own balanced tree. Each branch gets a cycling semantic colour shared by its children.

previews.components.mind-map.default.blade.php Blade
<div class="space-y-4">
    <x-ui.mind-map :height="380" label="{{ __('Launch plan') }}" />

    <p class="text-sm text-muted-foreground">
        {{ __('A dependency-free SVG mind map. Pass `:root` for the central topic and `:branches` — each `["label" => string, "children" => [string, …]]` — to map your own balanced tree. Each branch gets a cycling semantic colour shared by its children.') }}
    </p>
</div>

Installation

terminal
php artisan ui:add mind-map

Registry contract

php artisan ui:add mind-map 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/mind-map.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.

mind-map.md
# Brok UI: Mind Map (`mind-map`)

Dependency-free static SVG mind map: a central topic with balanced left/right branches and stacked child nodes, connected by smooth token-coloured bezier links.

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

## Install

```bash
php artisan ui:add mind-map
```

## Usage

```blade
<div class="space-y-4">
    <x-ui.mind-map :height="380" label="{{ __('Launch plan') }}" />

    <p class="text-sm text-muted-foreground">
        {{ __('A dependency-free SVG mind map. Pass `:root` for the central topic and `:branches` — each `["label" => string, "children" => [string, …]]` — to map your own balanced tree. Each branch gets a cycling semantic colour shared by its children.') }}
    </p>
</div>
```

## Props

- `root` (mixed|null, default `null`) — The central topic string. Defaults to a demo "Launch plan" topic.
- `branches` (mixed|null, default `null`) — First-level branches: each an object with label and a list of child label strings.
- `height` (int, default `380`) — SVG height in pixels; width fills the container.
- `label` (string, default `Mind map`) — Accessible name for the figure.

## Use when

- Use when hierarchy, causality, network structure, or process flow is the information itself.
- Brainstorming or presenting a central topic radiating out into free-form branches and sub-ideas.
- Needing a balanced left/right layout with no fixed direction or sequence between branches.

## Avoid when

- Do not use specialized diagrams when a list, table, or short paragraph would be easier to interpret.
- The structure is a strict reporting hierarchy; use org-chart instead.
- The steps follow a defined sequence or decision path; use flow-diagram instead.

## Anti-patterns

- Using a specialized diagram when linear content is clearer

## Rules

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

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

Examples

launch-plan.blade.php Blade
{{-- The default map framed by chart-card with a track-count badge. --}}
<x-ui.chart-card heading="Launch plan" subheading="Workstreams at a glance" trend="5 tracks" trend-variant="neutral">
    <x-ui.mind-map :height="380" label="{{ __('Launch plan') }}" />
</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
root mixed | null null The central topic string. Defaults to a demo "Launch plan" topic.
branches mixed | null null First-level branches: each an object with label and a list of child label strings.
height int 380 SVG height in pixels; width fills the container.
label string Mind map Accessible name for the figure.

Slots

  • default — Unused; the map renders from the root and branches props only.

Data slots

Stable hooks for CSS overrides and browser tests.

mind-map mind-map-branch mind-map-node mind-map-root

Behavior

  • Renders without arguments using built-in demo data, so it previews immediately.
  • Balances branches across left and right of the central topic and stacks each branch's children, connected by smooth token-coloured bezier links.
  • 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.
  • Brainstorming or presenting a central topic radiating out into free-form branches and sub-ideas.
  • Needing a balanced left/right layout with no fixed direction or sequence between branches.

Avoid when

  • Do not use specialized diagrams when a list, table, or short paragraph would be easier to interpret.
  • The structure is a strict reporting hierarchy; use org-chart instead.
  • The steps follow a defined sequence or decision path; use flow-diagram instead.

Use instead

  • List, table, or prose explanation

Anti-patterns

  • Using a specialized diagram when linear content is clearer
Anatomy
root mind-map-root mind-map-branch mind-map-node
Theming hooks
mind-map mind-map-branch mind-map-node mind-map-root

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 branch and node 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/mind-map.blade.php Blade
@props([
    // Central topic (string).
    'root' => null,
    // Branches: each ['label' => string, 'children' => [string, ...]].
    'branches' => null,
    'height' => 380,
    'label' => 'Mind map',
])

@php
    // ── Demo data so it renders without args ───────────────────────────────
    $root ??= __('Launch plan');
    $branches ??= [
        ['label' => __('Marketing'), 'children' => [__('Campaigns'), __('Social'), __('Email')]],
        ['label' => __('Engineering'), 'children' => [__('API'), __('Frontend'), __('QA')]],
        ['label' => __('Sales'), 'children' => [__('Outreach'), __('Demos')]],
        ['label' => __('Design'), 'children' => [__('Brand'), __('UI kit'), __('Motion')]],
        ['label' => __('Support'), 'children' => [__('Docs'), __('Helpdesk')]],
    ];

    $branches = array_values($branches);

    // ── Geometry ────────────────────────────────────────────────────────────
    $W = 760;
    $H = max(200, (int) $height);
    $cx = $W / 2;
    $cy = $H / 2;

    // Node sizing. Width grows with label length but is clamped, and the SVG
    // text is centred so it never overflows the rounded rect.
    $rootW = 150;
    $rootH = 44;
    $branchH = 32;
    $leafH = 26;
    $charW = 7.2;          // approx advance per character at the chosen font size
    $padX = 26;            // horizontal padding inside a node
    $branchMinW = 96;
    $branchMaxW = 168;
    $leafMinW = 78;
    $leafMaxW = 152;

    $sizeFor = fn (string $text, int $min, int $max) => (int) round(
        max($min, min($max, strlen($text) * $charW + $padX))
    );

    // Column x-positions, measured from the root pill edges.
    $branchGapX = 96;      // gap between root edge and branch node
    $leafGapX = 56;        // gap between branch node and leaf node

    // Split branches into right / left halves (balanced radial).
    $half = (int) ceil(count($branches) / 2);
    $rightIdx = array_slice(array_keys($branches), 0, $half);
    $leftIdx = array_slice(array_keys($branches), $half);

    // Cycle the categorical chart tokens per branch; children inherit the branch
    // hue. These are the tokens for arbitrary, meaningless categories — the
    // status tokens (info/success/warning/destructive) would tell a reader a
    // branch is healthy or broken, and the sixth entry used to be a near-white
    // surface token that painted one branch invisible.
    $palette = ['text-chart-1', 'text-chart-2', 'text-chart-3', 'text-chart-4', 'text-chart-5'];

    // Lay out one side. $dir = +1 (right) or -1 (left). Returns node records and
    // connector paths. Branches are stacked vertically and centred on $cy; each
    // branch's children are stacked centred on the branch node.
    $layoutSide = function (array $idxs, int $dir) use (
        $branches, $palette, $cx, $cy, $rootW, $branchH, $leafH,
        $branchGapX, $leafGapX, $sizeFor, $branchMinW, $branchMaxW, $leafMinW, $leafMaxW, $H
    ) {
        $nodes = [];
        $leaves = [];
        $links = [];
        if (! $idxs) {
            return [$nodes, $leaves, $links];
        }

        $branchVGap = 18;
        $leafVGap = 10;

        // Estimate each branch's vertical footprint (its children block).
        $blockH = [];
        foreach ($idxs as $i) {
            $kids = array_values($branches[$i]['children'] ?? []);
            $n = max(1, count($kids));
            $blockH[$i] = $n * $leafH + ($n - 1) * $leafVGap;
        }
        $totalH = array_sum($blockH) + (count($idxs) - 1) * $branchVGap;
        $startY = $cy - $totalH / 2;

        $rootEdgeX = $cx + $dir * ($rootW / 2);
        $branchCx = $cx + $dir * ($rootW / 2 + $branchGapX);

        $cursor = $startY;
        foreach ($idxs as $order => $i) {
            $branch = $branches[$i];
            $color = $palette[$i % count($palette)];
            $kids = array_values($branch['children'] ?? []);

            $by = $cursor + $blockH[$i] / 2;             // branch vertical centre
            $bw = $sizeFor($branch['label'], $branchMinW, $branchMaxW);
            $branchEdgeX = $branchCx + $dir * ($bw / 2);

            $nodes[] = [
                'label' => $branch['label'],
                'x' => $branchCx,
                'y' => $by,
                'w' => $bw,
                'color' => $color,
            ];

            // Connector: root edge → branch node (smooth horizontal bezier).
            $bIn = $branchCx - $dir * ($bw / 2);
            $mx = ($rootEdgeX + $bIn) / 2;
            $links[] = [
                'color' => $color,
                'd' => sprintf(
                    'M%.2f %.2f C%.2f %.2f %.2f %.2f %.2f %.2f',
                    $rootEdgeX, $cy, $mx, $cy, $mx, $by, $bIn, $by,
                ),
            ];

            // Leaves stacked, centred on the branch node.
            $leafCx = $branchCx + $dir * ($bw / 2 + $leafGapX);
            $ly = $by - $blockH[$i] / 2 + $leafH / 2;
            foreach ($kids as $kid) {
                $lw = $sizeFor($kid, $leafMinW, $leafMaxW);
                $leaves[] = [
                    'label' => $kid,
                    'x' => $leafCx,
                    'y' => $ly,
                    'w' => $lw,
                    'color' => $color,
                ];
                $lIn = $leafCx - $dir * ($lw / 2);
                $lmx = ($branchEdgeX + $lIn) / 2;
                $links[] = [
                    'color' => $color,
                    'd' => sprintf(
                        'M%.2f %.2f C%.2f %.2f %.2f %.2f %.2f %.2f',
                        $branchEdgeX, $by, $lmx, $by, $lmx, $ly, $lIn, $ly,
                    ),
                ];
                $ly += $leafH + $leafVGap;
            }

            $cursor += $blockH[$i] + $branchVGap;
        }

        return [$nodes, $leaves, $links];
    };

    [$rNodes, $rLeaves, $rLinks] = $layoutSide($rightIdx, 1);
    [$lNodes, $lLeaves, $lLinks] = $layoutSide($leftIdx, -1);

    $branchNodes = array_merge($rNodes, $lNodes);
    $leafNodes = array_merge($rLeaves, $lLeaves);
    $connectors = array_merge($rLinks, $lLinks);
@endphp

<div
    data-slot="mind-map"
    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"
    >
        {{-- Connectors (behind nodes). --}}
        @foreach ($connectors as $link)
            <path
                class="{{ $link['color'] }} opacity-40"
                d="{{ $link['d'] }}"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                fill="none"
            />
        @endforeach

        {{-- Leaf nodes. --}}
        @foreach ($leafNodes as $leaf)
            <g data-slot="mind-map-node" class="{{ $leaf['color'] }}">
                <rect
                    x="{{ round($leaf['x'] - $leaf['w'] / 2, 2) }}"
                    y="{{ round($leaf['y'] - $leafH / 2, 2) }}"
                    width="{{ $leaf['w'] }}"
                    height="{{ $leafH }}"
                    rx="{{ $leafH / 2 }}"
                    class="fill-card stroke-border"
                    stroke-width="1"
                />
                <text
                    x="{{ round($leaf['x'], 2) }}"
                    y="{{ round($leaf['y'], 2) }}"
                    text-anchor="middle"
                    dominant-baseline="central"
                    class="pointer-events-none fill-foreground text-2xs"
                >{{ $leaf['label'] }}</text>
            </g>
        @endforeach

        {{-- Branch nodes. --}}
        @foreach ($branchNodes as $node)
            <g data-slot="mind-map-branch" class="{{ $node['color'] }}">
                <rect
                    x="{{ round($node['x'] - $node['w'] / 2, 2) }}"
                    y="{{ round($node['y'] - $branchH / 2, 2) }}"
                    width="{{ $node['w'] }}"
                    height="{{ $branchH }}"
                    rx="{{ $branchH / 2 }}"
                    class="fill-card stroke-current"
                    stroke-width="1.5"
                />
                <text
                    x="{{ round($node['x'], 2) }}"
                    y="{{ round($node['y'], 2) }}"
                    text-anchor="middle"
                    dominant-baseline="central"
                    class="pointer-events-none fill-foreground text-xs font-medium"
                >{{ $node['label'] }}</text>
            </g>
        @endforeach

        {{-- Central root pill. --}}
        <g data-slot="mind-map-root">
            <rect
                x="{{ round($cx - $rootW / 2, 2) }}"
                y="{{ round($cy - $rootH / 2, 2) }}"
                width="{{ $rootW }}"
                height="{{ $rootH }}"
                rx="{{ $rootH / 2 }}"
                class="fill-primary"
            />
            <text
                x="{{ round($cx, 2) }}"
                y="{{ round($cy, 2) }}"
                text-anchor="middle"
                dominant-baseline="central"
                class="pointer-events-none fill-primary-foreground text-xs font-semibold"
            >{{ $root }}</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