Toggle

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

Loading...
<twig:Toggle variant="outline" size="sm" aria-label="Toggle bookmark" class="data-[state=active]:bg-transparent data-[state=active]:[&_svg_path]:fill-(--foreground) data-[state=active]:[&_svg_path]:stroke-(--foreground)">
    <twig:ux:icon name="lucide:bookmark" />
    Bookmark
</twig:Toggle>

Installation

bin/console ux:install toggle --kit shadcn

That's it!

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.12.0 tales-from-a-dev/twig-tailwind-extra:^1.0.0

Copy the following file(s) into your Symfony app:

assets/controllers/toggle_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    static values = { pressed: Boolean };

    connect() {
        if (!this.hasPressedValue) {
            this.pressedValue = this.element.getAttribute('aria-pressed') === 'true';
        }

        this.updateState();
    }

    toggle() {
        this.pressedValue = !this.pressedValue;
    }

    pressedValueChanged() {
        this.updateState();
    }

    updateState() {
        const pressed = this.pressedValue;
        this.element.setAttribute('aria-pressed', String(pressed));
        this.element.dataset.state = pressed ? 'active' : 'inactive';
    }
}
templates/components/Toggle.html.twig
{# @prop variant 'default'|'outline' The visual style variant. Defaults to `default` #}
{# @prop size 'default'|'sm'|'lg' The toggle size. Defaults to `default` #}
{# @prop pressed boolean Whether the toggle is initially pressed. Defaults to `false` #}
{# @block content The toggle label and/or icon #}
{%- props variant = 'default', size = 'default', pressed = false -%}
{%- set style = html_cva(
    base: "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-pressed:bg-accent aria-pressed:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
    variants: {
        variant: {
            default: 'bg-transparent',
            outline: 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground',
        },
        size: {
            default: 'h-9 min-w-9 px-2',
            sm: 'h-8 min-w-8 px-1.5',
            lg: 'h-10 min-w-10 px-2.5',
        },
    },
) -%}
<button
    type="button"
    class="{{ style.apply({variant: variant, size: size}, attributes.render('class'))|tailwind_merge }}"
    {{ attributes.defaults({
        'data-slot': 'toggle',
        'data-controller': 'toggle',
        'data-action': 'click->toggle#toggle',
        'aria-pressed': pressed ? 'true' : 'false',
    }) }}
>
    {%- block content %}{% endblock -%}
</button>

Happy coding!

Usage

<twig:Toggle>
    Toggle
</twig:Toggle>

Examples

Outline

Loading...
<div class="flex flex-wrap items-center gap-2">
    <twig:Toggle variant="outline" aria-label="Toggle italic">
        <twig:ux:icon name="lucide:italic" />
        Italic
    </twig:Toggle>
    <twig:Toggle variant="outline" aria-label="Toggle bold">
        <twig:ux:icon name="lucide:bold" />
        Bold
    </twig:Toggle>
</div>

With Text

Loading...
<twig:Toggle aria-label="Toggle italic">
    <twig:ux:icon name="lucide:italic" />
    Italic
</twig:Toggle>

Size

Loading...
<div class="flex flex-wrap items-center gap-2">
    <twig:Toggle variant="outline" aria-label="Toggle small" size="sm">
        Small
    </twig:Toggle>
    <twig:Toggle variant="outline" aria-label="Toggle default" size="default">
        Default
    </twig:Toggle>
    <twig:Toggle variant="outline" aria-label="Toggle large" size="lg">
        Large
    </twig:Toggle>
</div>

Disabled

Loading...
<div class="flex flex-wrap items-center gap-2">
    <twig:Toggle aria-label="Toggle disabled" disabled>
        Disabled
    </twig:Toggle>
    <twig:Toggle variant="outline" aria-label="Toggle disabled outline" disabled>
        Disabled
    </twig:Toggle>
</div>

API Reference

Toggle

Prop Type Description
variant 'default'|'outline' The visual style variant. Defaults to default
size 'default'|'sm'|'lg' The toggle size. Defaults to default
pressed boolean Whether the toggle is initially pressed. Defaults to false
Block Description
content The toggle label and/or icon