PixelSwitch

Two-state toggle switch with a sliding pixel thumb — flips a boolean setting on or off.

  • Controlled — checked + onChange (React), v-model:checked (Vue), [(checked)] or forms (Angular) — or uncontrolled with defaultChecked.
  • Tone and surface follow the kit-wide tokens (pixel keeps square corners, linear rounds the track).
  • Optional name/value/required mirror the state into a hidden input for native form submission.
  • Accessible: renders as role="switch" with aria-checked, aria-disabled, and aria-required.
  • SSR-safe and tree-shakable; no portals or browser-only APIs.
Status
stable
Category
forms
Since
v1.0.0

API

import { PixelSwitch } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
label (required)string—Label rendered next to the switch.
checkedboolean—Controlled checked state.
defaultCheckedboolean—Uncontrolled initial checked state.
onChange(next: boolean) => void—Fires with the next checked value when clicked.
disabledbooleanfalseDisables interaction + grays out the control.
tone'green' | 'cyan' | 'gold' | 'red' | 'purple' | 'pink' | 'neutral''green'Visual tone for the "on" state. Default: 'green'.
surface'pixel' | 'linear'—Surface variant. Inherits from PxlKitSurfaceProvider when omitted.
namestring—Form-serialization name. Hidden mirror sends 'on' / ''.
valuestring'on'HTML form value when checked. Defaults to 'on'.
requiredboolean—Marks the field as required for native form validation.
idstring—DOM id forwarded to the trigger.

ref points to <button>.

Accessibility

WCAG target: 2.1 AA

  • switch

Uses role="switch" with aria-checked reflecting the boolean state. When disabled, the control is both visually dimmed and functionally inert (disabled + aria-disabled). The hidden mirror input only renders while checked is true so unchecked switches submit as absent (standard HTML form semantics).

Keyboard

KeyActionWhen
TabMove focus to / away from the switch.—
SpaceToggle the checked state.—
EnterToggle the checked state (native button activation).—

Usage

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function Default() {
  const [on, setOn] = useState(false);
  return <PixelSwitch label="Enable notifications" checked={on} onChange={setOn} />;
}

Examples

Default

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function Default() {
  const [on, setOn] = useState(false);
  return <PixelSwitch label="Enable notifications" checked={on} onChange={setOn} />;
}

Checked

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function Checked() {
  const [on, setOn] = useState(true);
  return <PixelSwitch label="Dark mode" checked={on} onChange={setOn} />;
}

Tones

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function Tones() {
  const [on, setOn] = useState(true);
  return (
    <div className="flex flex-col gap-3">
      <PixelSwitch label="Neutral" tone="neutral" checked={on} onChange={setOn} />
      <PixelSwitch label="Green" tone="green" checked={on} onChange={setOn} />
      <PixelSwitch label="Cyan" tone="cyan" checked={on} onChange={setOn} />
      <PixelSwitch label="Gold" tone="gold" checked={on} onChange={setOn} />
      <PixelSwitch label="Red" tone="red" checked={on} onChange={setOn} />
      <PixelSwitch label="Purple" tone="purple" checked={on} onChange={setOn} />
      <PixelSwitch label="Pink" tone="pink" checked={on} onChange={setOn} />
    </div>
  );
}

Surfaces

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function Surfaces() {
  const [a, setA] = useState(true);
  const [b, setB] = useState(true);
  return (
    <div className="flex flex-col gap-3">
      <PixelSwitch label="Pixel surface" surface="pixel" checked={a} onChange={setA} />
      <PixelSwitch label="Linear surface" surface="linear" checked={b} onChange={setB} />
    </div>
  );
}

Disabled

import { PixelSwitch } from '@pxlkit/ui-kit';

export function Disabled() {
  return (
    <div className="flex flex-col gap-3">
      <PixelSwitch label="Disabled off" checked={false} onChange={() => {}} disabled />
      <PixelSwitch label="Disabled on" checked onChange={() => {}} disabled />
    </div>
  );
}

With Form Name

import { useState } from 'react';
import { PixelSwitch } from '@pxlkit/ui-kit';

export function WithFormName() {
  const [on, setOn] = useState(true);
  return (
    <PixelSwitch
      label="Subscribe to newsletter"
      name="newsletter"
      value="yes"
      checked={on}
      onChange={setOn}
      required
    />
  );
}

Related