Skip to content

Documentation

neelam-ui

Search documentation

Getting Started
Forms
Overlays
Navigation
Data Display
Layout
AI & Chat
Blocks
GitHub repository

Toggle

A two-state button for toolbar-style on/off actions — a native <button> carrying aria-pressed, which per WAI-ARIA is already a complete toggle button.

Usage#

import { Toggle } from "neelam-ui";
 
<Toggle size="icon" aria-label="Bold" defaultPressed>
  <Bold className="h-4 w-4" aria-hidden="true" />
</Toggle>

Toggle, switch, or checkbox?#

Toggle is an action that stays on — bold in an editor, a view filter in a toolbar. It is not a form value:

  • Toggle — a button that stays pressed. Not submitted with a form.
  • Switch — a setting that applies immediately.
  • Checkbox — a value collected now, submitted later.

If the control belongs in a <form>'s data, it is one of the other two.

Icon-only toggles need a name

size="icon" renders no text, so there is no accessible name unless you pass aria-label. Without it a screen reader announces only "button, not pressed".

Variants and sizes#

variant is default (transparent until pressed) or outline (a visible border at rest, for a toolbar where the buttons need to read as a set). Sizes are sm, md, lg, and icon.

<Toggle variant="outline" size="sm">Grid</Toggle>

Controlled#

Unlike Checkbox and Switch, whose state lives on the native input, a plain <button> has no built-in notion of a persistent pressed state — so Toggle tracks defaultPressed in its own state. Pass pressed and onPressedChange to drive it yourself:

const [pressed, setPressed] = useState(false);
 
<Toggle pressed={pressed} onPressedChange={setPressed} aria-label="Bold">
  <Bold className="h-4 w-4" aria-hidden="true" />
</Toggle>

Calling preventDefault() in your own onClick cancels the toggle, which is the escape hatch for "confirm before turning this off".

Keyboard#

Keyboard shortcuts
KeyBehaviour
TabMoves focus to the toggle. Disabled toggles are skipped.
EnterSpaceToggles the pressed state — native button activation.

Arrow keys are not handled here

A row of toggles is a set of independent buttons, each its own tab stop. A single-tab-stop toolbar with arrow-key roving is a different pattern, and this component deliberately does not impose it.

Accessibility#

  • aria-pressed does double duty: it is the state assistive tech reports and the hook the pressed styling is written against, so the two cannot disagree.
  • A native <button>, so role, focusability, and Enter/Space activation come from the platform. Nothing is recategorized.
  • type defaults to "button", so a toggle inside a <form> never submits it by accident.
  • Screen readers announce "pressed" / "not pressed", which is the distinction between a toggle button and a plain one.

API reference#

Props for Toggle
PropTypeDefault
defaultPressedbooleanfalse
onPressedChange((pressed: boolean) => void)
pressedboolean
size"sm" | "md" | "lg" | "icon" | null
variant"default" | "outline" | null

Also accepts every native <button> attribute apart from onChange — use onPressedChange.