KDS
Forms

ToggleGroup

Conjunto coordenado de Toggles — alinhamento, formatação combinada, filtros.

Overview

ToggleGroup é um conjunto de Toggles coordenados via context — variant, size e spacing definidos no Root propagam para todos os Items. Use type="single" para escolha exclusiva (alinhamento, modo de visualização) ou type="multiple" para formatações combináveis (bold + italic + underline).

A diferença em relação a RadioGroup: RadioGroup é form-bound (label-led, captura no submit). ToggleGroup é stateful UI (ícone-led, efeito imediato).

Preview
Alinhamento de texto — single, outline.

Anatomy

<ToggleGroup type="single" | "multiple" variant size spacing>
  └─ <ToggleGroupItem value="x">  ← um por opção
</ToggleGroup>

Quando spacing=0 (default) + variant="outline", os items se colam em "segmented control"; com spacing>0 viram chips separados.

Usage

import { ToggleGroup, ToggleGroupItem } from "@kalvner/kds/forms/toggle-group";

export function Alignment({ value, onValueChange }) {
  return (
    <ToggleGroup
      type="single"
      value={value}
      onValueChange={onValueChange}
      variant="outline"
      aria-label="Alinhamento"
    >
      <ToggleGroupItem value="left" aria-label="Esquerda">L</ToggleGroupItem>
      <ToggleGroupItem value="center" aria-label="Centro">C</ToggleGroupItem>
      <ToggleGroupItem value="right" aria-label="Direita">R</ToggleGroupItem>
    </ToggleGroup>
  );
}

Props

ToggleGroup (Root)

PropTipoDefaultDescrição
type'single' | 'multiple'Modo (radio-like vs. checkbox-like).
value / defaultValuestring | string[]Valor(es) ativo(s).
onValueChange(v) => voidCallback.
variant'default' | 'outline''default'Propaga aos Items.
size'sm' | 'default' | 'lg''default'Propaga aos Items.
spacingnumber0Gap entre items. 0 = segmented; >0 = chips.
disabledbooleanfalseBloqueia toda a árvore.
aria-labelstringObrigatório (descreve o grupo).

ToggleGroupItem

PropTipoDefaultDescrição
valuestringIdentificador único.
disabledbooleanfalseBloqueia este item.
aria-labelstringObrigatório quando só ícone.

Subcomponents

  • ToggleGroupItem — herda variant, size e spacing do Root via context. Você pode passar variant/size por item para override.

Variants

Single
Exclusivo — um item ativo por vez.
Multiple
Combinável — bold + italic juntos.

States

EstadoComportamento
Item data-state=offAparência neutra.
Item data-state=onBackground accent, foreground accent-foreground.
disabledToda a árvore bloqueada.
spacing=0 + outlineBorders compartilhados (segmented control).

Composition

Em barras de formatação, separe grupos relacionados com pequeno gap:

<div className="flex items-center gap-2">
  <ToggleGroup type="multiple" aria-label="Estilo">
    <ToggleGroupItem value="bold"><Bold /></ToggleGroupItem>
    <ToggleGroupItem value="italic"><Italic /></ToggleGroupItem>
  </ToggleGroup>
  <Separator orientation="vertical" className="h-6" />
  <ToggleGroup type="single" aria-label="Alinhamento">
    <ToggleGroupItem value="left"><AlignLeft /></ToggleGroupItem>
    <ToggleGroupItem value="center"><AlignCenter /></ToggleGroupItem>
  </ToggleGroup>
</div>

When to use

  • Toolbar de editor (formatação, alinhamento).
  • Modo de visualização (grade / lista / kanban).
  • Filtros combináveis em listas.
  • "Quick filters" em dashboards (Hoje / Semana / Mês — type="single").

When not to use

  • Form-bound exclusivo — use RadioGroup.
  • Form-bound múltiplo — use Checkbox em grupo.
  • Navegação entre painéis — use Tabs.
  • Mais de ~6 itens — vira sopa visual; troque por Select.

Best practices

  • aria-label no Root é obrigatório — descreve o grupo ("Alinhamento", "Formatação").
  • Items com só ícone também precisam de aria-label próprio.
  • Para "segmented control", mantenha spacing=0 + variant="outline".
  • Para chips de filtro independentes, use Toggles soltos, não ToggleGroup — chips de filtros raramente são exclusivos.

Accessibility

ConcernComportamento
RolesRoot é group; type="single" faz items virarem role="radio" num roving group.
Keyboard ( ) navegam entre items, Space/Enter alterna.
FocusRoving — só um item no tab order de cada vez.
Labelaria-label no Root + por item (se ícone-only) — ambos obrigatórios.

On this page