Dois níveis: paleta global (valores brutos) e tokens semânticos (significado de uso).
No código, use sempre os tokens semânticos — nunca os hex da paleta diretamente.
Nomes de tokens na UI do DS, snippets de código, identificadores de sistema. Único estilo com tracking 0.
code
12px / 16px · 400 font: mono · tracking: 0
const token = '--color-text-heading';
Foundation
Spacing
Escala baseada em múltiplos de 4px. Todos os paddings, margins e gaps devem usar tokens. Nunca use valores px avulsos.
--space-1
4px
--space-2
8px gap entre ícone e texto, gap entre items de lista
--space-3
12px
--space-4
16px gap horizontal entre avatar e conteúdo
--space-5
20px
--space-6
24px padding lateral de página, padding de cards
--space-8
32px
--space-10
40px
--space-12
48px padding de página (desktop)
Foundation
Border Radius
Escala de arredondamento — apenas múltiplos de 4px (exceto 999px). Botões usam radius-lg (16px).
Inputs e cards usam radius-md (8px).
Indicators usam radius-full (999px).
radius-none0pxdividers, tabelas
radius-sm4pxchips, badges, token labels
radius-md8pxinputs, cards, containers
radius-lg16pxbotões, modais, drawers
radius-2xl24pxstat cards, featured
radius-full999pxavatars, pills, indicators
Foundation
Elevation
Hierarquia de camadas via sombra. Elementos no mesmo plano visual devem ter a mesma elevação.
Não use mais de um nível de elevação em elementos adjacentes.
Sombras têm tom grape (roxo) como cor base, com acento tomato (laranja) nas camadas próximas em md e lg — difusas, sem dureza.
shadow-noneFlat — tabelas, dividers
shadow-smCards, toggle knob, switcher
shadow-mdDropdowns, popovers
shadow-lgModais, drawers, overlays
Foundation
Z-index
Escala de camadas para sobreposição. Nunca use valores z-index arbitrários no código.
Token
Valor
Componentes
Shadow par
Escala
--z-dropdown
100
Dropdowns, Selects, Menus
shadow-md
--z-sticky
200
TopBar, Sidebar, headers fixos
shadow-sm
--z-modal
300
Modal, Dialog, Drawer
shadow-lg
--z-toast
400
Toast, Notificações
shadow-lg
--z-tooltip
500
Tooltip — sempre no topo
shadow-md
Foundation
Size Scale
Altura padrão de controles interativos. Padrão do produto: size-lg (40px) para CTAs, size-md (36px) para inputs e botões secundários.
--size-sm · 32px
32px
Controles compactos, switchers internos
--size-md · 36px
36px
Inputs, selects, botões secundários
--size-lg · 40px
40px
Botões em modais e cards
--size-xl · 44px
44px
Tamanho mínimo de toque (acessibilidade móvel)
Ícones
--icon-sm · 16pxInline em labels, overlines, nav items
--icon-md · 20pxÍcones em botões, inputs, indicators
Espessuras de borda usadas nos componentes. Nunca use valores px avulsos para border-width.
Token
Valor
Uso
Exemplo visual
--border-width-default
1px
Cards, progress bars, dividers, tabs inativos
--border-width-strong
2px
Inputs, tab ativo (underline), chips com foco, estados selecionados
Foundation
Icons
Biblioteca: UIicons Rounded Regular (Freepik) — família usada em todas as telas do produto.
94 ícones em 8 categorias. Catálogo machine-readable em
design-system/registry/tokens/icon-catalog.json.
Rounded Regular — outline com cantos arredondados. Não misturar com outros estilos da família (solid, bold, etc.)
Cor
Herda do elemento pai via currentColor
Tamanho
Usar apenas --icon-sm (16px), --icon-md (20px), --icon-lg (24px)
Acessibilidade
Ícone decorativo: aria-hidden="true". Ícone funcional (sem texto): aria-label obrigatório no elemento pai
Adicionar ícone
Verificar catálogo → buscar glifo no CSS → extrair SVG da fonte → validar visualmente → registrar → checar integridade. Nunca inventar SVG de outra família. Ver procedimento completo em docs/foundations/icons.md.
Navigation — 21 ícones
home
dashboard
list
menu
ellipsis
chevron-left
chevron-right
chevron-up
chevron-down
arrow-right
arrow-up
arrow-down
arrows-up-down
expand
collapse
maximize
minimize
external-link
link
unlink
compass
Actions — 25 ícones
search
filter
sort-up
sort-down
refresh
plus
minus
pencil
trash
copy
paste
scissors
download
upload
floppy-disk
share
print
paperclip
tag
star
star-outline
bookmark
undo
redo
sliders
User — 11 ícones
user
user-plus
user-minus
users
address-card
settings
lock
unlock
key
sign-in
sign-out
Communication — 8 ícones
bell
envelope
message
headset
phone
mic-slash
video
video-slash
Time — 5 ícones
calendar
clock
stopwatch
alarm-clock
history
Files — 9 ícones
file
file-lines
file-arrow-down
file-arrow-up
folder
folder-open
database
cloud
table
Analytics — 10 ícones
chart-bar
chart-line
chart-pie
chart-area
chart-scatter
gauge
heatmap
trend-up
trend-down
funnel
State — 5 ícones
check
xmark
xmark-circle
triangle-exclamation
circle-info
Catálogo machine-readable:design-system/registry/tokens/icon-catalog.json — 94 ícones, 8 categorias, com nome semântico, glifo, SVG e descrição de uso.
Para adicionar novo ícone, seguir o procedimento de 6 etapas em docs/foundations/icons.md.
Os ícones específicos de cada componente estão documentados na página do componente.
Foundation
Logo
Marca LAP — símbolo squircle monocromático. Define tamanhos,
área de proteção e variantes de cor. As cores
seguem tokens de paleta existentes (jasmine, grape, tomato). Abaixo de 32px,
usar a marca simplificada (apenas a pílula, sem glifo interno).
Nunca recrie o símbolo manualmente — use o SVG canônico.
Linhas tracejadas indicam o limite de X em cada lado.
Variantes de cor · 6 combinações permitidas (★ par prioritário)
★ Tomato-500 sobre Jasmine-50Prioritário — marca clara com ênfase CTA
★ Jasmine-50 sobre Tomato-500Prioritário — CTA, hero screens
Jasmine-50 sobre Grape-500Surfaces inverse, identity
Grape-600 sobre Jasmine-50Marca clara alternativa
Jasmine-50 sobre neutro escuroOutros fundos escuros (não-marca)
Grape-600 sobre surfaceOutros fundos claros (não-marca)
Base Component
Button
Core action element of the UI. Three text variants (Primary, Outline, Ghost),
a Chip toggle for multi-select, and two icon-only types (IconButton, ButtonIcon).
Default radius: radius-lg (16px); chips use radius-full (999px).
Use when
The action is the screen's primary CTA (Primary), a secondary alternative (Outline), a dismiss/cancel (Ghost), a multi-select toggle (Chip), or an icon-only action (IconButton / ButtonIcon)
Don't use when
The action navigates between pages — use a link. For binary toggles, use Toggle or SegmentedControl. For decorative labels, use Tag/Badge
Primary
Botão de ação principal da interface. Inclui três variantes —
Primary (fundo sólido), Outline (apenas borda) e
Ghost (apenas texto) — cobrindo toda a hierarquia de CTAs.
Usar Primary para a ação principal de cada tela (um por grupo)
Outline como alternativa secundária visível
Ghost para ações de menor prioridade (cancelar, fechar, voltar)
fullWidth em CTAs de tela cheia no mobile
Manter --icon-sm (16px) para ícones dentro do botão
Ícone decorativo sempre com aria-hidden="true"
Combinar 1 Primary + 1 ou mais Outline/Ghost para hierarquia clara
Mais de 1 Primary por grupo de ações — promover a Outline
Ghost como ação principal — baixa percepção visual
Ícone sem label (usar IconButton para ações só-ícone)
Gradient flash em Outline ou Ghost — exclusivo do Primary
Tamanho menor que --size-sm (32px) — limite mínimo
Cores fora dos tokens do sistema
Misturar ícone à esquerda e à direita no mesmo grupo de botões
Usar <a> estilizado como botão para ações que não navegam
Contexto Escuro
Variantes Outline e Ghost adaptadas para superfícies escuras (--color-surface-inverse).
Usa jasmine-300 para texto e bordas em vez de tomato. O Primary não é alterado no contexto escuro — funciona bem em qualquer superfície.
Demo Interativa
Outline dark
sem ícone
ícone esquerda
ícone direita
disabled
Ghost dark
sem ícone
ícone esquerda
ícone direita
disabled
Variantes & Estados
Outline · dark
default
hover
pressed
focus
disabled
Outline · dark — ícone à esquerda
default
hover
pressed
focus
disabled
Outline · dark — ícone à direita
default
hover
pressed
focus
disabled
Ghost · dark
default
hover
pressed
focus
disabled
Ghost · dark — ícone à esquerda
default
hover
pressed
focus
disabled
Ghost · dark — ícone à direita
default
hover
pressed
focus
disabled
Tamanhos
sm · 32px
md · 36px
lg · 44px
xl · 44px
Mesmos quatro tamanhos do Primary. Ver Primary → Tamanhos para tabela completa de specs.
Anatomia
Cores por variante × estado
Variante
Estado
Background
Texto
Borda
Outline · dark
default
transparent
--color-accent-jasmine
--color-accent-jasmine
hover
transparent
--color-text-on-dark
--color-text-on-dark
pressed
transparent
--color-accent-jasmine
--color-accent-jasmine
focus
transparent
--color-accent-jasmine
--color-accent-jasmine
disabled
transparent
--color-text-disabled
rgba(--color-text-on-dark, 0.15)
Ghost · dark
default
transparent
--color-accent-jasmine
transparent
hover
transparent
--color-text-on-dark
transparent
pressed
transparent
--color-accent-jasmine
transparent
focus
transparent
--color-accent-jasmine
transparent
disabled
transparent
--color-text-disabled
transparent
Micro-interações
Nome
Trigger
Propriedade
De → Para
Entrada
Retorno
Reduced Motion
radius_morph
hover
border-radius
--btn-radius → --radius-md
--motion-duration-expressive (800ms)
--motion-duration-slow (400ms)
instantâneo
opacity_shift
hover
opacity
1 → 0.5
--motion-duration-expressive (800ms)
--motion-duration-slow (400ms)
instantâneo
Tokens consumidos
Token
Valor
Uso
--color-surface-inverse
#6B4F74 · grape-500
Superfície de fundo (contexto)
--color-accent-jasmine
#FFE9A8 · jasmine-200
Texto e borda dark context (outline, ghost, chip, IconButton, ButtonIcon)
Usar context="dark" quando o botão estiver sobre --color-surface-inverse ou superfícies grape
Usar jasmine-200 (--color-accent-jasmine) para bordas e texto em dark context — warm cream sobre grape
Garantir visibilidade do focus ring (tomato-500) sobre fundo escuro
Testar contraste do estado disabled na superfície escura
Não usar Primary no contexto escuro — Primary funciona em qualquer superfície sozinho
Não usar texto tomato em superfícies escuras (baixo contraste) — usar jasmine
Não hardcodear valores hex de jasmine; usar --color-accent-jasmine
Não misturar botões de contexto claro e escuro na mesma linha
Chip
Pills de toggle multi-seleção para filtros, seleção de estilo e segmentação em formulários.
Chips inativos usam tons grape; ativos (selected) usam --color-surface-inverse com texto jasmine.
Dois tamanhos: sm (32px) para palavras e frases curtas, lg (44px) exclusivamente para siglas de 1–3 letras (ex: P, M, G, SP, RJ, Sim, Não).
Não confundir com Tag/Badge — chips são interativos, tags são decorativos.
Demo Interativa
Fundo claro
Fundo escuro
Variantes & Estados
Estados — fundo claro
inativo
selecionado
focus
disabled
Estados — fundo escuro
default
selecionado
focus
disabled
Chip Dismissible — fundo claro
Corridadefault
Nataçãodefault
Ciclismodisabled
Chip Dismissible — fundo escuro
Corridadefault
Nataçãodefault
Ciclismodisabled
Tamanhos
sm · 32px
lg · 44px
Chip usa apenas dois tamanhos: --size-sm (32px) para palavras e frases curtas, e --size-xl (44px) exclusivamente para siglas de 1–3 letras (P, M, G, SP, RJ, Sim, Não). Fonte 12px uppercase bold (sm) e 14px uppercase bold (lg).
Usar chips para multi-seleção (grupos de filtro, seleção de estilo)
Sempre definir aria-pressed={selected} para acessibilidade
Agrupar chips relacionados com espaçamento consistente
Usar sm para barras de filtro compactas, lg para onboarding espaçoso
Não usar chips para seleção única — usar SegmentedControl
Não usar chip como CTA principal
Não misturar sm e lg no mesmo grupo
Não omitir aria-pressed — chips são toggles
IconButton
Ícone sem container — sem fundo, sem borda. Cor padrão: --color-text-accent (grape-400).
Usado em navegação, ações secundárias e controles inline. Dois tamanhos: md (ícone 20px) e sm (ícone 16px).
Demo Interativa
Fundo claro
enabled
disabled
Fundo escuro
enabled
disabled
Variantes & Estados
Estados — fundo claro
default
hover
pressed
focus
disabled
Estados — fundo escuro
default
hover
pressed
focus
disabled
Tamanhos
md · icon 20px · toque 36px
sm · icon 16px · toque 32px
Dois tamanhos de ícone. Padding: --space-2 (8px) em ambos. Alvo de toque md: 36px, sm: 32px.
Sempre fornecer aria-label descrevendo a ação ("Editar item", "Voltar")
Usar em navegação (voltar, fechar) e ações secundárias inline
Posicionar em toolbars, cabeçalhos de card e ações de lista
Usar sm para áreas compactas, md como padrão
Não usar como CTA principal — baixa affordance visual
Não omitir aria-label — botões só-ícone são inacessíveis sem ele
Não adicionar fundo ou borda — isso é território do ButtonIcon
Não misturar tamanhos md e sm no mesmo grupo de ações
ButtonIcon
Botão com ícone preenchido para ações principais sem label textual.
Fundo --color-primary (tomato-500), ícone --color-text-on-primary (jasmine-50),
radius --radius-lg (16px). Dois tamanhos: lg (48×48px) e md (36×36px).
Usar como botão de ação flutuante ou destaque isolado
lg para CTAs principais, md para ações em espaços compactos
Usar context="dark" sobre superfícies grape
Não usar sem aria-label
Não esperar gradient flash — exclusivo do Primary
Não usar para navegação — isso é papel do IconButton
Não adicionar label textual — usar Button regular se precisar de texto
Seção completa — Button
Props & API
Interface unificada de props para todos os tipos de botão. Props específicas de Chip e icon-buttons estão marcadas.
Button
Prop
Type
Default
Description
variant
'primary' | 'outline' | 'ghost' | 'chip'
'primary'
Visual variant
size
'sm' | 'md' | 'lg' | 'xl'
'xl'
Button height — maps to size tokens (32/36/40/44px)
context
'light' | 'dark'
'light'
Background context — changes colors for outline and ghost
fullWidth
boolean
false
If true, width: 100% — use for full-screen CTAs
disabled
boolean
false
Disables interaction and applies muted style
icon
ReactNode
—
Icon alongside label (16px — icon-sm)
iconPosition
'left' | 'right'
'left'
Icon position relative to label
selected
boolean
false
Chip only — indicates selected item
chipSize
'sm' | 'lg'
'sm'
Chip only — sm (32px) ou lg (44px)
onClick
() => void
—
Click handler
IconButton
Prop
Type
Default
Description
icon
ReactNode
—
Elemento ícone (md: 20px, sm: 16px)
label
string
—
Obrigatório.aria-label descrevendo a ação
size
'md' | 'sm'
'md'
Tamanho do ícone — md = --icon-md (20px), sm = --icon-sm (16px)
context
'light' | 'dark'
'light'
Background context
disabled
boolean
false
Disables interaction
onClick
() => void
—
Click handler
ButtonIcon
Prop
Type
Default
Description
icon
ReactNode
—
Elemento ícone (16px — icon-sm)
label
string
—
Obrigatório.aria-label descrevendo a ação
size
'lg' | 'md'
'lg'
Tamanho do container — lg = 48×48px, md = 36×36px
context
'light' | 'dark'
'light'
Contexto — dark usa outline jasmine sobre fundo grape
disabled
boolean
false
Desabilita interação
onClick
() => void
—
Handler de clique
Acessibilidade
Requisitos compartilhados de acessibilidade para todos os sub-componentes de botão.
Semantics & ARIA
Base element
Always <button> — never <div> or <span> with click handlers
IconButton / ButtonIcon
aria-label required, describing the action (e.g. aria-label="Edit item")
Decorative icon
Icon inside button with text label: add aria-hidden="true" to the icon element
Disabled state
Use native disabled attribute — not just visual styling
Chip
aria-pressed={selected} required — chips are toggle buttons
Focus visible
2px --color-border-focus ring via :focus-visible. Offset: 3px (buttons with label), 2px (chip, icon-button)
Keyboard
Key
Action
Enter
Activate button (fire onClick)
Space
Activate button (fire onClick)
Tab
Move focus to next focusable element
Shift+Tab
Move focus to previous focusable element
Gaps Mapeados
Gap
Status
Decisão
Contraste primary (tomato-500 / jasmine-50)
aceito
2.8:1 — abaixo de WCAG AA 4.5:1. Decisão de marca: tomato-500 é identidade não-negociável. Mitigação: label sempre descritiva; botão nunca é único meio de completar ação crítica.
Loading state — feedback para leitores de tela
aberto
Estado não definido. Implementar: aria-busy="true" no botão + spinner com aria-label + aria-live="polite" no container de resultado.
Base Component · stable · v1.8.0
TextInput
Campo de entrada de texto de linha unica. Coleta dados textuais em formularios, filtros, buscas e edicao.
Radius morph md (8px) → lg (12px) no foco — oposto do Button.
3 tamanhos, bordas acessiveis, trailing area com cascata de prioridade built-in.
Usar quando
Nome, email, telefone, CPF, URL, senha — qualquer valor textual curto que cabe em uma linha
Nao usar quando
Texto multilinha → Textarea · Escolha entre opcoes → Select · Busca com sugestoes → TextInput[variant=search] ou Combobox · Numerico com incremento → NumberInput · Data → DatePicker
Demo Interativa
R$
Formato: (11) 99999-9999
Corrida leve
Corrida intervalada
Corrida longa
CORRIDANATAÇÃO
Ciclismo
Ciclismo indoor
Remo
Ao focar: border-radius transiciona de radius-md (8px) → radius-lg (12px) em 250ms easing-expressive.
Ao blur: retorna a radius-md em 150ms — sempre, independente de ter valor.
Default e lg (52px). Inputs sao maiores que buttons no mesmo "size": TextInput sm = 40px vs Button sm = 32px.
Anatomia
Propriedade
sm
md
lg (default)
Altura
40px
44px
52px
Padding horizontal
12px
16px
16px
Font input
12px/16px 400
14px/20px 400
16px/24px 400
Font label
12px/16px 600
14px/20px 600
16px/20px 600
Icon size
16px (--icon-sm)
20px (--icon-md)
20px (--icon-md)
Icon-text gap
8px (--ti-icon-gap)
Label-container gap
4px (--ti-label-gap)
Container-helper gap
4px (--ti-helper-gap)
Parte
Obrigatoria
Notas
label
Sim
Sempre visivel, externa, vinculada via for/id. Nunca substituir por placeholder.
container
Sim
Area clicavel com borda, radius, altura por size.
input
Sim
Elemento <input> nativo.
placeholder
Nao
Exemplo de valor esperado — nunca repete a label.
leading_icon
Nao
Icone a esquerda. aria-hidden="true". 20px (lg/md) ou 16px (sm).
trailing_area
Nao
Area a direita com cascata: error icon > loading > password toggle > clear > custom.
prefix
Nao
Texto fixo antes do input (ex: "R$").
suffix
Nao
Texto fixo apos o input (ex: "kg").
helper_text
Nao
Texto auxiliar abaixo. Substituido por error/warning quando ativos.
character_counter
Nao
Contagem atual/max. Ativado via maxLength. Cor muda proximo ao limite.
Cascata de prioridade — trailing area:
Error icon — feedback critico, sempre visivel
Loading spinner — bloqueia interacao
Password toggle — funcional, sempre visivel em type=password
Clear button — conveniencia, so com valor
Custom trailing — slot generico, menor prioridade
Excecao: password toggle + clear button podem coexistir.
Estado
Border
Radius
Notas
default
#8B7194 grape-300 (4.2:1)
radius-md (8px)
—
hover
#7B5F85 grape-400 (5.3:1)
radius-md
—
focus
#7B5F85 grape-400
radius-lg (12px)
+ focus ring tomato-500
filled
#7B5F85 grape-400
radius-md
Volta ao md no blur
error
#A91E15 danger (7.0:1)
radius-md
+ error icon no trailing
warning
#6B530B warning (7.1:1)
radius-md
Nao bloqueia submit
disabled
#D0C4D5 grape-100
radius-md
opacity 0.38 + bg state-disabled
read-only
#8B7194 grape-300
radius-md
bg state-disabled
loading
#8B7194 grape-300
radius-md
Spinner no trailing, read-only
Micro-interacao
Propriedade
De → Para
Duracao
Easing
Radius morph
border-radius
radius-md (8px) → radius-lg (12px)
250ms in / 150ms out
easing-expressive
Border color
border-color
grape-300 → grape-400
150ms
easing-standard
Clear button fade
opacity
0 → 1
150ms
easing-standard
Counter color shift
color
text-subtle → warning → danger
150ms
easing-standard
Todas as micro-interacoes respeitam prefers-reduced-motion: reduce — mudanca instantanea.
Radius morph e oposto do Button: input "abre" ao focar (md→lg), button "fecha" ao hover (lg→md).
Token
CSS Variable
Valor
Uso
--ti-border-default
text-input.border-default
#8B7194
Borda repouso
--ti-border-active
text-input.border-active
#7B5F85
Borda foco/filled
--ti-border-error
text-input.border-error
#A91E15
Borda error
--ti-border-warning
text-input.border-warning
#6B530B
Borda warning
--ti-border-disabled
text-input.border-disabled
#D0C4D5
Borda disabled
--ti-height-sm
text-input.height-sm
40px
Altura sm
--ti-height-md
text-input.height-md
44px
Altura md
--ti-height-lg
text-input.height-lg
52px
Altura lg
--ti-padding-x-sm
text-input.padding-x-sm
12px
Padding sm
--ti-padding-x-md
text-input.padding-x-md
16px
Padding md
--ti-padding-x-lg
text-input.padding-x-lg
16px
Padding lg
--radius-md
semantic.radius.md
8px
Radius default
--radius-lg
semantic.radius.lg
12px
Radius foco
--motion-duration-normal
semantic.motion.duration-normal
250ms
Morph in
--motion-duration-fast
semantic.motion.duration-fast
150ms
Morph out / border
Do / Don't
Sempre forneca uma label visivel acima do campo
Valide ao blur — nao durante digitacao
Remova o erro assim que o campo for corrigido
Use placeholder para exemplo de valor (ex: joao@email.com)
Respeite prefers-reduced-motion
Use type="password" — toggle e built-in
Use os 3 tamanhos: sm para toolbars, md para formularios densos, lg para formularios padrao
Nao use placeholder como unica label do campo
Nao use props proibidas: tone, kind, variant, style, className, autoFocus
Nao recrie o password toggle manualmente — use type="password"
Nao use hex hardcoded — use tokens
Nao use mensagens genericas como "Valor invalido"
Nao combine loading + disabled — estados contraditorios
Sempre vinculada via <label for=[id]>. Sempre visivel. Nunca so placeholder.
Error
aria-invalid="true" + aria-describedby → id da mensagem + role="alert"
Warning
aria-describedby → id da mensagem. Sem aria-invalid.
Required
aria-required="true" no input + asterisco visual na label.
Password toggle
<button> com aria-label dinamico ("Mostrar senha" / "Ocultar senha").
Clear button
<button aria-label="Limpar campo">. Foco retorna ao input apos clear.
Focus ring
2px solid tomato-500, offset 2px. Sempre visivel em :focus-within.
Keyboard
Tab: foco no campo. Escape: limpa se clearable. Enter: submit em form.
Touch target
lg=52px (AAA), md=44px (AAA), sm=40px (AA).
Contraste
Ratio
Nivel
Border default (grape-300 vs branco)
4.2:1
AA
Border focus (grape-400 vs branco)
5.3:1
AA
Border error (danger vs branco)
7.0:1
AAA
Border warning (warning vs branco)
7.1:1
AAA
Texto input (charcoal-600 vs branco)
8.2:1
AAA
Placeholder (charcoal-400 vs branco)
3.3:1
AA-UI
Variante Search — a11y contract
Com sugestoes
role="combobox" no input + aria-expanded + aria-autocomplete="list" + aria-activedescendant → id da opcao em foco
Dropdown
role="listbox"; opcoes com role="option" + aria-selected
Teclado
ArrowDown/Up: navega opcoes · Enter: seleciona · Escape: fecha dropdown · Tab: sai do campo
Clear button
aria-label="Limpar busca"; foco retorna ao input apos clear
Variante Filter Chip — a11y contract
Input
role="combobox" + aria-expanded + aria-autocomplete="list". Sem icone de busca — placeholder comunica a acao.
Chip remove
<button aria-label="Remover [label]"> dentro de cada chip. Foco retorna ao input apos remocao.
Dropdown
Mesmo contrato do Search: role="listbox" + role="option"
Encaixe visual
radius-lg nas pontas externas (topo do campo + base do dropdown), radius-md na juncao. Gap de 1px (--border-width-default). Ref: registry/patterns/filter-chip.md
Gaps Mapeados
Gap
Status
Decisão
gap-001: Variante Filled
wont-implement
Sem demanda de produto. O outlined com radius-morph e a assinatura visual do LAP DS.
gap-002: Label floating
wont-implement
Decisao de a11y: label sempre visivel acima do campo. Floating label cria ambiguidade entre label e placeholder.
gap-003: FormField pattern
deferred
Aguarda reconstrucao do Textarea. Extrair o pattern label + input + helper quando Textarea for reescrito.
gap-004: Estado success
wont-implement
Antipadrao: validacao positiva acontece no submit, nao por campo. Use helperText com --color-success-text pos-submit se necessario.
gap-005: Mascara de input
deferred
Sera addon ou lib externa (IMask, Cleave). Demo de telefone no showcase serve como referencia de implementacao.
Base Component · beta
Textarea
Campo de texto multi-linha. Espelha o TextInput em tokens, labels e mensagens de estado —
sem ícone leading, sem trailing action, sem prefix/suffix. Suporta contagem de caracteres
e auto-resize via JS.
Demo Interativa
<Textarea
id="observations"
label="Observações"
placeholder="Descreva sua rotina de treino..."
helperText="Informações compartilhadas com seu treinador"
maxLength={400}
showCounter
rows={4}
/>
Variantes & Estados
rest
focused
filled
error
warning
disabled
read-only
with counter
counter · warn
Tamanhos
density controla padding vertical e tamanho de fonte — não o número de linhas. Use a prop rows para controlar a altura.
lg (default) · font 16px · padding-v 14px
md · font 14px · padding-v 10px
sm · font 12px · padding-v 8px
Anatomia
Density
Font
Padding-v
Padding-h
lg (default)
16px
14px
--ti-padding-x-lg (16px)
md
14px
10px
--ti-padding-x-md (16px)
sm
12px
8px
--ti-padding-x-sm (12px)
Parte
Obrigatória
Notas
.ta-field
Sim
Container externo. Label + wrapper + footer.
.ta-label
Não
Mesmo padrão do TextInput. Se omitido, obrigatório aria-label.
.ta-wrapper
Sim
Container da borda com focus stroke draw. border-radius: --radius-md — sempre retangular.
.ta-input
Sim
<textarea> nativo. resize: vertical por padrão; is-autoresize desabilita resize.
.ta-footer
Não
Row com helper (esquerda) e counter (direita).
.ta-counter
Não
Atualizado via JS no oninput. Muda cor: is-warn (80%+) · is-limit (100%).
Auto-resize em SSR (Next.js): scrollHeight só disponível no browser. Pode causar layout shift no hidration.
aberto
Documentar no componente React que auto-resize requer useEffect e não funciona durante SSR. Valor inicial de rows deve ser suficiente para evitar CLS.
Resize handle visual customizado — o handle nativo do browser varia por OS (iOS/Android/Desktop).
wont-implement
CSS não tem acesso confiável ao ::webkit-resizer cross-browser. Handle nativo é aceitável como comportamento de plataforma.
Base Component · beta
Checkbox
Permite ao usuário selecionar ou desmarcar uma opção de forma independente.
Usa forma de polígono — identidade visual do DS. Suporta indeterminate para grupos hierárquicos.
Borda unchecked abaixo de 3:1 — grape-200 vs branco (2.07:1). WCAG 1.4.11 requer 3:1 para UI components.
aceito
Identidade visual: grape-200 é a borda de repouso do sistema. Alternativa (grape-400) torna o componente excessivamente pesado em contexto de lista. Exceto quando comprovado por testes com usuários.
CheckboxGroup sem componente dedicado — comportamento de grupo (herança de estado indeterminate, navegação Tab) depende de implementação no consumer.
deferred
Deferred para iteração FormField — será resolvido junto com validação de grupos em formulário.
Base Component · beta
Radio
Permite ao usuário escolher exatamente uma opção de um grupo mutuamente exclusivo.
Sempre usado em um RadioGroup — nunca isolado.
Inclui variante Card para opções com conteúdo rico.
Borda outer circle unchecked (grape-200) abaixo de 3:1 — mesma decisão do Checkbox.
aceito
Idem Checkbox: identidade visual. Borda ativa (tomato-500) excede 3:1 quando selecionado.
Keyboard Arrow keys em RadioGroup precisam de JS — o comportamento nativo de browser varia (alguns implementam, outros não).
aberto
Implementação JS no RadioGroup component obrigatória para garantir comportamento consistente cross-browser. A referência é o padrão APG ARIA radiogroup.
Variante Card: sem interação de teclado documentada para o card como touch target.
aberto
O input nativo dentro do card herda o comportamento do RadioGroup padrão. Validar com screen reader em testes de usabilidade.
Base Component · beta
Select
Campo de seleção de opções via dropdown. Compartilha tokens e linguagem visual com o
TextInput — pill em repouso, rect ao abrir, mesma borda e shadow.
Suporta seleção simples e múltipla, modo busca e loading assíncrono.
Select usa --size-input-height: 52px — equivalente ao TextInput [lg].
Para contextos de formulário com densidade diferente, alinhe com o TextInput da mesma tela.
Corrida
height 52px · font 16px
Anatomia
Parte
Obrigatória
Notas
.sel-field
Sim
Container. Label + trigger + footer messages.
.sel-trigger
Sim
Pill (999px) em repouso, rect bottom (16px) ao abrir. Height: --size-input-height (52px).
.sel-value
Sim
Texto do valor selecionado ou placeholder.
.sel-arrow
Sim
Chevron — rotate 180° ao abrir.
.sel-clear
Não
Botão × — exibido quando há valor e prop clearable.
.sel-search-input
Não
Input de busca — substitui .sel-value quando searchable.
.si-dropdown
Sim
Lista de opções — reutiliza classe do TextInput. Rect top, lg bottom. Gap --dropdown-gap.
role="combobox" · aria-haspopup="listbox" · aria-expanded · aria-controls → dropdown ID
Dropdown
role="listbox"
Opção
role="option" · aria-selected
Multi tags
Cada tag deve ter aria-label descrevendo o valor · botão × deve ter aria-label="Remover [valor]"
Estado error
aria-invalid="true" no trigger + aria-describedby → mensagem de erro
Tecla
Ação
Enter / Space
Abre/fecha dropdown quando trigger está focado
↓ / ↑
Navega entre opções no dropdown aberto
Enter
Seleciona opção destacada
Escape
Fecha dropdown sem selecionar
Backspace
Remove a última tag (multi select)
Gaps Mapeados
Gap
Status
Decisão
Virtualização de listas longas (500+ opções) — sem suporte nativo ao virtual scroll.
deferred
Deferred para iteração de performance. Workaround atual: usar async com paginação ou busca para reduzir volume.
Keyboard navigation no Select não é implementada pelo showcase estático — requer JS no componente React.
aberto
Implementar keyboard handler no componente seguindo APG Combobox. Teclado é requisito de acessibilidade (WCAG 2.1.1 Keyboard).
creatable: UX de "criar novo" não definida visualmente — como distinguir "criar X" de "selecionar X"?
aberto
Proposta: opção especial no topo do dropdown com ícone de add e texto "Criar "[query]"". A ser validada em usabilidade antes de implementar.
Multi select sem limite de tags — campo pode crescer indefinidamente.
deferred
Deferred para iteração de FormField. Solução: prop maxSelections + indicador "+N mais".
Base Component · ds.divider
Divider
Separador visual que divide conteúdo em seções. Puramente presentacional — não tem estados de interação. Use <hr> para separações semânticas e <div role="separator"> para visuais decorativas.
Demo interativa
Seção de conteúdo A — aqui vai um bloco de texto ou formulário separado visualmente das demais seções.
Seção de conteúdo B — o divider acima marca visualmente a transição entre blocos de conteúdo.
ou
Seção de conteúdo C — o divider com label centra um texto entre as duas linhas.
Variantes
horizontal · default
horizontal · strong
horizontal · inset
horizontal · inset both
com label
Seção
com label · "ou"
ou
vertical
EsquerdaDireita
vertical · strong
EsquerdaDireita
Anatomia
Parte
Obrigatória
Notas
.dv (<hr> ou <div>)
Sim
Elemento base. Use <hr> para separações com significado semântico.
.dv-label-wrap
Não
Container flex quando há label. Envolve dois .dv e um .dv-label-text.
.dv-label-text
Condicional
Texto centralizado na linha (ex: "ou", "Seção"). Apenas na variante com label.
Props
Prop / Modificador
Tipo
Default
Notas
orientation
enum
horizontal
Horizontal: .dv · Vertical: .dv.dv-vertical
variant
enum
default
default · strong — controla espessura de cor
extension
enum
full
full · inset · inset-both
label
string
—
Texto centralizado na linha; ativa wrapper .dv-label-wrap
decorative
boolean
false
Se true, aplica aria-hidden="true"
Tokens consumidos
Token
Valor
Uso
--divider-thickness
1px
Espessura da linha
--divider-color
var(--color-border)
Cor padrão
--divider-color-strong
var(--color-border-strong)
Cor variante strong
--divider-spacing-y
var(--space-4)
Margem vertical (topo e base)
--divider-inset
var(--space-6)
Recuo para variante inset
--divider-label-gap
var(--space-3)
Gap entre linha e texto
--divider-label-font-size
12px
Tamanho do texto do label
--divider-label-color
var(--color-text-subtle)
Cor do texto do label
Acessibilidade
Semântico
<hr> implica role="separator". Para <div>: adicionar role="separator" manualmente.
Decorativo
aria-hidden="true" quando a separação é apenas visual e não adiciona semântica.
Orientação
aria-orientation="horizontal|vertical" em elementos com role="separator" não-<hr>.
Label
O texto label não requer atributo extra — está no DOM e é lido pelos screen readers.
Contraste
--divider-color (grape-100) vs fundo branco: contraste gráfico 3:1 ✓ AA (WCAG 1.4.11)
Fazer / Não Fazer
✓ Do
Use <hr> quando a separação tem significado semântico
Adicione aria-hidden="true" em dividers puramente decorativos
Use dv-strong para separações que precisam de mais contraste
Use o label "ou" entre opções alternativas de ação
✗ Don't
Não use para organizar conteúdo que deveria estar em seções separadas — use layout
Não sobrecarregue com dividers frequentes — use espaçamento entre blocos primeiro
Não use como borda de componentes — use border no componente
Não misture orientações em um mesmo contexto sem propósito claro
Base Component · ds.toggle
Toggle
Controla um estado binário on/off de efeito imediato — sem necessidade de confirmação. Use quando a mudança é aplicada instantaneamente (ex: ativar notificações, habilitar modo escuro).
Demo interativa
Estados
off · default
on · default
off · hover
on · hover
off · focus
on · focus
off · disabled
on · disabled
Anatomia
Parte
Obrigatória
Notas
.tg-item (<label>)
Sim
Container clicável; associa o track com a label textual
.tg-input (<input type="checkbox" role="switch">)
Sim
Oculto visualmente; mantém estado e acessibilidade nativos
.tg-track
Sim
Pílula visual 52×30px; recebe a cor on/off
.tg-knob
Sim
Círculo branco 24px; se move via translateX(22px)
Label textual (<span>)
Recomendado
Descreve o que o toggle controla — sempre presente exceto quando o contexto é inequívoco
Props
Prop
Tipo
Default
Notas
checked
boolean
false
Estado on/off controlado
defaultChecked
boolean
false
Estado inicial não controlado
disabled
boolean
false
Desabilita interação; aplica is-disabled no item
onChange
function
—
Callback ao mudar estado
aria-label
string
—
Obrigatório quando não há label textual visível
Tokens consumidos
Token
Valor
Uso
--color-primary
#FE674C
Track no estado on
--color-border-strong
#9E7BA2
Track no estado off
--color-border-focus
#FE674C
Outline de foco visível
--color-text-default
#433E3D
Cor da label textual
Acessibilidade
Elemento base
<input type="checkbox" role="switch"> nativo — nunca div com role="switch"
Teclado
Space: alterna on/off · Tab: move foco
Foco visível
Ring tomato 2px offset 2px no track · não remover
Sem label visível
aria-label obrigatório no <input>
Estado disabled
disabled nativo no input + is-disabled no item para opacity
Use quando a ação é imediata e não requer confirmação
Descreva o que é ativado, não o estado (ex: "Notificações", não "Ativo")
Use label textual sempre que possível
Pré-defina o estado padrão que faz mais sentido para o contexto
✗ Don't
Não use para escolha entre duas opções — use ds.segmented-control
Não use quando a ação precisa de confirmação — use Button + Modal
Não use para seleção múltipla — use ds.checkbox
Não omita label quando o contexto não for inequívoco
Base Component · ds.tag
Tag
Rótulo de categorização, status ou atributo associado a um item. Não-interativo por padrão. Variantes interativas (selectable, dismissible) permitem filtros e seleção múltipla. Diferente do Badge — que é um indicador numérico sobreposto.
Demo interativa
Categorias semânticas
NeutroConcluídoPendenteErroInfoPrimary
Categorias de marca
DestaqueEm análiseBeta
Selectable — filtros
Dismissible
ReactEm análiseBeta
Variantes & Estados
neutral · default
Label
neutral · com ícone
Label
neutral · disabled
Label
success · default
Concluído
success · com ícone
Concluído
success · disabled
Concluído
warning · default
Pendente
warning · com ícone
Pendente
warning · disabled
Pendente
error · default
Erro
error · com ícone
Erro
error · disabled
Erro
info · default
Info
primary · default
Primary
jasmine · default
Destaque
teal · default
Em análise
lavender · default
Beta
selectable · off
selectable · selected
selectable · hover
selectable · focus
dismissible
Label
dismissible · disabled
Label
Tamanhos
smmd (padrão)lg
Anatomia
Parte
Obrigatória
Notas
.tag (<span> ou <button>)
Sim
Container pill. Use <span> para não-interativo, <button> para selectable/clickable.
UIicons Rounded Regular · 12px · somente à esquerda do texto · aria-hidden="true"
.tag-dismiss (<button>)
Condicional
Botão × para variante dismissible. Deve ter aria-label="Remover {label}".
Props
Prop
Tipo
Default
Notas
category
enum
neutral
Esquema de cor semântico ou de marca
variant
enum
default
default · selectable · dismissible · clickable
size
enum
md
sm · md · lg
selected
boolean
false
Estado de seleção (variant selectable). Aplica is-selected.
disabled
boolean
false
Aplica is-disabled no container
icon
ReactNode
—
UIicons Rounded Regular · somente à esquerda
onDismiss
function
—
Callback do botão × (variant dismissible)
onClick
function
—
Callback para variants selectable/clickable
Props proibidas:color · tone · kind · borderRadius · shape — use sempre category.
Tokens consumidos
Token
Valor
Uso
--tag-font-size
12px
Tamanho padrão (md)
--tag-padding-v
3px
Padding vertical
--tag-padding-h
var(--space-2)
Padding horizontal
--tag-radius
var(--radius-full)
Border radius pill
--tag-border-width
1px
Borda (transparente por padrão; visível em is-selected)
--tag-icon-size
12px
Tamanho dos ícones
--tag-dismiss-size
14px
Tamanho do botão ×
--tag-group-gap
var(--space-2)
Gap entre tags em grupo
Acessibilidade
Não-interativo
<span> com role="status" quando comunica estado dinâmico.
Selectable
<button> com role="button" e aria-pressed="true|false".
Dismissible
O botão × deve ter aria-label="Remover {label da tag}".
Foco visível
Ring tomato 2px offset 2px — obrigatório para variants interativas.
Ícone
aria-hidden="true" sempre — o texto label descreve o conteúdo.
Fazer / Não Fazer
✓ Do
Use para comunicar status, categoria ou atributo de forma passiva
Mantenha o texto curto — 1 a 3 palavras
Escolha a categoria pela semântica do conteúdo (success=positivo, error=problema)
Use <button> para variants interativas, não <span>
✗ Don't
Não use tag para ações primárias — use ds.button
Não coloque ícone à direita — somente à esquerda (exceto dismiss)
Não misture categorias de cor sem critério semântico
Não use tag para contadores numéricos — use ds.badge
Base Component · ds.avatar
Avatar
Representa visualmente uma pessoa ou entidade. Retângulo portrait com cantos arredondados — forma característica do produto. Suporta foto, iniciais e ícone-fallback. sm é circular (pessoas em listas densas); md/lg/xl é square com raio generoso.
Demo interativa
Conteúdo — foto, iniciais, ícone
Foto
IB
Iniciais
Fallback
Com status de presença
IB
Online
AM
Ausente
CB
Offline
Grupo empilhado
IB
AM
CB
+2
Variantes & Estados
Paleta de iniciais
IB
Grape
AM
Jasmine
CB
Teal
DS
Lavender
Muted
Status de presença
JR
Online
AM
Ausente
CB
Offline
Tamanhos
IB
sm · 48px · círculo
IB
md · 88px · r2xl
IB
lg · 112px · r2xl
IB
xl · 140px · r2xl
Anatomia
Parte
Obrigatória
Notas
.av
Sim
Container portrait. overflow: hidden. Dimensões definidas pelo modificador de tamanho.
<img>
Condicional
Quando há foto. object-fit: cover; object-position: center top. Alt descritivo obrigatório.
.av-initials
Condicional
Fallback sem foto. 2 letras maiúsculas. ExtraBold Italic — estilo característico do produto. Sempre aria-hidden="true".
.av-icon
Condicional
Fallback quando não há foto nem nome. UIicons Rounded Regular.
.av-status
Não
Dot de presença posicionado no canto superior direito. Sempre com role="img" aria-label="[estado]".
.av-group
Não
Container flex com sobreposição negativa. role="group" aria-label="N participantes".
Props
Prop
Tipo
Default
Notas
size
enum
md
sm (48px · círculo) · md (88px · r2xl) · lg (112px) · xl (140px)
src
string
—
URL da foto. Se ausente: initials → icon fallback.
alt
string
obrigatório se src
Texto alternativo para <img>
initials
string
—
2 letras derivadas do nome. Obrigatório quando não há foto e o nome é conhecido.
color
enum
grape
grape · jasmine · teal · lavender · muted
status
enum
—
online · away · offline — exibe dot de presença
Tokens consumidos
Token
Valor
Uso
--av-size-sm/md/lg/xl
48/88/112/140px
Dimensão do container por tamanho
--av-initials-sm/md/lg/xl
18/32/40/52px
Font-size das iniciais por tamanho
--radius-full
9999px
Border radius sm (círculo)
--radius-2xl
24px
Border radius md/lg/xl (square)
--color-surface-inverse
#6B4F74
Fundo grape
--color-jasmine / --color-teal / --color-lavender
—
Fundos de marca
--av-status-size
10px
Diâmetro do dot de status
--av-status-border
2px
Borda branca do dot de status
--av-group-overlap
-12px
Sobreposição entre avatares no grupo
Acessibilidade
Foto
<img alt="Nome da pessoa"> — obrigatório. Se decorativa: alt="".
Iniciais
aria-hidden="true" no .av-initials. Informação do nome no title do container.
Status dot
role="img" aria-label="Online|Ausente|Offline" no .av-status.
Grupo
role="group" aria-label="N participantes" no .av-group. Cada avatar com title.
Overflow (+N)
title="N outros" no avatar overflow. Texto aria-hidden="true".
Fazer / Não Fazer
✓ Do
Use iniciais quando o nome é conhecido — mais pessoal que ícone genérico
Forneça alt descritivo em fotos
Adicione title no container de iniciais para acessibilidade
Use .av-group com aria-label para grupos
✗ Don't
Não omita alt em avatares com foto
Não use mais de 2 letras nas iniciais
Não misture tamanhos em um mesmo grupo empilhado
Não exiba status sem confirmar que a informação é confiável
Base Component · ds.spinner
Spinner
Indicador de carregamento indeterminado — duração desconhecida. Forma squircle característica do produto. Arco tomato desenha 60% do perímetro em loop; pontas arredondadas jamais se encontram. Para operações com progresso mensurável use ProgressBar.
Demo interativa
Fundo claro
sm
md
lg
xl
Fundo escuro (grape)
Variantes & Estados
light
dark
Tamanhos
Modificador
Dimensão
Espessura do traço
Uso recomendado
.sp-sm
20×24
2.5px
Inline em botões, inputs
.sp-md
28×34
2px
Padrão. Seções, cards
.sp-lg
40×48
2px
Loading de áreas maiores
.sp-xl
56×67
2px
Loading de página inteira, splash
Anatomia
Parte
Obrigatória
Notas
.sp (<div>)
Sim
Container com role="status" e aria-label.
<svg viewBox="0 0 40 48">
Sim
Squircle portrait. Dois paths sobrepostos: track + arc.
.sp-track
Sim
Path inferior, estático, grape-100.
.sp-arc
Sim
Path superior, tomato, pathLength="100" + stroke-dasharray: 60 40, gira 360° em loop com motion-easing-expressive.
.sp-dark
Não
Clareia o track para fundo escuro; arco permanece tomato.
Props
Prop
Tipo
Default
Notas
size
enum
md
sm · md · lg · xl
context
enum
light
light · dark
label
string
"Carregando"
Texto para aria-label.
Tokens consumidos
Token
Valor
Uso
--palette-grape-100
#D0C4D5
Stroke do track (estático)
--color-primary
#FE674C
Stroke do arco (tomato)
--color-spinner-track-dark
rgba(255,251,239,0.2)
Track em fundo escuro
--sp-duration
var(--motion-duration-slow)
Duração de uma volta completa
--motion-easing-expressive
cubic-bezier(0.56,0,0,1)
Aceleração/desaceleração (mesmo do input focus-fill)
Acessibilidade
Elemento base
role="status" obrigatório.
Texto acessível
aria-label="Carregando" ou descrição específica.
Região associada
aria-busy="true" enquanto pendente.
Movimento reduzido
prefers-reduced-motion: animação desacelera para 2s linear.
Fazer / Não Fazer
✓ Do
Use sempre role="status" e aria-label
Use .sp-dark em fundos escuros
Centralize o spinner na área de loading
✗ Don't
Não use quando há progresso mensurável — use ProgressBar
Não exiba sem feedback textual por mais de alguns segundos
Não use múltiplos spinners simultâneos na mesma tela
Base Component · ds.tooltip
Tooltip
Rótulo informativo não-interativo. Aparece em hover e focus sobre o trigger. Nunca contém informação essencial para a ação — apenas complementar.
Demo interativa
Passe o mouse ou use Tab para ver os tooltips.
Aparece acima do elemento âncora
Aparece abaixo do elemento âncora
Aparece à esquerda
Aparece à direita
Variantes & Estados
top (padrão)
Texto do tooltip
bottom
Texto do tooltip
left
À esquerda
right
À direita
Anatomia
Parte
Obrigatória
Notas
.tip-wrap
Sim
Container com position:relative. Envolve trigger e tooltip.
Trigger (elemento filho)
Sim
Qualquer elemento focável. Deve ter aria-describedby referenciando o id do tooltip.
.tip + role="tooltip"
Sim
O balão. Sempre com id único para vínculo via aria-describedby.
Seta ::after
Automática
Gerada via CSS. Direção definida pelo modificador de posição.
Props
Prop
Tipo
Default
Notas
content
string
—
Texto do tooltip. Máx. ~220px. Sem HTML interativo.
placement
enum
top
top · bottom · left · right
id
string
—
Obrigatório. Conecta trigger via aria-describedby.
disabled
boolean
false
Suprime exibição do tooltip.
Tokens consumidos
Token
Valor
Uso
--color-surface-inverse
grape-500
Fundo do balão
--color-text-on-dark
jasmine-50
Texto do tooltip
--radius-md
8px
Border-radius do balão
--tip-max-width
220px
Largura máxima
--tip-offset
8px
Distância entre balão e trigger
--tip-arrow-size
5px
Tamanho da seta (border trick)
--tip-duration
100ms
Duração da animação de entrada/saída
--z-tooltip
500
Z-index acima de modais e toasts
Acessibilidade
Vínculo semântico
role="tooltip" no balão + aria-describedby="[id]" no trigger. Tooltip é descrição complementar, não label principal.
Visível via teclado
Trigger deve ser focável (tabindex="0" ou elemento nativo). Tooltip aparece ao focar, some ao perder foco.
Escape
Pressionar Escape fecha o tooltip sem mover o foco (WCAG 1.4.13).
Nunca bloqueante
Tooltip não pode cobrir o conteúdo que o usuário está tentando interagir. Posicione com cuidado.
Ícones sem label
Em icon buttons, o trigger deve ter aria-label além do aria-describedby. O tooltip é a descrição; o aria-label é o nome.
Fazer / Não Fazer
✓ Do
Use para explicar ícones sem label (aria-label + aria-describedby)
Use para mostrar texto completo de elementos truncados
Use para exibir atalhos de teclado
Forneça sempre um id no tooltip e aria-describedby no trigger
✗ Don't
Não use para informação essencial para completar a ação
Não use para conteúdo longo (mais de 2 linhas)
Não coloque links ou botões dentro do tooltip — use Popover
Não substitua labels visíveis por tooltips em formulários
Base Component · ds.progress-bar
ProgressBar
Indica progresso de uma operação com valor conhecido ou indeterminado. Trilha + preenchimento. Sempre acessível via ARIA.
Demo interativa
Upload72%
Concluído100%
Sincronizando…
Falha na sincronização33%
Tamanhos
sm · 4px65%
md · 8px65%
lg · 12px65%
Variantes de cor
Primary72%
Success100%
Teal — execução48%
Danger — falha33%
Indeterminado
Carregando…
Sincronizando…
Indeterminado: omite aria-valuenow/max. Usa aria-valuetext descrevendo o estado.
Anatomia
Parte
Obrigatória
Notas
.pb-wrap
Sim
Container. Recebe modificadores de tamanho (pb-sm/md/lg) e cor (pb-primary/success/teal/danger).
.pb-header
Não
Linha de label + percentual. Omitir em barras compactas.
.pb-track
Sim
Trilha cinza. Elemento com role="progressbar" e atributos ARIA.
.pb-fill
Sim
Preenchimento. width = percentual. Adicionar .is-indeterminate para animação.
Props
Prop
Tipo
Default
Notas
value
number | null
—
0–100. null = indeterminado.
size
'sm'|'md'|'lg'
md
Altura da trilha: 4/8/12 px
variant
'primary'|'success'|'teal'|'danger'
primary
Cor do preenchimento
label
string
—
Rótulo visível acima da barra
aria-label
string
obrigatório
Sempre fornecer — o label visual pode estar ausente
Verificar par fill vs. track bg. Primary (#FE674C) sobre border (#AB98B2) ≈ 1.5:1 — abaixo de WCAG 1.4.11. Acompanhar decisão de produto sobre o primary.
Animação reduzida
prefers-reduced-motion: indeterminate sem animação, estático em 35%
ProgressBar primary: contraste fill (#FE674C) vs. track (#AB98B2) ≈ 1.5:1 — abaixo de WCAG 1.4.11 (3:1). Variantes success e teal passam. Decisão pendente de produto (mesma exceção do button primary).
Fazer / Não Fazer
✓ Do
Forneça sempre aria-label descrevendo a operação em progresso
Use success quando atingir 100% para sinalizar conclusão
Use danger quando a operação falhar
Para progresso desconhecido, use o estado indeterminado com aria-valuetext
✗ Don't
Não use ProgressBar para progresso interativo — use ds.slider
Não omita o aria-label — screen readers precisam do contexto
Não troque o estado indeterminado por um valor fixo fake (ex: sempre 50%) — use animação real
Não use barras de progresso para navegar — use ds.tabs ou stepper
Base Component · ds.link
Link
Elemento de navegação. Link navega — Button executa ação. Sempre usa <a href> com destino real. Inline herda o tamanho do contexto; Standalone tem escala própria.
target="_blank" + rel="noopener noreferrer" + texto sr-only "(abre em nova janela)".
Texto descritivo
Evitar "clique aqui". aria-label quando o texto visível não é descritivo do destino.
Underline
WCAG 1.4.1: links em texto devem ter underline visible OR ratio ≥ 3:1 vs texto circundante. always é o padrão mais seguro.
Fazer / Não Fazer
✓ Do
Use para navegação — para ações, use ds.button
Sempre forneça um href real
Use texto descritivo do destino (não "clique aqui")
Inclua texto sr-only em links externos
✗ Don't
Não use <a> sem href para ações — use <button>
Não remova o underline em texto corrido — viola WCAG 1.4.1
Não use target="_blank" sem rel="noopener noreferrer"
Não use apenas cor para distinguir links do texto (sem underline)
Base Component · ds.slider
Slider
Controle de seleção de valor em escala contínua ou discreta. Mesmo arquétipo visual do ProgressBar (track-fill), mas interativo — o usuário controla o valor arrastando o thumb. Para progresso não-interativo use ProgressBar.
Demo interativa
Volume60%
Progresso80%
Temperatura — discrete20°C
Disabled45%
Nível de detalhe — gradationMédio
Muito baixoBaixoMédioAltoMuito alto
Disabled — gradationBaixo
Muito baixoBaixoMédioAltoMuito alto
Variantes & Estados
rest · 60%
hover
focus
active · dragging
disabled
success
teal
danger · fora do intervalo
gradation · rest · nível 3
Muito baixoBaixoMédioAltoMuito alto
gradation · hover · nível 4
Muito baixoBaixoMédioAltoMuito alto
gradation · focus · nível 1
Muito baixoBaixoMédioAltoMuito alto
gradation · active · nível 5
Muito baixoBaixoMédioAltoMuito alto
gradation · disabled · nível 2
Muito baixoBaixoMédioAltoMuito alto
Tamanhos
sm — 4px track · 16px thumb
md — 8px track · 20px thumb (padrão)
lg — 12px track · 24px thumb
Anatomia
Parte
Obrigatória
Notas
.sl-wrap
Sim
Container. Recebe modificadores de tamanho (.sl-sm/md/lg) e cor (.sl-success/teal/danger).
.sl-header
Não
Linha label + valor atual. Omitir em sliders compactos sem label.
<input type="range"> ou .sl-track-wrap
Sim
Input nativo para produção; .sl-track-wrap para snapshots visuais.
Thumb
Sim
Controle arrastável. role="slider" com atributos ARIA completos.
Props
Prop
Tipo
Default
Notas
value
number
—
Valor controlado (0–max)
min / max
number
0 / 100
Limites da escala
step
number
1
Incremento (discrete). 1 = contínuo para efeitos práticos.
size
enum
md
sm · md · lg
variant
enum
primary
primary · success · teal · danger
label
string
—
Label descritivo acima do slider
showValue
enum
label
none · label · tooltip
disabled
boolean
false
Desabilita interação
onChange
function
—
Disparado continuamente durante arrasto
onChangeCommitted
function
—
Disparado ao soltar o thumb
Tokens consumidos
Token
Valor
Uso
--track-height-sm/md/lg
4/8/12px
Altura do track por tamanho — compartilhado com ProgressBar
--sl-thumb-size-sm/md/lg
16/20/24px
Diâmetro do thumb por tamanho
--sl-track-bg
var(--color-surface-grape)
Track vazio
--sl-fill-color
var(--color-primary)
Track preenchido. Overrideável por variante de cor.
--sl-thumb-bg
var(--color-surface)
Fundo do thumb
--sl-thumb-border
var(--color-primary)
Borda do thumb. Overrideável por variante.
--sl-thumb-border-width
2px
Espessura da borda do thumb
--shadow-knob
—
Sombra do thumb — mesma do Toggle knob
--track-border-accent
1.5px
Borda do track — característica compartilhada com ProgressBar
Acessibilidade
Elemento base
<input type="range"> nativo — role="slider" já está implícito.
ARIA obrigatório
aria-valuemin · aria-valuemax · aria-valuenow atualizados em tempo real.
Texto contextual
aria-valuetext quando o número precisa de contexto (ex: "20°C" em vez de "20").
ArrowRight/Left: move foco entre tabs (automático). Home/End: primeira/última. Tabs desabilitadas: ignoradas na navegação.
Ativação automática
Foco via Arrow ativa o painel imediatamente. Para painéis pesados, use activationMode="manual" (Enter/Space para ativar).
Foco visível
Ring tomato 2px no item focado — nunca remover.
Disabled
Use aria-disabled="true" no tab item. Não use o atributo disabled nativo — impede a leitura pelo screen reader.
Fazer / Não Fazer
✓ Do
Forneça sempre aria-label descritivo na tab bar
Use aria-disabled="true" em vez de disabled nativo
Mantenha labels curtos (1–3 palavras) para evitar overflow
Use pill para alternância de visualização (grade/lista/gráfico)
Use underline para navegação entre seções de conteúdo
✗ Don't
Não use tabs para fluxos sequenciais — use stepper ou wizard
Não use mais de 7 tabs — considere overflow ou navegação diferente
Não confunda com ds.segmented-control — tabs gerenciam painéis, segmented controla uma seleção
Não esconda conteúdo crítico em tabs inativas sem indicação
Base Component · ds.badge
Badge
Indicador numérico ou pontual sobreposto a outro elemento. Badge não é interativo — indica presença de novidade, contagem ou estado. Use sempre com .bdg-wrap num elemento-pai. Para rótulos de status, categoria ou atributo, use ds.tag.
Demo interativa
3
count · primary
99+
count · error (overflow)
dot · success (online)
dot · primary
Sobre ButtonIcon
2
count · primary
12
count · error
dot · success
Variantes & Estados
1
count · primary
5
count · error
12
count · warning
dot · success
dot · error
dot · neutral
Anatomia
Parte
Obrigatória
Notas
.bdg-wrap
Sim
Container com position: relative. Envolve o elemento-alvo e o badge sobreposto.
.bdg-count
Condicional
Pill numérico sobreposto. Deve ter aria-label="{N} {descrição}".
.bdg-dot
Condicional
Ponto colorido sobreposto. Deve ter aria-label descritivo quando tem significado.
Categoria (.bdg-{category})
Sim
Aplicada no .bdg-wrap. Define a cor do count ou dot.
<span> com role="status" para count/dot que comunicam estado dinâmico.
aria-label
Sempre descritivo: aria-label="3 notificações não lidas", não apenas aria-label="3".
Badge decorativo
Se o dot é puramente decorativo e a info está disponível por outro meio: aria-hidden="true".
Contraste count
Texto branco sobre primary (tomato-500): aceita por ADR de marca. Sobre success/error/warning: verificar token por token.
Borda separadora
--bdg-border-width: 2px branca separa o badge do fundo — auxilia percepção sobre imagens e cores variadas.
Fazer / Não Fazer
✓ Do
Use para indicadores numéricos sobre ícones e avatares
Forneça sempre um aria-label contextual no count/dot
Use "99+" para contagens acima do máximo
Use dot para presença de novidade sem quantificar
✗ Don't
Não use badge para rótulos ou status inline — use ds.tag
Não omita aria-label em badges com contagem
Não exiba count=0 por padrão — use showZero explicitamente
Não sobreponha badge em elementos não-posicionados sem .bdg-wrap
Base Component · ds.breadcrumb
Breadcrumb
Caminho hierárquico até a página atual. Cada nível anterior é um link navegável; o último é o item atual, não-clicável. Separador é um chevron-rightaria-hidden. Visualmente e funcionalmente é primo de Link + Tabs — não cria padrões novos.
Demo interativa
Padrão — md
Com ícone leading
Label longa — truncada
Variantes & Estados
item · rest
item · hover
item · focus
item · disabled
size · sm
size · md (padrão)
size · lg
com ícone leading
Anatomia
Parte
Obrigatória
Notas
<nav aria-label="Breadcrumb">
Sim
Container semântico. Rotula o landmark para screen readers.
.bc (<ol>)
Sim
Lista ordenada — breadcrumb é sequência hierárquica.
.bc-item (<a href>)
Sim
Cada nível anterior. Link real com href — nunca <div>.
.bc-current (<span aria-current="page">)
Sim
Último item. Não é link — é texto. Semibold.
.bc-sep (<li aria-hidden>)
Sim
Chevron entre itens. Oculto para screen readers.
.bc-leading
Não
Ícone opcional dentro do primeiro item (ex.: home).
.bc-truncate
Não
Helper para labels longas — trunca com ellipsis + title.
Props
Prop
Tipo
Default
Notas
items
array
—
Obrigatório. [{ label, href, icon?, current? }, ...]. Último item é current.
size
enum
md
sm · md · lg
leadingIcon
node
—
Ícone opcional antes do label no primeiro item.
label
string
"Breadcrumb"
Vai para aria-label do <nav>.
Tokens consumidos
Alias local
Token semântico
Uso
--bc-link-color
var(--color-text-link)
Item rest — grape-500
--bc-link-color-hover
var(--palette-grape-300)
Item hover — igual Link
--bc-link-color-visited
var(--palette-grape-600)
Item visited
--bc-link-color-disabled
var(--link-color-disabled)
Item disabled — charcoal-400
--bc-current-color
var(--color-text-default)
Item atual — charcoal-600
--bc-separator-color
var(--color-text-subtle)
Chevron — charcoal-500
--bc-underline-hover
var(--color-primary)
Underline tomato no hover — igual Link
--bc-gap
var(--space-2)
8px entre itens + separadores
--bc-icon-gap
var(--space-2)
8px entre ícone leading e label — padrão Tabs
Ícones (chevron, leading)
1em
Tracking automático com font-size — sm 12px / md 14px / lg 16px
Texto vs fundo ≥ 4.5:1 (AA). Ícone vs fundo ≥ 3:1 (1.4.11). Borda vs página ≥ 3:1.
Close button
aria-label="Fechar notificação". Ícone × com aria-hidden="true".
Fazer / Não Fazer
✓ Fazer
Use para confirmar ações concluídas (salvar, enviar, cadastrar)
Títulos curtos e descritivos: "Alterações salvas", "Atleta cadastrado"
Use variant="danger" com role="alert" para erros
Forneça ação "Desfazer" em ações destrutivas reversíveis
Mantenha duração mínima de 5s para leitores de tela
✗ Não Fazer
Não use para mensagens persistentes — use ds.banner
Não use para confirmação de ações destrutivas — use ds.modal
Não use títulos genéricos: "Erro", "Sucesso", "Aviso"
Não coloque múltiplas ações no mesmo toast
Não use para feedback inline de formulário — use validação do campo
Gaps Mapeados
Gap
Status
Decisão
gap-001 · Empilhamento de múltiplos toasts simultâneos
aberto
Sem especificação de limite, ordem ou comportamento de fila. Prioridade média.
gap-002 · Opções de posicionamento (além de top-right)
deferred
Top-right é o padrão. Posições adicionais para versão futura.
gap-003 · Prop dismissible no manifest
aberto
Close button (×) mostrado como modifier visual. Manifest ainda não inclui prop. Alinhar com toast-spec.md.
gap-004 · Progress indicator (timer bar)
deferred
Excluído desta versão para evitar novos tokens. Referenciado em toast-spec.md.
Base Component · ds.accordion
Accordion
Seções colapsáveis que o usuário expande para ver o conteúdo. Usado em FAQs, filtros, documentações e settings longos. Modo single permite apenas um item aberto; modo multiple permite vários simultaneamente.
Demo interativa
Single — apenas um aberto por vez (FAQ)
Acesse Configurações → Assinatura → Cancelar. O acesso permanece ativo até o fim do período pago. Você pode reativar a qualquer momento sem perder dados.
Sim. Upgrade é imediato com cobrança proporcional. Downgrade entra no próximo ciclo de cobrança.
Cartão de crédito (Visa, Mastercard, Elo), boleto bancário e PIX. Pagamento recorrente disponível apenas para cartão.
Conteúdo indisponível.
Multiple — vários abertos simultaneamente (settings)
Configure quais tipos de notificação deseja receber. Desativar push não afeta os alertas críticos de segurança.
Seu perfil está visível para todos os membros da equipe. Você pode restringir a visibilidade nas configurações abaixo.
Gerencie as conexões com serviços externos. Revogar acesso desconecta imediatamente.
Unidade expansível. Classes de estado: .is-expanded, .is-disabled.
.acc-header (<button>)
Sim
Botão clicável. aria-expanded e aria-controls obrigatórios.
.acc-header-text
Sim
Wrapper flex-col para título + subtitle.
.acc-title
Sim
Texto principal do header.
.acc-subtitle
Não
Texto secundário abaixo do título.
.acc-meta
Não
Info trailing (ex: count, tag, status). Antes do chevron.
.acc-chevron
Sim
Ícone que rotaciona 180° ao expandir. aria-hidden="true".
.acc-panel-wrap
Sim
Wrapper grid para animação de altura (grid-template-rows: 0fr → 1fr).
.acc-panel + role="region"
Sim
Conteúdo revelável. aria-labelledby aponta para o header.
.acc-panel-content
Sim
Padding interno do conteúdo.
Props
Prop
Tipo
Default
Notas
mode
enum
single
single · multiple. Controla se um ou vários items podem estar abertos.
variant
enum
separated
separated · flush
size
enum
md
sm · md · lg
expanded
string / string[]
—
IDs de items expandidos (controlado).
defaultExpanded
string / string[]
—
Valor inicial (não-controlado).
onExpandedChange
function
—
Disparado ao expandir/colapsar.
disabled
boolean
false
Desabilita o item individual via aria-disabled="true".
Tokens consumidos
Token
Valor
Uso
--color-text-heading
#6B4F74
Cor do título do header
--color-text-default
#433E3D
Cor do texto do conteúdo + chevron expandido
--color-text-subtle
#696060
Cor do subtitle, meta e chevron rest
--color-state-hover
#F4F2F6
Fundo do header no hover
--color-border-focus
#FE674C
Focus ring tomato no header
--focus-ring-width
2px
Espessura do focus ring
--opacity-disabled
0.38
Opacidade do item disabled
--divider-thickness
1px
Espessura do separador entre items
--divider-color
var(--color-border)
Cor do separador
--tab-bar-height-sm/md/lg
36/44/52px
Altura mínima do header por tamanho
--tab-font-size-sm/md/lg
13/14/16px
Font size por tamanho
--tab-item-px-sm/md/lg
12/16/20px
Referência de padding (usado via --space-*)
--icon-sm/md/lg
16/20/24px
Tamanho do container do chevron
--motion-duration-fast
150ms
Transição de cor no hover
--motion-duration-normal
250ms
Animação de expand/collapse + chevron
--motion-easing-standard
cubic-bezier(0.2, 0, 0, 1)
Easing de todas as transições
--radius-md
8px
Border-radius do header (hover bg)
Acessibilidade
Elemento base
<button> no header — semântica de interação nativa.
ARIA obrigatório
aria-expanded no header (true/false). aria-controls apontando para o id do panel. role="region" + aria-labelledby no panel.
Teclado
Enter/Space: toggle. ArrowDown: próximo header. ArrowUp: header anterior. Home: primeiro. End: último. Tab: segue a ordem natural do DOM.
Disabled
aria-disabled="true" no header. Não usar disabled nativo — impede leitura pelo screen reader.
Foco visível
Ring tomato 2px no header focado — match exato com Tabs.
Reduced motion
prefers-reduced-motion: reduce remove transições de altura e rotação do chevron.
Fazer / Não Fazer
✓ Do
Use single para FAQs — reduz sobrecarga cognitiva
Use multiple para settings e filtros — preserva contexto
Forneça aria-controls e aria-labelledby em cada par header/panel
Use subtitles para dar contexto sem precisar abrir o item
Use trailing meta para counts, status ou datas
✗ Don't
Não use para navegação — use ds.tabs ou ds.sidebar
Não coloque conteúdo crítico que o usuário precisa ver sempre — use layout direto
Não aninhe accordions sem necessidade real — UX fica confusa
Não use mais de 10 items — considere agrupar ou usar outra abordagem
Base Component · ds.pagination
Pagination
Controle de navegação entre páginas de dados em tabelas e listas longas. Variante default exibe números com ellipsis; variante simple exibe apenas anterior / próximo.
Demo interativa
Default — janela fixa de 7 slots · página 1/12
Anterior fica disabled na página 1; Próximo fica disabled na página 12.
Simple — anterior / próximo apenas (clique no botão habilitado para ver a transição)
página 1/5 · começa no início
página 5/5 · começa no fim
Variantes & Estados
default · página 1 (prev disabled)
default · meio (currentPage 6 de 12)
default · página final (next disabled)
default · hover (página 3)
default · focus (teclado)
simple · estados extremos
Anatomia
Parte
Obrigatória
Notas
.pagination-wrap + role="navigation"
Sim
Container <nav> com aria-label="Paginação" obrigatório.
.pagination-prev / .pagination-next
Sim
Botões de navegação. Compõem com .btn-icon-bare (variant default) ou ficam stand-alone com label de texto (variant simple).
.pagination-list + .pagination-item
Sim (variant default)
<ol> com botões numerados. Item ativo recebe aria-current="page".
Em mobile, considerar variant="simple" quando o número de páginas inflar a barra.
Não fazer
Não usar pagination para navegação principal da aplicação — use Navigation ou TabBar.
Não substituir --color-primary por hex ou outro token na página ativa.
Não tornar o ellipsis (…) clicável; ele é decorativo.
Não omitir aria-label nos botões numéricos — screen readers leriam só "1, 2, 3" sem contexto.
Base Component · beta
Popover
Painel flutuante de conteúdo rico ancorado a um trigger. Suporta links, botões e formulários dentro do painel. Usa role="dialog" com foco preso — Escape fecha e retorna foco ao trigger.
Demo interativa
Clique nos botões para abrir os popovers. Feche com Escape ou clique fora.
Detalhes
Conteúdo rico com link interativo e ações dentro do painel flutuante.
Informação
Aparece acima do trigger. Conteúdo pode incluir texto formatado e elementos interativos.
Foco move para o primeiro elemento focável dentro do painel.
Focus trap
Tab cicla entre os elementos focáveis dentro do painel. Shift+Tab cicla reverso.
Focus on close
Foco retorna ao trigger que abriu o popover.
Escape
Fecha o popover e retorna foco ao trigger (WCAG 1.4.13 + 2.1.1).
Click outside
Fecha o popover.
Disabled
aria-disabled="true" + tabindex="-1" no trigger. Popover não abre.
Tecla
Ação
Enter / Space
Abre/fecha popover quando trigger está focado
Tab
Move foco para o próximo elemento focável dentro do painel (cicla)
Shift+Tab
Move foco para o elemento focável anterior dentro do painel (cicla reverso)
Escape
Fecha popover, retorna foco ao trigger
Fazer / Não Fazer
✓ Do
Use para conteúdo contextual com ações (links, botões)
Use quando o conteúdo é muito rico para um Tooltip
Use para menus de contexto ou painéis de configuração rápida
Forneça sempre aria-labelledby apontando para o título
Use showClose em contextos touch/mobile
✗ Don't
Não use para texto simples sem interação — use Tooltip
Não use para fluxos complexos — use Modal ou tela dedicada
Não aninhe Popovers dentro de outros Popovers
Não use para notificações — use Toast
Não coloque formulários longos — máximo 3 campos
Gaps Mapeados
Gap
Status
Decisão
Auto-flip: painel não reposiciona automaticamente quando colide com viewport.
deferred
Deferred para v2. Workaround: escolher placement manual baseado no contexto.
Variante com arrow/caret direcional (como Tooltip).
deferred
Deferred para v2. Decisão: v1 usa painel flat sem seta, consistente com Select dropdown.
Suporte a 12 posições (start/center/end por lado).
deferred
Deferred para v2. 4 posições cobrem 95% dos casos de uso.
Composite Component · beta
Date Picker
Campo de data com calendário dropdown. Reusa o Text Input com ícone de calendário, o Popover para o painel flutuante e o Button para as setas de navegação. Suporta entrada direta pelo teclado com máscara dd/mm/aaaa e seleção via grade role="grid". Variantes single e range compartilham a mesma grade — sem calendário duplo.
Demo interativa
Clique no ícone de calendário ou foque o campo para abrir. Digite diretamente no formato dd/mm/aaaa ou use as setas para navegar. Feche com Escape ou clique fora.
Variantes & Estados
closed — default
Campo Text Input + ícone calendário
closed — focused
Borda ativa (grape-400) — sem morph nem stroke-draw
selected — preenchido
Valor formatado dd/mm/aaaa
error — data inválida
Data inválida
disabled
Calendário não abre
open — single (Abril 2026)
abril 2026
Dom
Seg
Ter
Qua
Qui
Sex
Sáb
dia 15 selecionado · dia 22 = hoje (ring)
open — range (10 → 18)
abril 2026
Dom
Seg
Ter
Qua
Qui
Sex
Sáb
10 = início · 18 = fim · 11–17 in-range
open — com min/max (dias fora desabilitados)
abril 2026
Dom
Seg
Ter
Qua
Qui
Sex
Sáb
min = 10/abril · max = 25/abril
Anatomia
Parte
Obrigatória
Notas
.ti-field
Sim
Container do campo. Reusa Text Input.
.ti-label
Sim
<label for={id}> descritivo. Reusa Text Input.
.ti-input
Sim
<input type="text"> com máscara dd/mm/aaaa. inputmode="numeric" para teclado numérico em mobile.
.ti-trail-btn
Sim
Ícone de calendário (fi-rr-calendar). Abre o painel ao clicar.
role="grid" no container. Cada dia é role="gridcell" com aria-label completo ("15 de abril de 2026").
Dia atual
aria-current="date".
Dia selecionado
aria-selected="true".
Dia fora de min/max
aria-disabled="true" + pointer-events:none.
Data inválida (typed entry)
aria-invalid="true" + .ti-error-msg com role="alert".
Focus visível
Ring tomato em todos os elementos interativos (campo, setas, células, trail button).
Tecla
Ação
Enter
No campo: abre o calendário · Na célula: seleciona o dia
Escape
Fecha o calendário e devolve foco ao campo
ArrowLeft / ArrowRight
Dia anterior / próximo dia (atravessa meses)
ArrowUp / ArrowDown
Semana anterior / próxima semana
Tab
Cicla entre os elementos focáveis dentro do painel
Fazer / Não Fazer
✓ Do
Use rótulos descritivos: "Data de nascimento", "Período do treino"
Mostre o formato no placeholder: "dd/mm/aaaa"
Permita digitação direta com máscara — não force abrir o calendário
Use variante range com 2 campos "Início" + "Fim"
Restrinja com min/max quando aplicável (ex.: data futura)
Valide a data no blur do campo e mostre "Data inválida"
✗ Don't
Não use rótulos genéricos como "Data" ou "Escolha"
Não substitua o label pelo placeholder
Não use dois calendários lado a lado para range — um só basta
Não use DatePicker para selecionar hora — aguarde TimePicker
Não abrevie dia da semana com 1 letra só (D·S·T·Q·Q·S·S vira ambíguo)
Não custome tokens — herde cores/raios/sombras do .pop-panel
Gaps Mapeados
Gap
Status
Decisão
Sem seletor de hora (time picker)
deferred
TimePicker fica como componente separado — fora do escopo de Date Picker.
Sem fallback para input nativo em mobile
deferred
Experiência custom funciona bem com inputmode="numeric"; fallback nativo adiado.
Base Component · ds.segmented-control
SegmentedControl
Seleção exclusiva entre 2 a 5 opções visíveis simultaneamente, com feedback visual imediato. Use para alternar visualizações ou filtros dentro da mesma área — quando a mudança é de dados, não de conteúdo separado.
Demo interativa
Formato do feedback · hug content (padrão)
Formato do feedback · full width
Período de visualização (3 opções)
Tamanhos
sm · 32px
md · 36px (padrão)
lg · 40px
Estados
default
hover · unselected
hover · selected
focus · unselected
focus · selected
disabled
Anatomia
Parte
Obrigatória
Notas
.sg-group (role="radiogroup")
Sim
Container em pílula com fundo sutil agrupando os segmentos. Recebe o aria-label que descreve o grupo.
.sg-segment (role="radio")
Sim
Cada opção individual clicável. Um e apenas um segmento por grupo tem aria-checked="true".
Indicador de seleção
Sim
O segmento ativo recebe background: var(--color-surface) + box-shadow: var(--shadow-sm) — não é um elemento à parte, é um estado visual do próprio segmento.
Quando true, o grupo ocupa 100% da largura disponível e os segmentos dividem o espaço igualmente. Use quando o controle é o item dominante de uma coluna ou card.
disabled
boolean
false
Desabilita o grupo inteiro.
Option — formato de cada item no array options:
Campo
Tipo
Obrigatório
Notas
value
string
Sim
Identificador único da opção; passado para onChange ao selecionar.
label
string | ReactNode
Sim
Texto exibido no segmento (1–2 palavras).
icon
ReactNode
Não
Ícone leading opcional, renderizado antes do label. Útil para view switchers (ex: lista vs grade).
disabled
boolean
Não
Desabilita apenas esta opção (sem afetar as demais). A navegação por setas pula a opção desabilitada.
Props proibidas:variant, tabs, color, selected — não existem nesta API.
Tokens consumidos
Token
Valor
Uso
--color-surface-grape
#F4F2F6
Fundo do container
--color-surface
#FFFFFF
Fundo do segmento selecionado e do hover (com opacity: 0.75) — efeito ghost
--color-text-default
#433E3D
Texto do segmento selecionado e em hover
--color-text-subtle
#696060
Texto dos segmentos não selecionados; rótulos do V&E grid
--radius-full
999px
Border-radius do container e dos segmentos (formato pílula)
--shadow-sm
—
Sombra do segmento selecionado
--palette-tomato-200
#FEB9AD
Borda tomato suave do segmento selecionado (mesmo tratamento do Tabs pill)
--color-border-focus
#FE674C
Outline de foco visível no segmento focado
--focus-ring-width · --focus-ring-offset-compact
2px · 2px
Espessura e offset do outline de foco
--size-sm · --size-md · --size-lg
32 · 36 · 40 px
Alturas dos três tamanhos do grupo
--space-1…--space-6
4–24 px
Padding do container, padding horizontal dos segmentos por tamanho (sm 12 / md 16 / lg 20), gap do V&E grid (12 × 24)
--motion-duration-fast · --motion-easing-standard
150ms · ease
Transição de background, color e box-shadow
--font-weight-semibold · --letter-spacing-tight
600 · -0.02em
Peso do segmento selecionado; tracking dos textos
--opacity-disabled
0.38
Opacidade do estado disabled
Acessibilidade
Elemento base
<div role="radiogroup"> com <button role="radio"> em cada segmento
Teclado
Tab entra (no segmento selecionado) e sai · Setas ← → ↑ ↓ movem seleção entre segmentos habilitados · Home vai ao primeiro habilitado · End vai ao último habilitado
Tabindex
Roving — apenas o segmento selecionado tem tabindex="0"; os demais ficam em -1
aria-checked
Obrigatório em cada segmento — "true" no ativo, "false" nos demais
aria-label
Obrigatório no container — descreve o grupo (ex: "Período de visualização")
Foco visível
Outline tomato 2px com offset 2px — não remover
Estado disabled
aria-disabled="true" nos segmentos + classe .is-disabled no container para opacity