Gaveta
Em andamentoUm painel que entra pela lateral ou por baixo, sobre a página: o detalhe de um item sem sair da lista, os filtros no celular, o carrinho. É um diálogo modal — prende o foco, deixa o resto da página inerte e devolve o foco a quem abriu.
- Função
- Estado e aviso
- Também chamado de
- drawer, gaveta, painel lateral, sheet, bottom sheet, folha
- Mora em
- app/components/molecules/Gaveta.tsx
- Última mudança
- 2026-09-30
Prévia
A peça ao vivo, vinda da ficha, na largura escolhida. Em larguras fixas, ela roda num quadro do tamanho de uma tela de verdade.
1280px, em tamanho real
Variantes na ficha
Lidas da ficha no build, na ordem do palco. Cada uma abre sozinha, sem o styleguide em volta — é o endereço que o palco carrega.
Quando usar
Quando a tarefa pede mais espaço que uma pergunta, mas a pessoa volta para a mesma página depois: filtros no celular, o detalhe de um item da lista, o carrinho.
Faça
- Dê um
tituloque diga a tarefa ("Filtrar cursos"): ele nomeia o diálogo. - Ponha a ação principal no
rodape("Ver 12 cursos"), que fica fixo embaixo quando o conteúdo rola. - Use
posicao="baixo"no celular e"direita"no computador, com otamanhodo conteúdo (320, 480 ou 640px). - Desligue
fecharNoFundoem formulário longo: um clique errado perderia o que se escreveu.
Evite
- Gaveta dentro de gaveta, ou uma gaveta que abre um Modal.
- Navegação de várias etapas dentro dela: é uma página.
- Tirar o botão "Fechar": a saída explícita é obrigatória.
Quando não usar. Para uma pergunta curta ou confirmação: o Modal. Para conteúdo que acompanha a página sem bloqueá-la: uma coluna da página, não um diálogo. Para navegar entre seções do site: o menu da Navbar.
Anatomia
As partes, na ordem em que o olho — e o teclado — passam.
- Véu — Preto a 40% (`--veu-medio`) sobre a página. É só desenho: o clique fora do painel fecha pelo documento (E137), por padrão; um clique dentro do painel não fecha — testado na ficha.
- Painel — Em `--card`, com sombra; à direita, da altura da tela (320, 480 ou 640px); por baixo, com os cantos de cima arredondados (240, 400 ou 560px, até 5/6 da tela).
- Cabeçalho — Título (h2, 18px, negrito), descrição opcional e o "Fechar" de 44px.
- Corpo — Rola sozinho quando o conteúdo passa da altura.
- Rodapé — As ações, à direita, sobre `--muted`, fixas embaixo.
Estados
Os estados que a peça tem no código, e o que muda em cada um.
| Estado | O que muda |
|---|---|
| Fechada | Não existe na página. |
| Abrindo | Entra deslizando 32px e aparecendo, em 200ms — só com movimento liberado. |
| Aberta | A página atrás fica inerte e não rola. |
| Pela esquerda | posicao="esquerda" (E142): o menu de navegação no celular — o do styleguide usa esta. Com focoInicial, o foco entra no campo indicado (o filtro), e não no primeiro focável — testado na ficha. |
Comportamento
| Situação | O que acontece |
|---|---|
| Abre | O foco vai ao primeiro campo, pulando o "Fechar"; sem campo, ao "Fechar"; sem nada, ao painel — testado na ficha. |
| Tab no último elemento | Volta ao primeiro: o foco não sai da gaveta — testado na ficha. |
| Fecha (Esc, X, fundo) | O foco volta a quem abriu; onFechar recebe escape, botao ou fundo — testado na ficha. |
| Fundo desligado | Com fecharNoFundo={false}, o clique fora não fecha nem tira o foco — testado na ficha. |
Relacionados
Como usar no código
A importação e o menor uso, lidos do arquivo. O … marca as props obrigatórias.
import { Gaveta } from '@/app/components/molecules/Gaveta';
<Gaveta aberta={…} onFechar={…} titulo={…}>…</Gaveta>Propriedades
Lidas do código da peça no build — não escritas à mão.
| Prop | Tipo | Padrão | Obrigatória | Descrição |
|---|---|---|---|---|
posicao | 'direita' | 'esquerda' | 'baixo' | 'direita' | não | Variante do cva. |
tamanho | 'sm' | 'md' | 'lg' | 'md' | não | Variante do cva. |
aberta | boolean | — | sim | — |
onFechar | (origem: OrigemDoFechamentoDaGaveta) => void | — | sim | Recebe a origem: 'botao' (o X), 'fundo' ou 'escape'. |
titulo | string | — | sim | Obrigatório: é o nome do diálogo. Sem ele, o leitor diz só "diálogo". |
descricao | string | — | não | — |
children | React.ReactNode | — | sim | — |
rodape | React.ReactNode | — | não | Ações do pé ("Aplicar filtros", "Finalizar compra"), fixas embaixo. |
fecharNoFundo | boolean | true | não | Clique no fundo fecha (padrão). O Esc e o X continuam fechando. |
focoInicial | React.RefObject<HTMLElement | null> | — | não | Onde o foco entra, quando não é o primeiro campo (E142): o filtro de um menu, por exemplo. |
Tokens de cor
Os tokens semânticos que o código da peça usa, com o valor de hoje em app/globals.css.
| Token | Valor | Usado como |
|---|---|---|
--border | #E5E5E5 | border-border |
--card | #FFFFFF | bg-card |
--foreground | #1A1A1A | text-ink |
--muted | #F4F6F9 | bg-muted |
--muted-foreground | #595959 | text-muted-foreground |
Teclado
| Tecla | Faz |
|---|---|
| Tab | Percorre os elementos da gaveta; do último, volta ao primeiro. |
| ShiftTab | Volta; do primeiro, vai ao último. |
| Esc | Fecha e devolve o foco a quem abriu. |
Leitor de tela
O que é anunciado, e por quê.
- É
role="dialog"comaria-modal, nomeado pelo título e descrito pela descrição. - O resto da página fica
inertenquanto ela está aberta: o leitor não sai sem perceber. - A mesma mecânica do Modal, num hook só (
app/lib/dialogo-modal.ts).
O que sustenta o estado
Esta peça usa dialogo. O estado dela é o de quem já foi ouvido com leitor de tela nesses padrões — e não uma passada sobre esta peça em específico.
Falta ouvir: dialogo — ver docs/a11y-screen-reader-checklist.md.
Status e recursos
| Tipo | Recurso | Status |
|---|---|---|
| Código | app/components/molecules/Gaveta.tsx | Disponível |
| Ficha | Storybook (abre em outra aba) | Disponível |
| Teste de acessibilidade | axe em npm run test | No gate de todo commit |
| Leitor de tela | Passada da E33 | Falta ouvir |
| Design | Figma | Desativado até a Fase 2 |
Histórico
Os últimos commits que tocaram o arquivo, lidos do git no build.
0298af82026-09-30E147: fechamento da Entrega 11 — REPORT.md, verificação cross-agent e o gate estático estrito
O histórico completo está no GitHub (abre em outra aba).