# Plano 10 — Layout Engine com Slots Configuráveis

> **Milestone:** M3 · **Depende de:** 09 · **Habilita:** 11–14
> **Objetivo:** definir a estrutura fundamental do front-end como uma **casca
> configurável**: o layout declara *slots*; módulos injetam conteúdo; o CMS
> reordena/oculta/preenche slots sem deploy.

---

## 1. Conceito

O layout é composto por **presets** (estrutura macro) + **slots** (pontos de
extensão nomeados). O módulo **não** conhece o template do portal: ele declara
"quero aparecer no slot `home.destaques`" e o Layout Engine resolve.

```
<x-layout::app preset="padrao">
    @slot('main.content')  ... @endslot
    @slot('main.left')     ... @endslot
</x-layout::app>
```

- **Preset** (Enum `LayoutPreset`): quais regiões existem (header/footer/sidebars) e o grid.
- **Slot**: ponto nomeado dentro de uma região (ex.: `header.search`, `home.destaques`,
  `footer.links`, `main.after`).
- **SlotRegistry**: em runtime, módulos registram provedores de slot via provider.
- **Slot config**: ordem, visibilidade e limite por slot vivem em JSONB, editáveis no CMS.

## 2. Estrutura

```
src/layout/
├── Engine/                    # motor em PHP (determinístico, testável)
│   ├── LayoutPreset.php       # Enum (DIVULGACAO, DIARIO_OFICIAL, NOTICIAS, PADRAO, SCRATCH)
│   ├── PresetConfig.php       # DTO readonly das regiões do preset
│   ├── SlotRegistry.php       # registro/resolução determinística de provedores
│   ├── SlotProvider.php       # contrato do provedor de slot
│   ├── SlotConfig.php         # config (readonly) de visibilidade/ordem/limite
│   ├── ThemeResolver.php      # precedeência de tema validada por schema
│   └── ThemeTokens.php        # DTO readonly → CSS custom properties
├── Components/                # API pública de Blade (namespace `layout`)
│   ├── portal.blade.php       # <x-layout::portal> — shell público (contrato do módulo)
│   ├── cms.blade.php          # <x-layout::cms>    — shell admin (contrato do módulo)
│   ├── slot.blade.php         # <x-layout::slot name="…">
│   └── ui/icon.blade.php      # <x-ui::icon name="…">
├── Portal/                    # views internas do shell público (namespace `portal`)
│   ├── partials/              # skip-links, a11y-bar, header, footer, metadata
│   └── regions/               # pilhas de slot (main.left/right/header/footer)
├── Cms/                       # views internas do shell admin (namespace `cms`, plano 12)
└── Assets/                    # design system: css/ (tokens, base, layout, a11y, print), js/, fonts/, icons/
```

- **Grid em CSS, não em PHP:** o shell emite `data-*` (preset/regiões) e o CSS resolve o grid
  (mobile-first, `fr` + `gap`); subgrid fica para **alinhamento entre componentes** (cards, forms),
  com fallback `@supports`. Nada de calcular percentuais em PHP.
- **Erros:** views em `resources/views/errors/{403,404,500,503}.blade.php` (idiomático),
  renderizando o shell com preset `SCRATCH`.

## 3. Contrato de slot

```php
interface SlotProvider {
    public function slot(): string;   // ex.: 'home.destaques'
    public function order(): int;     // default; sobrescrito por config (dado)
    public function render(): View;   // conteúdo do slot
}
```

> **Mínimo necessário (F3):** o contrato tem **só** isto. `SlotContext` / `WidgetSource` /
> `LayoutProfile` (planos 16, M3→M4) ficam **fora** do escopo de F3 — a estrutura já reserva os
> encaixes (`SlotRegistry`, `<x-layout::slot>`), sem construí-los agora.

- Módulo registra no `ModuleProvider::register()`: `SlotRegistry::add(HomeDestaquesSlot::class)`.
- `SlotRegistry` resolve, respeita `SlotConfig` (visível? ordem? limite?) e renderiza.
- Config vem do `services/Theme` (JSONB), com fallback no default do provider.
- **Falha resiliente**: slot indisponível → vazio, com log estruturado (nunca 500).

## 4. Presets e grid

- `LayoutPreset` governa `showHeader`, `showFooter`, `showMainHeader`, `showMainFooter`,
  `hasLeft`, `hasRight` (aprendizado do `cms/`).
- Grid mestre 21% / 55% / 21% com gap de 1.5%, via **CSS subgrid** para alinhamento.
- **Subgrid com Tailwind + daisyUI** (skill `css-moderno`): Tailwind 4 expõe
  `grid-cols-subgrid`/`grid-rows-subgrid`; daisyUI não tem grid próprio (usa utilitários
  Tailwind), então compõe sem conflito. Usar subgrid para **alinhamento entre componentes**
  (shell sidebar/conteúdo/aside, cards com header/body/footer alinhados, formulários
  label/input, tabela responsiva); para layout 1D, flexbox. Regras: o pai define as trilhas
  e o filho herda (`grid-template-rows: subgrid` + `grid-row: span N`); usar **um eixo só**
  quando o outro precisa de linhas próprias; **fallback**
  `@supports (grid-template-rows: subgrid)` (o layout deve funcionar sem ele); preferir
  subgrid a `display: contents` (a11y); manter a ordem do DOM = ordem de leitura.
- Overrides por página (`:has-left="false"`) permitidos; default = preset.
- Páginas de erro usam preset `SCRATCH` (SEO + acessibilidade sempre ativos) e
  informam a URL tentada + links de recuperação (aprendizado do `cms/`).

### 4.1. Semântica, mínimo JS e PWA (ADR-010)

- Cada região renderiza **landmarks** (`header/nav/main/aside/footer`) e um único `h1`.
- Navegação, tema, filtros simples e estados usam HTML/CSS nativos; JS só onde indispensável
  (editor, upload, interação com servidor).
- **PWA**: `manifest.webmanifest` + service worker de assets (registro após `load`),
  servidos pelo `base-struct`; funciona como *progressive enhancement*.
- A página pública **renderiza e navega sem JS** (validado por e2e com `javaScriptEnabled: false`).
- HTML moderno: sem handlers inline (`onclick`), `rel="noopener"` em `target="_blank"`,
  validação nativa de formulário e **nada** de elementos obsoletos (`<font>`, `<center>`,
  `<marquee>`).
- **i18n (ADR-012):** `<html lang>` correto (WCAG 3.1.1); seletor de idioma acessível no header;
  default no root e demais locales com prefixo (`/en/...`).

## 5. Atividades

- [ ] `LayoutPreset` enum + `config(): PresetConfig` + testes.
- [ ] Shell como componente (`<x-layout::portal>`/`<x-layout::cms>`): preset + regiões + grid CSS.
- [ ] Guardrails de subgrid documentados (uso cross-componente, fallback `@supports`, um eixo).
- [ ] `SlotRegistry` + `SlotProvider` + `SlotConfig` + resolução com fallback.
- [ ] Componentes `<x-layout::app>`, `<x-layout::slot name="..."/>`, regiões semânticas.
- [ ] `ThemeResolver` integrando `services/Theme`.
- [ ] Views base do portal (header/footer/main-section) com slots em todos os pontos
      hoje "hardcoded" no legado.
- [ ] Páginas de erro 404/500/503 acessíveis e úteis.
- [ ] Landmarks semânticos e `h1` único em todos os presets; PWA (manifest + service worker)
      no `base-struct`.
- [ ] Editor de layout no CMS (plano 12) consumindo `SlotConfig`.
- [ ] `comando de debug (backlog)` imprime preset × slots × provedores (debug/DX).

## 6. Testes e evidências

- [ ] Teste: preset define regiões corretas e grid esperado.
- [ ] Teste: provedor de slot aparece na ordem configurada; config inválida → default + log.
- [ ] Teste: slot sem provedor renderiza vazio sem erro.
- [ ] Teste: módulo registra slot e aparece na home **sem** importar o layout.
- [ ] Snapshot HTML estrutural (regiões semânticas presentes/conforme preset).
- [ ] Teste: layout funciona sem subgrid (fallback `@supports`) e mantém a ordem de leitura.
- [ ] Teste: a página pública funciona com JavaScript desabilitado (progressive enhancement).

## 7. Definition of Done

- [ ] Uma página é 100% montada por preset + slots, sem mark-up de módulo no layout.
- [ ] Reordenar/ocultar slot é dado, sem deploy.
- [ ] Erros 404/500 usam o engine e são acessíveis.

## 8. Riscos

| Risco | Mitigação |
|---|---|
| Slots virarem "ganchos" demais | Catálogo limitado e documentado; novos exigem ADR |
| CSS subgrid em navegador antigo | Fallback flex/grid simples; testar matriz de browsers |
| Config de slot divergir da view | Contrato tipado + teste de render |
