Skip to Content

Tabs

Organism

Tabs alternam visões relacionadas sem trocar o objeto principal da página.

Use Tabs quando o operador precisa alternar entre recortes irmãos: visão geral, evidências, histórico, auditoria, fontes ou configurações do mesmo caso. Tabs não devem esconder estado crítico nem substituir filtros operacionais.

Exemplos copiáveis

React

import * as React from "react" type PulsoTab = { id: string; label: string; badge?: string | number; disabled?: boolean; content: React.ReactNode } type PulsoTabsProps = { label: string tabs: PulsoTab[] defaultValue?: string value?: string onValueChange?: (value: string) => void } export function PulsoTabs({ label, tabs, defaultValue, value, onValueChange }: PulsoTabsProps) { const firstEnabledTab = tabs.find((tab) => !tab.disabled)?.id const [internalValue, setInternalValue] = React.useState(defaultValue ?? firstEnabledTab ?? tabs[0]?.id) const selectedValue = value ?? internalValue const selectedTab = tabs.find((tab) => tab.id === selectedValue) ?? tabs[0] function selectTab(tab: PulsoTab) { if (tab.disabled) return setInternalValue(tab.id) onValueChange?.(tab.id) } return ( <div className="grid gap-4"> <div role="tablist" aria-label={label} className="inline-flex w-fit rounded-[var(--radius-lg)] border border-[var(--border)] bg-[var(--muted)] p-1"> {tabs.map((tab) => { const selected = tab.id === selectedValue return ( <button key={tab.id} type="button" role="tab" id={`${tab.id}-tab`} aria-selected={selected} aria-controls={`${tab.id}-panel`} disabled={tab.disabled} tabIndex={selected ? 0 : -1} onClick={() => selectTab(tab)} className={["inline-flex min-h-9 items-center gap-2 rounded-[var(--radius-md)] px-3 text-sm font-medium", selected ? "bg-[var(--card)] text-[var(--fg)] shadow-sm" : "text-[var(--fg-muted)] hover:text-[var(--fg)]"].join(" ")}> {tab.label}{tab.badge ? <span className="rounded-full border border-[var(--border)] px-1.5 py-0.5 font-mono text-[11px]">{tab.badge}</span> : null} </button> ) })} </div> <section role="tabpanel" id={`${selectedTab.id}-panel`} aria-labelledby={`${selectedTab.id}-tab`} className="rounded-[var(--radius-lg)] border border-[var(--border)] bg-[var(--card)] p-4"> {selectedTab.content} </section> </div> ) }
<PulsoTabs label="Visões do caso" defaultValue="overview" tabs={[ { id: "overview", label: "Resumo", content: <p>Estado operacional e próxima decisão.</p> }, { id: "evidence", label: "Evidências", badge: 8, content: <p>Fontes auditáveis.</p> }, ]} />

Regras

  1. Cada aba representa uma visão irmã, não uma ação.
  2. Estado crítico não pode ficar escondido apenas em aba inativa.
  3. Use teclado: setas, Home e End.
  4. Não use Tabs para risk levels; risco é dado operacional.
  5. Badges mostram quantidade; severidade precisa de texto/estado explícito.
Last updated on