Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

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

<label class="toggle-button">
<input type="checkbox" name="standalone-demo-1" />
Toggle me
</label>
<label class="toggle-button">
<input type="checkbox" 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
</label>

Toggle group

Group toggle buttons by wrapping them in a <div role="radiogroup" class="toggle-group"> (or role="group" when multi-select).

Multi-select

Use type="checkbox" for multi-select groups.

<div role="group" class="toggle-group">
<label class="toggle-button">
<input type="checkbox" name="text-style" value="bold" />
<strong>B</strong>
</label>
<label class="toggle-button">
<input type="checkbox" name="text-style" value="italic" />
<i>I</i>
</label>
<label class="toggle-button">
<input type="checkbox" name="text-style" value="underline" />
<u>U</u>
</label>
</div>

Single-select

Use type="radio" for single-select groups.

<div role="radiogroup" class="toggle-group">
<label class="toggle-button">
<input type="radio" name="alignment" value="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" />
</svg>
</label>
<label class="toggle-button selected">
<input type="radio" checked name="alignment" value="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" />
</svg>
</label>
<label class="toggle-button">
<input type="radio" name="alignment" value="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" />
</svg>
</label>
</div>

Text + icon

<div role="radiogroup" class="toggle-group">
<label class="toggle-button selected">
<input type="radio" checked name="transport" value="walking" />
<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 type="radio" name="transport" 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 type="radio" name="transport" 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 .vertical class.

<div role="radiogroup" class="toggle-group vertical">
<label class="toggle-button">
<input type="radio" name="alignment-vertical" value="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" />
</svg>
</label>
<label class="toggle-button selected">
<input type="radio" checked name="alignment-vertical" value="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" />
</svg>
</label>
<label class="toggle-button">
<input type="radio" name="alignment-vertical" value="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" />
</svg>
</label>
</div>

Sizes

Choose between three sizes: default, .x-small and .small.

<label class="toggle-button x-small">
<input type="checkbox" />
x-small
</label>
<label class="toggle-button small">
<input type="checkbox" />
small
</label>
<label class="toggle-button">
<input type="checkbox" />
default
</label>

Anatomy

API

Toggle Button

Type Modifiers Default Description
Sizes default, .small, .x-small - The size of the button.
States .disabled - The state of the button.

Toggle Group

Type Modifiers Default Description
Sizes default, .small, .x-small - The size of the group. Children inherit the size if desired through CSS variables.
Children label.toggle-button containing an input[type="radio|checkbox"] - The interactive elements within the group. Visual states are driven by input:checked.

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);
}
}
}