UaiUI

Sistema de design com bordas retas, sombras duras e temas por tokens. Cada componente tem preview, código e requisitos de acessibilidade.

Densidade

Accordion

Esconde conteúdo secundário que a pessoa consulta sob demanda: perguntas frequentes, detalhes de um pedido, seções longas de configurações.

Um sistema de design com bordas retas, sombras duras e temas por tokens.

Instalação

  1. Importe uaiui-ds/tokens-wrapper.css e coloque os componentes dentro do wrapper .uaiui (veja Tokens e temas).
  2. Importe uaiui-ds/components/accordion.css. O código abaixo é o conteúdo desse arquivo.
  3. Acrescente o comportamento: alternar aria-expanded no botão e hidden no 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.

Conteúdo visível.

Atributos

Atributos do accordion
PropValoresPadrãoDescrição
aria-expandedtrue | falsefalseNo button. Aberto ou fechado se vê pela seta (para baixo fechado, para cima aberto) e pelo painel, nunca só pela cor.
aria-controlsid—No button. Aponta para o id do painel.
hiddenboolean—No painel fechado: oculta também para leitores de tela.
aria-disabledtrue—No button desabilitado. Ele continua na ordem de Tab.
h2 … h6nívelh3O 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 (h2 a h6) com aria-expanded e aria-controls. O painel leva role="region" e aria-labelledby apontando 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.