Tema
BaseAppShell, BasePage e BasePageAside
A moldura institucional de todo sistema do Tribunal: cabeçalho azul-marinho com o logo, o nome do sistema, a busca e a pessoa conectada; menu lateral com os módulos; e a área de conteúdo, de até 1280 px. Cada tela entra nela como um BasePage, com o título da tela e o conteúdo. Telas de detalhe e de formulário usam o BasePageAside para a coluna lateral de 360 px.
Pular para o conteúdo
Gestão de ObrasSecretaria de Engenharia
Gestão de ObrasSecretaria de EngenhariaContratos de obras
42 contratos ativos. Medição de outubro aberta até 10/11.
Último item do menu escolhido
ContratosNesta página o exemplo fica numa caixa de 560 px de altura; no sistema, a moldura ocupa a janela inteira. Abaixo de 992 px de largura de janela, o menu lateral vira uma faixa horizontal sob o cabeçalho.
Uso
- Use um
BaseAppShellpor sistema, na raiz do app. Cada tela é umBasePagedentro dele: otitlevira o únicoh1da tela, osubtitlediz o estado da página e o slotactionsrecebe as ações da tela (no máximo um botão primário). - O menu lateral vem da propriedade
nav: grupos com um título curto (Obras,Secretaria) e itens comlabel,hrefeicon. Dê ícone a todos os itens: com o menu recolhido, só os ícones aparecem. - Marque a tela aberta com
current: true. Ela ganha fundo, peso e uma barra lateral, não só cor. - Use
countpara o que pede atenção, como medições abertas, ecountLabelpara dizer o que é contado: o leitor de tela ouve "7 medições abertas" junto com o nome do item. - Com roteador, ouça
navigate, chameevent.preventDefault()e navegue peloitem.href. Onavigatesó chega no clique simples: com Ctrl, Cmd, Shift, Alt ou o botão do meio, o navegador abre o link em outra aba ou janela, como noRouterLink. Sem roteador, os links funcionam sozinhos. - O botão de menu no começo do cabeçalho recolhe o menu lateral para uma faixa só de ícones. Ligue
v-model:collapsedpara guardar a preferência da pessoa. - O slot
searchrecebe a busca geral do sistema: umBaseSearchcomlandmarke, se for a única busca com atalho da tela,shortcut. Ele ganha as cores do cabeçalho. Buscas de lista ficam na tela, não no cabeçalho. - O slot
toolsrecebe botões de ícone, como avisos e ajuda:BaseButtoncomkind="ghost"eicon-only. user-nameeuser-rolemostram quem está conectado. Com o slotuser-menu, o nome vira um botão que abre um painel com links e botões (perfil, sair).- O slot
logorecebe o logo branco do Tribunal, sempre sobre o azul-marinho e nunca recolorido. Comhome-href, logo e nome do sistema levam à tela inicial. - Sem
nav, não há menu lateral nem botão de menu: serve para telas de entrada e de erro.
Menu recolhido
Pular para o conteúdo
Gestão de Obras
Contratos de obras
Passe o mouse sobre um ícone ou chegue nele com Tab para ver o nome do item. O ponto azul marca os itens com quantidade.
Conteúdo com coluna lateral
O BasePageAside põe a coluna lateral de 360 px à direita do conteúdo quando a área de conteúdo tem pelo menos 1080 px. Abaixo disso, a coluna vai para baixo do conteúdo. A medida é a da área de conteúdo, não a da janela: com o menu recolhido, as duas colunas aparecem em telas menores. Nesta página, estreita, elas aparecem empilhadas.
Itens da medição
Planilha da medição 29.Resumo
Valor medido e saldo do contrato.Histórico
Envios e devoluções.Use para detalhe com decisão (resumo, histórico e barra de decisão na lateral) e formulário em etapas (o andamento na lateral). Na lateral, ponha o que apoia a decisão, nunca o único caminho para concluir a tarefa: no celular ela fica no fim da página. A barra de decisão vai no topo da lateral: a coluna rola com a página, então o que fica embaixo só aparece quando a pessoa chega ao fim do conteúdo.
Estilo
| Propriedade | Token |
|---|---|
| Fundo do cabeçalho | --tj-background-brand, igual nos dois temas |
| Texto do cabeçalho | --tj-text-on-brand; secundário --tj-text-on-brand-secondary |
| Busca e botões no cabeçalho | fundo --tj-layer-on-brand, borda --tj-border-on-brand |
| Altura do cabeçalho | --tj-header (64 px; 56 px abaixo de 992 px) |
| Menu lateral | fundo --tj-layer-01, borda --tj-border-subtle-02, largura --tj-sidebar (256 px; 64 px recolhido) |
| Item do menu | texto --tj-text-secondary, hover --tj-layer-hover-01 |
| Item atual | fundo --tj-layer-selected-01, texto --tj-text-brand, barra --tj-border-interactive |
| Dica e ponto do menu recolhido | dica com fundo --tj-background-inverse e texto --tj-text-inverse; ponto --tj-icon-interactive |
| Área de conteúdo | fundo --tj-background, largura máxima --tj-content-max (1280 px), margem --tj-space-8 (--tj-space-4 no celular) |
| Painel da pessoa | fundo --tj-layer-01, sombra --tj-shadow-2 |
| Coluna lateral | 360 px, intervalo --tj-space-6 |
| Foco no cabeçalho | contorno sólido de 2 px em --tj-text-on-brand, além do --tj-ring |
Código
vue
<BaseAppShell
v-model:collapsed="menuRecolhido"
product="Gestão de Obras"
unit="Secretaria de Engenharia"
home-href="/"
:nav="menu"
user-name="Marta Coelho"
user-role="Fiscal de contrato"
@navigate="(item, event) => { event.preventDefault(); router.push(item.href) }"
>
<template #logo><img src="/logo-tjes-branco.png" alt="Poder Judiciário · Tribunal de Justiça do Estado do Espírito Santo"></template>
<template #search><BaseSearch v-model="busca" label="Buscar em todo o sistema" landmark shortcut /></template>
<template #user-menu>
<a href="/perfil">Meu perfil</a>
<button type="button" @click="sair">Sair</button>
</template>
<BasePage title="Contrato 012/2026" subtitle="Reforma do Fórum de Alegre">
<template #actions><BaseButton kind="secondary">Editar contrato</BaseButton></template>
<BasePageAside>
<BaseCard title="Medições">…</BaseCard>
<template #aside><BaseCard title="Resumo">…</BaseCard></template>
</BasePageAside>
</BasePage>
</BaseAppShell>BaseAppShell
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
product | string | — | Nome do sistema ao lado do logo (obrigatório) |
unit | string | — | Segunda linha, como a secretaria; some abaixo de 992 px |
homeHref | string | — | Faz do logo e do nome um link para a tela inicial |
nav | NavGroup[] | [] | Grupos do menu lateral; sem ele não há menu |
navLabel | string | 'Menu principal' | Nome acessível do menu lateral |
v-model:collapsed | boolean | false | Menu recolhido em faixa de ícones (computador) |
userName | string | — | Pessoa conectada, com avatar |
userRole | string | — | Papel da pessoa, como "Fiscal de contrato" |
NavGroup: { label?: string, items: NavItem[] }. NavItem: { label: string, href: string, icon?: string, count?: number, countLabel?: string, current?: boolean }.
Slots: default (as telas), logo, search, tools, user-menu (links e botões do painel da pessoa; um <hr> separa grupos) e nav-footer (nota no pé do menu lateral, escondida quando recolhido e no celular). Eventos: navigate, com o item e o evento do clique, e update:collapsed.
BasePage
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
title | string | — | Título da tela, o único h1 (obrigatório) |
subtitle | string | — | Linha abaixo do título com o estado da página |
Slots: default (conteúdo) e actions (botões da tela, à direita do título; ocupam a largura toda no celular). Fora de um BaseAppShell, o BasePage desenha o próprio <main> num contêiner do Bootstrap, para apps que ainda não usam a moldura.
BasePageAside
Slots: default (conteúdo principal) e aside (coluna lateral de 360 px). Sem o slot aside, só o conteúdo aparece.
Acessibilidade
- Segue o padrão Landmarks do WAI-ARIA APG: o cabeçalho é
<header>(banner), o menu lateral é<nav>com o nome denavLabele o conteúdo é o único<main>. Dentro da moldura, oBasePagenão cria outro<main>. - O primeiro Tab da tela mostra o link "Pular para o conteúdo". Ele leva o foco ao
<main>sem mudar o endereço, para não confundir roteadores com#. - No computador, o cabeçalho fica fixo no topo. A página reserva a altura dele (
scroll-padding-top), então o item focado, o destino de uma âncora e o começo do conteúdo depois do link de pular nunca ficam escondidos sob o cabeçalho. - Cada grupo do menu é uma lista com o nome do seu título. A tela aberta tem
aria-current="page", e o leitor de tela anuncia "página atual". - O botão de menu tem nome fixo, "Menu lateral", e diz o estado por
aria-expanded;aria-controlsaponta para o menu. "Recolhido" quer dizer só ícones: os links continuam todos na ordem de leitura e de Tab. - Recolhido, cada item mantém o nome e a quantidade para o leitor de tela. O nome aparece numa dica ao lado do ícone, tanto com o mouse quanto com o foco do teclado, e Esc esconde a dica sem mover o foco. Um ponto sobre o ícone avisa que o item tem quantidade acima de zero.
- O painel da pessoa segue o padrão Disclosure: o botão diz
aria-expanded, os itens são links e botões comuns, alcançados com Tab. Esc fecha e devolve o foco ao botão; clicar fora, escolher um item ou sair do painel com Tab também fecha. Ao escolher um item, o foco volta ao botão, a não ser que o próprio item o tenha levado a outro lugar, como um modal. Ele não é umrole="menu", então as setas não são prometidas. - No celular, o nome da pessoa e o nome do sistema saem da tela, mas continuam para o leitor de tela no botão da pessoa e no bloco do logo (um link, com
home-href). - O foco no cabeçalho ganha contorno branco sólido, porque o anel translúcido não chega a 3:1 sobre o azul-marinho.
- As transições do menu recolhido, do link de pular e da seta do painel somem com
prefers-reduced-motion. Em alto contraste do Windows, o item atual ganha contorno e o cabeçalho, borda. - Os testes passam o axe em jsdom com o menu aberto, sem menu, com o painel da pessoa aberto e com página e coluna lateral, e rodam as regras de página inteira (
region,bypass, marcos únicos) com a moldura completa. O jsdom não aplica CSS, então o menu recolhido, o contraste e as quebras de layout são conferidos nas páginas desta documentação. - Nesta documentação, os exemplos ficam dentro do
<main>da própria página, então aparecem com um<main>aninhado e um cabeçalho que não é o banner. Os marcos foram conferidos nos testes, não aqui.