# História 18 — Estrutura do site (tema, shell e menus)

- **Spec:** `tests/18-estrutura-site.spec.ts`
- **Camadas:** E2E (Playwright + axe), PHPUnit feature (`ThemeResolverTest`, `CmsShellTest`,
  `MenusNavigationTest`, `ConfiguracoesThemeTest`, `ConfiguracoesShellTest`)
- **Donos:** Layout (`Layout\Engine`, `Layout\Cms`), Infraestrutura/Configurações,
  Infraestrutura/Menus
- **Status:** ativa

## Jornada

Uma pessoa com `configuracoes.editar`/`menus.editar` muda a **estrutura do site como dado**
(ADR-016/ADR-036/ADR-046): um token de tema, a posição da navegação do painel e o menu principal.
O portal e o painel refletem a mudança no request seguinte, sem deploy, sem CSS/HTML no banco e
sem quebrar acessibilidade. Cada teste restaura o dado original pela própria UI (`finally`).

## Critérios de aceite

1. **Tema:** `Configurações → Aparência` salva `color_primary` válido; o `GET /theme.css` muda de
   corpo e `ETag`, o portal passa a referenciar a folha e publica o token em
   `getComputedStyle(...).getPropertyValue('--tenant-primary')`; o valor original volta no fim.
2. **Shell:** `Configurações → Painel` muda `sidebar_position`; `html[data-cms-sidebar]` acompanha
   e a **ordem de foco** (DOM) segue a posição: à direita o conteúdo vem antes da sidebar; à
   esquerda, depois. O valor original volta no fim.
3. **Menu:** editar o rótulo `pt_BR` de um item do `principal` aparece no `<nav>` do portal; o
   rótulo original volta e o temporário desaparece.
4. **Adicionar/remover:** item novo com rota viva do portal aparece no header; a remoção responde
   **200** (nunca 500), some da tabela e do portal — sem deixar item órfão.
5. **Axe:** zero violações WCAG 2.0/2.1/2.2 **A/AA** nas telas tocadas do painel (Aparência, Painel,
   Menus, árvore/edição de item) e no portal no estado alterado.
6. **Determinismo:** nenhuma espera fixa; o portal é aberto com chave de cache única por estado
   (o HTML público tem `max-age`) e as asserções esperam o efeito observável; o spec não depende de
   dado de outro arquivo (`test.describe.serial` dentro dele).

## Pré-condições

- `make up` + `php artisan db:seed` (usuário do painel e menu `principal` de referência).
- Assets compilados (`make check`).
- Se o `db:seed` foi rodado antes de um módulo novo existir, rode-o de novo: o perfil
  `Administrador` deriva as permissões do manifesto (`menus.ver`/`menus.editar` incluídas).

## Invariantes verificados

- `theme.css` é same-origin, com `ETag`; o painel só grava tokens do `ThemeSchema`.
- A posição da sidebar é atributo/dado (CSS decide), nunca estrutura por JS; o DOM acompanha a
  ordem visual (foco = ordem).
- O portal lê o menu pela porta `Core\Contracts\Navigation`; link interno via `route()` (locale
  preservado) e nenhum link morto.
- Sem `<style>`/`style=`/`<script>` inline nas telas exercitadas.

## Fora de escopo

- Contraste de **qualquer** cor livre (o axe prova o estado exercitado; o contrato é o editor
  aceitar cor CSS segura, não um verificador de contraste em PHP).
- Ação “Configurar layout da área” (P1.S4b) e SEO publicado (P1.S5) têm prova em feature/unit;
  não são jornada de edição desta história.
- Reset de token/posição para “herdar o default”: a restauração grava os valores efetivos
  originais (mesmo comportamento observável), como na história 17.

## Evidência

`make check` (PHPUnit) + `make check E2E=1`: o spec roda no projeto **`cms-serial`**, que é
`dependencies` de todos os demais (o dado global é mutado/restaurado antes do resto do lote),
com axe embutido.
