# Guia de Performance Web — Padrões Recomendados para a V2

Este guia consolida as boas práticas de performance, acessibilidade e segurança aplicadas ao CMS V2,
servindo como referência obrigatória para todo novo desenvolvimento.

---

## 1. Carregamento de Scripts

### 1.1. Estratégia

| Estratégia | Quando usar | Exemplo |
|------------|-------------|---------|
| `defer` | **Padrão para todo script que manipula DOM** | `<script defer src="app.js">` |
| `async` | Analytics, widgets independentes (sem dependências) | `<script async src="analytics.js">` |
| Sync | **NUNCA usar** no `<head>` — bloqueia renderização | ❌ Evitar |

### 1.2. Regras

- Todo `<script src="...">` DEVE ter `defer` ou `async` — sem exceção.
- Scripts com `defer` preservam a ordem de execução (jQuery antes de plugins jQuery).
- Scripts inline (sem `src`) não precisam de `defer`, mas DEVEM ser agrupados em um único `DOMContentLoaded`.

```html
<!-- ✅ Correto -->
<script defer src="{{ asset('js/app.js') }}"></script>

<!-- ❌ Incorreto — bloqueia renderização -->
<script src="{{ asset('js/legacy.js') }}"></script>
```

### 1.3. Unificação de DOMContentLoaded

NUNCA crie múltiplos listeners `DOMContentLoaded` no mesmo documento. Agrupe toda a lógica de
inicialização em um ÚNICO bloco:

```javascript
document.addEventListener('DOMContentLoaded', function() {
    // 1. Estado da UI (sidebar, tema, etc.)
    // 2. Inicialização de componentes (tooltips, flatpickr, etc.)
    // 3. Event listeners (busca, formulários, etc.)
    // 4. Acessibilidade (controle de fonte, contraste, etc.)
});
```

---

## 2. Resource Hints

### 2.1. Ordem recomendada no `<head>`

```html
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>...</title>
<meta name="description" content="...">

<!-- Resource Hints — sempre antes de CSS/JS -->
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
<link rel="preload" href="/fonts/inter.woff2" as="font" crossorigin>

<!-- CSS (crítico inline, não-crítico async) -->
<style>/* CSS crítico */</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
```

### 2.2. Quando usar cada hint

| Hint | Propósito | Uso |
|------|-----------|-----|
| `preconnect` | Estabelece conexão TCP+TLS antecipada | CDNs, APIs third-party |
| `dns-prefetch` | Resolve DNS antecipadamente | Fallback para preconnect |
| `preload` | Recurso crítico desta página (alta prioridade) | Fontes, hero images, CSS crítico |
| `prefetch` | Recurso da próxima navegação (baixa prioridade) | Próxima página |

---

## 3. CSS Delivery

### 3.1. CSS Crítico vs. Não-Crítico

| Tipo | O que é | Como carregar |
|------|---------|---------------|
| **Crítico** | CSS necessário para renderizar o conteúdo acima da dobra | Inline no `<head>` dentro de `<style>` |
| **Não-crítico** | Todo o restante (Bootstrap, componentes, etc.) | Async via `preload` + `onload` |

### 3.2. Padrão de CSS Assíncrono

```html
<link rel="preload" href="{{ asset('css/bootstrap.min.css') }}" as="style"
      onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}"></noscript>
```

### 3.3. Arquitetura com `@layer`

Declare a ordem de cascata no topo do arquivo CSS principal:

```css
@layer reset, base, layout, components, utilities, print;
```

- **reset**: Normalização, box-sizing
- **base**: Variáveis (`:root`), tipografia base
- **layout**: Grid, sidebar, header, footer
- **components**: Botões, cards, tabelas, formulários
- **utilities**: Classes utilitárias (`.cursor-pointer`, `.sr-only`)
- **print**: Estilos de impressão

### 3.4. Propriedades Customizadas com `@property`

Use `@property` para variáveis de tema — garante type safety e permite animação:

```css
@property --theme-primary {
    syntax: "<color>";
    inherits: true;
    initial-value: #1d3557;
}
```

### 3.5. Performance de Renderização

| Propriedade | Efeito | Uso |
|-------------|--------|-----|
| `content-visibility: auto` | Adia renderização de seções fora da tela | Seções longas, grids de cards |
| `contain: layout style` | Isola renderização do componente | Sidebar, menu, cards isolados |
| `will-change` | Promove elemento para GPU (usar com moderação) | Apenas em animações iminentes |

```css
.secao-longa {
    content-visibility: auto;
    contain-intrinsic-size: 500px; /* Tamanho aproximado para scroll */
}
```

---

## 4. Imagens

### 4.1. Atributos Obrigatórios

```html
<!-- ✅ Correto -->
<img src="foto.jpg"
     loading="lazy"
     width="800"
     height="600"
     alt="Descrição acessível"
     decoding="async">

<!-- ✅ Hero image (acima da dobra) -->
<img src="hero.jpg"
     loading="eager"
     fetchpriority="high"
     width="1200"
     height="600"
     alt="Banner principal">
```

### 4.2. Regras

- `loading="lazy"` em TODAS as imagens abaixo da dobra.
- `loading="eager"` + `fetchpriority="high"` na hero image (única).
- `width` e `height` sempre especificados — previne CLS (Cumulative Layout Shift).
- `decoding="async"` libera o thread principal mais cedo.
- `alt` descritivo em imagens funcionais, `alt=""` em decorativas.

---

## 5. Acessibilidade

### 5.1. Skip Link

Primeiro elemento filho do `<body>` — obrigatório em todo layout CMS e Portal:

```html
<a href="#main-content" class="skip-link">Pular para o conteúdo principal</a>
```

CSS correspondente:

```css
.skip-link {
    position: absolute;
    top: -100%;
    left: 0;
    z-index: 9999;
    padding: 0.5rem 1rem;
    background: #000;
    color: #fff;
}
.skip-link:focus { top: 0; }
```

### 5.2. Tabelas Acessíveis

TODO `<th>` DEVE ter `scope="col"` (para colunas) ou `scope="row"` (para linhas):

```html
<table>
    <thead>
        <tr>
            <th scope="col">Nome</th>
            <th scope="col">Email</th>
            <th scope="col" class="text-end">Ações</th>
        </tr>
    </thead>
    <tbody>
        ...
    </tbody>
</table>
```

### 5.3. `aria-current` em Navegação

Use `aria-current="page"` no link ativo do menu de navegação:

```html
<a href="/cms" class="cms-menu-link" aria-current="page">Dashboard</a>
```

### 5.4. Preferências do Usuário

```css
:root { color-scheme: light; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}
```

---

## 6. Cache HTTP

### 6.1. Estratégia por Tipo de Recurso

| Tipo | Cache-Control | Validação |
|------|---------------|-----------|
| HTML | `no-cache` | ETag + Last-Modified |
| JS/CSS versionados | `public, max-age=31536000, immutable` | ETag |
| Imagens | `public, max-age=86400` | ETag |
| API data | `no-store` | — |
| Dados sensíveis | `no-store, private` | — |

### 6.2. Cache Busting

Assets versionados DEVEM incluir hash no filename:

```html
<link rel="stylesheet" href="{{ asset('css/app.a1b2c3d4.css') }}">
<script defer src="{{ asset('js/app.e5f6g7h8.js') }}"></script>
```

---

## 7. Segurança (Headers HTTP)

### 7.1. Headers Obrigatórios em Todas as Respostas

```
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Content-Security-Policy: ... (strict com nonces)
```

### 7.2. Content Security Policy (CSP)

Sempre usar CSP strict com nonces para scripts inline:

```
script-src 'self' 'nonce-{RANDOM}';
object-src 'none';
base-uri 'none';
```

NUNCA usar `'unsafe-inline'` em `script-src`.

### 7.3. Cookies de Sessão

```
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
```

---

## 8. Inline Styles — Proibido

É PROIBIDO usar `style="..."` em elementos HTML no CMS V2.

### 8.1. Exceções Permitidas

- `style="display: none;"` para toggle condicional via Blade (`@if` → renderiza ou não)
- `style="background-image: url(...)"` para imagens dinâmicas
- Propriedades dinâmicas controladas por JavaScript (ex: posição, animação)

### 8.2. Padrão Correto

```html
<!-- ❌ Incorreto -->
<button style="border-radius: 6px; background-color: #f8f9fa;">

<!-- ✅ Correto -->
<button class="cms-search-btn">
```

Crie classes CSS no arquivo `cms-layout.css` dentro do layer apropriado.

---

## 9. Imports de CDN

### 9.1. Subresource Integrity (SRI)

TODO recurso carregado de CDN externo DEVE ter `integrity` + `crossorigin`:

```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/.../style.css"
      integrity="sha384-abc123..."
      crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/.../script.js"
        integrity="sha384-def456..."
        crossorigin="anonymous" defer></script>
```

### 9.2. Gerando Hashes SRI

```bash
# Para CSS
curl -s https://url-do-recurso | openssl dgst -sha384 -binary | openssl base64 -A

# Para JS
curl -s https://url-do-recurso | openssl dgst -sha384 -binary | openssl base64 -A
```

---

---

## 9. Otimização de Imagens com WebP

### 9.1. Componente Picture (Padrão Obrigatório)

Toda imagem no portal DEVE ser renderizada via `v2-service-media::components.picture`, **nunca** com `<img src="...">` direto.

```blade
{{-- ✅ Correto: WebP responsivo + fallback --}}
@include('v2-service-media::components.picture', [
    'originalPath' => $item->file_path,
    'variants'     => $item->variants,
    'alt'          => 'Descrição acessível',
])

{{-- ❌ Incorreto: sem WebP, sem responsividade --}}
<img src="{{ v2_storage_url($item->file_path) }}" alt="...">
```

### 9.2. Comportamento Anti Double-Download

O componente aplica `loading="lazy"` no `<img>` fallback mesmo quando o parâmetro `lazy=false` é passado, desde que haja variantes WebP. Isso impede que o Chrome preload scanner inicie um download desnecessário do PNG/JPG original.

Quando `lazy=false` com variantes, o primeiro `<source>` WebP recebe `fetchpriority="high"` para garantir carregamento prioritário.

### 9.3. Regras para Seeders

Ao processar imagens em seeders, SEMPRE extraia e armazene as variantes:

```php
$media = $galleryService->seedFromPath($localPath, $config, $filename, $altText);

// Extrai variantes do resultado do gallery
$variants = isset($media['variants']) 
    ? (is_string($media['variants']) ? json_decode($media['variants'], true) : $media['variants']) 
    : [];

// Armazena nos parâmetros da seção
'image'          => $media['file_path'],
'image_id'       => $media['id'],
'image_variants' => $variants,
```

### 9.4. Onde usar `v2_storage_url()` vs `Storage::url()`

| Helper | Onde usar |
|--------|-----------|
| `v2_storage_url($path)` | **SEMPRE** em views (Blade). 50+ views já usam este helper |
| `StorageService::url()` | **NUNCA** em views. Uso exclusivo em serviços/controllers |

A exceção é o helper `v2_storage_url()` definido em `V2/Core/Helpers/helpers.php`, que resolve o disco correto automaticamente.

---

## 10. Checklist Prático para Novos Módulos/Views

- [ ] Scripts com `defer` (nunca sync)
- [ ] Imagens SEMPRE via `@include('v2-service-media::components.picture', [...])`
- [ ] Imagens com `loading="lazy"` + `alt` descritivo
- [ ] `v2_storage_url()` em vez de `Storage::url()` em views
- [ ] `<th>` com `scope="col"`
- [ ] `aria-current="page"` no link ativo
- [ ] Skip link presente (se for layout completo)
- [ ] Zero inline styles (exceto exceções)
- [ ] `@property` para novas variáveis de tema
- [ ] Classes em `@layer` apropriado
- [ ] Cache headers corretos para assets
- [ ] CSP compatível (sem `'unsafe-inline'` em scripts)

---

## Referências

- [HTML Living Standard](https://html.spec.whatwg.org/)
- [Web Vitals](https://web.dev/vitals/)
- [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [CSS Containment](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Containment)
- [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
