Icon
Renders a centrally maintained icon by name, with semantic currentColor styling, fixed size presets, and decorative or labelled accessibility modes; <x-ui.icon.tile> sits a named icon or an inline SVG in a tinted tile.
Preview
{{-- One glyph: the playground's size control resizes it. See the gallery for every name. --}}
<x-ui.icon name="search" size="lg" label="{{ __('Search') }}" />
Installation
php artisan ui:add icon
Registry contract
php artisan ui:add icon
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/icon.blade.php -
resources/views/components/ui/icon/tile.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: Icon (`icon`)
Renders a centrally maintained icon by name, with semantic currentColor styling, fixed size presets, and decorative or labelled accessibility modes; <x-ui.icon.tile> sits a named icon or an inline SVG in a tinted tile.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add icon
```
## Usage
```blade
{{-- One glyph: the playground's size control resizes it. See the gallery for every name. --}}
<x-ui.icon name="search" size="lg" label="{{ __('Search') }}" />
```
## Props
- `name` (archive|arrow-down|arrow-left-right|arrow-up|bar-chart|bell|book-open|bot|building|calendar|check|chevron-down|chevron-end|chevron-start|chevron-up|circle|clock|close|command|compass|copy|credit-card|download|external-link|file-text|film|filter|folder|help-circle|history|image|inbox|info|keyboard|languages|link|log-out|menu|minus|monitor|moon|more-horizontal|paperclip|pencil|play|plug|plus|receipt|redo|search|settings|shield|sliders|star|star-filled|sun|tag|trash|undo|user|users|video|wallet|warning|zoom-in|zoom-out, default `required`) — Centrally maintained icon name.
- `size` (xs|sm|md|lg|xl, default `md`) — Fixed icon size preset.
- `label` (string|null, default `null`) — Accessible name when the icon carries meaning; omit for decorative icons.
- `strokeWidth` (1|1.5|2|2.5, default `2`) — Allowlisted SVG stroke width.
- `tone` (string, default `primary`) — Declared by @props in the registry Blade source.
## Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- A standard interface glyph must stay consistent across applications.
- An app shell or sidebar needs navigation glyphs (compass, users, folder, calendar, clock, credit-card, receipt, file-text, settings, inbox, book-open, bar-chart, wallet, plug, log-out) instead of hand-drawn SVGs.
- An account, settings or workspace menu needs item glyphs (user, building, shield, sliders, arrow-left-right, sun, moon, languages, help-circle, history) placed first inside a dropdown item.
- A favourite, pin or rating toggle needs an outline star for off and the same star filled (star-filled) for on, swapped with the pressed state.
- A notifications trigger needs a bell, a collapse or move-up control needs chevron-up, or a sort or reorder control needs arrow-up and arrow-down.
- A media or source row needs a kind glyph (link, image, video, film, bot), an outbound link needs external-link, a media control needs play, zoom-in or zoom-out, or a status needs a plain circle.
## Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- A product-specific illustration or brand mark is required.
## Anti-patterns
- Adding display ornament without informational value
## Rules
- Use the `<brok:icon>` tag (or `<x-ui.icon>`) 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/icon
- Registry JSON (files, props, contract): https://brokui.dev/r/open/icon.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
{{-- The row-action set: the overflow trigger, the commands it usually holds (edit, copy, archive, delete, export, filter, label, keyboard shortcuts), and a pager whose chevrons follow the reading direction. --}}
@php
$actions = [
'more-horizontal' => __('More actions'),
'pencil' => __('Edit'),
'copy' => __('Copy link'),
'archive' => __('Archive'),
'trash' => __('Delete'),
'download' => __('Export CSV'),
'filter' => __('Filter'),
'tag' => __('Add label'),
'keyboard' => __('Keyboard shortcuts'),
];
@endphp
<div class="flex w-full max-w-md flex-col gap-6 text-sm text-foreground">
<ul class="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2">
@foreach ($actions as $name => $action)
<li class="flex items-center gap-2">
<x-ui.icon :name="$name" size="sm" class="text-muted-foreground" />
<span>{{ $action }}</span>
</li>
@endforeach
</ul>
<div class="flex items-center gap-2">
<span class="flex items-center gap-2">
<x-ui.icon name="chevron-start" size="sm" class="text-muted-foreground" />
{{ __('Previous') }}
</span>
<span class="flex items-center gap-2 ms-auto">
{{ __('Next') }}
<x-ui.icon name="chevron-end" size="sm" class="text-muted-foreground" />
</span>
</div>
</div>
{{-- Control glyphs: a notifications button, a collapse toggle and a sort control, each named for screen readers. --}}
<div class="flex flex-wrap items-center gap-4 text-sm text-foreground">
<x-ui.button size="icon" variant="ghost" :aria-label="__('Notifications')">
<x-ui.icon name="bell" />
</x-ui.button>
<x-ui.button size="icon" variant="ghost" :aria-label="__('Collapse section')" aria-expanded="true">
<x-ui.icon name="chevron-up" />
</x-ui.button>
<div class="flex items-center gap-2">
<x-ui.button size="icon" variant="ghost" :aria-label="__('Move up')">
<x-ui.icon name="arrow-up" />
</x-ui.button>
<x-ui.button size="icon" variant="ghost" :aria-label="__('Move down')">
<x-ui.icon name="arrow-down" />
</x-ui.button>
</div>
</div>
{{-- A favourite toggle: the outline star when off, the same star filled when on, swapped with aria-pressed. --}}
<div class="flex flex-wrap items-center gap-6 text-sm text-foreground">
<div class="flex items-center gap-2">
<x-ui.icon name="star" size="sm" class="text-muted-foreground" />
<span>{{ __('Not a favorite') }}</span>
</div>
<div class="flex items-center gap-2">
<x-ui.icon name="star-filled" size="sm" />
<span>{{ __('Favorite') }}</span>
</div>
<div x-data="{ favorite: false }">
<x-ui.button
size="icon"
variant="ghost"
:aria-label="__('Favorite')"
x-on:click="favorite = !favorite"
x-bind:aria-pressed="favorite ? 'true' : 'false'"
aria-pressed="false"
>
<x-ui.icon name="star" x-show="!favorite" />
<x-ui.icon name="star-filled" x-show="favorite" x-cloak />
</x-ui.button>
</div>
</div>
<div class="flex flex-wrap items-center gap-5 text-foreground">
@foreach (\Jml\Brok\Icons\IconRegistry::names() as $name)
<span class="inline-flex flex-col items-center gap-2 text-xs text-muted-foreground">
<x-ui.icon :name="$name" size="lg" />
<span>{{ $name }}</span>
</span>
@endforeach
</div>
{{-- Media and source glyphs: a kind glyph per source row, an outbound link that mirrors in RTL, and media controls whose play glyph keeps its direction. --}}
@php
$kinds = [
'link' => __('Web page'),
'image' => __('Screenshot'),
'video' => __('Screen recording'),
'film' => __('Film clip'),
'bot' => __('Assistant note'),
];
@endphp
<div class="flex w-full max-w-md flex-col gap-6 text-sm text-foreground">
<ul class="flex flex-col gap-4">
@foreach ($kinds as $name => $kind)
<li class="flex items-center gap-2">
<x-ui.icon :name="$name" size="sm" class="text-muted-foreground" />
<span class="min-w-0 flex-1 truncate">{{ $kind }}</span>
<x-ui.icon name="circle" size="xs" class="text-success" :label="__('Ready')" />
</li>
@endforeach
</ul>
<div class="flex flex-wrap items-center gap-2">
<x-ui.button size="icon" variant="ghost" :aria-label="__('Play')">
<x-ui.icon name="play" />
</x-ui.button>
<x-ui.button size="icon" variant="ghost" :aria-label="__('Zoom in')">
<x-ui.icon name="zoom-in" />
</x-ui.button>
<x-ui.button size="icon" variant="ghost" :aria-label="__('Zoom out')">
<x-ui.icon name="zoom-out" />
</x-ui.button>
<a href="#" class="ms-auto inline-flex items-center gap-2 rounded-sm text-foreground underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring">
{{ __('Open source page') }}
<x-ui.icon name="external-link" size="sm" class="text-muted-foreground" />
</a>
</div>
</div>
{{-- The menu set: account, workspace and preference rows, each glyph beside the action it names. --}}
@php
$actions = [
'user' => __('Profile'),
'building' => __('Workspace'),
'arrow-left-right' => __('Switch account'),
'shield' => __('Security'),
'sliders' => __('Preferences'),
'languages' => __('Language'),
'sun' => __('Light theme'),
'moon' => __('Dark theme'),
'history' => __('Audit log'),
'help-circle' => __('Help'),
];
@endphp
<ul class="grid w-full max-w-md grid-cols-1 gap-x-6 gap-y-4 text-sm text-foreground sm:grid-cols-2">
@foreach ($actions as $name => $action)
<li class="flex items-center gap-2">
<x-ui.icon :name="$name" size="sm" class="text-muted-foreground" />
<span>{{ $action }}</span>
</li>
@endforeach
</ul>
Navigation
{{-- The navigation set in an app shell: each glyph beside the area it names. --}}
@php
$areas = [
'compass' => __('My focus'),
'users' => __('Clients'),
'folder' => __('Projects'),
'calendar' => __('Calendar'),
'clock' => __('Time'),
'credit-card' => __('Billing'),
'receipt' => __('Invoices'),
'file-text' => __('Pages'),
'inbox' => __('Inbox'),
'book-open' => __('SOPs'),
'bar-chart' => __('Reports'),
'wallet' => __('Finance'),
'plug' => __('Integrations'),
'settings' => __('Settings'),
'log-out' => __('Sign out'),
];
@endphp
<ul class="grid w-full max-w-md grid-cols-1 gap-x-6 gap-y-4 text-sm text-foreground sm:grid-cols-2">
@foreach ($areas as $name => $area)
<li class="flex items-center gap-2">
<x-ui.icon :name="$name" size="sm" class="text-muted-foreground" />
<span>{{ $area }}</span>
</li>
@endforeach
</ul>
<div class="flex flex-wrap items-center gap-4">
@foreach (['primary', 'solid', 'muted', 'outline', 'success', 'warning', 'destructive', 'info'] as $tone)
<x-ui.icon.tile name="check" :tone="$tone" :label="__(ucfirst($tone))" />
@endforeach
<x-ui.icon.tile size="sm">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2 2 7l10 5 10-5-10-5Z" /><path d="M2 17l10 5 10-5" /><path d="M2 12l10 5 10-5" /></svg>
</x-ui.icon.tile>
<x-ui.icon.tile size="lg">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2 2 7l10 5 10-5-10-5Z" /><path d="M2 17l10 5 10-5" /><path d="M2 12l10 5 10-5" /></svg>
</x-ui.icon.tile>
</div>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | archive | arrow-down | arrow-left-right | arrow-up | bar-chart | bell | book-open | bot | building | calendar | check | chevron-down | chevron-end | chevron-start | chevron-up | circle | clock | close | command | compass | copy | credit-card | download | external-link | file-text | film | filter | folder | help-circle | history | image | inbox | info | keyboard | languages | link | log-out | menu | minus | monitor | moon | more-horizontal | paperclip | pencil | play | plug | plus | receipt | redo | search | settings | shield | sliders | star | star-filled | sun | tag | trash | undo | user | users | video | wallet | warning | zoom-in | zoom-out | required | Centrally maintained icon name. |
| size | xs | sm | md | lg | xl | md | Fixed icon size preset. |
| label | string | null | null | Accessible name when the icon carries meaning; omit for decorative icons. |
| strokeWidth | 1 | 1.5 | 2 | 2.5 | 2 | Allowlisted SVG stroke width. |
| tone | string | primary | Declared by @props in the registry Blade source. |
Slots
default— `<x-ui.icon.tile>` only: an inline `<svg>` drawn inside the tile when no `name` is given.x-ui.icon.tile— Installed subcomponent from the registry item.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Unknown names fail with a clear exception instead of rendering unsafe markup.
- Directional glyphs (chevron-start, chevron-end, log-out, external-link) mirror under dir="rtl": chevron-start points to the inline start and chevron-end to the inline end in both directions; every other glyph keeps its drawing.
- play is a media control and does not mirror: playback direction does not follow the reading direction.
- The set is drawn in this repository (icons/icons.json `source`): 24px grid, round caps and joins, 2px stroke, MIT open core. npm run icons:sync regenerates the Blade map and IconRegistry from that file.
- star-filled is the one state glyph: the star outline with a currentColor fill, the same size and stroke as star, so swapping the two never shifts the layout.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Present data for rapid scanning.
Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- A standard interface glyph must stay consistent across applications.
- An app shell or sidebar needs navigation glyphs (compass, users, folder, calendar, clock, credit-card, receipt, file-text, settings, inbox, book-open, bar-chart, wallet, plug, log-out) instead of hand-drawn SVGs.
- An account, settings or workspace menu needs item glyphs (user, building, shield, sliders, arrow-left-right, sun, moon, languages, help-circle, history) placed first inside a dropdown item.
- A favourite, pin or rating toggle needs an outline star for off and the same star filled (star-filled) for on, swapped with the pressed state.
- A notifications trigger needs a bell, a collapse or move-up control needs chevron-up, or a sort or reorder control needs arrow-up and arrow-down.
- A media or source row needs a kind glyph (link, image, video, film, bot), an outbound link needs external-link, a media control needs play, zoom-in or zoom-out, or a status needs a plain circle.
Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- A product-specific illustration or brand mark is required.
Use instead
- Table for exact comparison
- Plain text for a single value
Anti-patterns
- Adding display ornament without informational value
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- Decorative icons are hidden from assistive technology. Meaningful standalone icons require label.
- 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.
Validation
Validation support: allowlisted-name. Keep the error message connected with aria-describedby.
<form wire:submit="save" class="space-y-2">
<brok:icon
wire:model="value"
:aria-invalid="$errors->has('value') ? 'true' : 'false'"
aria-describedby="value-error"
/>
@error('value')
<p id="value-error" role="alert">{{ $message }}</p>
@enderror
<brok:button type="submit" wire:loading.attr="disabled">
<span wire:loading.remove>Save</span>
<span wire:loading>Saving…</span>
</brok:button>
</form>
Source
The exact, editable files ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
'name',
'size' => 'md',
'label' => null,
'strokeWidth' => 2,
])
@php
// Generated by scripts/generate-icons.mjs from icons/icons.json.
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$sizeClass = $styles['icon']['sizes'][$size] ?? $styles['icon']['sizes']['md'];
$icons = [
'archive' => '<rect x="3" y="4" width="18" height="4" rx="1" /><path d="M5 8v11a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8M10 12h4" />',
'arrow-down' => '<path d="M12 5v14M19 12l-7 7-7-7" />',
'arrow-left-right' => '<path d="M4 8h16M16 4l4 4-4 4" /><path d="M20 16H4M8 12l-4 4 4 4" />',
'arrow-up' => '<path d="M12 19V5M5 12l7-7 7 7" />',
'bar-chart' => '<path d="M4 21h16M7 17v-5M12 17V5M17 17V9" />',
'bell' => '<path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9Z" /><path d="M10 21a2 2 0 0 0 4 0" />',
'book-open' => '<path d="M3 5h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5v13h-6a3 3 0 0 1-3 3 3 3 0 0 1-3-3H3Z" /><path d="M12 9v12" />',
'bot' => '<rect x="4" y="8" width="16" height="12" rx="2" /><path d="M12 8V4M10 4h4M2 13v3M22 13v3M9 13v2M15 13v2" />',
'building' => '<path d="M4 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16" /><path d="M16 9h2a2 2 0 0 1 2 2v10" /><path d="M2 21h20M8 7h4M8 11h4M8 15h4" />',
'calendar' => '<rect x="3" y="5" width="18" height="16" rx="2" /><path d="M3 10h18M8 3v4M16 3v4" />',
'check' => '<path d="m5 12 4 4L19 6" />',
'chevron-down' => '<path d="m6 9 6 6 6-6" />',
'chevron-end' => '<path d="m9 18 6-6-6-6" />',
'chevron-start' => '<path d="m15 18-6-6 6-6" />',
'chevron-up' => '<path d="m6 15 6-6 6 6" />',
'circle' => '<circle cx="12" cy="12" r="9" />',
'clock' => '<circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" />',
'close' => '<path d="M18 6 6 18M6 6l12 12" />',
'command' => '<path d="M9 6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3z" />',
'compass' => '<circle cx="12" cy="12" r="9" /><path d="m15.5 8.5-2 5-5 2 2-5Z" />',
'copy' => '<rect x="8" y="8" width="13" height="13" rx="2" /><path d="M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" />',
'credit-card' => '<rect x="2" y="5" width="20" height="14" rx="2" /><path d="M2 10h20M6 15h4" />',
'download' => '<path d="M12 3v12M7 10l5 5 5-5" /><path d="M4 15v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4" />',
'external-link' => '<path d="M14 4h6v6M20 4l-9 9" /><path d="M18 14v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4" />',
'file-text' => '<path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8Z" /><path d="M14 3v5h5M9 13h6M9 17h6" />',
'film' => '<rect x="3" y="3" width="18" height="18" rx="2" /><path d="M7 3v18M17 3v18M3 8h4M3 12h18M3 16h4M17 8h4M17 16h4" />',
'filter' => '<path d="M3 4h18l-7 8.5V19l-4 2v-8.5Z" />',
'folder' => '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />',
'help-circle' => '<circle cx="12" cy="12" r="9" /><path d="M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.6.3-1 .9-1 1.6v.6M12 17h.01" />',
'history' => '<path d="M3 12a9 9 0 1 0 2.64-6.36L3 8.3" /><path d="M3 4v4.3h4.3" /><path d="M12 7v5l3 2" />',
'image' => '<rect x="3" y="3" width="18" height="18" rx="2" /><circle cx="9" cy="9" r="2" /><path d="m21 15-4.5-4.5L5 21" />',
'inbox' => '<path d="M5.5 5h13l2.5 8v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5Z" /><path d="M3 13h5l1.5 3h5l1.5-3h5" />',
'info' => '<circle cx="12" cy="12" r="9" /><path d="M12 11v5M12 8h.01" />',
'keyboard' => '<rect x="2" y="5" width="20" height="14" rx="2" /><path d="M6 9h.01M10 9h.01M14 9h.01M18 9h.01M8 12.5h.01M12 12.5h.01M16 12.5h.01M8 16h8" />',
'languages' => '<path d="M3 5h9M7.5 3v2M5 5c.5 3.5 2.5 6.5 6 8M10 5c-.5 3.5-2.5 6.5-6 8" /><path d="m12 21 4.5-10 4.5 10M13.5 18h6" />',
'link' => '<path d="M10 14a4.5 4.5 0 0 0 6.4 0l3.2-3.2a4.5 4.5 0 0 0-6.4-6.4L11.8 5.8" /><path d="M14 10a4.5 4.5 0 0 0-6.4 0l-3.2 3.2a4.5 4.5 0 0 0 6.4 6.4l1.4-1.4" />',
'log-out' => '<path d="M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4" /><path d="m15 8 4 4-4 4M19 12H9" />',
'menu' => '<path d="M4 7h16M4 12h16M4 17h16" />',
'minus' => '<path d="M5 12h14" />',
'monitor' => '<rect x="3" y="4" width="18" height="12" rx="2" /><path d="M8 20h8M12 16v4" />',
'moon' => '<path d="M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z" />',
'more-horizontal' => '<circle cx="5" cy="12" r="1" /><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" />',
'paperclip' => '<path d="M20 11.5l-8.2 8.2a5 5 0 0 1-7.1-7.1l8.5-8.5a3.3 3.3 0 0 1 4.7 4.7l-8.5 8.5a1.7 1.7 0 0 1-2.4-2.4L14.5 7" />',
'pencil' => '<path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z" /><path d="m15 5 3 3" />',
'play' => '<path d="M7 4.5v15l12.5-7.5Z" />',
'plug' => '<path d="M9 3v5M15 3v5M12 17v4" /><path d="M6 8h12v3a6 6 0 0 1-12 0Z" />',
'plus' => '<path d="M12 5v14M5 12h14" />',
'receipt' => '<path d="M6 3h12v18l-3-2-3 2-3-2-3 2Z" /><path d="M9 8h6M9 12h6" />',
'redo' => '<path d="M15 15l5-5-5-5" /><path d="M20 10H9.5a5.5 5.5 0 0 0 0 11H13" />',
'search' => '<circle cx="11" cy="11" r="7" /><path d="m20 20-4-4" />',
'settings' => '<path d="M10.2 5.2L10.7 2.6A9.5 9.5 0 0 1 13.3 2.6L13.8 5.2A7 7 0 0 1 15.5 5.9L17.7 4.4A9.5 9.5 0 0 1 19.6 6.3L18.1 8.5A7 7 0 0 1 18.8 10.2L21.4 10.7A9.5 9.5 0 0 1 21.4 13.3L18.8 13.8A7 7 0 0 1 18.1 15.5L19.6 17.7A9.5 9.5 0 0 1 17.7 19.6L15.5 18.1A7 7 0 0 1 13.8 18.8L13.3 21.4A9.5 9.5 0 0 1 10.7 21.4L10.2 18.8A7 7 0 0 1 8.5 18.1L6.3 19.6A9.5 9.5 0 0 1 4.4 17.7L5.9 15.5A7 7 0 0 1 5.2 13.8L2.6 13.3A9.5 9.5 0 0 1 2.6 10.7L5.2 10.2A7 7 0 0 1 5.9 8.5L4.4 6.3A9.5 9.5 0 0 1 6.3 4.4L8.5 5.9A7 7 0 0 1 10.2 5.2Z" /><circle cx="12" cy="12" r="3" />',
'shield' => '<path d="M12 3 5 6v5c0 4.5 3 8.3 7 10 4-1.7 7-5.5 7-10V6Z" />',
'sliders' => '<path d="M4 7h9M17 7h3M4 17h3M11 17h9" /><circle cx="15" cy="7" r="2" /><circle cx="9" cy="17" r="2" />',
'star' => '<path d="M12 3.3l2.1 6.4h6.7l-5.4 4 2.1 6.4-5.5-4-5.5 4 2.1-6.4-5.4-4h6.7Z" />',
'star-filled' => '<path d="M12 3.3l2.1 6.4h6.7l-5.4 4 2.1 6.4-5.5-4-5.5 4 2.1-6.4-5.4-4h6.7Z" fill="currentColor" />',
'sun' => '<circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />',
'tag' => '<path d="M3 5v6.2a2 2 0 0 0 .6 1.4l8.3 8.3a2 2 0 0 0 2.8 0l6.2-6.2a2 2 0 0 0 0-2.8L12.6 3.6A2 2 0 0 0 11.2 3H5a2 2 0 0 0-2 2Z" /><path d="M8 8h.01" />',
'trash' => '<path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2" /><path d="m5 6 1 13a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-13" /><path d="M10 11v6M14 11v6" />',
'undo' => '<path d="M9 15l-5-5 5-5" /><path d="M4 10h10.5a5.5 5.5 0 0 1 0 11H11" />',
'user' => '<circle cx="12" cy="8" r="4" /><path d="M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1" />',
'users' => '<circle cx="9" cy="8" r="3.5" /><path d="M3 20v-1a5 5 0 0 1 5-5h2a5 5 0 0 1 5 5v1M15.5 4.6a3.5 3.5 0 0 1 0 6.8M18 14.2a5 5 0 0 1 3 4.8v1" />',
'video' => '<rect x="2" y="6" width="14" height="12" rx="2" /><path d="m16 10.5 6-3.5v10l-6-3.5" />',
'wallet' => '<path d="M17 7V5a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h14a1 1 0 0 1 1 1v3M3 6v12a2 2 0 0 0 2 2h14a1 1 0 0 0 1-1v-3" /><path d="M21 12h-4a2 2 0 0 0 0 4h4Z" />',
'warning' => '<path d="M10.3 4.2 2.8 17a2 2 0 0 0 1.7 3h15a2 2 0 0 0 1.7-3L13.7 4.2a2 2 0 0 0-3.4 0Z" /><path d="M12 9v4M12 17h.01" />',
'zoom-in' => '<circle cx="11" cy="11" r="7" /><path d="m20 20-4-4M8 11h6M11 8v6" />',
'zoom-out' => '<circle cx="11" cy="11" r="7" /><path d="m20 20-4-4M8 11h6" />',
];
$body = $icons[(string) $name] ?? null;
$mirrored = in_array((string) $name, ['chevron-end', 'chevron-start', 'external-link', 'log-out', 'redo', 'undo'], true);
$width = in_array((float) $strokeWidth, [1.0, 1.5, 2.0, 2.5], true) ? (float) $strokeWidth : 2.0;
if ($body === null) {
throw new \InvalidArgumentException('Unknown icon ['.(string) $name.'].');
}
@endphp
<svg
{{ $attributes->class([$sizeClass, 'shrink-0', 'rtl:-scale-x-100' => $mirrored]) }}
data-slot="icon"
data-icon="{{ $name }}"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="{{ $width }}"
stroke-linecap="round"
stroke-linejoin="round"
@if (filled($label)) role="img" aria-label="{{ $label }}" @else aria-hidden="true" @endif
>
{!! $body !!}
</svg>
{{--
Icon tile — the tinted square a feature card, contact row or integration
chip leads with. Pass a named icon (`name`) or drop an inline <svg> in the
slot; `tone` maps to the fill + glyph colour, `size` to the box and the
glyph inside it. The tile is decorative by default; pass `label` for a
tile that carries meaning on its own.
--}}
@props([
'name' => null,
'tone' => 'primary',
'size' => 'md',
'label' => null,
])
@php
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$recipe = $styles['icon-tile'] ?? [];
$tone = array_key_exists($tone, $recipe['tones'] ?? []) ? $tone : 'primary';
$size = array_key_exists($size, $recipe['sizes'] ?? []) ? $size : 'md';
$classes = trim(($recipe['base'] ?? '').' '.($recipe['tones'][$tone] ?? '').' '.($recipe['sizes'][$size] ?? ''));
@endphp
<span
data-slot="icon-tile"
data-tone="{{ $tone }}"
data-size="{{ $size }}"
@if (filled($label)) role="img" aria-label="{{ $label }}" @else aria-hidden="true" @endif
{{ $attributes->merge(['class' => $classes]) }}
>
@if ($name !== null)
<x-ui.icon :name="$name" />
@else
{{ $slot }}
@endif
</span>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.11.0 - 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