PixelSegmented

Single-select segmented control for toggling between a small set of mutually exclusive options inline.

  • Controlled — value + onChange (React), v-model (Vue), [(value)] or forms (Angular) — over a list of options.
  • Compact horizontal layout for 2-5 options that share visual real estate.
  • Seven tones and pixel/linear surfaces share the kit-wide design tokens.
  • Optional name emits a hidden input so it serializes inside native forms.
  • disabled cascades to every option button (not just visually).
Status
stable
Category
forms
Since
v1.0.0

API

import { PixelSegmented } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
labelstring—Caption rendered above the segmented control. Omitted when empty.
value (required)string—Active option value.
options (required)Option[]—Segment items.
onChange (required)(next: string) => void—Fires with the new value when a segment is clicked.
disabledbooleanfalseDisables every segment.
tone'green' | 'cyan' | 'gold' | 'red' | 'purple' | 'pink' | 'neutral''green'Visual tone for the active segment. Default: 'green'.
surface'pixel' | 'linear'—Surface variant. Inherits from PxlKitSurfaceProvider when omitted.
namestring—Form-serialization name.
requiredboolean—Marks the field as required for native form validation.
aria-labelstring—Accessible name for the group; use it when no visible label is rendered.

ref points to <div>.

Accessibility

WCAG target: 2.1 AA

  • radiogroup

Each option renders as a <button> with aria-pressed reflecting the active selection, and aria-disabled mirroring the disabled prop. When name is set a hidden <input> mirrors the current value so the control participates in native <form> submissions. The implementation uses aria-pressed (toggle-button semantics) rather than role="radio" on each button.

Keyboard

KeyActionWhen
TabMoves focus to the next option button in the segmented control.—
SpaceActivates the focused option (native <button> behavior).—
EnterActivates the focused option (native <button> behavior).—

Usage

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Default() {
  const [value, setValue] = useState('grid');
  return (
    <PixelSegmented
      label="View"
      value={value}
      options={VIEWS}
      onChange={setValue}
    />
  );
}

Examples

Default

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Default() {
  const [value, setValue] = useState('grid');
  return (
    <PixelSegmented
      label="View"
      value={value}
      options={VIEWS}
      onChange={setValue}
    />
  );
}

Controlled

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Controlled() {
  const [value, setValue] = useState('list');
  return (
    <div className="space-y-2">
      <PixelSegmented
        label="Active view"
        value={value}
        options={VIEWS}
        onChange={setValue}
        tone="cyan"
      />
      <p className="text-xs text-retro-muted">Picked: {value}</p>
    </div>
  );
}

Tones

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Tones() {
  const [value, setValue] = useState('grid');
  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
      <PixelSegmented label="Neutral" tone="neutral" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Green" tone="green" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Cyan" tone="cyan" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Gold" tone="gold" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Red" tone="red" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Purple" tone="purple" value={value} options={VIEWS} onChange={setValue} />
      <PixelSegmented label="Pink" tone="pink" value={value} options={VIEWS} onChange={setValue} />
    </div>
  );
}

Surfaces

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Surfaces() {
  const [pixel, setPixel] = useState('grid');
  const [linear, setLinear] = useState('grid');
  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
      <PixelSegmented
        label="Pixel surface"
        surface="pixel"
        value={pixel}
        options={VIEWS}
        onChange={setPixel}
      />
      <PixelSegmented
        label="Linear surface"
        surface="linear"
        value={linear}
        options={VIEWS}
        onChange={setLinear}
      />
    </div>
  );
}

Disabled

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Disabled() {
  return (
    <PixelSegmented
      label="View (locked)"
      value="grid"
      options={VIEWS}
      onChange={() => {}}
      disabled
    />
  );
}

Required

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function Required() {
  const [value, setValue] = useState('grid');
  return (
    <PixelSegmented
      label="Choose a view"
      value={value}
      options={VIEWS}
      onChange={setValue}
      required
    />
  );
}

With form name

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

const VIEWS = [
  { value: 'grid', label: 'Grid' },
  { value: 'list', label: 'List' },
  { value: 'kanban', label: 'Kanban' },
];

export function WithFormName() {
  const [value, setValue] = useState('grid');
  return (
    <form>
      <PixelSegmented
        label="View"
        name="view"
        value={value}
        options={VIEWS}
        onChange={setValue}
        required
      />
    </form>
  );
}

Related