Components
Toggle
Buttons (disguised as input checkbox/radio) that can be toggled on and off.
Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.
Toggle button
---import { ToggleButton } from "@opui/astro"---
<ToggleButton name="standalone-demo-1">Toggle me</ToggleButton><ToggleButton name="standalone-demo-2"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ><path d="M12 21a9 9 0 1 0-9-9 9 9 0 0 0 9 9Z"></path><path d="M12 8v4" ></path><path d="M12 16h.01"></path></svg > Toggle with icon</ToggleButton><label class="toggle-button"> <input id="toggle-1" name="standalone-demo-1" type="checkbox" /> Toggle me</label><label class="toggle-button"> <input id="toggle-2" name="standalone-demo-2" type="checkbox" /> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" > <path d="M12 21a9 9 0 1 0-9-9 9 9 0 0 0 9 9Z"></path> <path d="M12 8v4"></path> <path d="M12 16h.01"></path> </svg> Toggle with icon</label>Toggle group
Group toggle buttons by wrapping them in a ToggleGroup component.
Multi-select
Use selection="multiple" for multi-select groups.
---import { ToggleGroup, ToggleButton } from "@opui/astro"---
<ToggleGroup name="text-style"> <ToggleButton value="bold" aria-label="Bold"><strong>B</strong></ToggleButton> <ToggleButton value="italic" aria-label="Italic"><i>I</i></ToggleButton> <ToggleButton value="underline" aria-label="Underline"><u>U</u></ToggleButton></ToggleGroup><div class="toggle-group" role="group"> <label class="toggle-button"> <input id="toggle-3" name="text-style" type="checkbox" value="bold" aria-label="Bold" /> <strong>B</strong> </label> <label class="toggle-button"> <input id="toggle-4" name="text-style" type="checkbox" value="italic" aria-label="Italic" /> <i>I</i> </label> <label class="toggle-button"> <input id="toggle-5" name="text-style" type="checkbox" value="underline" aria-label="Underline" /> <u>U</u> </label></div>Single-select
Use selection="single" for single-select groups.
---import { ToggleGroup, ToggleButton } from "@opui/astro"---
<ToggleGroup selection="single" name="alignment"> <ToggleButton value="left" aria-label="Align left"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm0-4h12v-2H3v2zm0-4h18v-2H3v2zm0-4h12v-2H3v2zm0-6v2h18V3H3z" ></path></svg > </ToggleButton> <ToggleButton value="center" checked aria-label="Align center"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm4-4h10v-2H7v2zm-4-4h18v-2H3v2zm4-4h10v-2H7v2zM3 3v2h18V3H3z" ></path></svg > </ToggleButton> <ToggleButton value="right" aria-label="Align right"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12v-2H9v2zM3 3v2h18V3H3z" ></path></svg > </ToggleButton></ToggleGroup><div class="toggle-group" role="radiogroup"> <label class="toggle-button"> <input id="toggle-6" name="alignment" type="radio" value="left" aria-label="Align left" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm0-4h12v-2H3v2zm0-4h18v-2H3v2zm0-4h12v-2H3v2zm0-6v2h18V3H3z" ></path> </svg> </label> <label class="toggle-button"> <input id="toggle-7" name="alignment" type="radio" value="center" checked aria-label="Align center" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm4-4h10v-2H7v2zm-4-4h18v-2H3v2zm4-4h10v-2H7v2zM3 3v2h18V3H3z" ></path> </svg> </label> <label class="toggle-button"> <input id="toggle-8" name="alignment" type="radio" value="right" aria-label="Align right" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12v-2H9v2zM3 3v2h18V3H3z" ></path> </svg> </label></div>Text + icon
---import { ToggleGroup, ToggleButton } from "@opui/astro"---
<ToggleGroup selection="single" name="transport"> <ToggleButton value="walking" checked> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M13 6.5A2.25 2.25 0 1 0 13 2a2.25 2.25 0 0 0 0 4.5m-2.639-.081c.185.045.35.146.493.272a3.24 3.24 0 0 0 2.904.72c.186-.044.379-.056.564-.01l.132.033a1.5 1.5 0 0 1 .919.673l1.332 2.177a1 1 0 0 0 .657.46l1.431.285a1.5 1.5 0 0 1-.587 2.942l-2.504-.5a1.5 1.5 0 0 1-.986-.688l-.183-.3a.54.54 0 0 0-.966.09a1.5 1.5 0 0 0 .17 1.389l.994 1.433a1.5 1.5 0 0 1 .265.767l.25 4.25a1.5 1.5 0 0 1-2.995.176l-.2-3.391a1 1 0 0 0-.247-.602l-.851-.968a.88.88 0 0 0-1.477.252L7.39 21.061a1.5 1.5 0 0 1-2.783-1.122l3.076-7.634q.02-.081.052-.162l.565-1.47a.469.469 0 0 0-.865-.362l-1.268 2.806a1.5 1.5 0 0 1-2.735-1.232l1.624-3.61a1.5 1.5 0 0 1 .846-.792l3.075-1.14a1.5 1.5 0 0 1 .883-.049z" ></path> </svg> Walking </ToggleButton> <ToggleButton value="cycling"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M12.75 3a.75.75 0 0 0 0 1.5h1.427l.955 3.5H8.5V5.75A.75.75 0 0 0 7.75 5h-3a.75.75 0 0 0 0 1.5H7v2.188L6.698 10.5a4.25 4.25 0 1 0 4.298 4.065l4.656-4.657l.274 1.003a4.25 4.25 0 1 0 1.447-.394l-1.9-6.964A.75.75 0 0 0 14.75 3zm3.58 9.394l.696 2.553a.75.75 0 1 0 1.448-.394L17.777 12a2.75 2.75 0 1 1-1.447.394m-5.765.48a4.26 4.26 0 0 0-2.387-2.128L8.385 9.5h5.554zm-2.64-.611c.71.336 1.254.968 1.471 1.737h-1.76zm-1.48-.246l-.435 2.61a.75.75 0 0 0 .74.873h2.646a2.751 2.751 0 1 1-2.95-3.483" ></path> </svg> Cycling </ToggleButton> <ToggleButton value="commuting"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M16.25 3A3.75 3.75 0 0 1 20 6.75v9a3.75 3.75 0 0 1-2.89 3.651l2.462 1.172a.75.75 0 0 1-.55 1.392l-.095-.038L13.83 19.5h-3.661l-5.097 2.427a.75.75 0 1 1-.645-1.354L6.89 19.4A3.75 3.75 0 0 1 4 15.75v-9A3.75 3.75 0 0 1 7.75 3zM8 15a1 1 0 1 0 0 2a1 1 0 0 0 0-2m8 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2m.25-10.5h-8.5A2.25 2.25 0 0 0 5.5 6.75v5.75h13V6.75a2.25 2.25 0 0 0-2.25-2.25m-3 1.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1 0-1.5z" ></path> </svg> Commuting </ToggleButton></ToggleGroup><div class="toggle-group" role="radiogroup"> <label class="toggle-button"> <input id="toggle-9" name="transport" type="radio" value="walking" checked /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M13 6.5A2.25 2.25 0 1 0 13 2a2.25 2.25 0 0 0 0 4.5m-2.639-.081c.185.045.35.146.493.272a3.24 3.24 0 0 0 2.904.72c.186-.044.379-.056.564-.01l.132.033a1.5 1.5 0 0 1 .919.673l1.332 2.177a1 1 0 0 0 .657.46l1.431.285a1.5 1.5 0 0 1-.587 2.942l-2.504-.5a1.5 1.5 0 0 1-.986-.688l-.183-.3a.54.54 0 0 0-.966.09a1.5 1.5 0 0 0 .17 1.389l.994 1.433a1.5 1.5 0 0 1 .265.767l.25 4.25a1.5 1.5 0 0 1-2.995.176l-.2-3.391a1 1 0 0 0-.247-.602l-.851-.968a.88.88 0 0 0-1.477.252L7.39 21.061a1.5 1.5 0 0 1-2.783-1.122l3.076-7.634q.02-.081.052-.162l.565-1.47a.469.469 0 0 0-.865-.362l-1.268 2.806a1.5 1.5 0 0 1-2.735-1.232l1.624-3.61a1.5 1.5 0 0 1 .846-.792l3.075-1.14a1.5 1.5 0 0 1 .883-.049z" ></path> </svg> Walking </label> <label class="toggle-button"> <input id="toggle-10" name="transport" type="radio" value="cycling" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M12.75 3a.75.75 0 0 0 0 1.5h1.427l.955 3.5H8.5V5.75A.75.75 0 0 0 7.75 5h-3a.75.75 0 0 0 0 1.5H7v2.188L6.698 10.5a4.25 4.25 0 1 0 4.298 4.065l4.656-4.657l.274 1.003a4.25 4.25 0 1 0 1.447-.394l-1.9-6.964A.75.75 0 0 0 14.75 3zm3.58 9.394l.696 2.553a.75.75 0 1 0 1.448-.394L17.777 12a2.75 2.75 0 1 1-1.447.394m-5.765.48a4.26 4.26 0 0 0-2.387-2.128L8.385 9.5h5.554zm-2.64-.611c.71.336 1.254.968 1.471 1.737h-1.76zm-1.48-.246l-.435 2.61a.75.75 0 0 0 .74.873h2.646a2.751 2.751 0 1 1-2.95-3.483" ></path> </svg> Cycling </label> <label class="toggle-button"> <input id="toggle-11" name="transport" type="radio" value="commuting" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M16.25 3A3.75 3.75 0 0 1 20 6.75v9a3.75 3.75 0 0 1-2.89 3.651l2.462 1.172a.75.75 0 0 1-.55 1.392l-.095-.038L13.83 19.5h-3.661l-5.097 2.427a.75.75 0 1 1-.645-1.354L6.89 19.4A3.75 3.75 0 0 1 4 15.75v-9A3.75 3.75 0 0 1 7.75 3zM8 15a1 1 0 1 0 0 2a1 1 0 0 0 0-2m8 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2m.25-10.5h-8.5A2.25 2.25 0 0 0 5.5 6.75v5.75h13V6.75a2.25 2.25 0 0 0-2.25-2.25m-3 1.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1 0-1.5z" ></path> </svg> Commuting </label></div>Vertical orientation
Change the layout of the group with the orientation="vertical" prop.
---import { ToggleGroup, ToggleButton } from "@opui/astro"---
<ToggleGroup selection="single" name="alignment-vertical" orientation="vertical"> <ToggleButton value="left" aria-label="Align left"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm0-4h12v-2H3v2zm0-4h18v-2H3v2zm0-4h12v-2H3v2zm0-6v2h18V3H3z" ></path></svg > </ToggleButton> <ToggleButton value="center" checked aria-label="Align center"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm4-4h10v-2H7v2zm-4-4h18v-2H3v2zm4-4h10v-2H7v2zM3 3v2h18V3H3z" ></path></svg > </ToggleButton> <ToggleButton value="right" aria-label="Align right"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" ><path fill="currentColor" d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12v-2H9v2zM3 3v2h18V3H3z" ></path></svg > </ToggleButton></ToggleGroup><div class="toggle-group vertical" role="radiogroup"> <label class="toggle-button"> <input id="toggle-12" name="alignment-vertical" type="radio" value="left" aria-label="Align left" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm0-4h12v-2H3v2zm0-4h18v-2H3v2zm0-4h12v-2H3v2zm0-6v2h18V3H3z" ></path> </svg> </label> <label class="toggle-button"> <input id="toggle-13" name="alignment-vertical" type="radio" value="center" checked aria-label="Align center" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm4-4h10v-2H7v2zm-4-4h18v-2H3v2zm4-4h10v-2H7v2zM3 3v2h18V3H3z" ></path> </svg> </label> <label class="toggle-button"> <input id="toggle-14" name="alignment-vertical" type="radio" value="right" aria-label="Align right" /> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12v-2H9v2zM3 3v2h18V3H3z" ></path> </svg> </label></div>Sizes
Choose between three sizes: default, .x-small and .small.
---import { ToggleButton } from "@opui/astro"---
<ToggleButton size="x-small"> x-small </ToggleButton><ToggleButton size="small"> small </ToggleButton><ToggleButton> default </ToggleButton><label class="toggle-button x-small"> <input id="toggle-15" name="alignment-vertical" type="radio" /> x-small</label><label class="toggle-button small"> <input id="toggle-16" name="alignment-vertical" type="radio" /> small</label><label class="toggle-button"> <input id="toggle-17" name="alignment-vertical" type="radio" /> default</label>Anatomy
API
Toggle Button
Individual toggle button component.
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Whether the toggle button is disabled. |
label | string | - | The label text for the toggle button. |
name | string | - | The name of the input element. Inherited from ToggleGroup if used within one. |
pressed | boolean | false | Whether the toggle button is pressed. Maps to aria-pressed
in checkbox mode and to checked on the underlying input. |
size | "default" | "small" | "x-small" | "default" | The size of the toggle button. |
type | "checkbox" | "radio" | "checkbox" | The type of input element. Inherited from ToggleGroup (driven by the
selection prop) if used within one. |
value | string | - | The value attribute of the input element. Defaults to label if not provided. |
Toggle Group
Container for grouping multiple toggle buttons.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | The name property for child inputs. Used for native form submission. |
size | "default" | "small" | "x-small" | "default" | The size of the toggle group. |
orientation | "vertical" | - | Changes the layout direction of the group. |
selection | "single" | "multiple" | "multiple" | The selection mode. "single" maps to radio inputs;
"multiple" maps to checkbox inputs. |
Browser support
Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.
See also the full browser support guide.
Installation
@layer components.root { :where(.toggle-group) { --_bg-color: var(--surface-default); --_border-color: var(--border-color); --_border-radius: var(--button-border-radius);
background-color: var(--_bg-color); border: 1px solid var(--_border-color); border-radius: var(--_border-radius); display: inline-flex; inline-size: fit-content; overflow: hidden;
/* Sizes */ &.small { .toggle-button { --_size: var(--field-size-small); } }
&.x-small { .toggle-button { --_size: var(--field-size-x-small); } }
/* Toggle button */ .toggle-button { border: 0; border-radius: 0; margin: 0; }
&:not(.vertical) { .toggle-button { border-inline-end: 1px solid var(--_border-color);
&:last-of-type { border-inline-end: 0; } } }
&.vertical { flex-direction: column;
.toggle-button { border-block-end: 1px solid var(--_border-color);
&:last-of-type { border-block-end: 0; } } } }}/* TODO:* Somehow handle when a group gets too wide for its container. Should it be handled?*/@layer components.root { :where(.toggle-button) { --_button-bg-color: transparent; --_size: var(--field-size); --_svg-size: var(--size-4);
align-items: center; background-color: var(--_button-bg-color); block-size: var(--_size); border: 1px solid var(--border-color); border-radius: var(--button-border-radius); color: var(--text-primary); cursor: pointer; display: inline-flex; flex: auto; gap: var(--size-2); inline-size: fit-content; justify-content: center; min-inline-size: var(--_size); outline-offset: calc(-1 * var(--size-2)); padding: 0 var(--size-2); position: relative; user-select: none;
input[type="checkbox"], input[type="radio"] { /* utils.css > .sr-only */ block-size: 1px; clip-path: inset(50%); inline-size: 1px; overflow: hidden; position: absolute; white-space: nowrap; }
/* Element states */ @media (pointer: fine) { &:hover:not(.disabled) { --_button-bg-color: light-dark(oklch(0% 0 0 / 0.04), oklch(100% 0 0 / 0.08)); } }
&:has(input:focus-visible) { --focus-ring-color: var(--text-muted); --focus-ring-offset: -6px; outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color); outline-offset: var(--focus-ring-offset); }
/* Disabled */ &.disabled { border-color: color-mix(in oklch, var(--border-color) 50%, transparent); color: color-mix(in oklch, var(--text-primary) 30%, transparent); cursor: not-allowed; }
/* svgs */ svg { block-size: var(--_svg-size); flex-shrink: 0; inline-size: var(--_svg-size); }
/* Selected */ &:has(:where(input[type="checkbox"], input[type="radio"]):checked) { --_button-bg-color: oklch(from var(--primary) l c h / 25%);
@media (pointer: fine) { &:hover { --_button-bg-color: oklch(from var(--primary) l c h / 35%); } } }
/* Sizes */ &.small { --_size: var(--field-size-small); }
&.x-small { --_size: var(--field-size-x-small); } }}