Skip to content
Brok UI

Loading…

No results

Button

Open source Core

Displays a button or link with variants, sizes, a pill shape, static or Livewire-targeted loading state, leading/trailing icon slots, an accessibility-safe icon-only mode and an optional animated arrow.

Version
v1.10.1
Stability
stable
License
MIT
Related
Button Group
Toggle

Preview

Variant
Size
Shape
Disabled
Loading
previews.components.button.default.blade.php Blade
<x-ui.button>{{ __('Button') }}</x-ui.button>

Variant options

Default Current
Secondary Current
Outline Current
Ghost Current
Link Current
Destructive Current

Size options

Sm Current
Md Current
Lg Current
Icon Current
Icon sm Current
Icon lg Current

Shape options

Default Current
Pill Current

Installation

terminal
php artisan ui:add button

Registry contract

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

button.md
# Brok UI: Button (`button`)

Displays a button or link with variants, sizes, a pill shape, static or Livewire-targeted loading state, leading/trailing icon slots, an accessibility-safe icon-only mode and an optional animated arrow.

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

## Install

```bash
php artisan ui:add button
```

## Usage

```blade
<x-ui.button>{{ __('Button') }}</x-ui.button>
```

## Props

- `variant` (default|primary|destructive|danger|outline|secondary|ghost|link, default `default`) — Semantic emphasis or intent. Primary aliases default; danger aliases destructive.
- `size` (sm|md|lg|icon|icon-sm|icon-lg, default `md`) — Control height and horizontal padding.
- `shape` (default|pill, default `default`) — Legacy silhouette override.
- `type` (button|submit|reset, default `button`) — Native button type when rendering a button element.
- `disabled` (bool, default `false`) — Prevents interaction while preserving disabled semantics.
- `loading` (bool, default `false`) — Disables interaction and exposes a busy loading state.
- `wireTarget` (string, default `null`) — Livewire action or property to watch. While that request is in flight the control is disabled and a delayed status message is announced.
- `wireLoadingLabel` (string, default `Working…`) — Screen-reader text announced while the wireTarget request is in flight.
- `withArrow` (bool, default `false`) — Legacy animated-arrow shortcut.
- `arrowClass` (string, default ``) — Legacy classes for the shortcut arrow.
- `href` (string|null, default `null`) — Renders an anchor when navigation semantics are required.
- `as` (button|a|null, default `null`) — Explicitly renders one of the supported native elements.
- `iconLabel` (string|null, default `null`) — Enables icon-only mode, supplies the accessible name, and maps sm/md/lg to square icon sizes.
- `wrap` (bool, default `false`) — Allows the label to wrap; labels truncate by default to protect constrained layouts.
- `shortcut` (string|null, default `null`) — A keyboard shortcut hint such as "e", "shift+j", "ctrl+enter" or "mod+enter", drawn as kbd keys after the label on every variant and exposed as aria-keyshortcuts (Shift+J, Control+Enter). mod is the platform command key, as in the hotkeys store: ⌘ and Meta+Enter for a request from macOS or iOS (Sec-CH-UA-Platform, else the User-Agent), Ctrl and Control+Enter elsewhere; a page in a shared full-page cache must vary on the platform or name the key. The visible hint is hidden on a touch screen (pointer: coarse). A hint only: bind the key with x-hotkey or the hotkeys store. Icon-only buttons keep aria-keyshortcuts without the visible hint.

## Use when

- Use for explicit actions and commits; keep one action visually primary when several sit together.
- Triggering an immediate action such as saving, submitting, or opening a surface.
- Presenting a prominent call to action with a consistent variant and size.

## Avoid when

- Do not use button treatment for navigation-only view changes when links, tabs, or radios describe state more clearly.
- Navigating to another URL without button semantics; render the component as a link or use a normal link.
- Representing a persistent on/off value; use switch or checkbox.

## Anti-patterns

- Using a button for navigation-only state changes
- Presenting multiple equally dominant primary actions

## Rules

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

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

Examples

block.blade.php Blade
<div class="mx-auto flex max-w-xs flex-col gap-3">
    <x-ui.button class="w-full">{{ __('Sign in') }}</x-ui.button>
    <x-ui.button class="w-full" variant="outline">{{ __('Create account') }}</x-ui.button>
</div>
disabled.blade.php Blade
<div class="flex flex-wrap items-center gap-3">
    <x-ui.button :disabled="true">{{ __('Default') }}</x-ui.button>
    <x-ui.button :disabled="true" variant="secondary">{{ __('Secondary') }}</x-ui.button>
    <x-ui.button :disabled="true" variant="outline">{{ __('Outline') }}</x-ui.button>
    <x-ui.button :disabled="true" variant="ghost">{{ __('Ghost') }}</x-ui.button>
</div>
icons.blade.php Blade
<div class="flex flex-wrap items-center gap-3">
    {{-- Leading icon --}}
    <brok:button>
        <x-slot:icon>
            <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M12 5v14M5 12h14" />
            </svg>
        </x-slot:icon>
        {{ __('New project') }}
    </brok:button>

    {{-- Trailing icon --}}
    <brok:button variant="outline">
        {{ __('Continue') }}
        <x-slot:iconTrailing>
            <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M5 12h14M13 6l6 6-6 6" />
            </svg>
        </x-slot:iconTrailing>
    </brok:button>

    {{-- Icon-only --}}
    <brok:button icon-label="Settings" variant="ghost">
        <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <circle cx="12" cy="12" r="3" />
            <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
        </svg>
    </brok:button>
</div>
long-content.blade.php Blade
<div class="w-64 max-w-full">
    <x-ui.button class="w-full">{{ __('Save changes for an exceptionally long translated workspace name') }}</x-ui.button>
</div>
pill.blade.php Blade
<div class="flex flex-wrap items-center gap-3">
    <x-ui.button shape="pill">{{ __('Pill') }}</x-ui.button>
    <x-ui.button shape="pill" variant="secondary">{{ __('Secondary') }}</x-ui.button>
    <x-ui.button shape="pill" variant="outline">{{ __('Outline') }}</x-ui.button>
    <x-ui.button shape="pill" size="icon" variant="outline" aria-label="{{ __('Add') }}">
        <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M12 5v14M5 12h14" />
        </svg>
    </x-ui.button>
</div>
shortcut.blade.php Blade
{{-- A shortcut hint on every variant. The hint is decorative; the button
     carries aria-keyshortcuts, and x-hotkey or the hotkeys store binds the key. --}}
<div class="flex flex-wrap items-center gap-2">
    <x-ui.button shortcut="e">{{ __('Archive') }}</x-ui.button>
    <x-ui.button variant="secondary" shortcut="shift+j">{{ __('Select below') }}</x-ui.button>
    <x-ui.button variant="outline" shortcut="ctrl+enter">{{ __('Send') }}</x-ui.button>
    <x-ui.button variant="ghost" shortcut="s">{{ __('Snooze') }}</x-ui.button>
    <x-ui.button variant="link" shortcut="o">{{ __('Open original') }}</x-ui.button>
    <x-ui.button variant="destructive" shortcut="#">{{ __('Delete') }}</x-ui.button>
    <x-ui.button size="sm" variant="outline" shortcut="esc">{{ __('Close') }}</x-ui.button>
</div>
sizes.blade.php Blade
@php
    // Icon-only sizes render an icon, never a text label — a glyph stays centred
    // in the square while a word would overflow the small box.
    $sizeIcon = <<<'SVG'
        <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M12 5v14M5 12h14" />
        </svg>
    SVG;
@endphp
<div class="flex flex-col gap-4">
    <div class="flex flex-wrap items-center gap-3">
        <x-ui.button size="sm">{{ __('Small') }}</x-ui.button>
        <x-ui.button size="md">{{ __('Medium') }}</x-ui.button>
        <x-ui.button size="lg">{{ __('Large') }}</x-ui.button>
    </div>
    <div class="flex flex-wrap items-center gap-3">
        <x-ui.button size="icon-sm" variant="outline" aria-label="{{ __('Add (small)') }}">{!! $sizeIcon !!}</x-ui.button>
        <x-ui.button size="icon" variant="outline" aria-label="{{ __('Add') }}">{!! $sizeIcon !!}</x-ui.button>
        <x-ui.button size="icon-lg" variant="outline" aria-label="{{ __('Add (large)') }}">{!! $sizeIcon !!}</x-ui.button>
    </div>
</div>
variants.blade.php Blade
<div class="flex flex-wrap items-center gap-3">
    <x-ui.button variant="default">{{ __('Default') }}</x-ui.button>
    <x-ui.button variant="secondary">{{ __('Secondary') }}</x-ui.button>
    <x-ui.button variant="outline">{{ __('Outline') }}</x-ui.button>
    <x-ui.button variant="ghost">{{ __('Ghost') }}</x-ui.button>
    <x-ui.button variant="link">{{ __('Link') }}</x-ui.button>
    <x-ui.button variant="destructive">{{ __('Destructive') }}</x-ui.button>
    <x-ui.button :with-arrow="true">{{ __('With arrow') }}</x-ui.button>
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
variant default | primary | destructive | danger | outline | secondary | ghost | link default Semantic emphasis or intent. Primary aliases default; danger aliases destructive.
size sm | md | lg | icon | icon-sm | icon-lg md Control height and horizontal padding.
shape deprecated default | pill default Legacy silhouette override.
type button | submit | reset button Native button type when rendering a button element.
disabled bool false Prevents interaction while preserving disabled semantics.
loading bool false Disables interaction and exposes a busy loading state.
wireTarget string null Livewire action or property to watch. While that request is in flight the control is disabled and a delayed status message is announced.
wireLoadingLabel string Working… Screen-reader text announced while the wireTarget request is in flight.
withArrow deprecated bool false Legacy animated-arrow shortcut.
arrowClass deprecated string Legacy classes for the shortcut arrow.
href string | null null Renders an anchor when navigation semantics are required.
as button | a | null null Explicitly renders one of the supported native elements.
iconLabel string | null null Enables icon-only mode, supplies the accessible name, and maps sm/md/lg to square icon sizes.
wrap bool false Allows the label to wrap; labels truncate by default to protect constrained layouts.
shortcut string | null null A keyboard shortcut hint such as "e", "shift+j", "ctrl+enter" or "mod+enter", drawn as kbd keys after the label on every variant and exposed as aria-keyshortcuts (Shift+J, Control+Enter). mod is the platform command key, as in the hotkeys store: ⌘ and Meta+Enter for a request from macOS or iOS (Sec-CH-UA-Platform, else the User-Agent), Ctrl and Control+Enter elsewhere; a page in a shared full-page cache must vary on the platform or name the key. The visible hint is hidden on a touch screen (pointer: coarse). A hint only: bind the key with x-hotkey or the hotkeys store. Icon-only buttons keep aria-keyshortcuts without the visible hint.

Slots

  • default — Button label and inline content.
  • iconLeading — Leading decorative icon.
  • icon — Legacy alias for iconLeading.
  • iconTrailing — Trailing decorative icon or affordance.

Renders as button, a.

Data slots

Stable hooks for CSS overrides and browser tests.

button button-icon-leading button-icon-trailing button-label button-loading-label button-shortcut

Behavior

  • Native buttons activate with Enter and Space; link mode preserves anchor semantics.
  • The loading state prevents repeat activation and announces busy state.
  • Icon-only mode keeps its label in screen-reader text and treats the icon as decorative.
  • Shortcut hint (shortcut="e"): the keys render after the label as decorative kbd elements (data-slot="button-shortcut") in the label colour with a faint current-colour edge, so they read on filled, outline, ghost, link and destructive buttons alike; the button gets aria-keyshortcuts with the ARIA key names. Modifiers ctrl, cmd/meta, alt/option and shift and the keys enter, esc, space, tab, backspace, delete and the arrows get their usual labels; other keys show upper-cased.
  • Renders a native button by default and an anchor when href is provided.
  • For disabled anchors, href is removed and aria-disabled/tabindex are applied.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Buttons and direct actions

Commit an explicit user action.

Use when

  • Use for explicit actions and commits; keep one action visually primary when several sit together.
  • Triggering an immediate action such as saving, submitting, or opening a surface.
  • Presenting a prominent call to action with a consistent variant and size.

Avoid when

  • Do not use button treatment for navigation-only view changes when links, tabs, or radios describe state more clearly.
  • Navigating to another URL without button semantics; render the component as a link or use a normal link.
  • Representing a persistent on/off value; use switch or checkbox.

Use instead

  • Link for navigation
  • Tabs or radios for view changes

Anti-patterns

  • Using a button for navigation-only state changes
  • Presenting multiple equally dominant primary actions
Anatomy
root leading icon label trailing icon loading indicator
Theming hooks
button button icon focus ring

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
Enter Space
Focus
native
  • Use iconLabel for icon-only buttons so the accessible name cannot be omitted by accident.
  • Use destructive only for actions with destructive consequences.
  • 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/button.blade.php Blade
@props([
    'variant' => 'default',
    'size' => 'md',
    'shape' => 'default',
    'type' => 'button',
    'disabled' => false,
    'loading' => false,
    // Optional Livewire action/property target. When present, Livewire disables
    // the control during the matching request and exposes a delayed spinner.
    'wireTarget' => null,
    'wireLoadingLabel' => 'Working…',
    'withArrow' => false,
    'arrowClass' => '',
    'href' => null,
    'as' => null,
    // Accessible icon-only mode. A non-empty label supplies the screen-reader
    // name and maps the normal size vocabulary onto the matching square size.
    'iconLabel' => null,
    // Buttons are single-line and overflow-safe by default. Opt into wrapping
    // for prose-like actions whose full label must remain visible.
    'wrap' => false,
    // A keyboard shortcut hint such as "e", "shift+j" or "mod+enter": drawn
    // as <kbd> keys after the label on every variant and exposed as
    // aria-keyshortcuts. `mod` is the platform's command key, as in the
    // hotkeys store: ⌘ (Meta) for a request from macOS or iOS, else Ctrl
    // (Control). It is a hint only; bind the key with x-hotkey or the hotkeys
    // store. Icon-only buttons keep the attribute without the visible hint,
    // and the hint is hidden on a touch screen (pointer: coarse).
    'shortcut' => null,
])

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $variant = $styles['normalizeVariant']($variant);
    $button = $styles['button'];

    $variantAliases = ['primary' => 'default', 'danger' => 'destructive'];
    $resolvedVariant = $variantAliases[$variant] ?? $variant;

    $isIconOnly = filled($iconLabel);
    $iconSizeAliases = ['sm' => 'icon-sm', 'md' => 'icon', 'lg' => 'icon-lg'];
    $resolvedSize = $isIconOnly ? ($iconSizeAliases[$size] ?? $size) : $size;

    // The link variant is text-only: drop the fixed control height/padding so it
    // sits inline like an anchor while still honouring the sm/lg type scale.
    $sizeClass = $resolvedVariant === 'link'
        ? ($button['linkSizes'][$resolvedSize] ?? $button['linkSizes']['md'])
        : ($button['sizes'][$resolvedSize] ?? $button['sizes']['md']);

    // Shape overrides only the corner radius. The base radius is removed rather
    // than out-competed: Tailwind orders `rounded-full` before `rounded-md`, so
    // appending the pill class alone would leave the base rounding in charge.
    $shapeClass = $shape === 'pill' ? ($button['shapes']['pill'] ?? '') : '';
    $baseClass = $shapeClass === ''
        ? $button['base']
        : trim((string) preg_replace('/(?<!\S)rounded(?:-(?:none|xs|sm|md|lg|xl|\dxl|full))?(?!\S)/', '', $button['base']));

    $classes = trim($baseClass.' '.($button['variants'][$resolvedVariant] ?? $button['variants']['default']).' '.$sizeClass.' '.$shapeClass);

    $isDisabled = $disabled || $loading;

    $shortcutKeys = [];
    $shortcutAria = null;
    if (filled($shortcut)) {
        $keyNames = [
            'ctrl' => ['Ctrl', 'Control'], 'control' => ['Ctrl', 'Control'], 'cmd' => ['⌘', 'Meta'], 'meta' => ['⌘', 'Meta'],
            'alt' => ['Alt', 'Alt'], 'option' => ['⌥', 'Alt'], 'shift' => ['Shift', 'Shift'], 'enter' => ['Enter', 'Enter'],
            'escape' => ['Esc', 'Escape'], 'esc' => ['Esc', 'Escape'], 'space' => ['Space', 'Space'], 'tab' => ['Tab', 'Tab'],
            'backspace' => ['Backspace', 'Backspace'], 'delete' => ['Delete', 'Delete'],
            'up' => ['↑', 'ArrowUp'], 'down' => ['↓', 'ArrowDown'], 'left' => ['←', 'ArrowLeft'], 'right' => ['→', 'ArrowRight'],
        ];
        $shortcutParts = array_filter(explode('+', trim((string) $shortcut)), 'strlen');
        if (in_array('mod', array_map('mb_strtolower', $shortcutParts), true)) {
            // The server picks the label for `mod`: Sec-CH-UA-Platform first
            // (Chromium), then the User-Agent (Safari and Firefox). A page in a
            // shared full-page cache must vary on it, or name the key itself.
            $platformHint = trim((string) request()->header('Sec-CH-UA-Platform'), "\" ");
            $isApplePlatform = $platformHint !== ''
                ? in_array(mb_strtolower($platformHint), ['macos', 'ios'], true)
                : preg_match('/Macintosh|Mac OS X|iPhone|iPad|iPod/i', (string) request()->userAgent()) === 1;
            $keyNames['mod'] = $isApplePlatform ? ['⌘', 'Meta'] : ['Ctrl', 'Control'];
        }
        $ariaParts = [];
        foreach ($shortcutParts as $part) {
            [$label, $aria] = $keyNames[mb_strtolower($part)] ?? [mb_strtoupper($part), mb_strtoupper($part)];
            $shortcutKeys[] = $label;
            $ariaParts[] = $aria;
        }
        $shortcutAria = $ariaParts === [] ? null : implode('+', $ariaParts);
    }
    $allowedTags = ['button', 'a'];
    $tag = $as !== null && in_array($as, $allowedTags, true)
        ? $as
        : ($href !== null ? 'a' : 'button');
@endphp

<{{ $tag }}
    @if ($tag === 'button')
        type="{{ $type }}"
        @disabled($isDisabled)
    @else
        href="{{ $isDisabled ? null : $href }}"
        @if ($isDisabled) aria-disabled="true" tabindex="-1" @endif
    @endif
    @if ($loading) aria-busy="true" @endif
    @if (filled($wireTarget))
        wire:target="{{ $wireTarget }}"
        @if ($tag === 'button')
            wire:loading.attr="disabled"
        @else
            wire:loading.attr="aria-disabled"
            wire:loading.class="pointer-events-none"
        @endif
    @endif
    @if ($shortcutAria) aria-keyshortcuts="{{ $shortcutAria }}" @endif
    data-slot="button"
    data-variant="{{ $resolvedVariant }}"
    data-size="{{ $resolvedSize }}"
    @if ($isIconOnly) data-icon-only="true" @endif
    {{ $attributes->merge(['class' => $classes]) }}
>
    @if ($loading)
        <svg class="size-4 shrink-0 animate-spin motion-reduce:animate-none" viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
        </svg>
    @else
        @if (filled($wireTarget))
            <svg
                wire:loading.delay.shorter
                wire:target="{{ $wireTarget }}"
                class="size-4 shrink-0 animate-spin motion-reduce:animate-none"
                viewBox="0 0 24 24"
                fill="none"
                aria-hidden="true"
            >
                <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
            </svg>
        @endif
        @if (isset($iconLeading) || isset($icon))
            <span
                class="contents shrink-0"
                data-slot="button-icon-leading"
                aria-hidden="true"
                @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
            >{{ $iconLeading ?? $icon }}</span>
        @elseif ($isIconOnly)
            <span
                class="contents shrink-0"
                data-slot="button-icon-leading"
                aria-hidden="true"
                @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
            >{{ $slot }}</span>
        @endif
    @endif
    @if ($isIconOnly)
        <span data-slot="button-label" class="sr-only">{{ __($iconLabel) }}</span>
    @else
        <span
            data-slot="button-label"
            @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
            {{-- An <svg> written straight into the slot (instead of the icon slot)
                 is block-level under Preflight and would wrap onto its own line:
                 keep it inline, sized and vertically centred beside the text.
                 :where() keeps these defaults at zero specificity, so an svg's
                 own size class (an icon tile's size-5) still wins. --}}
            @class([
                'min-w-0 [:where(&)_svg]:inline-block [:where(&)_svg]:size-4 [:where(&)_svg]:shrink-0 [:where(&)_svg]:align-[-0.125em]',
                'whitespace-normal break-words text-wrap' => $wrap,
                'truncate' => ! $wrap,
            ])
        >{{ $slot }}</span>
        @if (filled($wireTarget))
            <span
                data-slot="button-loading-label"
                wire:loading
                wire:target="{{ $wireTarget }}"
                class="min-w-0 truncate"
                role="status"
            >{{ __($wireLoadingLabel) }}</span>
        @endif
    @endif
    @if (filled($wireTarget) && $isIconOnly)
        <span wire:loading.delay.shorter wire:target="{{ $wireTarget }}" class="sr-only" role="status">
            {{ __($wireLoadingLabel) }}
        </span>
    @endif
    @if ($shortcutKeys !== [] && ! $isIconOnly)
        <span
            data-slot="button-shortcut"
            dir="ltr"
            aria-hidden="true"
            class="{{ $button['shortcut'] ?? 'inline-flex shrink-0 items-center gap-1 pointer-coarse:hidden' }}"
            @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
        >@foreach ($shortcutKeys as $key)<kbd class="{{ $button['shortcutKey'] ?? 'inline-flex h-5 min-w-5 items-center justify-center rounded-sm border border-current/25 px-1 font-body text-xs font-medium leading-none' }}">{{ $key }}</kbd>@endforeach</span>
    @endif
    @isset($iconTrailing)
        <span
            class="contents shrink-0"
            data-slot="button-icon-trailing"
            aria-hidden="true"
            @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
        >{{ $iconTrailing }}</span>
    @endisset
    @if ($withArrow)
        <svg
            class="{{ $button['arrow'] }} {{ $arrowClass }}"
            @if (filled($wireTarget)) wire:loading.remove wire:target="{{ $wireTarget }}" @endif
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
            stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"
        >
            <path d="M5 12h14M13 6l6 6-6 6" />
        </svg>
    @endif
</{{ $tag }}>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.10.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

Migration history

  • 1.7.1 → 1.8.0 — Replace legacy button shape and arrow shortcuts with consumer classes and the iconTrailing slot.