Skip to content

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; a área de conteúdo, de até 1280 px; e, se o sistema quiser, o rodapé. 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

Contratos de obras

42 contratos ativos. Medição de outubro aberta até 10/11.

Último item do menu escolhido

Contratos

Nesta 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; o cabeçalho continua preso no alto, e a faixa rola com a página.

Na faixa, os links que não cabem ficam para o lado, e a faixa avisa isso:

  • A tela abre com a faixa rolada até o item da tela atual, que fica no meio dela ou, perto de uma das pontas, o mais perto do meio que a faixa deixa. Sem isso, num celular de 390 px, o item atual podia ficar fora da tela. A faixa volta a esse item quando a janela muda de largura, como quando o celular gira. Enquanto a pessoa não mexe na faixa, ela também volta ao item quando os links crescem depois de a tela abrir (a quantidade ou o nome de um item que chega da API, ou a fonte Roboto que termina de carregar) e quando aparece a barra de rolagem da página. Depois que a pessoa rola a faixa ou passa por ela com Tab, a faixa fica onde a pessoa deixou até a próxima troca de tela.
  • Na borda que esconde links, aparecem um esmaecido e uma seta, que é um botão e rola a faixa para aquele lado. A seta some quando a faixa chega ao fim daquele lado e volta se os links crescerem depois, como quando a quantidade de um item chega da API.
  • Cada link tem pelo menos 44 × 44 px, o alvo de toque recomendado para o dedo.

O cabeçalho se ajusta à largura da própria moldura, não à da janela, e nenhum item fica sobre outro a partir de 320 px. Quando falta espaço, o que importa menos sai primeiro:

Largura da molduraO que muda no cabeçalho
Abaixo de 1200 pxA busca vira um ícone. Clicar nele, chegar nele com Tab ou usar o atalho abre o campo numa linha própria, que fica aberta enquanto houver texto digitado.
Abaixo de 992 pxA secretaria sai e a pessoa aparece só pelo avatar.
Abaixo de 576 pxCom logo, o nome do sistema sai e a seta do painel da pessoa também.

Em qualquer largura, se ainda faltar espaço, a secretaria e depois o nome do sistema terminam em reticências, e o logo diminui por último.

Uso ​

  • Use um BaseAppShell por sistema, na raiz do app. Cada tela é um BasePage dentro dele: o title vira o único h1 da tela, o subtitle diz o estado da página e o slot actions recebe 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 com label, href e icon. 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 na borda esquerda do próprio item, não só cor.
  • Dê a cada item o ícone do seu assunto, da página Ícones. No menu recolhido só os ícones aparecem, então dois itens com o mesmo ícone ficam iguais. Um nome longo quebra em duas linhas no menu aberto, sem reticências.
  • Use count para o que pede atenção, como medições abertas, e countLabel para dizer o que é contado: o leitor de tela ouve "7 medições abertas" junto com o nome do item.
  • Com roteador, ouça navigate, chame event.preventDefault() e navegue pelo item.href. O navigate só 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 no RouterLink. 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:collapsed para guardar a preferência da pessoa.
  • O slot search recebe a busca geral do sistema: um BaseSearch com landmark e, 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 tools recebe botões de ícone, como avisos e ajuda: BaseButton com kind="ghost" e icon-only.
  • user-name e user-role mostram quem está conectado. Com o slot user-menu, o nome vira um botão que abre um painel com links e botões (perfil, sair). Para a ação, use o BaseUserMenuItem, que já vem no estilo do painel. Para a escolha do tema, ponha o BaseThemeSwitch antes do "Sair": escolher um tema não fecha o painel.
  • O slot logo recebe o BaseLogo com background="dark", o logo oficial com o endosso em branco, sempre sobre o azul-marinho. Com home-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.
  • Passe a trilha da tela aberta em breadcrumb, a partir do roteador, e uma lista vazia nas telas sem trilha. O BasePage mostra a trilha numa linha reservada acima do título, então o título fica na mesma altura em todas as telas. Não ponha o BaseBreadcrumb solto antes do BasePage: o título desce e a tela pula ao trocar de página. O passo a passo está no Modelo de página.
  • O cabeçalho fica preso no alto da janela enquanto a página rola, em qualquer largura. No celular ele tem uma linha só, de 56 px; a faixa de ambiente do sistema é a borda de baixo dele e acompanha.
  • O menu lateral ocupa a altura toda da moldura, por mais longa que seja a tela; o conteúdo dele fica preso sob o cabeçalho e rola sozinho quando não cabe na janela. O menu nunca deixa a página mais alta: numa tela curta, os links que não cabem rolam dentro do menu.
  • O slot footer recebe o BaseFooter. Ele ocupa a largura toda, embaixo do menu lateral e do conteúdo, fora do <main>, fica no fim do conteúdo e vai para o fim da janela quando a tela tem pouco conteúdo. Ele não é fixo: rola com a página e nunca cobre o conteúdo. No computador, o texto do rodapé começa na mesma linha do conteúdo da tela. Sem o slot, não há rodapé.

Anatomia ​

  1. Link "Pular para o conteúdo": aparece só no primeiro Tab.
  2. Botão de menu: recolhe e abre o menu lateral; só existe com nav.
  3. Logo e nome: o slot logo, o product e o unit; com home-href, levam à tela inicial.
  4. Busca: o slot search.
  5. Ferramentas: o slot tools, com botões de ícone.
  6. Pessoa conectada: avatar, user-name e user-role; com o slot user-menu, abre um painel.
  7. Menu lateral: os grupos e itens de nav, com a quantidade de count, e o slot nav-footer no fim.
  8. Conteúdo: o BasePage, com a linha da trilha (breadcrumb), o título (title), o slot status, o subtitle, o slot actions e o conteúdo; o BasePageAside acrescenta a coluna lateral pelo slot aside.
  9. Rodapé: o slot footer, com o BaseFooter, embaixo do conteúdo.

Com rodapé ​

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.

Faça e não faça ​

FaçaNão faça
Um BaseAppShell na raiz do sistema de obras, com cada tela num BasePage.Montar uma moldura nova em cada tela.
Dar ícone a todos os itens do menu: recolhido, só eles aparecem.Deixar itens sem ícone, que viram espaços vazios no menu recolhido.
Pôr no cabeçalho a busca geral do sistema.Pôr no cabeçalho a busca da lista de contratos; ela fica na tela.

Estilo ​

PropriedadeToken
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çalhofundo --tj-layer-on-brand, borda --tj-border-on-brand
Altura do cabeçalho--tj-header (64 px; 56 px abaixo de 992 px)
Menu lateralfundo --tj-layer-01, borda --tj-border-subtle-02, largura --tj-sidebar (272 px; 64 px recolhido)
Item do menutexto --tj-text-secondary, hover --tj-layer-hover-01
Item atualfundo --tj-layer-selected-01, texto --tj-text-brand, barra de --tj-space-1 em --tj-border-interactive dentro do item
Ícone do item--tj-icon-sm (16 px), cor --tj-icon-secondary; --tj-icon-interactive no item atual
Botão do menu e pessoabotão do menu em --tj-text-on-brand com ícone de 20 px; a pessoa com menu tem contorno --tj-border-on-brand e a seta para baixo
Dica e ponto do menu recolhidodica com fundo --tj-background-inverse e texto --tj-text-inverse; ponto --tj-icon-interactive
Rodapéos tokens do BaseFooter
Área de conteúdofundo --tj-background, largura máxima --tj-content-max (1280 px), margem --tj-space-8 (--tj-space-4 no celular)
Linha da trilhaaltura mínima --tj-space-6, reservada mesmo vazia; --tj-space-2 até o título
Blocos da telaum embaixo do outro, a --tj-page-gap (24 px)
Painel da pessoafundo --tj-layer-01, sombra --tj-shadow-2
Coluna lateral360 px, intervalo --tj-page-gap
Foco no cabeçalhocontorno sólido de 2 px em --tj-text-on-brand, além do --tj-ring

Código ​

vue
<BaseAppShell
  v-model:collapsed="menuCollapsed"
  product="Gestão de Obras"
  unit="Secretaria de Engenharia"
  home-href="/"
  :nav="menu"
  user-name="Marta Coelho"
  user-role="Fiscal de contrato"
  :breadcrumb="breadcrumbOf(route)"
  @navigate="(item, event) => { event.preventDefault(); router.push(item.href) }"
>
  <template #logo><BaseLogo background="dark" /></template>
  <template #search><BaseSearch v-model="query" label="Buscar em todo o sistema" landmark shortcut /></template>
  <template #user-menu>
    <a href="/perfil">Meu perfil</a>
    <BaseUserMenuItem icon="box-arrow-right" @click="signOut">Sair</BaseUserMenuItem>
  </template>
  <BasePage title="Contrato 012/2026" subtitle="Reforma do Fórum de Alegre">
    <template #status><BaseBadge tone="success">Em execução</BaseBadge></template>
    <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>

Propriedades ​

BaseAppShell ​

Propriedade Tipo Padrão Descrição
productstringobrigatóriaNome do sistema ao lado do logo
unitstring—Segunda linha, como a secretaria; some abaixo de 992 px
homeHrefstring—Faz do logo e do nome um link para a tela inicial
navreadonly NavGroup[][]Grupos do menu lateral; sem ele não há menu
navLabelstring'Menu principal'Nome acessível do menu lateral
userNamestring—Pessoa conectada, com avatar
userRolestring—Papel da pessoa, como "Fiscal de contrato"
breadcrumbreadonly BreadcrumbItem[]—Trilha da tela aberta, que o BasePage mostra acima do título; uma lista vazia mantém a linha reservada
v-model:collapsedbooleanfalseMenu recolhido em faixa de ícones (computador)

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), nav-footer (nota no pé do menu lateral, escondida quando recolhido e no celular) e footer (o BaseFooter, embaixo do conteúdo e fora do <main>). Eventos: navigate, com o item e o evento do clique, e update:collapsed.

BasePage ​

Propriedade Tipo Padrão Descrição
titlestringobrigatóriaTítulo da tela, o único h1
subtitlestring—Linha abaixo do título com o estado da página

Slots: default (os blocos da tela, empilhados a --tj-page-gap), actions (botões da tela, à direita do título; ocupam a largura toda no celular), status (o selo da situação do registro, ao lado do título) e breadcrumb (troca a trilha que vem do BaseAppShell; com ele a linha também fica reservada). 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 ​

Sem propriedades.

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 de navLabel, o conteúdo é o único <main> e o BaseFooter do slot footer é o <footer> (contentinfo), fora do <main>. Dentro da moldura, o BasePage não cria outro <main>.
  • Teclado:
TeclaO que faz
TabO primeiro mostra "Pular para o conteúdo"; depois passa pelo cabeçalho (menu, logo, busca, ferramentas, pessoa), pelo menu lateral, pelo conteúdo e pelos links do rodapé
EnterNo link de pular, leva o foco ao conteúdo; num link do menu, abre a tela; num botão, aciona
EspaçoAciona o botão de menu e o botão da pessoa
EscFecha o painel da pessoa e devolve o foco ao botão; no menu recolhido, esconde a dica do item sem mover o foco
  • 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 #.
  • Na faixa do celular e do tablet, a seta de cada borda é um botão: "Ver links anteriores" no começo e "Ver mais links" no fim. Com clique, toque, Enter ou Espaço, ela rola a faixa para aquele lado, e o clique nunca passa para o link de baixo. Quando a faixa chega ao fim daquele lado, a seta some e o foco vai para o link da ponta. O esmaecido é só visual. Com Tab, o link em foco que estava cortado pela borda ou coberto pela seta vai para o meio da faixa. No alto contraste do Windows, o esmaecido some e a seta fica sobre o fundo do sistema, com uma linha do lado de dentro.
  • O cabeçalho fica fixo no topo, no computador e no celular. 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-controls aponta 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 é um role="menu", então as setas não são prometidas. A exceção é o BaseThemeSwitch, um grupo de rádio dentro do painel: o Tab entra nele pela opção escolhida, as setas trocam de opção só dentro dele, e escolher um tema não fecha o painel.
  • Em molduras estreitas, 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). A busca recolhida continua sendo o mesmo campo, com o mesmo nome, na mesma ordem de Tab.
  • 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, da seta do painel e das bordas da faixa somem com prefers-reduced-motion. Em alto contraste do Windows, o item atual ganha contorno e o cabeçalho, borda; o conteúdo não ganha contorno quando recebe o foco do link de pular.
  • 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.