Toggle
Expose a stable named pressed action and make its local effect visible.
A pressed action
Use Toggle for a two-state action such as emphasis or showing a local layer. Keep its label stable; aria-pressed conveys the state. Use Switch for a feature setting and Checkbox for an independent form option.
A pressed visual state is not a native checkbox form field. If a form needs this value, serialize and validate it explicitly in the consuming task.
Anatomy and specifications
| Part | Actual CSS / semantics |
|---|---|
| Root | Native button and HTMLButtonElement ref |
| Target | Minimum 44px height; wrapping text; 12px inline padding |
| Surface | 2px border, --radius-md, --text-sm weight 500 |
| Pressed | Primary fill/foreground and aria-pressed |
| Hover / focus | Muted hover surface; token ring |
| Unavailable | Primitive disabled behavior and visible unavailable state |
Working local emphasis
This English-only sample changes its actual local emphasis.
Local emphasis off.
'use client';
import { useState } from 'react';
import { Button, Toggle, ToggleGroup, ToggleGroupItem } from '@mobtranslate/ui';
export function ToggleSelectionExample() {
const [pressed, setPressed] = useState(false);
return <div data-toggle-lab className="space-y-4"><Toggle pressed={pressed} onPressedChange={setPressed} className={state => state.pressed ? 'local-toggle-on' : 'local-toggle-off'}>Emphasize local example</Toggle><p style={{ fontWeight: pressed ? 700 : 400 }}>This English-only sample changes its actual local emphasis.</p><Toggle disabled>Unavailable emphasis</Toggle><p role="status">Local emphasis {pressed ? 'on' : 'off'}.</p></div>;
}
export function ToggleGroupSelectionExample() {
const [multiple, setMultiple] = useState(false);
const [value, setValue] = useState<string[]>(['title']);
const [vertical, setVertical] = useState(false);
return <div data-toggle-group-lab className="space-y-4">
<Button type="button" variant="outline" aria-pressed={multiple} onClick={() => { setMultiple(current => !current); setValue([]); }}>Allow multiple local segments</Button>
<Button type="button" variant="outline" aria-pressed={vertical} onClick={() => setVertical(current => !current)}>Use vertical local segments</Button>
<ToggleGroup multiple={multiple} value={value} onValueChange={setValue} orientation={vertical ? 'vertical' : 'horizontal'} aria-label="Local reading sections" className={state => state.multiple ? 'local-segments-multiple' : 'local-segments-single'}>
<ToggleGroupItem value="title">Title section</ToggleGroupItem><ToggleGroupItem value="notes">Notes section</ToggleGroupItem><ToggleGroupItem value="unavailable" disabled>Unavailable section</ToggleGroupItem>
</ToggleGroup>
<div data-local-sections>{value.includes('title') && <h3 className="font-semibold">Local title section</h3>}{value.includes('notes') && <p>English-only local notes section.</p>}{value.length === 0 && <p>No local sections selected.</p>}</div>
<p role="status">Pressed local sections: {value.join(', ') || 'none'}. {multiple ? 'Multiple choices permitted.' : 'At most one; empty is allowed.'}</p>
</div>;
}
API reference
| Prop | Contract |
|---|---|
| pressed / defaultPressed | Controlled boolean / initial state; false default. |
| onPressedChange | (boolean, eventDetails); update actual state. |
| disabled | false; primitive guards activation. |
| value | String identity when used in a ToggleGroup. |
| children / native button props / render / ref | Stable accessible label and forwarded button semantics. |
| className / style / render / native props | Installed primitive props; styled wrappers retain string-or-state class callbacks. A custom render must preserve semantics, events and refs. |
Behavior and failures
- Space/Enter activate the native button; focus remains on it while its pressed state changes.
- Do not change a stable action name into an unrelated command after pressing. Express the selected result through state and nearby content.
- Disabled must prevent both the callback and the effect, not merely dim the surface.
- Use explicit serialization for real form/request state; pressing a toggle is not a save receipt.
- An icon-only toggle needs a meaningful accessible name and enough target area.
Verification and release scope
- Check actual names, roles, state and focus with pointer and keyboard. Inspect the retained value and submitted FormData when relevant.
- Exercise empty, selected, mixed, disabled, read-only, invalid and pending/recovery states that apply to the control. Styling alone does not prove enforcement.
- Check both themes, long labels, 320px reflow, meaningful graphic/text contrast, reduced motion, forced colors and the actual target device.
- Use a named screen-reader/browser pair to verify spoken state. DOM assertions and local examples do not prove announcements or backend persistence.
- Selection never establishes language quality, source authority, consent, qualified review or permission. Keep that evidence explicit in the product task.