bx

Toggle

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

tsx
<div className="flex items-center gap-2">
  <Toggle><b aria-hidden="true">B</b><span className="sr-only">Bold</span></Toggle>
  <Toggle pressed><i aria-hidden="true">I</i><span className="sr-only">Italic</span></Toggle>
  <Toggle className="border"><u aria-hidden="true">U</u><span className="sr-only">Underline</span></Toggle>
</div>

Toggle is a two-state on/off button (bold, italic, mute…). It differs from Switch, which represents an "enable/disable this feature" preference: Toggle is a local display/formatting mode.

Installation

Terminal
$ bext ui add toggle

Usage

tsx
import { Toggle } from "@bext-stack/ui/primitives"

<Toggle><b aria-hidden="true">B</b><span className="sr-only">Bold</span></Toggle>

Anatomy

<Toggle pressed={boolean} ariaLabel="…">Content</Toggle>

API Reference

Toggle

Prop Type Default Description
pressed boolean false Initial pressed state. Sets data-state="on", aria-pressed="true" and the accent background.
ariaLabel string — aria-label of the <button>. Use it when the content is an icon or a single letter.
disabled boolean false Disables the button: it cannot be focused or pressed, and is dimmed.
id string — id of the <button>.
children ReactNode — Button content. It is the accessible name unless ariaLabel is set.
className string — Extra classes on the <button>, for example "border" for an outlined look.

Data Attributes

Attributes you can target in custom CSS to override the style for each state.

Attribute On Values Description
data-ui="toggle" root — The island flips the state on click.
data-state root "on" | "off" Current state. The pressed look is styled from it (data-[state=on]:bg-accent), so it follows every click.
aria-pressed root "true" | "false" Same state, for assistive technologies. Updated by the island.

Keyboard

Key Action
Space / Enter Toggle on ↔ off.

Accessibility

Toggle renders a native <button type="button"> with aria-pressed, which the island keeps in sync with data-state, so the pressed state is announced. Its accessible name is its content, or ariaLabel when set: give an icon-only toggle an ariaLabel, or a visually hidden text such as <span class="sr-only">Bold</span>. Keep the name the same in both states: the state is carried by aria-pressed, not by the label.

Try it live
Open in play

Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom: fine-grained reactivity, no virtual DOM.

src/app/page.tsx 2 files · signals · runs in your browser
Pure PRISM + real bext signals: fine-grained, no re-render.