Skip to content
Brok UI

Loading…

No results

Toggle

Open source Core

A two-state button that can be on or off.

Version
v1.3.2
Stability
stable
License
MIT
Related
Toggle Group
Checkbox
Switch

Preview

Variant
Size
Tone
previews.components.toggle.default.blade.php Blade
<div class="flex items-center gap-2">
    <x-ui.toggle aria-label="{{ __('Bold') }}"><strong>B</strong></x-ui.toggle>
    <x-ui.toggle variant="outline" :pressed="true">{{ __('Italic') }}</x-ui.toggle>
</div>

Variant options

Default Current
Outline Current

Size options

Sm Current
Md Current
Lg Current

Tone options

Neutral Current
Accent Current

Installation

terminal
php artisan ui:add toggle

Registry contract

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

toggle.md
# Brok UI: Toggle (`toggle`)

A two-state button that can be on or off.

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

## Install

```bash
php artisan ui:add toggle
```

## Usage

```blade
<div class="flex items-center gap-2">
    <x-ui.toggle aria-label="{{ __('Bold') }}"><strong>B</strong></x-ui.toggle>
    <x-ui.toggle variant="outline" :pressed="true">{{ __('Italic') }}</x-ui.toggle>
</div>
```

## Props

- `pressed` (bool, default `false`) — Initial pressed state, tracked client-side by Alpine.
- `variant` (default|outline, default `default`) — Visual style: default (filled on press) or outline.
- `size` (md|sm|lg, default `md`) — Control height and padding: sm, md, or lg.
- `tone` (neutral|accent, default `neutral`) — Fill of the pressed state: neutral uses the secondary tokens, so a product that keeps its brand colour for the one primary action and focus is not diluted; accent restores the accent fill of 1.2.

## Use when

- Use for immediate state changes that are obvious and reversible.
- Standalone toolbar actions with an on/off state, such as bold or italic in a text toolbar.
- Needing a button-shaped control that tracks pressed state client-side.

## Avoid when

- Do not use for choices that need explanation, confirmation, or a later Save step.
- Grouping several mutually exclusive toggles together; use toggle-group instead.
- Submitting a boolean value in a plain HTML form; use checkbox or switch.

## Anti-patterns

- Using a switch when Save is required
- Using a switch for irreversible actions

## Rules

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

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

Examples

tones.blade.php Blade
<div class="flex items-center gap-2">
    <x-ui.toggle :pressed="true" aria-label="{{ __('Bold (neutral, the default)') }}"><strong>B</strong></x-ui.toggle>
    <x-ui.toggle tone="accent" :pressed="true" aria-label="{{ __('Bold (accent)') }}"><strong>B</strong></x-ui.toggle>
</div>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
pressed bool false Initial pressed state, tracked client-side by Alpine.
variant default | outline default Visual style: default (filled on press) or outline.
size md | sm | lg md Control height and padding: sm, md, or lg.
tone neutral | accent neutral Fill of the pressed state: neutral uses the secondary tokens, so a product that keeps its brand colour for the one primary action and focus is not diluted; accent restores the accent fill of 1.2.

Slots

  • default — Button content, typically an icon or short label.

Data slots

Stable hooks for CSS overrides and browser tests.

toggle

Behavior

  • Clicking the button flips its pressed state, which updates aria-pressed and the data-state attribute used for styling.
  • State is tracked entirely client-side with Alpine; there is no hidden form input.
  • tone="neutral" (the default) fills the pressed state with the secondary tokens; tone="accent" uses the accent fill of 1.2. data-tone on the root reports the tone.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Immediate reversible state

Change an immediate reversible system state.

Use when

  • Use for immediate state changes that are obvious and reversible.
  • Standalone toolbar actions with an on/off state, such as bold or italic in a text toolbar.
  • Needing a button-shaped control that tracks pressed state client-side.

Avoid when

  • Do not use for choices that need explanation, confirmation, or a later Save step.
  • Grouping several mutually exclusive toggles together; use toggle-group instead.
  • Submitting a boolean value in a plain HTML form; use checkbox or switch.

Use instead

  • Checkbox for a value saved later
  • Button plus confirmation for consequential actions

Anti-patterns

  • Using a switch when Save is required
  • Using a switch for irreversible actions
Anatomy
root
Theming hooks
toggle

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
native
Focus
native
  • The control is a native button with a live aria-pressed attribute, so assistive tech announces it as a toggle button.
  • Focus and activation follow standard button keyboard behavior (Enter and Space).
  • 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

Needs wire:key

Add a stable wire:key when Livewire can reorder this interactive component.

livewire-component.blade.php Blade
<div wire:key="toggle-{{ $record->id }}">
    <div class="flex items-center gap-2">
        <x-ui.toggle aria-label="{{ __('Bold') }}"><strong>B</strong></x-ui.toggle>
        <x-ui.toggle variant="outline" :pressed="true">{{ __('Italic') }}</x-ui.toggle>
    </div>
</div>

Validation

Validation support: native. Keep the error message connected with aria-describedby.

livewire-form.blade.php Blade
<form wire:submit="save" class="space-y-2">
    <brok:toggle
        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 file ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/toggle.blade.php Blade
@props([
    'pressed' => false,
    'variant' => 'default',
    'size' => 'md',
    // neutral (default): the pressed fill uses the secondary tokens; accent:
    // the accent fill of 1.x.
    'tone' => 'neutral',
])
@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $variant = $styles['normalizeVariant']($variant);
    $variant = in_array($variant, ['default', 'outline'], true) ? $variant : 'default';
    $size = in_array($size, ['sm', 'md', 'lg'], true) ? $size : 'md';
    $recipe = $styles['toggle'];
    // The fallbacks keep an older _styles.php (accent in base, no tones) working.
    $accent = 'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground';
    $tones = ($recipe['tones'] ?? []) + ['neutral' => 'data-[state=on]:bg-secondary data-[state=on]:text-secondary-foreground', 'accent' => $accent];
    $tone = isset($tones[$tone]) ? $tone : 'neutral';
    $base = trim(str_replace($accent, '', $recipe['base']));
    $classes = trim($base.' '.$tones[$tone].' '.($recipe['variants'][$variant] ?? $recipe['variants']['default']).' '.($recipe['sizes'][$size] ?? $recipe['sizes']['md']));
@endphp
<button
    type="button"
    x-data="{ pressed: @js((bool) $pressed) }"
    data-slot="toggle"
    data-variant="{{ $variant }}"
    data-size="{{ $size }}"
    data-tone="{{ $tone }}"
    :data-state="pressed ? 'on' : 'off'"
    :aria-pressed="pressed"
    @click="pressed = !pressed"
    {{ $attributes->merge(['class' => $classes]) }}
>{{ $slot }}</button>

Ownership & lifecycle

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