UaiUI
Sistema de design com bordas retas, sombras duras e temas por tokens. Cada componente tem preview, código e requisitos de acessibilidade.
Accordion
Esconde conteúdo secundário que a pessoa consulta sob demanda: perguntas frequentes, detalhes de um pedido, seções longas de configurações.
<div class="uai-accordion">
<div class="uai-accordion-item">
<h2>
<button class="uai-accordion-trigger" id="a1-h" aria-expanded="true" aria-controls="a1-p">
<span>O que é o UaiUI?</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h2>
<div class="uai-accordion-panel" id="a1-p" role="region" aria-labelledby="a1-h">Um sistema de design com bordas retas, sombras duras e temas por tokens.</div>
</div>
<div class="uai-accordion-item">
<h2>
<button class="uai-accordion-trigger" id="a2-h" aria-expanded="false" aria-controls="a2-p">
<span>Posso trocar as cores?</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h2>
<div class="uai-accordion-panel" id="a2-p" role="region" aria-labelledby="a2-h" hidden>Sim. Troque os primitivos e os componentes seguem, porque só leem tokens.</div>
</div>
<div class="uai-accordion-item">
<h2>
<button class="uai-accordion-trigger" id="a3-h" aria-expanded="false" aria-controls="a3-p">
<span>Funciona no escuro?</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h2>
<div class="uai-accordion-panel" id="a3-p" role="region" aria-labelledby="a3-h" hidden>Sim. Todos os pares de cor foram conferidos nos modos Claro e Escuro.</div>
</div>
</div>Instalação
- Importe
uaiui-ds/tokens-wrapper.csse coloque os componentes dentro do wrapper.uaiui(veja Tokens e temas). - Importe
uaiui-ds/components/accordion.css. O código abaixo é o conteúdo desse arquivo. - Acrescente o comportamento: alternar
aria-expandedno botão ehiddenno painel.
/* accordion: botão dentro de um título + painel. Itens empilham com espaço, sem juntar bordas. */
.uai-accordion-item { display: flex; flex-direction: column; }
.uai-accordion-item h2, .uai-accordion-item h3, .uai-accordion-item h4 { margin: 0; font: inherit; }
.uai-accordion-trigger {
display: flex; align-items: center; justify-content: space-between; width: 100%; box-sizing: border-box;
gap: var(--accordion-header-gap); min-height: var(--accordion-header-min-height);
padding: 0 var(--accordion-header-padding-x);
border: var(--border-width-default) solid var(--accordion-border); border-radius: var(--radius-control);
background: var(--accordion-header-normal-background); color: var(--accordion-header-normal-txtOn);
font: var(--font-weight-label) var(--accordion-title-font-size)/var(--accordion-title-line-height) var(--font-family-label), monospace;
text-transform: var(--font-transform-label);
text-align: left; cursor: pointer;
transition: background-color var(--motion-duration-press), color var(--motion-duration-press);
}
.uai-accordion-trigger:hover { background: var(--accordion-header-action-background); color: var(--accordion-header-action-txtOn); }
.uai-accordion-trigger:active { background: var(--accordion-header-active-background); color: var(--accordion-header-active-txtOn); }
.uai-accordion-trigger:focus-visible { outline: var(--focus-ring-width) solid var(--ui-focus-ring); outline-offset: var(--focus-ring-offset); }
.uai-accordion-trigger svg { width: var(--accordion-icon-size); height: var(--accordion-icon-size); flex: none; stroke-width: var(--accordion-chevron-weight); fill: none; stroke: currentColor; }
.uai-accordion-trigger[aria-expanded="true"] svg { transform: rotate(180deg); }
.uai-accordion-trigger[aria-disabled="true"] {
background: var(--accordion-disabled-background); color: var(--accordion-disabled-txtOn);
border-color: var(--accordion-disabled-border); cursor: not-allowed;
}
.uai-accordion-panel {
padding: var(--accordion-panel-padding-y) var(--accordion-panel-padding-x);
border: var(--border-width-default) solid var(--accordion-border); border-top: 0;
background: var(--accordion-panel-background); color: var(--accordion-panel-txt);
font: 400 var(--accordion-panel-font-size)/var(--accordion-panel-line-height) var(--font-family-body), sans-serif;
}
.uai-accordion-panel[hidden] { display: none; }
.uai-accordion { display: flex; flex-direction: column; gap: var(--size-scale-x3); }
@media (prefers-reduced-motion: reduce) { .uai-accordion-trigger { transition: none; } }document.addEventListener('click', (e) => {
const t = e.target.closest('.uai-accordion-trigger');
if (!t || t.getAttribute('aria-disabled') === 'true') return;
const open = t.getAttribute('aria-expanded') !== 'true';
t.setAttribute('aria-expanded', String(open));
document.getElementById(t.getAttribute('aria-controls')).hidden = !open;
});Uso
<div class="uai-accordion-item">
<h3>
<button class="uai-accordion-trigger" id="s1-h" aria-expanded="true" aria-controls="s1-p">
<span>Seção sozinha</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h3>
<div class="uai-accordion-panel" id="s1-p" role="region" aria-labelledby="s1-h">Um item isolado, sem grupo.</div>
</div>Quando usar. Para esconder conteúdo secundário. Se todo o conteúdo precisa ser lido junto, deixe aberto e sem accordion.
Quando não usar. Conteúdo essencial ao próximo passo: deixe visível. Alternar painéis do mesmo nível, um por vez: use abas (ainda não existem). Disparar ação: use button. Esconder opções de formulário: veja antes se checkbox ou radio com texto de apoio resolve.
Exemplos
Item desabilitado
Cinza, sem resposta ao hover, e continua na ordem de Tab. O painel de quem usa o componente mantém as cores normais.
<div class="uai-accordion">
<div class="uai-accordion-item">
<h3>
<button class="uai-accordion-trigger" id="d1-h" aria-expanded="true" aria-controls="d1-p">
<span>Seção disponível</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h3>
<div class="uai-accordion-panel" id="d1-p" role="region" aria-labelledby="d1-h">Conteúdo visível.</div>
</div>
<div class="uai-accordion-item">
<h3>
<button class="uai-accordion-trigger" id="d2-h" aria-expanded="false" aria-controls="d2-p" aria-disabled="true">
<span>Seção indisponível</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6"/></svg>
</button>
</h3>
<div class="uai-accordion-panel" id="d2-p" role="region" aria-labelledby="d2-h" hidden>Este conteúdo não abre.</div>
</div>
</div>Atributos
| Prop | Valores | Padrão | Descrição |
|---|---|---|---|
aria-expanded | true | false | false | No button. Aberto ou fechado se vê pela seta (para baixo fechado, para cima aberto) e pelo painel, nunca só pela cor. |
aria-controls | id | — | No button. Aponta para o id do painel. |
hidden | boolean | — | No painel fechado: oculta também para leitores de tela. |
aria-disabled | true | — | No button desabilitado. Ele continua na ordem de Tab. |
h2 … h6 | nível | h3 | O button fica dentro de um título; escolha o nível conforme a página. |
A tabela rola na horizontal; a região recebe foco pelo teclado.
Itens empilham com espaço entre eles, sem juntar bordas. O grupo (um aberto por vez, ou abrir e fechar todos) ainda não existe como componente.
Acessibilidade
- Botão dentro de um título (
h2ah6) comaria-expandedearia-controls. O painel levarole="region"earia-labelledbyapontando para o botão. - Teclado: Enter e Espaço abrem e fecham; Tab percorre os cabeçalhos.
- O cabeçalho inteiro é a área de clique, com a altura mínima do botão.
- Anel de foco de 2 px, afastado 2 px, em volta do cabeçalho.
- A cor muda em 100 ms. Se a altura do painel animar, no máximo 200 ms e sem transição com redução de movimento.