# Xertica UI — Base de Conhecimento Completa

> **Propósito deste documento:** esta é uma fonte de conhecimento única e exaustiva sobre a biblioteca **xertica-ui**, preparada para alimentar o **Knowledge Base do FDM** (Xertica Agent Engine). O objetivo é permitir que um assistente de IA responda com precisão qualquer dúvida sobre a xertica-ui — instalação, arquitetura, componentes, padrões de página, temas/branding, CLI, internacionalização, gerenciamento de estado, o assistente de IA embutido e as regras de uso obrigatórias para agentes de IA que geram código com esta biblioteca.

**Biblioteca documentada:** `xertica-ui` v3.0.1 — design system React enterprise construído sobre **Tailwind CSS v4**, **Radix UI** e **Lucide Icons**, com uma camada de documentação "AI-first" (arquivos `llms.txt`, `llms-compact.txt`, `llms-full.txt`, `components.json`, `docs/decision-tree.md`) pensada para consumo por agentes de IA.

**Como este documento foi construído:** consolidação de toda a documentação oficial do pacote (`docs/*.md`, `docs/components/*.md`, `docs/patterns/*.md`, `guidelines/Guidelines.md`, `guideline.md`, `README.md`, `package.json`) cruzada com leitura direta do código-fonte para confirmar comportamentos, detectar funcionalidades não documentadas e sinalizar divergências entre documentos. Onde a documentação e o código-fonte divergem, isso é explicitado na Seção 17 ("Divergências Conhecidas") em vez de escolhido silenciosamente.

---

## Sumário

1. [Visão Geral e Posicionamento](#1-visão-geral-e-posicionamento)
2. [Instalação e Configuração](#2-instalação-e-configuração)
3. [Arquitetura — FSD + FDA](#3-arquitetura-fsd--fda)
4. [Sistema de Layout](#4-sistema-de-layout)
5. [Gerenciamento de Estado](#5-gerenciamento-de-estado)
6. [Internacionalização (i18n)](#6-internacionalização-i18n)
7. [Sizing de Formulários](#7-sizing-de-formulários)
8. [Branding, Temas e Identidade Visual](#8-branding-temas-e-identidade-visual)
9. [Assistente de IA (XerticaAssistant)](#9-assistente-de-ia-xerticaassistant)
10. [CLI — `npx xertica-ui`](#10-cli--npx-xertica-ui)
11. [MCP — Model Context Protocol](#11-mcp--model-context-protocol)
12. [Guidelines e Regras Não-Negociáveis para IA](#12-guidelines-e-regras-não-negociáveis-para-ia)
13. [Uso por Agentes de IA — Protocolo de Consulta](#13-uso-por-agentes-de-ia--protocolo-de-consulta)
14. [Árvore de Decisão de Componentes](#14-árvore-de-decisão-de-componentes)
15. [Padrões de Página (Page Patterns)](#15-padrões-de-página-page-patterns)
16. [Referência Completa de Componentes (A–Z)](#16-referência-completa-de-componentes-az)
17. [Divergências Conhecidas e Notas de Precisão](#17-divergências-conhecidas-e-notas-de-precisão)

---



---

## 1. Visão Geral e Posicionamento

### Pitch

> "Enterprise-grade React design system built on Tailwind CSS v4, Radix UI, and Lucide Icons — with a robust AI-first documentation layer for precise LLM-driven composition and autonomous agent interaction."

### Principais diferenciais

- **AI-first single source of truth**: `llms.txt`, `llms-compact.txt`, `llms-full.txt` e `docs/llms.md` como entry points dedicados para agentes de IA.
- **CLI de scaffolding** (`npx xertica-ui@latest init`) que gera app completo com roteamento, layout e arquitetura FSD/FDA pré-configurados, incluindo modo monolíngue transparente e feature flags persistidas (`.xertica.json`, `.languages.json`).
- **10 temas de cor embutidos**, selecionáveis no `init` ou via `npx xertica-ui update` → *Theme only*:

| Tema | ID | Primary | Sidebar | Dark bg |
|---|---|---|---|---|
| Xertica Classic (default) | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
| Xertica (identidade "serigrafia") | `xertica` | `#1E1E1E` | `#1E1E1E` | `#141311` |
| Zinc | `zinc` | `#18181B` | `#18181B` | `#05050d` |
| Slate | `slate` | `#0F172A` | `#0F172A` | `#05050d` |
| Blue | `blue` | `#2563EB` | `#1E3A8A` | `#03050f` |
| Violet | `violet` | `#7C3AED` | `#4C1D95` | `#07040f` |
| Rose | `rose` | `#BE123C` | `#881337` | `#0f0305` |
| Emerald | `emerald` | `#047857` | `#064E3B` | `#030f08` |
| Amber | `amber` | `#B45309` | `#78350F` | `#0f0a03` |
| Orange | `orange` | `#C2410C` | `#7C2D12` | `#0f0703` |

O tema **`xertica`** tem identidade visual própria ("serigrafia"): botões pill-shaped (`--radius-button: 9999px`) com stroke visível em toda variante filled; accent geral (`--primary`) é Negro Xertica (`#1E1E1E`, `#F2EDD8` no dark); amarelo (`#FAF338`) fica restrito a apenas dois controles — `Button` variante `default` e o estado marcado do `Switch` — via `--button-primary-bg`/`--button-primary-foreground`; `secondary` button é preto; fundo de página é off-white quente (`#FFFEF8`) com cards em branco puro.

Cada tema controla: `--primary` e `--sidebar` (light+dark), `--chart-1..5`, `--gradient-diagonal`, e (só no dark mode) `--background`/`--card`/`--popover`/`--muted`/`--accent`/`--border`/`--input`. Superfícies do modo claro permanecem sempre branco/zinc, exceto quando o tema define `backgroundLight` (só `xertica` faz isso). `--radius-button`/stroke/cores de `secondary` só mudam quando o tema define `buttonRadius`/`buttonStroke*`/`buttonSecondary*` (também só `xertica`).

- Token `--mobile-content-padding` (default `1.25rem`) controla padding horizontal em telas < 768px.
- **Contrato de independência de componente**: `xertica-ui/style.css` é o único import global obrigatório; componentes públicos são desenhados para funcionar isoladamente sem exigir `<XerticaProvider>` para a maioria das primitivas (o provider é uma conveniência recomendada, não um requisito universal). Componentes com configuração externa inevitável (ex.: Google Maps) devem renderizar um estado de configuração/erro em vez de derrubar a app.

> **Divergência de escala:** o README anuncia "**100+** Components" no título do catálogo, enquanto a contagem auditada em `docs/llms.md` é **84**. Ver nota na Seção 8.

### Stack tecnológica

| Tecnologia | Versão |
|---|---|
| React | 18.3 |
| TypeScript | 5.7 |
| Tailwind CSS | 4.0 |
| Vite | 6.0 |
| Radix UI | Latest |
| Lucide React | 0.469+ |
| Vitest | 4.1 |

### Scripts principais

`npm run dev` roda o scaffold `templates/` com o design system em live source (via alias de monorepo) — o mesmo app que um consumidor de `npx xertica-ui init` recebe; `npm run dev:pages` roda o harness raiz para o showcase standalone de `xertica-ui/pages`; `build`, `storybook`, `test` (Vitest), `type-check`. Nota: `templates/` tem dependências próprias — requer `npm install` dentro de `templates/` antes do primeiro `dev`.

### Licença

Proprietária — Xertica.ai Team.

### Links relevantes

- [`llms.txt`](./llms.txt) · [`llms-compact.txt`](./llms-compact.txt) · [`llms-full.txt`](./llms-full.txt) — entry points para agentes de IA
- [`docs/llms.md`](./docs/llms.md) — índice mestre de documentação


---

## 2. Instalação e Configuração

Xertica UI é um design system React enterprise-grade construído sobre **Tailwind CSS v4**, **Radix UI** e **Lucide Icons**, com uma camada de documentação "AI-first" pensada para composição precisa por LLMs e agentes autônomos. Cobre desde primitivas de UI até templates de página completos (login, dashboard, CRUD), assistente de IA embutido e integração com Google Maps.

### Requisitos e dependências

- Node.js >= 18
- React >= 18 (stack de referência usa **React 18.3**)
- Projeto Vite, Next.js ou CRA
- **Peer dependencies obrigatórias** (não instaladas automaticamente em `npm install xertica-ui`): `react@^18`, `react-dom@^18`, `react-router-dom@^7`

Pacotes recomendados (não são peer deps, mas esperados em projetos com arquitetura `features/`):

| Pacote | Versão | Papel |
|---|---|---|
| `@tanstack/react-query` | `^5.x` | Server state |
| `zustand` | `^5.x` | Client UI state |
| `i18next` | `^26.x` | Motor de i18n |
| `react-i18next` | `^17.x` | Binding React do i18next |

### Duas formas de instalar

**A) CLI (recomendado para projeto novo)**

```bash
npx xertica-ui@latest init
```

> Sempre use `@latest` — sem essa flag, o `npx` pode rodar uma versão local em cache em vez de buscar a mais recente do registry.

Prompts interativos:

| Prompt | Opções |
|---|---|
| Páginas a incluir | Login, Home, Template (multi-select) |
| Idiomas suportados | pt-BR, English, Español (multi-select, mínimo 1) |
| Tema de cor padrão | Xertica, Xertica Classic, Slate, Zinc, Blue, Violet, Rose, Emerald, Amber, Orange |
| Habilitar dark mode? | sim (padrão) / não |
| Incluir AI Assistant? | sim (padrão) / não |
| Instalar dependências | sim / não |

O `init`: cria a estrutura FSD/FDA, instala dependências, configura Tailwind v4, injeta `tokens.css`, copia **apenas as pastas de locale** dos idiomas escolhidos, escafolda condicionalmente o assistente (`AssistantPage`, `features/assistant/`) e persiste as escolhas em `src/locales/.languages.json` (`{ version: 1, codes: [...] }`) e `.xertica.json` (`{ version: 1, hasAssistant, disableDarkMode }`).

**B) Instalação manual em projeto existente**

```bash
npm install xertica-ui
```

1. Importar CSS (deve vir **antes** dos seus próprios estilos):
```tsx
import 'xertica-ui/style.css';
```
2. Configurar Tailwind para escanear a lib:
```js
// tailwind.config.js
export default {
  content: ['./src/**/*.{ts,tsx}', './node_modules/xertica-ui/**/*.{js,ts,jsx,tsx}'],
};
```
3. Montar o *provider stack* completo na raiz.

### Provider stack (ordem importa)

```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { XerticaProvider } from 'xertica-ui/brand';
import { BrowserRouter as Router } from 'react-router-dom';
import { AuthProvider } from './app/context/AuthContext';
import { AppErrorBoundary, PageErrorBoundary } from './shared/error-boundary';
import 'xertica-ui/style.css';

const queryClient = new QueryClient({
  defaultOptions: { queries: { retry: 1, refetchOnWindowFocus: false } },
});

function App() {
  return (
    <AppErrorBoundary>
      <QueryClientProvider client={queryClient}>
        <XerticaProvider>
          <Router>
            <AuthProvider>
              <PageErrorBoundary>
                <YourRoutes />
              </PageErrorBoundary>
            </AuthProvider>
          </Router>
        </XerticaProvider>
      </QueryClientProvider>
    </AppErrorBoundary>
  );
}
```

Razão de cada camada, de fora para dentro:

1. `AppErrorBoundary` — captura qualquer crash, inclusive falha de providers
2. `QueryClientProvider` — habilita React Query para toda a árvore
3. `XerticaProvider` — tema, layout, toasts, tooltips, maps, contexto do assistente
4. `Router` — precisa vir antes de `AuthProvider` (usa `useNavigate`)
5. `AuthProvider` — sessão e guards de navegação
6. `PageErrorBoundary` — isola erros por página; o chrome do app continua vivo

`XerticaProvider` inicializa internamente: sistema de tema (dark/light + injeção de tokens), toasts (Sonner via portal), `TooltipProvider` (Radix), `LayoutContext`, contexto do AI Assistant, `LanguageContext` (pt-BR/en/es por padrão) e o loader lazy do Google Maps. Seu único prop documentado é `children: ReactNode` (obrigatório).

### `useAuth()` / Route Guards

`AuthContext` expõe `user: AuthUser | null`, `isLoading: boolean`, `login(email, password) => boolean`, `logout() => void`. `ProtectedRoute`/`GuestRoute` (gerados pela CLI em `AuthGuard.tsx`) sempre checam `isLoading` antes de redirecionar, para evitar *flash redirects* durante a hidratação do `localStorage`.

### CSS crítico — `@theme inline`

O `src/styles/index.css` do consumidor deve seguir esta ordem exata:

```css
@import 'xertica-ui/style.css';       /* 1. estilos compilados da lib */
@import './xertica/tokens.css';       /* 2. seus tokens de tema */
@source '../node_modules/xertica-ui'; /* 3. Tailwind escaneia a lib */

@theme inline {                       /* 4. MUST be `inline`, not plain @theme */
  --color-primary: var(--primary);
  --color-destructive: var(--destructive);
}
```

`@theme {}` puro compila os tokens de cor **estaticamente** no build, usando os valores default da lib; `@theme inline` preserva as referências `var()`, permitindo que `tokens.css` sobrescreva em runtime. Essa é a causa mais comum de "cores de tema não aplicam".

### Scripts do projeto escafoldado

```bash
npm run dev / build / type-check / lint / lint:fix / format / format:check / check
```

### Troubleshooting rápido

| Sintoma | Causa | Fix |
|---|---|---|
| Componentes transparentes/sem estilo | Falta `import 'xertica-ui/style.css'` | Importar no entry, antes dos estilos próprios |
| Dark mode não aplica | Falta classe `dark` no `<html>` ou `@theme` sem `inline` | Usar `<ThemeToggle>` ou corrigir CSS |
| Erros de TS em subpath imports | `moduleResolution: "node"` | Trocar para `"bundler"`, `"node16"` ou `"nodenext"` |
| Toasts não aparecem | Falta `<XerticaProvider>` (que injeta `<Toaster>`) | Envolver a app; ou adicionar `<Toaster />` manualmente |
| Dialogs/Modals não aparecem | Falta `<XerticaProvider>` (contexto de portal/Tooltip) | Envolver a app na raiz |
| `useLayout()` lança "must be used within LayoutProvider" | Componente fora de `<XerticaProvider>` | Garantir ancestralidade; usar `useOptionalLayout()` se o componente pode existir fora (ex.: Storybook) |
| Sidebar sobrepõe conteúdo | Área de conteúdo não lê `sidebarWidth` | Aplicar `sidebarWidth`/`sidebarExpanded` como `paddingLeft` |
| Assistente de IA não responde (modo real) | Chave Gemini ausente/inválida | `initialGeminiApiKey` no provider, `useApiKey()`, ou `demoMode={true}` para testes |

---



---

## 3. Arquitetura — FSD + FDA

### 2.1 Domínios de componentes (dentro do pacote)

| Domínio | Path | Conteúdo |
|---|---|---|
| `ui` | `components/ui/[name]/` | Primitivas do design system (Button, Card, Calendar, Chart...) |
| `layout` | `components/layout/` | Sidebar, Header, SidebarPrimitive |
| `blocks` | `components/blocks/` | Componentes compostos de alto nível a partir de `ui/` |
| `assistant` | `components/assistant/` | XerticaAssistant, MarkdownMessage, CodeBlock etc. |
| `brand` | `components/brand/` | XerticaLogo, ThemeToggle, LanguageSelector, XerticaProvider |
| `pages` | `components/pages/` | LoginPage, HomePage, TemplatePage etc. |
| `shared` | `components/shared/` | utils.ts, use-mobile.ts, layout-constants.ts, error-boundary.tsx, error-fallbacks.tsx |

Cada domínio de componente tem um par de arquivo espelho em `docs/components/`. O pacote npm **inclui o código-fonte** (`components/`, `contexts/`, `hooks/`, `lib/`, `i18n.ts`, `locales/` — via campo `files` do `package.json`), justamente para que agentes de IA possam inspecionar implementação, stories e docs direto em `node_modules/xertica-ui/` — essa é a decisão de design "AI-first" central da lib.

### 2.2 Subpath exports

```
xertica-ui           → components/index.ts        (barrel completo)
xertica-ui/ui         → todas as primitivas de UI
xertica-ui/blocks      → padrões compostos (ActivityCard, ProjectCard...)
xertica-ui/layout     → Sidebar, Header
xertica-ui/brand       → XerticaProvider, XerticaLogo, ThemeToggle, LanguageSelector...
xertica-ui/assistant   → XerticaAssistant, MarkdownMessage, CodeBlock...
xertica-ui/media       → VideoPlayer, AudioPlayer, FloatingMediaWrapper
xertica-ui/hooks       → useLayout, useTheme, useLanguage, useBrandColors, useAssistente, useApiKey
xertica-ui/pages       → HomePage, TemplatePage (templates prontos)
xertica-ui/style.css   → folha de estilos compilada
```

> **Divergência de fontes:** `docs/architecture.md` (auditado em 2026-05-20) lista apenas **7** subpaths e não menciona `xertica-ui/pages`. O `README.md` raiz (mais recente, versão 3.0.1) já documenta e usa `xertica-ui/pages` ativamente (`import { TemplatePage } from 'xertica-ui/pages'`), inclusive alertando que `HomePage`/`TemplatePage` chamam `useAuth()` internamente e exigem `<AuthProvider>` como ancestral. Trate o README como fonte mais atual quanto ao número de subpaths.

Cada subpath expõe `types` (`.d.ts`), `import` (ESM) e `require` (CJS/UMD). TypeScript exige `"moduleResolution": "bundler"` (ou `node16`/`nodenext`).

### 2.3 Estrutura FSD/FDA no projeto consumidor (gerado pela CLI)

```
src/
  app/              ← BrowserRouter, XerticaProvider, AuthGuard, AppLayout
  shared/
    config/         ← navigation.ts (definição de rotas)
    lib/            ← auth.ts (helpers de localStorage)
    types/          ← auth.ts (interface User)
  features/
    auth/ui/        ← LoginContent, ForgotPasswordContent, VerifyEmailContent, ResetPasswordContent
    home/
      data/mock.ts  ← tipos + fetch tipado + factory functions (ex: getMockRichSuggestions())
      hooks/        ← useFeatureCards() — queryKey language-aware
      store/        ← dashboardStore.ts (Zustand)
      ui/           ← HomeContent
    template/ui/    ← TemplateContent, FormTemplate
    assistant/      ← AssistantConfig + useAssistantConfig() (só quando o Assistant é incluído)
  pages/            ← wrappers finos: LoginPage, HomePage, TemplatePage, AssistantPage...
  styles/           ← index.css, xertica/tokens.css
  i18n.ts           ← gerado pela CLI — imports/resources só dos idiomas selecionados
  locales/
    <lang>/         ← ver seção i18n
    .languages.json
```

**Regra de import entre camadas:** cada `feature` só importa de `shared/` ou de seu próprio domínio; `pages/` apenas compõe `features/` (nunca contém lógica de negócio própria).

Cada slice `features/<nome>/` segue a convenção:

```
features/<nome>/
├── data/mock.ts       ← tipos + dados mock + async fetch*() (ponto de troca p/ API real)
├── hooks/use<Xxx>.ts  ← useQuery envolvendo o fetch, com queryKey language-aware
├── store/<nome>Store.ts ← Zustand, só se houver estado de UI client-side compartilhado
├── ui/<Nome>Content.tsx
└── index.ts           ← barrel
```

> **Nota fonte-vs-template:** no código-fonte da própria lib, `features/home/hooks/` inclui `useDashboardStats.ts` e `useTeamMembers.ts`, usados apenas pelo showcase interno (`TemplateContent.tsx`). Esses dois hooks **não** são gerados no template escafoldado — projetos consumidores que precisarem deles devem criar seus próprios seguindo o mesmo padrão.

### 2.4 Padrão Headless UI (3 camadas de composabilidade)

**Tier 1 — Monolítico (drop-in):**
```tsx
<Sidebar navigationGroups={groups} />
<XerticaAssistant demoMode={true} />
<RichTextEditor value={html} onChange={setHtml} />
```

**Tier 2 — Compound Components (controle estrutural):**
```tsx
<Sidebar.Root navigationGroups={groups}>
  <Sidebar.Header logo={<MyLogo />} />
  <Sidebar.Nav />
  <Sidebar.Search search={searchConfig} />
  <Sidebar.Footer showUser showSettings />
</Sidebar.Root>
```

**Tier 3 — Hooks Headless (controle total, zero UI):**

| Hook | Import | Descrição |
|---|---|---|
| `useSidebar` | `xertica-ui/layout` | Expansão, overflow, estado de navegação |
| `useAssistant` | `xertica-ui/assistant` | Estado completo: mensagens, conversas, handlers |
| `useRichTextEditor` | `xertica-ui/ui` | Formatação, busca, gerenciamento de links |

```tsx
const { expanded, toggleExpanded, navigationGroups } = useSidebar({ defaultExpanded: true });
const { mensagens, mensagem, setMensagem, handleEnviarMensagem } = useAssistant({ demoMode: true });
```

Princípios de design: (1) o hook detém todo o estado — nada fica no componente apresentacional; (2) retrocompatível — Tier 1 nunca muda; (3) fonte única de verdade é o hook; (4) todos os handlers usam `useCallback`; (5) TypeScript-first com `Props`/`Return` explícitos para cada hook.

### 2.5 Notas internas de refatoração (`architecture-improvements.md`)

Este documento (PT-BR, análise de Maio/2026) é uma **auditoria de qualidade do código-fonte da própria biblioteca** — não afeta como consumidores usam a lib, mas é útil contexto de "por quê" e "débito técnico conhecido":

- **✅ Resolvido (v2.2.0):** `ThemeToggle` bypassava `ThemeContext` manipulando `classList` direto; corrigido, e o bug de `disableDarkMode` hard-coded em `templates/src/app/App.tsx`/`bin/language-config.ts` (que tornava `toggleTheme()` um no-op) também foi corrigido.
- **✅ Resolvido (v2.1.9):** mock assíncrono via `setTimeout` foi extraído para o padrão *swap point* em `features/*/data/mock.ts` (ver Seção 4).
- **Pendente (crítico):** tipos duplicados entre `contexts/AssistenteContext.tsx` (`Message`, `Conversa`, `SearchResult`...) e `components/assistant/xertica-assistant/xertica-assistant.tsx` (`Message` com campos diferentes, `Conversation` em vez de `Conversa`) — solução proposta: `types.ts` compartilhado.
- **Pendente (alto):** detecção de mobile duplicada via `window.innerWidth < 768` em 4+ arquivos (`use-assistant.ts`, `use-sidebar.ts`, `AudioPlayer.tsx`, `LayoutContext.tsx`) em vez de reutilizar o hook `useIsMobile()` já existente (`components/shared/use-mobile.ts`, baseado em `matchMedia`); `AudioPlayer.tsx` (663 linhas) sem hook headless; `xertica-assistant.tsx` com **1573 linhas** aguardando decomposição em subcomponentes (`AssistantMessageList`, `AssistantHistoryTab` etc.).
- **Pendente (médio/baixo):** `sidebar.tsx` com 1089 linhas; atalhos de teclado e persistência em cookie misturados dentro de `LayoutContext`; `hexToRgb` inline em `BrandColorsContext`; tooltips duplicados (`SidebarTooltipContent` ≈ `AssistantTooltipContent`); alturas hardcoded em `use-sidebar.ts`.
- **Padrões de referência positivos**, citados como modelo a seguir: `useRichTextEditor` e `useTreeView` (este último com navegação por teclado WAI-ARIA no hook, não no componente).

---



---

## 4. Sistema de Layout

O layout é um shell de três colunas gerenciado globalmente por `LayoutContext` (injetado por `<LayoutProvider>`, incluído dentro de `<XerticaProvider>` — nunca instanciado diretamente):

```
┌──────────────┬─────────────────────────┬──────────────┐
│   Sidebar    │    Main Content Area    │  AI Panel    │
│ (collapsed:  │  paddingLeft = sidebar  │ (assistant,  │
│    80px)     │      (Header no topo)   │  opcional)   │
│ (expanded:   │                         │              │
│ sidebarWidth)│                         │              │
└──────────────┴─────────────────────────┴──────────────┘
```

Sidebar e AI Panel são **mutuamente exclusivos** — abrir um fecha o outro. Em mobile, a sidebar sobrepõe o conteúdo (overlay) em vez de empurrá-lo; o `Header` fornece o trigger de toggle.

### `useLayout()` — API completa

```tsx
import { useLayout } from 'xertica-ui/hooks';

const {
  sidebarExpanded,          // boolean, default false, persistido em cookie `sidebar_state`
  setSidebarExpanded,       // (v: boolean) => void
  toggleSidebar,            // () => void — fecha o assistente automaticamente
  sidebarWidth,             // number, default 320 (px) — só afeta o estado expandido
  setSidebarWidth,          // (width: number) => void
  assistenteExpanded,       // boolean, default false
  setAssistenteExpanded,    // (v: boolean) => void
  toggleAssistente,         // () => void — fecha a sidebar automaticamente
  toggleAssistenteWithTab,  // (tab: string) => void — abre o assistente numa aba específica
  isMobile,                 // boolean, default false — viewport < 768px
} = useLayout();
```

- O estado colapsado é **sempre 80px fixo** — nunca deve mudar.
- Atalho global: `Ctrl+B` (Windows/Linux) ou `Cmd+B` (macOS) alterna a sidebar.
- A `Sidebar` funciona de forma autônoma mesmo sem `LayoutProvider`, mas perde sincronização com outros componentes layout-aware.
- Para componentes que podem existir fora do provider (ex.: Storybook), usar `useOptionalLayout()`, que retorna `null` em vez de lançar erro:

```tsx
import { useOptionalLayout } from 'xertica-ui/hooks';
const layout = useOptionalLayout();
const sidebarWidth = layout?.sidebarWidth ?? 0;
```

### Padrão de uso — conteúdo reagindo ao layout

```tsx
function PageContent({ children }) {
  const { sidebarExpanded, sidebarWidth } = useLayout();
  return (
    <div
      style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}
      className="flex-1 flex flex-col overflow-hidden transition-all duration-300"
    >
      {children}
    </div>
  );
}
```

### Regras para IA

- Nunca usar `useState` para controlar sidebar/assistente — sempre `useLayout()`.
- Nunca hardcodar `pl-64` ou `padding-left: 280px` — sempre ler `sidebarWidth`.
- Usar `toggleSidebar()` / `toggleAssistente()` (não `setSidebarExpanded(true)` direto) em ações do usuário, pois os toggles cuidam da exclusão mútua.

> **Divergência:** o `README.md` raiz mostra o exemplo `const { sidebarWidth, isSidebarOpen, toggleSidebar } = useLayout();` — mas `isSidebarOpen` **não existe** na tabela de propriedades documentada em `docs/layout.md` nem no exemplo de `docs/getting-started.md` (ambos usam `sidebarExpanded`). Trate `docs/layout.md` como fonte autoritativa para os nomes exatos de propriedades; o snippet do README parece impreciso/desatualizado nesse ponto específico.

---



---

## 5. Gerenciamento de Estado

Estratégia em camadas:

| Camada | Ferramenta | Responsabilidade |
|---|---|---|
| Server state | TanStack React Query v5 | Fetch, cache, refetch em background, loading/error |
| Client UI state | Zustand v5 | Filtros, toggles, controles de formulário, aba selecionada |
| Auth state | `AuthContext` | Sessão do usuário, login, logout |
| Layout state | `LayoutContext` / `useLayout()` | Largura/estado da sidebar, assistente |
| Local component state | `useState` | Estado efêmero (diálogos, edição inline) |

### Adicionando uma nova feature

1. `features/<nome>/data/mock.ts` com tipos e funções `fetch*` mock
2. `features/<nome>/hooks/use<Nome>.ts` envolvendo o fetch em `useQuery`
3. `features/<nome>/store/<nome>Store.ts` para estado de UI client-side
4. Re-exportar em `features/<nome>/index.ts`
5. Consumir via hook no componente

### React Query — hook language-aware

```ts
// features/home/hooks/useTeamMembers.ts
import { useQuery } from '@tanstack/react-query';
import { useLanguage } from 'xertica-ui/hooks';
import { fetchTeamMembers, type TeamMember } from '../data/mock';

export function useTeamMembers() {
  const { language } = useLanguage();
  return useQuery<TeamMember[]>({
    queryKey: ['home', 'team-members', language], // language como 3º elemento
    queryFn: fetchTeamMembers,
    staleTime: 2 * 60 * 1000,
  });
}
```

> Não exportar uma constante `*_KEY` a nível de módulo — a key inclui `language`, disponível só em tempo de chamada do hook.

**Referência de `staleTime`:** `useFeatureCards` → 10 min; `useAssistantConfig` → 30 min. (Hooks internos do showcase, não gerados no template: `useDashboardStats` → 5 min, `useTeamMembers` → 2 min.)

### Trocando mock por API real

Apenas a função `fetch*` em `data/mock.ts` muda — hook, componente e contrato de tipo permanecem intactos:

```ts
// antes (mock)
export async function fetchTeamMembers(): Promise<TeamMember[]> {
  await new Promise(resolve => setTimeout(resolve, 250));
  return MOCK_TEAM_MEMBERS;
}
// depois (API real)
export async function fetchTeamMembers(): Promise<TeamMember[]> {
  const res = await fetch('/api/team/members');
  if (!res.ok) throw new Error('Failed to fetch team members');
  return res.json();
}
```

### Zustand

```ts
// features/home/store/dashboardStore.ts
import { create } from 'zustand';

interface DashboardStore {
  activeTab: string;
  setActiveTab: (tab: string) => void;
  progress: number;
  setProgress: (value: number) => void;
}

export const useDashboardStore = create<DashboardStore>(set => ({
  activeTab: 'overview',
  setActiveTab: tab => set({ activeTab: tab }),
  progress: 45,
  setProgress: value => set({ progress: value }),
}));
```

Consumir sempre com seletor: `useDashboardStore(s => s.progress)`, nunca `const store = useDashboardStore()` inteiro (evita re-renders desnecessários).

| Usar `useState` | Usar Zustand |
|---|---|
| Diálogo aberto/fechado | Aba de filtro ativa |
| Valor de input de rename | Slider compartilhado entre seções |
| Hover | Switch habilitado |
| Toggle de componente único | Estado que precisa sobreviver ao unmount |

### AuthContext

`AuthProvider` deve ficar **dentro** de `<Router>` (usa `useNavigate`). `isLoading` deve sempre ser checado nos route guards antes de ler `user`, para evitar *flash redirects* durante a hidratação do `localStorage`.

### Árvore de decisão

```
Precisa de dado de servidor/função assíncrona?
  └─ SIM → React Query (useQuery em features/*/hooks/)
      └─ Mock? → usar fetch de features/*/data/mock.ts
      └─ API real? → trocar só o corpo do fetch

Estado é browser-only?
  └─ Compartilhado entre componentes → Zustand (features/*/store/)
  └─ Usado por um único componente → useState

Autenticação/usuário atual? → useAuth() de AuthContext
Largura da sidebar/painel do assistente? → useLayout() de xertica-ui/hooks
```

### Regras para IA

- Nunca hardcodar mock arrays em componentes — sempre em `features/*/data/mock.ts`.
- Nunca chamar `fetch` direto num componente — sempre via hook de React Query.
- Nunca usar `useState([]) + useEffect(fetch)` — usar `useQuery`.
- `QueryClientProvider` fica **fora** de `XerticaProvider`.
- `AuthProvider` fica **dentro** de `<Router>`.
- Sempre definir `staleTime` adequado — nunca deixar no default `0`.
- Zustand sempre com seletor.

---



---

## 6. Internacionalização (i18n)

Baseado em **`i18next`** + **`react-i18next`**. O `LanguageSelector` está diretamente ligado ao `i18next`: trocar idioma atualiza todo `useTranslation()` e invalida o cache do React Query (para que strings vindas de "API"/mock também atualizem).

### Estrutura de locales — uma pasta por idioma, dividida por categoria

```
src/locales/
├── .languages.json               ← seleção gerenciada pela CLI ({ version: 1, codes: [...] })
├── pt-BR/                        ← default e fallback
│   ├── common.json, nav.json, errors.json, languageSelector.json, themeToggle.json
│   ├── pages/    → home.json, templates.json, login.json, forgotPassword.json,
│   │               resetPassword.json, verifyEmail.json, loginTemplate.json,
│   │               formTemplate.json, dashboardTemplate.json, crudTemplate.json
│   └── components/ → assistant.json, sidebar.json, media.json, projectCard.json,
│                     profileCard.json, notificationCard.json, activityCard.json,
│                     stats.json, team.json
├── en/    ← mesma estrutura
└── es/    ← mesma estrutura
```

Cada JSON contém apenas as chaves da sua categoria, sem chave de wrapper no topo — todos os arquivos sob `locales/<lang>/` são descobertos automaticamente por `import.meta.glob` em `i18n.ts` (adicionar um novo JSON não exige tocar em `i18n.ts`).

> **Divergência:** o `README.md` raiz mostra uma árvore de locales **plana** (`locales/pt-BR.json`, `en.json`, `es.json`), enquanto `docs/i18n.md` (e `docs/architecture.md`, `docs/installation.md`) documentam a estrutura atual em **pastas por idioma com subcategorias**, explicitando que arquivos `.json` planos são um formato **legado (pré-2.2.0)**, removidos automaticamente na sincronização. Como o pacote está na v3.0.1, `docs/i18n.md` é a fonte correta e atual; o exemplo do README está desatualizado.

### Bootstrap (`src/i18n.ts`)

Usa `import.meta.glob(..., { eager: true, import: 'default' })` por idioma, mescla os chunks descartando o prefixo de pasta (`pages/`, `components/`) e mantém apenas o nome-base do arquivo como chave de topo. Há um **guard crítico** contra dupla inicialização do singleton do `i18next` (compartilhado/hoisted entre app e `xertica-ui` via `node_modules`): se `i18n.isInitialized`, usa `addResourceBundle(lang, 'translation', resources, true, false)` em vez de `i18n.init()`, que substituiria todo o resource store. Esse guard é gerado automaticamente pela CLI.

```ts
const savedLanguage = localStorage.getItem('xertica_language') ?? 'pt-BR';
if (!i18n.isInitialized) {
  i18n.use(initReactI18next).init({
    resources: { 'pt-BR': { translation: ptBR }, en: { translation: en }, es: { translation: es } },
    lng: savedLanguage,
    fallbackLng: 'pt-BR',
    interpolation: { escapeValue: false },
  });
} else {
  for (const [lang, resources] of Object.entries(defaultResourcesByLang)) {
    i18n.addResourceBundle(lang, 'translation', resources, true, false);
  }
}
```

`main.tsx` deve importar `'./i18n'` **antes** de qualquer componente.

### Uso em componentes

```tsx
import { useTranslation } from 'react-i18next';
function HomeContent() {
  const { t } = useTranslation();
  return <h1>{t('home.welcome')}</h1>;
}
```

Interpolação: `t('team.showing', { count: 5, total: 127 })` a partir de `"Exibindo {{count}} de {{total}} usuários"`.

### Fluxo de troca de idioma

`LanguageSelector` → `setLanguage(lang)` em `LanguageContext` → grava em `localStorage` (`xertica_language`) → `i18n.changeLanguage(lang)` → todos os `useTranslation()` re-renderizam → `queryClient.invalidateQueries()` é chamado (backstop defensivo).

### Namespaces de tradução

Um único namespace `translation`; cada chave de topo mapeia para um arquivo JSON: arquivos raiz (`common`, `nav`, `errors`, `languageSelector`, `themeToggle`), arquivos de página (`pages/<key>.json` → `home`, `templates`, `login`, `forgotPassword`, `resetPassword`, `verifyEmail`, `loginTemplate`, `formTemplate`, `dashboardTemplate`, `crudTemplate`) e arquivos de componente (`components/<key>.json` → `assistant`, `sidebar`, `media`, `projectCard`, `profileCard`, `notificationCard`, `activityCard`, `stats`, `team`).

### Mock data + factory functions (regra crítica)

Funções de fetch usam `i18n.t()` (a **instância**, não o hook) porque rodam fora do ciclo React (dentro de `queryFn`):

```ts
// ✅ correto — função, avaliada a cada chamada
export function getMockRichSuggestions(): Suggestion[] {
  return [{ id: 'rich-1', text: i18n.t('assistant.richSuggestions.viewPerformance') }];
}
// ❌ errado — const congela no idioma do momento do module load
export const MOCK_RICH_SUGGESTIONS = [{ id: 'rich-1', text: i18n.t('...') }];
```

### `queryKey` language-aware

```ts
export function useFeatureCards() {
  const { language } = useLanguage();
  return useQuery({
    queryKey: ['home', 'feature-cards', language],
    queryFn: fetchFeatureCards,
    staleTime: 10 * 60 * 1000,
  });
}
```

Trocar de `pt-BR` → `en` gera uma nova `queryKey` → cache miss → refetch com `i18n.t()` já em inglês; voltar para `pt-BR` é cache hit instantâneo.

### Configuração de idiomas disponíveis (runtime)

O conjunto de idiomas é **configurável em runtime** via prop `availableLanguages` em `<XerticaProvider>` (a lib expõe `DEFAULT_LANGUAGES` com pt-BR/en/es):

```tsx
// Monolíngue
<XerticaProvider availableLanguages={[{ code: 'en', label: 'English' }]}>

// Defaults + idioma customizado
<XerticaProvider availableLanguages={[
  ...DEFAULT_LANGUAGES,
  { code: 'fr', label: 'Français', shortLabel: 'FR', resources: fr },
]}>
```

Modo monolíngue: `LanguageSelector` renderiza `null` automaticamente (`isMonolingual === true`); force-exibir com `<LanguageSelector showWhenMonolingual />`.

Registro imperativo alternativo: `registerLanguageResource('fr', fr)`.

`LanguageDefinition`:
```ts
interface LanguageDefinition {
  code: string;               // BCP-47, usado em localStorage e i18n.changeLanguage()
  label: string;               // label completo no dropdown
  shortLabel?: string;         // ex: "PT" — default code.slice(0,2).toUpperCase()
  resources?: Record<string, unknown>; // registrado automaticamente se presente
}
```

`useLanguage()` retorna: `language`, `setLanguage`, `availableLanguages`, `isMonolingual`.

Via CLI: `npx xertica-ui update` → **Languages** recalcula o diff, copia/remove pastas de locale, regenera `App.tsx`/`i18n.ts`, atualiza `.languages.json` (e migra arquivos `.json` planos legados, se encontrados).

### Regras para IA

- Sempre importar `'./i18n'` em `main.tsx` antes de qualquer componente.
- `t()` dentro de componentes/hooks; `i18n.t()` fora do ciclo React.
- Sempre incluir `language` na `queryKey` de hooks com strings traduzidas.
- Fallback traduzido: sempre factory function, nunca `const` congelada.
- Nunca modificar `DEFAULT_LANGUAGES` diretamente — usar `availableLanguages`.
- `Language` é tipado como `string` (não union estrita) para suportar locais adicionados em runtime.
- `useLanguage` é exportado de `xertica-ui/hooks` e do root `xertica-ui`, **não** de `xertica-ui/brand` (que exporta só `LanguageSelector` e o tipo `Language`).

---



---

## 7. Sizing de Formulários

Escala padronizada de 3 níveis — `"sm" | "md" | "lg"` — aplicada a todos os componentes de formulário.

### Inputs de texto (baseados em altura)

| Size | Altura | Padding | Font | Componentes |
|---|---|---|---|---|
| `sm` | `h-8` (32px) | `px-2 py-1` | `text-sm` | Input, SelectTrigger, Search, InputOTPSlot |
| **`md`** (default) | **`h-10` (40px)** | **`px-3 py-2`** | **`text-base`** | todos |
| `lg` | `h-12` (48px) | `px-4 py-3` | `text-base` | Input, SelectTrigger, Search, InputOTPSlot |

`Textarea` usa só padding/font (altura controlada por `rows`, não por `size`).

### Toggles (baseados em indicador)

| Size | Checkbox | RadioGroupItem | Switch Track | Switch Thumb |
|---|---|---|---|---|
| `sm` | 14px | 16px | 28×14px | 12px |
| **`md`** | **16px** | **20px** | **32×18px** | **16px** |
| `lg` | 20px | 24px | 40×22px | 20px |

`Label`: `sm` → `text-xs`, `md` → `text-sm` (default), `lg` → `text-base` — deve sempre casar com o size do input associado.

### Componentes com prop `size`

`Input`, `SelectTrigger`, `Textarea`, `Search`, `InputOTPSlot`, `Checkbox`, `RadioGroupItem`, `Switch`, `Label` — todos `"sm" | "md" | "lg"`, default `"md"`.

### Caso especial: `Button`

`Button` usa escala **própria**: `"default" | "sm" | "lg" | "icon"` (default `"default"` = h-9, `sm` = h-8, `lg` = h-10, `icon` = size-9). Nunca passar `"md"` para `Button`.

`Slider` e `FileUpload` não têm prop `size` (track-based / drop-zone).

### Regras para IA

- Usar o **mesmo `size`** em todos os elementos de uma mesma linha de formulário.
- `md` é o default — nunca declarar `size="md"` explicitamente.
- `sm` para UIs densas (filtros de tabela, busca em toolbar); `lg` para forms de destaque (login, primeiro contato).
- `Label` deve casar o size do `Input` correspondente.

---



---

## 8. Branding, Temas e Identidade Visual

### Visão geral dos componentes de marca

Todos exportados de `xertica-ui/brand` (`components/brand/index.ts` reexporta `xertica-provider`, `isotype`, `xertica-logo`, `xertica-xlogo`, `xertica-orbe`, `theme-toggle`, `language-selector`):

| Componente | Propósito |
|---|---|
| `XerticaProvider` | Provider raiz de tema, idioma, cores de marca, layout, assistente, API keys, Maps, tooltips e toasts |
| `ThemeToggle` | Botão de alternância claro/escuro |
| `LanguageSelector` | Dropdown de idioma (pt-BR / en / es, extensível em runtime) |
| `XerticaLogo` | Logotipo horizontal completo (SVG, usa `currentColor`) |
| `XerticaXLogo` | Variante compacta "X" (usa `currentColor`) — obrigatória quando a Sidebar está colapsada (80px) |
| `XerticaOrbe` | Marca animada (Lottie) do núcleo do assistente de IA |
| `Isotype` / `IsotypeMini` / `IsotypeFrames` / `IsotypeTwist` / `IsotypeDiagonal` | Família de "isotipos" da marca (grades/formas geométricas com cores fixas) |

### `XerticaProvider` — props reais do código-fonte

**Atenção a uma divergência de documentação**: `docs/components/branding.md` e `docs/components/xertica-provider.md` descrevem props diferentes entre si e diferentes do código-fonte atual (`components/brand/xertica-provider/XerticaProvider.tsx`). A assinatura real, verificada em `XerticaProviderProps`, é:

```tsx
interface XerticaProviderProps {
  children: React.ReactNode;
  apiKey?: string;                       // Gemini/AI key, repassada para ApiKeyProvider
  googleMapsApiKey?: string;
  defaultBrandTheme?: string;            // legado — usado como fallback de defaultColorTheme
  defaultColorTheme?: string;            // id de um ColorTheme (ex.: 'xertica-original', 'blue', 'rose'...)
  primaryColor?: string;                 // cor hex — gera um tema 'custom' on-the-fly
  useCustomTokens?: boolean;             // default false — pula a injeção de tokens em runtime
  disableDarkMode?: boolean;             // default false
  availableLanguages?: LanguageDefinition[];
  defaultLanguage?: Language;
}
```

Internamente (`XerticaProvider.tsx`), `defaultColorTheme || defaultBrandTheme` é passado como `defaultTheme` ao `BrandColorsProvider`. A árvore de providers montada é: `ThemeProvider` → `BrandColorsProvider` → `LanguageProvider` → `ApiKeyProvider` → `GoogleMapsLoaderProvider` → `AssistenteProvider` → `LayoutProvider` → `TooltipProvider` → `{children}` + `Toaster`.

### 1. Lista completa e exata de `defaultColorTheme`

Definida em `contexts/theme-data.ts` (array `colorThemes: ColorTheme[]`), consumida por `BrandColorsProvider` (`contexts/BrandColorsContext.tsx`). São **exatamente 10 temas registrados**, nesta ordem:

| `id` | `name` | Observação |
|---|---|---|
| `xertica-original` | Xertica Classic | **Default** (`BrandColorsProvider`: `defaultTheme || 'xertica-original'`) — identidade clássica, gradiente `#FDB0F2→#72CDFD` |
| `xertica` | Xertica | Nova identidade (papel quente `#FFFEF8`, "Negro Xertica" `#1E1E1E`, botões pill com stroke, amarelo `#FAF338` restrito ao botão primário/Switch); **sem gradientes** — `gradientStart`/`gradientEnd` apontam para o mesmo valor sólido |
| `zinc` | Zinc | Escala de cinza |
| `slate` | Slate | Azul acinzentado |
| `blue` | Blue | Azul corporativo |
| `violet` | Violet | Roxo vibrante |
| `rose` | Rose | Rosa/vermelho |
| `emerald` | Emerald | Verde |
| `amber` | Amber | Âmbar |
| `orange` | Orange | Laranja |

Adicionalmente existe um pseudo-tema **`'custom'`**, não presente no array `colorThemes` — é atribuído automaticamente a `currentTheme` quando a prop `primaryColor` é passada ao `XerticaProvider`/`BrandColorsProvider` (`contexts/BrandColorsContext.tsx:31-35`): a paleta é derivada de `colorThemes[0].colors` (isto é, `xertica-original`) com `primary`, `primaryDarkMode`, `sidebarLight`, `sidebarDark` e `chart1` sobrescritos pela cor informada.

**Confirmação direta à pergunta do CHANGELOG**: sim, `'xertica'` é um `id` de tema **distinto** de `'xertica-original'`. O tema clássico manteve o `id` antigo por compatibilidade (`CHANGELOG.md:173`: "o `id` não foi alterado, então projetos scaffolded e chamadas `setTheme('xertica-original')` continuam funcionando sem mudanças").

### 2. Regra exata da substituição pelo Isotype

`currentTheme` é exposto pelo hook `useBrandColors()` (`contexts/BrandColorsContext.tsx`), que lê o estado interno do `BrandColorsProvider` (o mesmo `id` de tema listado acima, ou `'custom'`).

A regra confirmada em código — **`currentTheme === 'xertica'`** (comparação estrita com o novo tema, não com `'xertica-original'`) — aparece em 5 pontos:

```tsx
// components/brand/xertica-orbe/XerticaOrbe.tsx:30-31
// "Exclusively on the `xertica` theme, this renders the `IsotypeFrames` brand
//  mark instead of the animated orb — never make this swap theme-reactive for any other theme."
const { currentTheme } = useBrandColors();
const isXerticaTheme = currentTheme === 'xertica';
// ...mais adiante (linha 1873): if (isXerticaTheme) { return <IsotypeFrames className="w-full h-full" />; }
```

```tsx
// components/pages/login-page/LoginPage.tsx:126-127
return currentTheme === 'xertica' ? (
  <AuthPageShell leftPanel="isotype">{content}</AuthPageShell>
) : ( /* AuthPageShell com imageSrc/gradiente original */ );
```

O mesmo padrão (`currentTheme === 'xertica' ? <AuthPageShell leftPanel="isotype"> : ...`) se repete em `components/pages/forgot-password-page/ForgotPasswordPage.tsx:95-96`, `components/pages/reset-password-page/ResetPasswordPage.tsx:153-154` e `components/pages/verify-email-page/VerifyEmailPage.tsx:105-106`. Todas as 4 telas de auth usam `IsotypeTwist variant="magenta-blue"` via `AuthPageShell` (`components/blocks/auth/AuthPageShell.tsx:4,43`) quando `leftPanel="isotype"`.

Isso corrige explicitamente um bug histórico documentado no CHANGELOG (`CHANGELOG.md:47`): antes, o isotipo aparecia em qualquer tema; a correção amarrou a troca a `currentTheme === 'xertica'` lido via `useBrandColors()`.

### 3. Variantes da família Isotype

Todos em `components/brand/isotype/`, cores fixas (`ISOTYPE_COLORS` em `patterns.ts`) — **nunca reagem a tokens de tema**, pois são marca, não UI:

```ts
// patterns.ts
purple: '#5A4A96', blue: '#1899AF', magenta: '#C45BAA',
yellow: '#FAF338', green: '#2E8B5A', red: '#DE5B48', brown: '#2A2415'
```

| Componente | Forma | Prop de variante | Valores | Default |
|---|---|---|---|---|
| `Isotype` | Grade 4×4 (16 células) | `pattern: IsotypePatternId` | `'core'` (único registrado) | `'core'` |
| `IsotypeMini` | Grade 3×3 (9 células) | `pattern: IsotypeMiniPatternId` | `'core'` (único registrado) | `'core'` |
| `IsotypeFrames` | 2 quadrados vazados sobrepostos, `mix-blend-mode: multiply` | `variant: IsotypeFrameVariantId` | `'blue-magenta'` → `['blue','magenta']`; `'blue-yellow'` → `['blue','yellow']` | `'blue-magenta'` |
| `IsotypeTwist` | 2 formas-fita angulares sobrepostas, `mix-blend-mode: multiply` | `variant: IsotypeTwistVariantId` | `'magenta-blue'` → `['magenta','blue']`; `'blue-red'` → `['blue','red']` | `'magenta-blue'` |
| `IsotypeDiagonal` | Composição única fixa (corte diagonal), sem prop de variante | — | — | — |

`Isotype`/`IsotypeMini` aceitam ainda `variantSeed?: number` — um PRNG determinístico (`mulberry32`) que zera exatamente **uma** célula extra do padrão base para variação decorativa; marcas canônicas (Hero, nav) devem omitir essa prop e usar o padrão puro. Nos pares Frames/Twist, a cor "de trás" (`back`) é pintada primeiro e a "da frente" (`front`) domina o blend de interseção — a cor resultante da mistura nunca é hardcoded, é efeito do `mix-blend-mode`.

### 4. Sistema de tokens CSS e dark mode

Arquivos-fonte: `styles/xertica/tokens.css` (valores), `styles/xertica/theme-map.css` (`@theme inline` do Tailwind v4, mapeia `--color-*` para os tokens), `styles/xertica/base.css` (`@import 'tailwindcss'`, `@custom-variant dark`, utilities).

**Mecanismo de dark mode**: classe `.dark` em `<html>`, definida por `@custom-variant dark (&:is(.dark *));` (`styles/xertica/base.css:8`). Quem manipula essa classe é `ThemeProvider`/`useTheme()` (`contexts/ThemeContext.tsx`):
- Estado inicial: `disableDarkMode` → força `'light'`; senão `defaultTheme` prop; senão `localStorage['xertica-theme']`; senão `window.matchMedia('(prefers-color-scheme: dark)')`; senão `'light'`.
- A cada mudança, `root.classList.remove('light','dark')` e adiciona a classe correspondente, persistindo em `localStorage` (chave `xertica-theme`).
- Um listener de `prefers-color-scheme` só é ativado se não houver preferência salva.

`tokens.css` também define um seletor alternativo `:root[data-mode='dark'], .dark { ... }` (linha 157), mas nenhum código no repositório escreve `data-mode` — na prática só a classe `.dark` é usada.

**Discrepância de precisão encontrada**: `docs/components/theme-toggle.md` afirma que `ThemeToggle` é "self-contained... does not require any context provider" e manipula `document.documentElement.classList` diretamente. O código-fonte real (`components/brand/theme-toggle/ThemeToggle.tsx`) **contradiz isso**: o componente chama `useTheme()` de `contexts/ThemeContext.tsx`, cujo hook lança `throw new Error('useTheme must be used within a ThemeProvider')` se não houver `ThemeProvider` (que só existe dentro de `XerticaProvider` ou usado isoladamente) acima na árvore. Ou seja, **`ThemeToggle` na prática exige um `ThemeProvider`** — a doc está desatualizada nesse ponto.

**Principais tokens** (`styles/xertica/tokens.css`, tema `default`, formato `rgba(...)`):

```css
:root, :root[data-theme='default'] {
  --xertica-primary: rgba(44,39,91,1);
  --background: rgba(255,255,255,1);       --foreground: rgba(9,9,11,1);
  --card: rgba(255,255,255,1);             --popover: rgba(255,255,255,1);
  --primary: var(--xertica-primary);       --primary-foreground: rgba(250,250,250,1);
  --secondary: rgba(244,244,245,1);        --muted: rgba(244,244,245,1);
  --accent: rgba(244,244,245,1);           --destructive: rgba(220,38,38,1);
  --success/--info/--warning: ...          --border/--input/--ring: ...
  --sidebar: rgba(44,39,91,1);             --sidebar-foreground: rgba(250,250,250,1);
  --sidebar-primary/-accent/-border/-ring: ...
  --chart-1 .. --chart-8: /* paleta vibrante de 8 cores */
  --gradient-diagonal: linear-gradient(135deg,#fdb0f2 0%,#72cdfd 100%);
  --radius: 6px; --radius-button: 12px; --radius-card: 12px;
  --button-stroke-width: 0px; --button-stroke-color: transparent;
  --button-secondary-bg / --button-primary-bg / *-foreground: ...
}
:root[data-mode='dark'], .dark { /* mesmas chaves, valores escuros */ }
```

(Nota: `docs/components/branding.md` mostra um exemplo simplificado em HSL/rem — o arquivo real gerado usa `rgba()` e `--radius: 6px`, não `0.5rem`; trate o doc como ilustrativo, não literal.)

`@theme inline` em `theme-map.css` remapeia cada token para o namespace do Tailwind v4 (`--color-primary: var(--primary)`, `--radius-lg: calc(var(--radius) + 6px)` etc.), habilitando utilitários como `bg-primary`, `text-sidebar-foreground`, `rounded-lg`.

**Aplicação em runtime por tema selecionado**: `BrandColorsProvider` (`contexts/BrandColorsContext.tsx`) não troca arquivos CSS — ele calcula, a cada mudança de `currentTheme`/modo claro-escuro, um conjunto de declarações e as aplica de duas formas: (1) tokens "primários" (`--primary`, `--ring`, `--chart-*`, `--sidebar-*`, `--radius`, `--gradient-diagonal` etc.) via `root.style.setProperty(...)` (inline style em `<html>`, máxima especificidade); (2) tokens de "superfície"/botão que variam por modo (`--background`, `--card`, `--muted`, `--border`, `--button-*`) via uma tag `<style id="xertica-brand-colors-injection">` injetada em `<head>`, com blocos `:root:not(.dark) {...}` e `:root[data-mode='dark'], .dark {...}`. Um `MutationObserver` observa mudanças na classe de `<html>` para reaplicar as cores ao alternar claro/escuro.

---



---

## 9. Assistente de IA (XerticaAssistant)

### Padrões de uso

Dois padrões de consumo, ambos de `xertica-ui/assistant`:
- `<XerticaAssistant />` — painel drop-in completo, zero config.
- `useAssistant()` — hook headless com todo o state/lógica, para UI customizada.

### Props principais de `XerticaAssistant` (resumo — ver `docs/components/assistant.md` para a tabela completa)

`mode: 'collapsed'|'expanded'|'fullPage'` (default `'expanded'`), `isExpanded`, `onToggle`, `defaultTab: 'chat'|'historico'|'favoritos'`, `demoMode` (default `true`), `customResponses: MockResponse[]`, `userName` (default `'Usuário'`), `initialMessages`, `savedConversations`, `suggestions`/`richSuggestions`, `onSendMessage`, `responseGenerator`, `onRichAction`, `onEvaluation`, `feedbackOptions`, `showHistory`/`showFavorites` (default `true`), e feature flags todos default `true`: `enableAudioInput`, `enableFileAttachment`, `enableDocumentCreation`, `enablePodcastGeneration`, `enableSearch`.

### `generateDemoResponse` / `gerarResposta` — como funciona o modo demo

Implementação real em `utils/demo-responses.ts` (exporta `generateDemoResponse`, com alias `getDemoResponse`) e uma cópia equivalente em `components/shared/assistant-utils.ts` (`gerarResposta`, usada internamente pelo hook). É um **motor puramente baseado em `includes()` de palavras-chave em português** (sem chamada de rede/LLM):

```ts
export const generateDemoResponse = (mensagemUsuario: string): string | Partial<Message> => {
  const mensagemLower = mensagemUsuario.toLowerCase();
  if (mensagemLower.includes('o que') && (...includes('fazer')||includes('pedir'))) return '...';
  if (mensagemLower.includes('desempenho') || includes('performance')) return { content: '...', chartData: [...], chartConfig: {...} };
  if (mensagemLower.includes('tabela') || includes('relatório')) return { content: '...', tableData: {...} };
  if (mensagemLower.includes('criar documento')) return { content: '...', attachmentType: 'document', documentContent: '...' };
  if (mensagemLower.includes('gerar podcast')) return { content: '...', attachmentType: 'podcast', audioUrl: 'data:audio/mp3;base64,...' };
  if (mensagemLower.includes('pesquisar')) return { content: '...', attachmentType: 'search', searchResults: [...] };
  // fallback: sorteia uma de 4 respostas genéricas
};
```

`gerarResposta(mensagem, customResponses)` (`components/shared/assistant-utils.ts:87`) verifica **primeiro** `customResponses: MockResponse[]` (`{ trigger: string|RegExp; response: string|Partial<Message>; delay?: number }`), testando `RegExp.test()` ou `includes()` case-insensitive, antes de cair nas mesmas regras de palavra-chave.

### Como integrar com uma API real de LLM (achado de código-fonte, não documentado em `assistant.md`)

O hook `useAssistant` (`components/assistant/xertica-assistant/use-assistant.ts`), na versão atual do branch (`feat/fdm-integration`), tem **três** caminhos de resposta em `handleEnviarMensagem`, verificados por prioridade (linhas 362-425):

1. **`streamResponseGenerator?: (message: string) => AsyncGenerator<Partial<Message>, void, unknown>`** — maior prioridade, ainda **não documentado** em `docs/components/assistant.md` (que só menciona `responseGenerator`). Insere imediatamente uma mensagem placeholder (`content: ''`) e faz merge (`{...msg, ...chunk}`) a cada `yield` do generator, produzindo efeito de streaming token-a-token — ideal para plugar um LLM que retorna Server-Sent Events/stream (ex.: Claude/Gemini streaming). Cada `yield` deve conter o snapshot acumulado de `content`, não um delta.
2. **`responseGenerator?: (message: string) => Promise<string | Partial<Message>>`** — usado quando não há `streamResponseGenerator`; chamado dentro de um `setTimeout` artificial de `1000 + Math.random()*1000` ms (mantém a sensação de "digitando" mesmo com API real).
3. **`demoMode` (fallback)** — chama `gerarResposta(mensagemAtual, customResponses)`.

```tsx
// Integração real com streaming (padrão recomendado pelo código atual)
<XerticaAssistant
  demoMode={false}
  streamResponseGenerator={async function* (message) {
    let acc = '';
    for await (const chunk of callMyLLMStream(message)) {
      acc += chunk;
      yield { content: acc }; // snapshot acumulado, não delta
    }
  }}
/>

// Integração não-streaming
<XerticaAssistant
  demoMode={false}
  responseGenerator={async (message) => {
    const response = await callGeminiAPI(apiKey, message);
    return response; // string ou Partial<Message> (com chartData, tableData, attachmentType, etc.)
  }}
/>
```

Erros em ambos os caminhos são capturados e substituídos por uma mensagem de erro genérica (`'Ocorreu um erro ao receber a resposta.'`).

Recomendação do AI Rules da doc (`assistant.md`): nunca reimplementar o state de mensagens manualmente — sempre usar `useAssistant()`; `suggestions`/`richSuggestions`/`feedbackOptions` devem vir de `useAssistantConfig()` (React Query), com fallback nas factories de `features/assistant/data/mock.ts`.

### `AssistantChart`

`import { AssistantChart } from 'xertica-ui/ui'`. BarChart responsivo pré-configurado para a largura estreita do painel (`~400px`, `min-h-[200px]`). Props: `data: any[]` e `config: ChartConfig` (mapeamento de chave → `{ label, color }`, cor tipicamente um token `var(--chart-N)`). Chaves dos dados são livres — não há nomes obrigatórios.

### `ModernChatInput`

`import { ModernChatInput } from 'xertica-ui/assistant'`. Input controlado (`value`/`onChange` obrigatórios) usado internamente pelo `XerticaAssistant`. `onSubmit(action?: ActionType)` onde `ActionType = 'document'|'podcast'|'search'|null`. Textarea auto-resize (até 100px), popover de `+` com 3 chips de ação (Document/Podcast/Search, cada um desativável via prop), gravação de voz **simulada** (não usa MediaRecorder real — após 3s dispara `onVoiceRecording` com transcript placeholder), Enter envia / Shift+Enter quebra linha. Props de feature flag espelham as do `XerticaAssistant` (`enableAudioInput`, `enableFileAttachment`, `enableDocumentCreation`, `enablePodcastGeneration`, `enableSearch`, todas default `true`).

### `MarkdownMessage`

`import { MarkdownMessage } from 'xertica-ui/assistant'`. Único prop obrigatório: `content: string`. Renderiza Markdown com `CodeBlock` para blocos de código, tabelas GFM estilizadas com tokens (`border-border`, `hover:bg-muted/50`, `rounded-[var(--radius)]`, `overflow-x-auto`), headers h1–h4 e blockquotes mapeados para tipografia do design system, e `target="_blank" rel="noopener noreferrer"` automático em links externos. Suporta atualização incremental (streaming).

---



---

## 10. CLI — `npx xertica-ui`

Binário `xertica-ui` (`package.json: "bin": {"xertica-ui":"dist/cli.js"}`, compilado de `bin/cli.ts`, 1575 linhas). Dois comandos via `commander`: **`init [directory]`** e **`update`** (alias `update-theme`).

### `npx xertica-ui init [directory]`

**Prompt 1 — tipo de projeto** (`select`, `'What do you want to create?'`):
- `'Full system'` (`value: 'system'`) — "Auth, sidebar, pages, i18n — the current full scaffold"
- `'Web page'` (`value: 'page'`) — "Just the marketing landing page — pick a theme and languages, done"

**Fluxo "Full system"** — prompts na ordem:
1. `Pages/templates` (multiselect): `Login Page (+ Forgot/Verify/Reset)`, `Home Page`, `Template Page` — todas pré-selecionadas.
2. `Languages` (multiselect, `min: 1`): `Português (BR)` / `English` / `Español`, todas pré-selecionadas; hint avisa que apps monolíngues escondem o `LanguageSelector`.
3. `Theme` (select): lista os 10 `colorThemes` (mesma ordem da tabela acima), `xertica-original` como `initial: 0`.
4. `Include AI Assistant? (XerticaAssistant chat page + sidebar variant)` (confirm, default `true`).
5. `Enable dark mode support?` (confirm, default `true`).
6. `Install dependencies automatically?` (confirm, default `true`).

**Fluxo "Web page"** (`initPageProject`) — só 3 prompts: `Languages`, `Theme` (idênticos ao acima) e `Install dependencies automatically?`. Não pergunta páginas/assistente/dark mode — grava fixo `hasAssistant: false`, `disableDarkMode: false`.

**Diferenças geradas**:

| Aspecto | Full system | Web page |
|---|---|---|
| `App.tsx` | `generateAppTsx(...)` — com `BrowserRouter`, `AuthProvider`, `AuthGuard`, error boundaries | `generateLandingAppTsx(...)` — renderiza `LandingPage` direto, sem router/auth |
| Arquivos raiz | inclui `.env.example`, `guidelines`, `CLAUDE.md` | omite esses 3 (não há arquitetura FSD de auth nem chaves de assistant/maps a documentar) |
| Estrutura | `src/app/`, `src/shared/`, `src/features/{auth,home,template,assistant,settings}` conforme seleção, `src/pages/*` | apenas `src/pages/LandingPage.tsx` |
| `.xertica.json` | `projectType: 'system'` | `projectType: 'page'` |

`.xertica.json` (schema, `bin/cli.ts:30-37`):
```ts
interface XerticaConfig {
  version: 1;
  hasAssistant: boolean;
  disableDarkMode?: boolean;
  themeId?: string;
  projectType?: 'page' | 'system'; // ausente em projetos pré-existentes → tratado como 'system'
}
```

Idiomas são persistidos separadamente em `src/locales/.languages.json` (`{version:1, codes:[...]}`), via `readLanguagesConfig`/`writeLanguagesConfig` (`bin/language-config.ts`).

### `npx xertica-ui update`

Sem argumentos, opera no `cwd`. Menu principal (`select`, `'What do you want to update?'`), opções na ordem exata:

1. **`Theme only`** — regenera só `src/styles/xertica/tokens.css` via `generateTokensCss`, com o tema atual pré-selecionado (lido de `.xertica.json`).
2. **`Languages`** — detecta idiomas atuais (config → fallback inspecionando `src/locales/` → fallback total), multiselect com `min: 1`, pede confirmação e regenera `App.tsx` + `src/i18n.ts` preservando `disableDarkMode`.
3. **`Assistant`** — **oculto se `projectType === 'page'`**. Detecta estado atual via `.xertica.json`, com fallback por presença de `src/features/assistant/`/`src/pages/AssistantPage.tsx` (e faz backfill no `.xertica.json`). Adiciona/remove os arquivos e regenera `AuthGuard.tsx`, `HomePage.tsx`, `TemplatePage.tsx`.
4. **`Dark Mode`** — confirm simples, `initial` refletindo o estado atual; regenera `App.tsx` com o novo `disableDarkMode`.
5. **`Project files`** — escolhe versão (Latest ou específica via `npm install xertica-ui@<versão>`), depois multiselect de quais partes atualizar: `App shell`, `Shared`, `Features`, `Pages`, `Root config files` (system) ou `App shell + landing page`, `Root config files` (page); sobrescreve os arquivos selecionados a partir do `node_modules/xertica-ui/templates` recém-instalado.

### `generateAppTsx` vs `generateLandingAppTsx` (`bin/language-config.ts`)

`generateAppTsx(selectedCodes, disableDarkMode)` produz um `App.tsx` com `QueryClientProvider` → `XerticaProvider` (`apiKey`, `googleMapsApiKey` lidos de `import.meta.env`, `useCustomTokens={true}`, `availableLanguages`/`disableDarkMode` condicionais) → `Router` → `AuthProvider` → `AuthGuard` dentro de error boundaries.

`generateLandingAppTsx(selectedCodes, disableDarkMode)` produz uma versão minimalista: `QueryClientProvider` → `XerticaProvider` (sem `apiKey`/`googleMapsApiKey`) → `<LandingPage />` direto, sem router/auth. O `QueryClientProvider` é mantido mesmo sem rotas porque `LanguageProvider` (dentro de `XerticaProvider`) chama `useQueryClient()` internamente para invalidar cache ao trocar idioma.

Ambas usam o helper `buildAvailableLanguagesProp`: se a seleção de idiomas é idêntica ao default (`pt-BR, en, es`), a prop `availableLanguages` é omitida inteiramente do JSX gerado; `disableDarkMode={true}` só é emitido quando verdadeiro.

---



---

## 11. MCP — Model Context Protocol

Definições estáticas em `mcp/tools.json` e `mcp/resources.json` — um esqueleto de servidor MCP para o design system (não um servidor implementado, apenas as declarações de tools/resources).

**Tools** (`mcp/tools.json`):

| Tool | Descrição | Input |
|---|---|---|
| `xertica_validate_code` | Analisa um arquivo TypeScript/React com regras AST restritas da Xertica | `filePath: string` (obrigatório) |
| `xertica_scaffold_pattern` | Injeta um layout padrão (Cookbook) na arquitetura | `patternName: 'form'\|'dashboard'\|'crud'\|'login'\|'analytics'`, `targetPath: string` (ambos obrigatórios) |

**Resources** (`mcp/resources.json`):

| URI | Nome | Descrição |
|---|---|---|
| `xertica-ui://docs/guidelines` | Xertica UI AI Guidelines | Regras arquiteturais e restrições absolutas para agentes IA ao usar Xertica UI |
| `xertica-ui://docs/components` | Xertica UI Component Reference | Especificação unificada dos componentes UI core, props e variações |
| `xertica-ui://docs/patterns` | Xertica UI AI Cookbook | Design patterns pré-fabricados para Dashboard, Forms, Auth e Analytics |



---

## 12. Guidelines e Regras Não-Negociáveis para IA

Existem dois arquivos de guidelines com **escopos diferentes e complementares**, não conflitantes em conteúdo factual:

- **`docs/guidelines.md`** — voltado a quem **consome** a lib e a agentes de IA gerando UI: linguagem visual (cores, tipografia, espaçamento, radius), regras de composição de componentes, estrutura de página, estados de interação.
- **`guidelines/Guidelines.md`** (raiz do repo) — explicitamente escopado para **contribuidores da própria biblioteca** ("Scope: These guidelines apply to contributors developing the xertica-ui package itself... For guidelines on projects that consume the package, see `templates/guidelines/Guidelines.md`"): estrutura interna de pacote, regras de autoria de componente, build system, CLI, versionamento.

> **Nota:** o texto de `guidelines/Guidelines.md` aponta para `templates/guidelines/Guidelines.md` como a versão "para consumidores" — um arquivo distinto do `docs/guidelines.md` lido aqui (provavelmente a cópia injetada pela CLI em projetos escafoldados). Não há contradição de regra entre os arquivos lidos, apenas essa divisão de público; onde os dois tratam do mesmo tópico (tokens de cor, regras de i18n, radius), o conteúdo é consistente.

### 7.1 `docs/guidelines.md` — regras para quem consome a lib

**Cores** — tabela de tokens semânticos (`bg-background`/`text-foreground`, `bg-card`, `bg-popover`, `bg-muted`, `bg-primary`, `bg-secondary`, `bg-accent`, `bg-destructive`, `border-border`, `border-input`, `ring-ring`). Regra: hex/`rgb()` são **proibidos em qualquer contexto**; para contextos semânticos/status (erro, warning, sucesso) sempre usar tokens (`bg-destructive`, `bg-success`, `bg-warning`); para layout/UI geral não-semântica, utilitários Tailwind padrão (`bg-blue-500`) são aceitáveis quando não há token mapeado.

**Tipografia** — título de página `text-3xl font-bold tracking-tight`; heading de seção `text-2xl font-semibold`; título de card `text-lg font-semibold`; valor de KPI `text-2xl font-bold`; corpo `text-sm`; caption `text-xs text-muted-foreground`; código `font-mono text-sm bg-muted px-1 rounded`. Fonte do projeto (default Inter) nunca via inline/utility custom.

**Espaçamento** — padding de card 24px (`p-6`), painel compacto 16px (`p-4`), gap entre seções `space-y-6`/`gap-6`, gap inline `gap-2`. Grids: stats `grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4`; dashboard duplo `lg:grid-cols-7`; formulário `md:grid-cols-2 gap-6`. Nunca larguras fixas em px em grids.

**Radius** — sempre `--radius` via classes Tailwind mapeadas (`rounded-sm`/`-md`/`-lg`/`-full`); nunca `rounded-[8px]` fixo sem justificativa forte.

**Composição** — Forms: sempre `react-hook-form` + `zod`, estrutura `<Form>` → `<FormField>` → `<FormItem>` → `<FormLabel>`+`<FormControl>`+`<FormMessage>`; nunca `<span>` de erro manual; grid 2 colunas para entidades grandes; campos longos com `col-span-full`. Botões: ações de submit em modal alinhadas à esquerda no `CardFooter`, em página completa alinhadas à direita; ações destrutivas sempre exigem `AlertDialog`; botões icon-only usam `size="icon"`; nunca dois botões `variant="default"` lado a lado. Tabelas: `<Table>` completo, menus de ação via `<DropdownMenu>` com `asChild`, status sempre como `<Badge>`, filtros com ícone absoluto + `pl-8`. Cards: nunca `<div className="bg-white rounded-lg shadow border">` cru; container sem heading usa `<Card><CardContent>`; `CardFooter` com `justify-between`/`justify-end`.

**Página** — todo título de página via `<PageHeader title subtitle actions backHref/onBack>`, nunca `<h2>`/`<div>` cru. Composição padrão: `ScrollArea` → padding externo `p-2 sm:p-4 md:p-6` → `max-w-6xl mx-auto` → `PageHeader` → grid/blocos.

**Estados de interação** — loading sempre via skeleton (nunca spinner, exceto ações inline tipo "salvando..."); cada card composto tem seu `*Skeleton` (`ActivityCardSkeleton`, `ProfileCardSkeleton`, `ProjectCardSkeleton`, `NotificationCardSkeleton`, `QuickActionCardSkeleton`, `FeatureCardSkeleton`, `StatsCardSkeleton`); `rows={n}` para casar altura com o estado carregado. Empty state via `<Empty>` com ícone + mensagem + CTA. Erros: `text-destructive`/`bg-destructive`, `<FormMessage>`, `toast.error()` via Sonner.

**Acessibilidade** — tudo construído sobre Radix (navegação por teclado, ARIA, foco, screen reader) — nunca desabilitar esse comportamento; `aria-label` sempre passado por `t()`.

### 7.2 `guidelines/Guidelines.md` — regras para contribuidores da lib

Stack: React 18 · TypeScript 5 · Tailwind v4 · Radix UI · Lucide React · Vite 6.

Documentação "AI-first" obrigatória por PR que muda componente: `docs/components/[name].md`, seção relevante em `llms-full.txt`, `components.json`, `CHANGELOG.md`.

Estrutura de pacote completa (`components/`, `contexts/`, `features/`, `hooks/`, `lib/`, `utils/`, `locales/`, `i18n.ts`, `styles/`, `templates/`, `bin/`, `dist/`, `docs/`, `llms*.txt`, `components.json`) — ver Seção 2 desta base para o detalhamento.

Regras não-negociáveis de autoria: nunca usar elemento HTML nativo interativo onde já existe componente da lib; tudo sobre Radix; ícones só `lucide-react`; sem estilos inline crus; nenhuma string hardcoded (tudo via `useTranslation()`); todo bloco data-bearing precisa de `*Skeleton` companheiro.

Estrutura de arquivo por componente:
```
components/ui/my-component/
  index.ts | my-component.tsx | my-component.stories.tsx | my-component.mdx | my-component.test.tsx
```

`lib/query-client.ts` é o único singleton "aprovado" cross-cutting — código de lib deve usar `useQueryClient()`, não o singleton direto, para suportar clients customizados do consumidor.

Checklist de 12 passos para "Adicionar Novo Componente" (criar 4 arquivos → exportar do barrel → skeleton se data-bearing → strings em 3 locales → `docs/components/[name].md` → `components.json` → `llms-full.txt` → `llms.txt` → `llms-compact.txt` se comum → `decision-tree.md` se sobrepõe → atualizar contagem em README/llms.md/llms-full.txt → `CHANGELOG.md`).

Build: `npm run build` (multi-entry Vite ES+CJS, 7 pontos — sem UMD, pois Vite não suporta multi-entry + UMD), `build:types` (tsc declarations), `build:cli` (tsup). `prepublishOnly` roda os três em sequência.

CLI internals: geradores `generateAppTsx`/`generateI18nFile` vivem em `bin/language-config.ts`; `SUPPORTED_LANGUAGES` ali é a fonte única de verdade de locales expostos pela CLI. Arquivos estáticos `templates/src/app/App.tsx`/`templates/src/i18n.ts` são só referência — **não** são copiados no `init` (são reconstruídos pelos geradores).

Versionamento SemVer; checklist de pré-release com 10 itens antes de `npm publish`.

---



### 12.3 `guideline.md` (raiz) — Diretriz Global Xertica UI para Agentes de IA

Este é um terceiro arquivo de regras, na raiz do repositório do pacote (distinto de `docs/guidelines.md` e `guidelines/Guidelines.md` cobertos acima), dirigido explicitamente a LLMs/Copilots/Assistentes CLI.

Este documento define as regras rígidas e inegociáveis que **todos os Agentes de Inteligência Artificial** (LLMs, Copilots, Assistentes CLI) devem seguir ao gerar, modificar ou refatorar código em projetos que utilizem o design system `xertica-ui`.

#### 1. Regras Obrigatórias (Invariantes)

1. **SEMPRE usar componentes da `xertica-ui`.**
   - Antes de sugerir uma tag HTML (ex: `<button>`, `<input>`, `<select>`, `<table>`), você **DEVE** verificar se não existe um componente exportado pela biblioteca (ex: `<Button>`, `<Input>`, `<Select>`, `<Table>`).
2. **NUNCA criar componentes básicos manualmente.**
   - Não crie novos botões, modais, cards ou formulários do zero usando divs e Tailwind. Utilize as fundações existentes. A prioridade máxima é a **composição**.
3. **Usar Tailwind CSS com discernimento de contexto.**
   - O Tailwind (`className`) pode ser usado em componentes próprios e contêineres para layout (`flex`, `grid`, `gap`, `p-4`, `mt-2`) e também para cores em contextos não-semânticos.
   - **Contextos semânticos/status** (estados de erro, aviso, sucesso, badges de status): use obrigatoriamente tokens semânticos (`bg-destructive`, `bg-success`, `bg-warning`, `text-muted-foreground`, etc.) para garantir suporte a dark mode e temas.
   - **Contextos de layout e UI geral** (componentes customizados, stories, elementos decorativos): utilitários Tailwind padrão (`bg-blue-500`, `text-gray-700`, `bg-slate-100`) são aceitáveis quando nenhum token semântico se aplica ao contexto.
   - **Proibição absoluta** (todos os contextos): valores hex brutos (`#3B82F6`), `rgb()`/`hsl()` em `className` ou `style`, e `style={{ color: '...' }}` para fins de tema.
4. **NUNCA inventar props.**
   - Utilize as props exatas documentadas no TSDoc/JSDoc do componente. Se um componente de botão só aceita `variant="primary" | "ghost"`, passar `variant="success"` irá quebrar a tipagem e o design system.
5. **Acessibilidade por Padrão.**
   - Quando usar HTML semântico junto com os componentes, mantenha atributos ARIA exigidos pela situação e nunca remova o foco visível (`focus-visible`).

#### 2. Padrões de Composição & Governança

- **Prefira Contexto Semântico:** Se estiver criando uma página e existe um `<Card>`, construa estruturas modulares `Card > CardHeader > CardTitle > CardContent`.
- **Formulários:** SEMPRE use a sintaxe de `react-hook-form` + `@hookform/resolvers/zod` em conjunto com `<Form>`, `<FormField>`, `<FormItem>`, `<FormControl>`, e `<FormMessage>`.
- **Ícones:** Apenas `lucide-react` é suportado oficialmente. NUNCA misture bibliotecas de ícones ou cole blocos SVG literais a menos que solicitado.

#### 3. Fluxo de Construção de Telas (Para Agentes)

Sempre que a você for dada a tarefa de criar ou alterar uma tela:

1. **Identificar o Tipo de Tela:** É um CRUD? É um Formulário Complexo? É um Dashboard Analítico?
2. **Buscar um Template / Pattern:** Acesse `/docs/patterns/` e procure a equivalência mais próxima para entender a macroestrutura padrão.
3. **Consultar Componentes Necessários:** Analise quais componentes da UI serão necessários e entenda suas anotações via JSDoc (uso, limitações).
4. **Compor o Layout:**
   - Escreva o layout priorizando os contêineres principais (`div` flex/grid).
   - Coloque os componentes semânticos (`Card`, `Table`).
   - Aplique as restrições da regra obrigatória 1 e 3.
5. **Validar a Consistência:** Verifique contra você mesmo:
   - "Eu criei um `<button>` onde caberia um `<Button variant="ghost">`?" Se sim, refatore.
   - "Eu passei classes Tailwind como `text-destructive` onde usei `text-red-500` para indicar um erro?" Se sim, refatore — contextos semânticos exigem tokens semânticos.

#### 4. O Validador de Uso (`ts-morph`)

Todos os códigos gerados podem e serão submetidos ao validador de uso do projeto (`ai-validator`). A persistência de erros em violar as regras acima resultará em falha na aprovação do PR ou recusa da geração pelo usuário.

**Exemplos Curtos do que EVITAR:**

❌ Errado:

```tsx
<button className="bg-primary text-white p-2 rounded">Enviar</button>
```

✅ Correto:

```tsx
<Button variant="primary">Enviar</Button>
```

❌ Errado:

```tsx
<div className="flex bg-white shadow rounded-lg p-6">...</div>
```

✅ Correto:

```tsx
<Card>
  <CardContent className="p-6 flex">...</CardContent>
</Card>
```

❌ Errado:

```tsx
<input type="text" className="border-gray-200 rounded p-2 w-full" />
```

✅ Correto:

```tsx
<Input type="text" className="w-full" />
```

> A aderência rígida a este documento é o que garante a coesão visual e funcional em projetos complexos geridos por IA.


---

## 13. Uso por Agentes de IA — Protocolo de Consulta

### Fluxo recomendado antes de escrever componentes

`docs/getting-started.md` prescreve, para agentes (Antigravity, Cursor, Copilot etc.) adicionando features a um projeto que já usa `xertica-ui`:

1. Começar por `docs/llms.md` — índice mestre.
2. Ler `docs/ai-usage.md` antes de escrever qualquer código — regras estritas do permitido.
3. Usar o que já existe — navegar `/docs/components` antes de criar componente custom.
4. Seguir padrões — `/docs/patterns` tem receitas pré-validadas por tipo de página.
5. Nunca violar `docs/guidelines.md`.

`docs/llms.md` detalha a **ordem de leitura completa** para onboarding num projeto que usa a lib: (1) `docs/llms.md` → (2) `docs/ai-usage.md` → (3) `docs/guidelines.md` → (4) `docs/getting-started.md` → (5) `docs/installation.md` → (6) `docs/layout.md` → (7) `docs/state-management.md` → (8) `docs/i18n.md` → (9) `docs/architecture.md` (para contribuidores) → (10) `docs/patterns/` → (11) `docs/components/` (sob demanda).

### Protocolo de extração de conhecimento (`ai-usage.md`)

Ordem de prioridade das fontes de verdade: (1) `docs/llms.md`; (2) `docs/components/*.md` (props e exemplos completos); (3) `docs/patterns/*.md`; (4) código-fonte `components/ui/*.tsx` — **apenas para entender o contrato de props, nunca para "remixar" a implementação** ou derivar versões alternativas do componente.

### Regras estritas

- **HTML nativo proibido para superfícies de UI**: `<button>`→`<Button>`, `<input>`→`<Input>`, `<div className="card">`→`<Card>`, `<a>` de ação→`<Button asChild>` envolvendo `<Link>`, `<select>`→`<Select>`, `<textarea>`→`<Textarea>`. Se um engine de validação acusar "`<button>` inserted illegally", a correção é substituir pelo componente da lib — nunca envolver o nativo.
- **SVGs/ícones**: nunca gerar markup `<svg>` cru — sempre `lucide-react`. Tamanho padrão em controles `w-4 h-4` (16px); em hero/empty states, `w-8 h-8`/`w-12 h-12`.
- **Cores** — proibido em qualquer contexto: hex/`rgb()` literais em `style`. Obrigatório para contextos semânticos/status: tokens (`bg-destructive` em vez de `bg-red-500`). Aceitável para layout/UI não-semântica: utilitários Tailwind padrão. Exceção adicional: configuração de séries em gráficos `recharts` pode exigir cores literais.
- **Inferência de props**: nunca assumir que todo prop HTML nativo é repassado — checar sempre a tabela de props no `.md` do componente antes de usar.

### Checklist de validação de componente

Antes de gerar código usando um componente: (1) está exportado da lib (conferir catálogo em `docs/llms.md`); (2) todos os props obrigatórios fornecidos; (3) todos os props passados são documentados (nunca inventar prop); (4) ícones via `lucide-react`; (5) cores via classes de token semântico; (6) o componente não foi reimplementado do zero com Tailwind.

### Regras de roteamento

Passar hooks de router diretamente (nunca strings) para componentes que precisam:
```tsx
// ❌ errado
<Sidebar navigate="/home" location="/home" />
// ✅ correto (mock em preview/teste)
<Sidebar navigate={() => {}} location={{ pathname: '/home' }} />
```

### Tabela de anti-padrões (resumo)

`<button>` → usar `<Button>`; `<div className="bg-white rounded shadow">` → `<Card>`; `text-red-500` para erro → `text-destructive`; `bg-green-500` para sucesso → `bg-success`; `pl-64` hardcoded → `sidebarWidth` de `useLayout()`; gerenciar sidebar manualmente → `useLayout()`; ícones SVG customizados → `lucide-react`; `style={{ color: '#...' }}` → classe de token semântico; inventar variante de componente → usar apenas variantes documentadas; múltiplos `<XerticaProvider>` → um único, na raiz.

### Checklist de debug de emergência

Componentes sem estilo → checar `import 'xertica-ui/style.css'`, `<XerticaProvider>` presente, Tailwind escaneando `xertica-ui`. Dialogs/modais não aparecem → checar `<XerticaProvider>` (portal Radix) e `Toaster` (auto-injetado).

### Arquivos de referência para LLM (`llms.txt` / `llms-compact.txt` / `llms-full.txt`)

| Arquivo | Propósito |
|---|---|
| `llms.txt` | Índice seguindo o padrão llmstxt.org, com links para toda a doc |
| `llms-compact.txt` | Referência rápida (~4K tokens): regras críticas, imports, 12 componentes-chave com exemplos |
| `llms-full.txt` | Documentação completa de todos os componentes num único arquivo, para LLMs de contexto grande |
| `components.json` | Registro machine-readable: nome, categoria, import path, keywords, componentes relacionados |
| `docs/decision-tree.md` | Árvores de decisão (Dialog vs Sheet, Tooltip vs HoverCard etc.) |

### Estatísticas do catálogo (`docs/llms.md`, auditado em 2026-06-25)

**Total de componentes: 84** (exclui hooks e utilitários compartilhados) = 60 UI + 5 Assistant + 6 Brand + 3 Media + 2 Layout + 8 Page Templates. Hooks: 10 (`useMobile`/`useIsMobile`, `useLayout`, `useTheme`, `useLanguage`, `useAuth`, `useBrandColors`, `useAssistente`, `useApiKey`, `useAudioPlayer`, `useLayoutShortcuts`).

> **Divergência numérica:** `guidelines/Guidelines.md` descreve `llms-full.txt` como contendo "documentação completa de **97** componentes", enquanto `docs/llms.md` (auditado e corrigido conforme `docs/doc-audit.md`, datado 2026-06-25) fixa o total em **84**, com a conta batendo explicitamente (60+5+6+3+2+8=84). O `README.md` raiz, por sua vez, usa a cifra de marketing "**100+** Components" no título do catálogo. Para contagem exata e auditada, `docs/llms.md` é a fonte mais confiável e recente; os números "97" e "100+" devem ser tratados como aproximações desatualizadas/arredondadas.

### Filosofia da biblioteca (`llms.md`)

Composição sobre configuração; tokens semânticos para contextos semânticos (utilitários Tailwind aceitáveis para não-semântico); nenhuma alternativa HTML nativa; acessível por padrão (Radix); documentação LLM-first (toda doc de componente traz regras explícitas, anti-padrões e guidance de composição).

### `docs/doc-audit.md` — status da documentação

Auditoria datada de 2026-06-25, que **substitui integralmente** um relatório histórico anterior de 2026-05-19 (esse relatório histórico é sobre cobertura de *documentação*, não deve ser confundido com `architecture-improvements.md`, que é uma auditoria diferente, de *qualidade de código*, também de Maio/2026). Conclusão: documentação completa e atualizada — 100% de cobertura em componentes UI (60/60), Assistant (5/5), Brand (6/6), Layout (2/2), Media (3/3), Blocks (12, cobertura agregada em `card-patterns.md`), Pages (8, agregada em `pages.md`), Hooks/Contextos (10, agregados em `hooks.md`+`use-mobile.md`+`layout.md`). Nenhuma seção truncada, nenhum TODO/WIP real encontrado. Padrões documentados em `docs/patterns/`: `analytics`, `crud`, `dashboard`, `detail-page`, `form`, `login`, `settings`, `wizard` (8 padrões).

---



---

## 14. Árvore de Decisão de Componentes

> Guia de decisão para escolher entre componentes com propósitos semelhantes. Fonte: `docs/decision-tree.md` do pacote `xertica-ui` (conteúdo original em inglês, preservado como está). Cada árvore termina com uma recomendação ✅.

Use this file when you need to choose between components that serve similar purposes. Each tree ends in a ✅ recommendation.

---

### Overlays & Modals

**"I need to show content over the current page"**

```
Does the user MUST confirm or cancel before proceeding?
├── YES → Use AlertDialog
│         (blocking, cannot dismiss by clicking outside)
│
└── NO → Is it a destructive action without a form?
    ├── YES → Use AlertDialog (always confirm destructive actions)
    │
    └── NO → Does it need to be wide / have a scrollable form?
        ├── YES (wide form, detail view) → Use Sheet (side-anchored)
        │         variant="right" for editing, variant="left" for nav drawers
        │
        ├── YES (mobile, bottom action) → Use Drawer
        │         (slides from bottom, mobile-optimized)
        │
        └── NO (compact modal) → Use Dialog
                  (centered, dismissible, for forms and confirmations)
```

**Quick rule:**
| Scenario | Component |
|---|---|
| "Are you sure you want to delete?" | `AlertDialog` |
| Edit form in overlay | `Dialog` (small) or `Sheet` (large) |
| Filter panel on mobile | `Drawer` |
| Settings panel from the right | `Sheet variant="right"` |
| File upload modal | `Dialog` |

---

### Floating Panels

**"I need something to appear anchored to an element"**

```
Is the content SHORT (1–2 lines, text-only)?
├── YES → Does it appear on hover (no interaction needed)?
│   ├── YES → Use Tooltip
│   │         (for icon buttons, abbreviated labels)
│   └── NO → Use Tooltip (still — if it's just a label)
│
└── NO → Does it contain interactive elements (forms, buttons, links)?
    ├── YES → Use Popover
    │         (date pickers, filter panels, color pickers)
    └── NO → Does it appear on hover (read-only rich content)?
        ├── YES → Use HoverCard
        │         (user profile previews, link previews, image thumbnails)
        └── NO (triggered by click, read-only) → Use Popover
```

**Quick rule:**
| Scenario | Component |
|---|---|
| "Open settings" icon button label | `Tooltip` |
| User avatar → show profile card on hover | `HoverCard` |
| Date picker | `Calendar` inside `Popover` |
| Filter options panel | `Popover` |
| Search suggestions | `Command` inside `Popover` |

---

### Feedback & Notifications

**"I need to communicate something to the user"**

```
Is the message EPHEMERAL (auto-dismisses after a few seconds)?
├── YES → Use toast() from sonner
│         toast.success() / toast.error() / toast.info() / toast.warning()
│
└── NO → Is it BLOCKING (user must act before continuing)?
    ├── YES + DESTRUCTIVE → Use AlertDialog
    ├── YES + INFORMATIONAL → Use Dialog
    │
    └── NO (persistent, inline, part of the page) → Use Alert
              variant="default" / "destructive" / "warning" / "info" / "success"
```

**Quick rule:**
| Scenario | Component |
|---|---|
| "Saved successfully" | `toast.success()` |
| "Error: server unavailable" | `toast.error()` |
| "This action will delete 50 records" | `AlertDialog` |
| "Your trial expires in 3 days" (in-page) | `Alert variant="warning"` |
| "Complete your profile" banner | `Alert variant="info"` |

---

### Navigation

**"I need navigation controls"**

```
Is this the PRIMARY app navigation (always visible, vertical)?
├── YES → Use Sidebar
│         (supports routes, groups, assistant mode)
│
└── NO → Is it showing the user's LOCATION in the hierarchy?
    ├── YES → Use Breadcrumb (inside Header via breadcrumbs prop)
    │
    └── NO → Is it HORIZONTAL top-level navigation?
        ├── YES (marketing / top sections) → Use NavigationMenu
        │
        └── NO → Is it switching between CONTENT SECTIONS on the same page?
            ├── YES → Use Tabs
            └── NO (step-by-step flow) → Use Stepper
```

**Quick rule:**
| Scenario | Component |
|---|---|
| App sidebar (Home, Settings, Users) | `Sidebar` |
| Home / Users / João Silva (where am I?) | `Breadcrumb` via `Header` |
| Dashboard / Analytics / Reports (page tabs) | `Tabs` |
| Step 1 → Step 2 → Step 3 (wizard) | `Stepper` |
| Marketing nav (Features, Pricing, Docs) | `NavigationMenu` |

---

### Form Inputs

**"I need the user to enter data"**

```
Single line of text?
├── YES → Is it specifically a search query with clear/icon?
│   ├── YES → Use Search
│   └── NO → Use Input
│
└── NO → Multiple lines / longer content?
    ├── YES → Needs rich formatting (bold, lists)?
    │   ├── YES → Use RichTextEditor
    │   └── NO → Use Textarea
    │
    └── NO → Choosing from a list of options?
        ├── 1 of many (short list, all visible) → Use RadioGroup
        ├── 1 of many (long list, searchable) → Use Select or Command
        ├── Multiple of many → Use Checkbox group
        └── ON/OFF single option → Use Switch (prominent) or Checkbox (subtle)
```

**Quick rule:**
| Scenario | Component |
|---|---|
| Name, email, phone | `Input` |
| Message, description, notes | `Textarea` |
| Status (Active / Inactive / Pending) | `Select` |
| Enable notifications (yes/no) | `Switch` |
| Agree to terms (tick box) | `Checkbox` |
| Choose role (Admin / Editor / Viewer) | `RadioGroup` |
| Date of birth | `Calendar` inside `Popover` |
| OTP / 2FA code | `InputOTP` |
| Attach a file | `FileUpload` |
| Range / volume | `Slider` |
| Search with autocomplete | `Command` |

---

### Toggle Controls

**"I need a button that stays pressed/active"**

```
Is it a persistent setting that applies immediately (on/off)?
├── YES → Is it prominent (its own row in settings)?
│   ├── YES → Use Switch
│   └── NO (subtle, next to a label) → Use Checkbox
│
└── NO → Is it part of a TOOLBAR (formatting, view modes)?
    ├── Single toggle → Use Toggle
    └── Group of related options → Use ToggleGroup
        ├── type="single" (one active at a time, like view mode)
        └── type="multiple" (multi-select, like formatting Bold+Italic)
```

---

### Loading States

**"Data is loading"**

```
Do you know the completion percentage?
├── YES → Use Progress
│
└── NO → Is it the whole page/card loading?
    ├── YES → Use Skeleton (mimic the shape of the content)
    └── NO (small spinner inline) → Use Button loading prop or custom spinner
```

---

### Data Display

**"I need to show a collection of records"**

```
Is it structured tabular data with columns?
├── YES → Use Table + Pagination
│
└── NO → Is it a chronological sequence of events?
    ├── YES → Use Timeline
    │
    └── NO → Is it KPI metrics / numbers?
        ├── YES → Use StatsCard
        │
        └── NO → Is the collection empty?
            └── YES → Use Empty (never show blank space)
```

---

### Media

**"I need to display audio or video"**

```
Is it VIDEO?
├── YES → Use VideoPlayer
│         (custom controls, floating mode, full HTML5 video)
│
└── NO (AUDIO) → Is it a global player (persists across pages)?
    ├── YES → Use AudioPlayer variant="bar"
    └── NO (embedded in a card) → Use AudioPlayer variant="card"

Both players support FloatingMediaWrapper for picture-in-picture.
```

---

### Choosing Between Similar Components

#### Dialog vs AlertDialog

|                                    | Dialog                    | AlertDialog           |
| ---------------------------------- | ------------------------- | --------------------- |
| Dismissible by clicking outside    | ✅ yes                    | ❌ no                 |
| Required before destructive action | ❌ optional               | ✅ mandatory          |
| Can contain forms                  | ✅ yes                    | ✅ yes                |
| Use case                           | Edit, detail view, create | Delete, revoke, reset |

#### Sheet vs Dialog

|          | Sheet                                  | Dialog                             |
| -------- | -------------------------------------- | ---------------------------------- |
| Position | Edge-anchored (right/left/top/bottom)  | Centered                           |
| Best for | Large forms, detail panels, navigation | Compact confirmations, small forms |
| Mobile   | Less ideal                             | Better                             |

#### Tooltip vs HoverCard vs Popover

|           | Tooltip            | HoverCard                         | Popover                    |
| --------- | ------------------ | --------------------------------- | -------------------------- |
| Trigger   | Hover / Focus      | Hover                             | Click                      |
| Content   | Short text         | Rich read-only (profile, preview) | Interactive (form, picker) |
| Dismissal | Auto (mouse leave) | Auto (mouse leave)                | Click outside              |

#### Switch vs Checkbox

|               | Switch                                           | Checkbox                            |
| ------------- | ------------------------------------------------ | ----------------------------------- |
| Visual weight | High (prominent)                                 | Low (subtle)                        |
| Best for      | Feature toggles, settings that apply immediately | Multi-select lists, form agreements |
| Has label?    | Usually standalone label in a row                | Always paired with inline label     |

#### Sonner (Toast) vs Alert

|          | Sonner                   | Alert                       |
| -------- | ------------------------ | --------------------------- |
| Duration | Ephemeral (auto-dismiss) | Persistent (always visible) |
| Position | Fixed corner of screen   | Inline in page              |
| Use case | Action feedback          | Status banners, warnings    |

#### Tabs vs Accordion vs Collapsible

|                       | Tabs                           | Accordion             | Collapsible             |
| --------------------- | ------------------------------ | --------------------- | ----------------------- |
| Shows one at a time   | ✅ yes                         | ✅ yes (default)      | n/a (single)            |
| Content always in DOM | ✅ yes                         | ❌ no                 | ❌ no                   |
| Horizontal trigger    | ✅ yes                         | ❌ vertical           | ❌ vertical             |
| Multiple open         | ❌ no                          | Optional              | n/a                     |
| Best for              | Page sections, analytics views | FAQs, settings groups | Single optional section |


---

## 15. Padrões de Página (Page Patterns)

> Estruturas de página completas e testadas, com código TSX de exemplo, erros comuns e componentes relacionados. Fonte: `docs/patterns/*.md`.
### Padrão: Analytics

**Quando usar:** métricas de adoção, engajamento ou performance que precisam ser comparadas ao longo do tempo — qualquer tela em que o usuário filtre e compare dados por período ou segmento, alternando entre múltiplas visões analíticas.

**Estrutura/composição:**
- Header da página: título + `Select` de período (filtro **global**, aplica-se a todos os gráficos de todas as abas) + botão de exportação.
- `Tabs` para alternar entre visões analíticas (ex.: Geral, Funil, Performance) — nunca páginas separadas por visão.
- Dentro de cada `TabsContent`: grid de `StatCard`/`Card` com **3 colunas** (diferente do Dashboard, que usa 4) seguido de um `Card` de gráfico full width.
- Container com `max-w-7xl mx-auto` para não esticar gráficos em monitores ultra-wide.

```tsx
import {
  Tabs, TabsList, TabsTrigger, TabsContent,
  Card, CardHeader, CardTitle, CardContent,
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
  Button,
} from 'xertica-ui/ui';
import { CalendarIcon, Download } from 'lucide-react';
import { useState } from 'react';

export function AnalyticsDashboard() {
  const [period, setPeriod] = useState('30');

  return (
    <div className="p-6 w-full max-w-7xl mx-auto space-y-6">
      <div className="flex flex-col gap-4 md:flex-row md:items-center justify-between">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Analytics</h1>
          <p className="text-muted-foreground">Adoption and engagement metrics</p>
        </div>
        <div className="flex items-center gap-2">
          <Select value={period} onValueChange={setPeriod}>
            <SelectTrigger className="w-[180px]">
              <CalendarIcon className="mr-2 size-4 text-muted-foreground" />
              <SelectValue placeholder="Period" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="7">Last 7 days</SelectItem>
              <SelectItem value="30">Last 30 days</SelectItem>
            </SelectContent>
          </Select>
          <Button variant="outline" className="gap-2">
            <Download className="size-4" /> Export CSV
          </Button>
        </div>
      </div>

      <Tabs defaultValue="general">
        <TabsList>
          <TabsTrigger value="general">General</TabsTrigger>
          <TabsTrigger value="funnel">Adoption Funnel</TabsTrigger>
        </TabsList>
        <TabsContent value="general" className="mt-6 space-y-6">
          <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
            <Card>
              <CardHeader className="pb-2">
                <CardTitle className="text-sm font-medium text-muted-foreground">Total Sessions</CardTitle>
              </CardHeader>
              <CardContent>
                <div className="text-2xl font-bold">12,430</div>
                <p className="mt-1 text-xs text-muted-foreground">
                  <span className="text-primary font-medium">+8.2%</span> vs previous period
                </p>
              </CardContent>
            </Card>
          </div>
          <Card>
            <CardHeader><CardTitle>Daily Engagement (MAU)</CardTitle></CardHeader>
            <CardContent className="h-[400px]">
              <div className="flex h-full w-full items-center justify-center rounded-md border border-dashed bg-muted/20 text-muted-foreground">
                Line Chart Area — filtered by last {period} days
              </div>
            </CardContent>
          </Card>
        </TabsContent>
      </Tabs>
    </div>
  );
}
```

**Erros comuns / o que evitar:**
- Não deixar o filtro de período isolado por aba — ele deve ficar no header e controlar todos os gráficos.
- Usar `Select` para período (não date picker) a menos que seja necessária seleção exata de datas.
- Não esquecer `max-w-7xl mx-auto`.
- Não criar páginas separadas para cada visão — usar `Tabs`.
- Placeholders de gráfico sempre `h-[400px]` com `border-dashed bg-muted/20`.

**Componentes relacionados:** `Tabs`, `Select`, `Card`, `Chart`. Padrões relacionados: Dashboard (overview mais simples), CRUD.

---

### Padrão: CRUD (Lista)

**Quando usar:** página padrão de listagem/gestão de qualquer entidade (times, usuários, catálogos) quando a tarefa principal é visualizar, filtrar e agir sobre registros, e o fluxo de criar/editar é curto o suficiente para caber em modal ou painel lateral.

**Estrutura/composição:**
- Header da página: título + descrição + botão "New Entity" no canto superior direito.
- **Um único** `Card` envolvendo tudo: linha de filtros (busca + dropdowns) e a `Table` (não separar em cards distintos).
- `Table` com colunas via `TableHead`, linhas via `TableRow`/`TableCell`, última coluna reservada para menu de ações (`DropdownMenu`).
- Criação/edição acontece em `Dialog` ou `Sheet` — nunca em rota nova.

```tsx
import {
  Button, Input, Table, TableHeader, TableRow, TableHead, TableBody, TableCell,
  DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
  Badge, Card,
} from 'xertica-ui/ui';
import { Search, Plus, MoreHorizontal } from 'lucide-react';

export function TeamMembersCRUD() {
  return (
    <div className="flex flex-col gap-6 p-6 w-full max-w-[1400px] mx-auto">
      <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Team Members</h1>
          <p className="text-muted-foreground">Manage member access to the system.</p>
        </div>
        <Button className="gap-2"><Plus className="size-4" /> New Member</Button>
      </div>

      <Card>
        <div className="flex p-4 border-b gap-2 flex-wrap">
          <div className="relative w-full max-w-sm">
            <Search className="absolute left-2.5 top-2.5 size-4 text-muted-foreground" />
            <Input placeholder="Search by email..." className="w-full h-9 pl-8" />
          </div>
        </div>

        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Name</TableHead>
              <TableHead>Status</TableHead>
              <TableHead className="text-right">Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            <TableRow>
              <TableCell className="font-medium">Admin Dev</TableCell>
              <TableCell><Badge variant="outline">Active</Badge></TableCell>
              <TableCell className="text-right">
                <DropdownMenu>
                  <DropdownMenuTrigger asChild>
                    <Button variant="ghost" size="icon" className="size-8">
                      <MoreHorizontal className="size-4" />
                    </Button>
                  </DropdownMenuTrigger>
                  <DropdownMenuContent align="end">
                    <DropdownMenuItem>Edit Profile</DropdownMenuItem>
                    <DropdownMenuItem className="text-destructive">Revoke Access</DropdownMenuItem>
                  </DropdownMenuContent>
                </DropdownMenu>
              </TableCell>
            </TableRow>
          </TableBody>
        </Table>
      </Card>
    </div>
  );
}
```

**Erros comuns / o que evitar:**
- Input de busca sempre no padrão `relative` + ícone `absolute` + `pl-8` (nunca ícone fora do input).
- Menu de ações sempre `DropdownMenu` com `DropdownMenuTrigger asChild` envolvendo um botão ícone `ghost`.
- Status sempre em `Badge`, nunca texto puro.
- Ações destrutivas usam `className="text-destructive"` no `DropdownMenuItem`.
- Botão de criação sempre no canto superior direito, com ícone `Plus`.
- Nunca navegar para nova rota em criar/editar — usar `Dialog` ou `Sheet`.
- Filtro + tabela ficam dentro de um **único** `Card`, não cards separados.

**Componentes relacionados:** `Table`, `DropdownMenu`, `Badge`, `Input`, `Dialog` (modais de criar/editar), `Sheet` (painéis laterais). Padrões relacionados: Dashboard, Form (formulário usado dentro do Dialog/Sheet).

---

### Padrão: Dashboard

**Quando usar:** página inicial/overview de aplicações de gestão, quando o usuário precisa monitorar várias métricas simultaneamente (telas executivas/operacionais).

**Estrutura/composição:**
- Header: título + CTA (ex.: "Download Report").
- Linha de KPIs: 4 `StatsCard` em grid responsivo (`lg:grid-cols-4`).
- Painel inferior em grid de 7 colunas: `Card` de gráfico ocupando `col-span-4` + `Card` de feed de atividades ocupando `col-span-3` (gráfico sempre maior que o feed).
- Container `max-w-[1400px] mx-auto`.

```tsx
import { Card, CardContent, CardHeader, CardTitle, Badge, Button, StatsCard } from 'xertica-ui/ui';
import { Users, DollarSign, Activity, TrendingUp } from 'lucide-react';

export function DashboardPage() {
  return (
    <div className="flex flex-col gap-6 p-6 w-full max-w-[1400px] mx-auto">
      <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
          <p className="text-muted-foreground">System overview</p>
        </div>
        <Button>Download Report</Button>
      </div>

      <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
        <StatsCard title="Total Revenue" value="$45,231.89" trend="+20.1%"
          icon={<DollarSign className="size-4 text-muted-foreground" />} />
        <StatsCard title="Active Users" value="+2,350" trend="+180 this month"
          icon={<Users className="size-4 text-muted-foreground" />} />
        <StatsCard title="Sales" value="+12,234" trend="+19% vs last month"
          icon={<Activity className="size-4 text-muted-foreground" />} />
        <StatsCard title="Retention Rate" value="89.3%" trend="+1.2%"
          icon={<TrendingUp className="size-4 text-muted-foreground" />} />
      </div>

      <div className="grid gap-4 grid-cols-1 lg:grid-cols-7">
        <Card className="col-span-1 lg:col-span-4">
          <CardHeader><CardTitle>Overview</CardTitle></CardHeader>
          <CardContent className="h-[350px]">
            <div className="flex h-full w-full items-center justify-center rounded-md border border-dashed bg-muted/20 text-muted-foreground">
              Chart Area
            </div>
          </CardContent>
        </Card>

        <Card className="col-span-1 lg:col-span-3">
          <CardHeader><CardTitle>Recent Events</CardTitle></CardHeader>
          <CardContent>
            <div className="space-y-4 text-sm">
              <div className="flex items-center gap-4">
                <Badge variant="outline">09:00</Badge>
                <span>John Smith logged in</span>
              </div>
            </div>
          </CardContent>
        </Card>
      </div>
    </div>
  );
}
```

**Erros comuns / o que evitar:**
- KPIs sempre no padrão `StatsCard`: título pequeno (`text-sm font-medium`), valor grande (`text-2xl font-bold`), trend complementar.
- Card de gráfico deve sempre ocupar mais espaço que o de feed (4 vs 3 num grid de 7).
- Nunca fixar larguras em pixel nos grids — usar `lg:grid-cols-4`/`lg:grid-cols-7` responsivos.
- Placeholders de gráfico sempre `border-dashed bg-muted/20`, nunca em branco.
- Usar `max-w-[1400px]` para não esticar demais em monitores grandes.

**Componentes relacionados:** `Card`, `Badge`, `Chart`, `StatsCard`. Padrões relacionados: Analytics (quando precisa filtragem por período), CRUD (páginas para as quais o dashboard linka).

---

### Padrão: Detail Page

**Quando usar:** exibir o registro completo de uma entidade (usuário, projeto, pedido) — ao clicar numa linha de tabela, quando a entidade tem múltiplas seções relacionadas (overview, histórico, documentos), quando há ações de Edit/Delete/Export e é necessário breadcrumb de volta à lista.

**Estrutura/composição:**
- `PageHeader` com `PageHeaderHeading` (nome da entidade) e, à direita, os botões de ação (Edit, Delete) — este é o padrão de header usado em várias telas do design system, então não é repetido em detalhe nos próximos padrões.
- `Card` de resumo: `Avatar` + nome + `Badge` de status + metadados (email, data de criação).
- `Tabs` para as seções de conteúdo (Overview, History, Documents) — nunca empilhar tudo verticalmente quando há mais de 2 seções.
- Aba Overview usa `<dl>` de pares label/valor; aba History usa `Table` de eventos.
- Estado de carregamento via `Skeleton` reproduzindo o layout real.

```tsx
import {
  Card, CardContent, Tabs, TabsContent, TabsList, TabsTrigger,
  Badge, Button, Avatar, AvatarFallback, AvatarImage,
  PageHeader, PageHeaderHeading,
} from 'xertica-ui/ui';
import { Edit, Trash2 } from 'lucide-react';

export function EntityDetailPage({ entity, onEdit, onDelete }) {
  return (
    <div className="flex flex-col h-full overflow-hidden">
      <PageHeader>
        <PageHeaderHeading>{entity.name}</PageHeaderHeading>
        <div className="flex items-center gap-2 ml-auto">
          <Button variant="outline" size="sm" onClick={onEdit}>
            <Edit className="w-4 h-4 mr-2" /> Editar
          </Button>
          <Button variant="destructive" size="sm" onClick={onDelete}>
            <Trash2 className="w-4 h-4 mr-2" /> Excluir
          </Button>
        </div>
      </PageHeader>

      <div className="flex-1 overflow-y-auto p-6 space-y-6">
        <Card>
          <CardContent className="pt-6">
            <div className="flex items-start gap-4">
              <Avatar className="w-16 h-16">
                <AvatarImage src={entity.avatar} />
                <AvatarFallback>{entity.name.slice(0, 2).toUpperCase()}</AvatarFallback>
              </Avatar>
              <div className="flex-1 space-y-1">
                <div className="flex items-center gap-2">
                  <h2 className="text-xl font-semibold">{entity.name}</h2>
                  <Badge variant={entity.status === 'active' ? 'default' : 'secondary'}>
                    {entity.status}
                  </Badge>
                </div>
                <p className="text-muted-foreground">{entity.email}</p>
              </div>
            </div>
          </CardContent>
        </Card>

        <Tabs defaultValue="overview">
          <TabsList>
            <TabsTrigger value="overview">Visão Geral</TabsTrigger>
            <TabsTrigger value="history">Histórico</TabsTrigger>
            <TabsTrigger value="documents">Documentos</TabsTrigger>
          </TabsList>
          <TabsContent value="overview" className="mt-4">{/* OverviewTab */}</TabsContent>
          <TabsContent value="history" className="mt-4">{/* HistoryTab: Table de eventos */}</TabsContent>
          <TabsContent value="documents" className="mt-4">{/* DocumentsTab */}</TabsContent>
        </Tabs>
      </div>
    </div>
  );
}
```

**Erros comuns / o que evitar (regras explícitas do documento):**
- Usar `Tabs` sempre que houver mais de 2 seções lógicas — nunca empilhar tudo sem tabs.
- Status sempre via `Badge` — nunca `<span>` colorido cru.
- Nome da entidade em `PageHeaderHeading`; botões de ação no lado direito do `PageHeader`, nunca dentro da área de conteúdo.
- Sempre mostrar `Skeleton` durante o carregamento — nunca página vazia ou apenas um spinner.
- Ações destrutivas (Delete) devem ser confirmadas em `AlertDialog` — nunca deletar com um único clique.

**Componentes relacionados:** `PageHeader`, `Card`, `Tabs`, `Badge`, `Button`, `Table`, `Separator`, `Avatar`, `Skeleton`.

---

### Padrão: Form

**Quando usar:** criação/edição de qualquer entidade (usuário, produto, registro, configuração); páginas de settings com múltiplos campos; entrada de dados multi-seção dentro de `Dialog`, `Sheet` ou página inteira.

**Stack obrigatória:** `react-hook-form` + `zod` + `@hookform/resolvers` (`zodResolver`) — nunca validação manual via `useState`.

**Estrutura/composição:**
- `Card` (`CardHeader` com título + descrição) envolvendo o `Form` (contexto do react-hook-form) e a tag `<form>`.
- `CardContent` em grid (`grid-cols-1 md:grid-cols-2 gap-6`), com um `FormField` por campo → `FormItem` → `FormLabel` + `FormControl` (Input/Select/Textarea) + `FormMessage` (erro automático).
- Campos longos (Textarea, selects ricos) recebem `col-span-full`; campos escalares curtos ficam em uma coluna.
- `CardFooter` com ações alinhadas à direita: `Cancel` (`variant="ghost"`) antes do `Submit`.
- `Select` dentro de um form sempre envolve o `SelectTrigger` com `FormControl`.

```tsx
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
import {
  Button, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,
  Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
  Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
} from 'xertica-ui/ui';

const profileSchema = z.object({
  fullName: z.string().min(3, 'Full name must be at least 3 characters'),
  email: z.string().email('Invalid email format'),
  role: z.string({ required_error: 'Please select a role' }),
  bio: z.string().optional(),
});
type ProfileFormValues = z.infer<typeof profileSchema>;

export function ProfileForm() {
  const form = useForm<ProfileFormValues>({
    resolver: zodResolver(profileSchema),
    defaultValues: { fullName: '', email: '', bio: '' },
  });

  function onSubmit(values: ProfileFormValues) { /* call API */ }

  return (
    <Card className="w-full max-w-2xl mx-auto">
      <CardHeader>
        <CardTitle>Edit Profile</CardTitle>
        <CardDescription>Update your personal information and preferences.</CardDescription>
      </CardHeader>
      <Form {...form}>
        <form onSubmit={form.handleSubmit(onSubmit)}>
          <CardContent className="grid gap-6 md:grid-cols-2">
            <FormField control={form.control} name="fullName" render={({ field }) => (
              <FormItem className="col-span-full">
                <FormLabel>Full Name</FormLabel>
                <FormControl><Input placeholder="John Doe" {...field} /></FormControl>
                <FormMessage />
              </FormItem>
            )} />
            <FormField control={form.control} name="role" render={({ field }) => (
              <FormItem>
                <FormLabel>Role</FormLabel>
                <Select onValueChange={field.onChange} defaultValue={field.value}>
                  <FormControl>
                    <SelectTrigger><SelectValue placeholder="Select a role" /></SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    <SelectItem value="admin">Administrator</SelectItem>
                  </SelectContent>
                </Select>
                <FormMessage />
              </FormItem>
            )} />
            <FormField control={form.control} name="bio" render={({ field }) => (
              <FormItem className="col-span-full">
                <FormLabel>Bio</FormLabel>
                <FormControl><Textarea className="resize-none" {...field} /></FormControl>
                <FormDescription>Optional. Displayed on your public profile.</FormDescription>
                <FormMessage />
              </FormItem>
            )} />
          </CardContent>
          <CardFooter className="justify-end gap-2">
            <Button type="button" variant="ghost">Cancel</Button>
            <Button type="submit">Save Changes</Button>
          </CardFooter>
        </form>
      </Form>
    </Card>
  );
}
```

**Erros comuns / o que evitar:**
- Sempre `react-hook-form` + `zod` — nunca validação manual com `useState`.
- Erros sempre via `<FormMessage />` — nunca `<span>`/`<p>` de erro manuais.
- Layout de duas colunas: `grid grid-cols-1 md:grid-cols-2 gap-6`.
- Campos longos sempre `col-span-full`.
- Botões de submit sempre à direita no `CardFooter` (páginas inteiras e modais).
- Cancelar usa `variant="ghost"` e vem **antes** do submit.
- `<form>` deve estar dentro do componente `<Form>` (contexto).
- `Select` em form sempre com `FormControl` envolvendo o `SelectTrigger`.

**Componentes relacionados:** `Form`, `Input`, `Select`, `Textarea`, `Dialog` (formulários em modal). Padrões relacionados: CRUD (tabela que abre este form), Login (form simplificado de auth).

---

### Padrão: Login (Autenticação)

**Quando usar:** página de login de qualquer aplicação, autenticação step-up (reverificar identidade), e como base para Register, Forgot Password, Reset Password, Verify Email.

**Estrutura/composição:**
- Tela cheia (`h-screen`) com fundo `bg-muted/30` (nunca branco puro) e um `Card` centralizado (`max-w-sm`) que fornece a superfície branca.
- `CardHeader` centralizado: ícone circular (`rounded-full bg-primary/10 text-primary`) com ícone `lucide-react`, `CardTitle`, `CardDescription`.
- `CardContent` em `grid gap-4`: campos de email e senha (usando `Label` + `Input`); o link "Forgot password?" fica alinhado à direita do label da senha via `ml-auto`.
- `CardFooter` com botão de submit `w-full`.
- Extensões: integrar `react-hook-form` + `zod` (ver padrão Form) para validação; ou layout de duas colunas (`grid md:grid-cols-2`) com imagem de marca à esquerda, apenas quando o design exigir.

```tsx
import {
  Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
  Input, Button, Label,
} from 'xertica-ui/ui';
import { LockIcon } from 'lucide-react';

export function LoginPage() {
  return (
    <div className="flex h-screen w-full items-center justify-center bg-muted/30">
      <Card className="w-full max-w-sm">
        <CardHeader className="text-center">
          <div className="flex justify-center mb-4">
            <div className="size-12 rounded-full bg-primary/10 flex items-center justify-center text-primary">
              <LockIcon className="size-6" />
            </div>
          </div>
          <CardTitle className="text-2xl">System Access</CardTitle>
          <CardDescription>Enter your corporate credentials to continue.</CardDescription>
        </CardHeader>

        <CardContent className="grid gap-4">
          <div className="grid gap-2">
            <Label htmlFor="email">Email</Label>
            <Input id="email" type="email" placeholder="you@company.com" required />
          </div>
          <div className="grid gap-2">
            <div className="flex items-center">
              <Label htmlFor="password">Password</Label>
              <a href="/forgot-password" className="ml-auto inline-block text-sm underline text-muted-foreground hover:text-primary">
                Forgot password?
              </a>
            </div>
            <Input id="password" type="password" required />
          </div>
        </CardContent>

        <CardFooter>
          <Button className="w-full">Sign In</Button>
        </CardFooter>
      </Card>
    </div>
  );
}
```

**Erros comuns / o que evitar:**
- Fundo sempre `bg-muted`/`bg-muted/30` — nunca branco puro.
- Largura do card `max-w-sm` para login de coluna única; `max-w-md` apenas com campos extras.
- Botão de submit sempre `w-full` no `CardFooter`.
- Link "Forgot password?" sempre alinhado à direita via `ml-auto`.
- Não usar layout de duas colunas por padrão — só quando o design exigir imagem de marca explicitamente.
- Ícone do header sempre em container `rounded-full bg-primary/10 text-primary` com ícone `lucide-react`, nunca uma imagem.

**Componentes relacionados:** `Card`, `Input`, `Button`, `Label`. Padrão relacionado: Form (validação com react-hook-form + zod).

---

### Padrão: Settings

**Quando usar:** configuração de preferências pessoais (tema, idioma, notificações), configuração em nível de aplicação por administradores, gestão de conta (perfil, senha, API keys), sempre que as configurações se agrupam em categorias lógicas (Geral, Segurança, Notificações, etc.).

**Estrutura/composição:**
- `PageHeader` com `PageHeaderHeading` + `PageHeaderDescription` (padrão de header já descrito no padrão Detail Page).
- `Tabs` de categorias quando houver mais de 2 grupos (Geral, Segurança, Notificações, API) — nunca substituir por sidebar customizada.
- Cada categoria é composta por um ou mais `Card`, cada um com `CardHeader` (título + descrição), `CardContent` (campos) e `CardFooter` (Salvar/Cancelar).
- Toggles booleanos sempre via `Switch` (nunca `Checkbox` nesse contexto), com `Separator` entre itens de lista.
- Campos de senha sempre `Input type="password"`.
- Chaves de API são lidas/gravadas via o hook `useApiKey()` de `xertica-ui/hooks` — nunca em state local puro ou `localStorage` direto.
- Toast de confirmação (`sonner`) após cada salvamento.

```tsx
import {
  Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
  Tabs, TabsContent, TabsList, TabsTrigger,
  Button, Input, Label, Switch, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
  Separator, PageHeader, PageHeaderHeading, PageHeaderDescription,
} from 'xertica-ui/ui';
import { useApiKey } from 'xertica-ui/hooks';
import { toast } from 'sonner';
import { useState } from 'react';

export function SettingsPage() {
  return (
    <div className="flex flex-col h-full overflow-hidden">
      <PageHeader>
        <div>
          <PageHeaderHeading>Configurações</PageHeaderHeading>
          <PageHeaderDescription>Gerencie suas preferências e configurações da conta.</PageHeaderDescription>
        </div>
      </PageHeader>

      <div className="flex-1 overflow-y-auto p-6">
        <Tabs defaultValue="general" className="space-y-6">
          <TabsList>
            <TabsTrigger value="general">Geral</TabsTrigger>
            <TabsTrigger value="security">Segurança</TabsTrigger>
            <TabsTrigger value="notifications">Notificações</TabsTrigger>
            <TabsTrigger value="api">API Keys</TabsTrigger>
          </TabsList>

          <TabsContent value="notifications">
            <NotificationSettings />
          </TabsContent>
          <TabsContent value="api">
            <ApiKeySettings />
          </TabsContent>
        </Tabs>
      </div>
    </div>
  );
}

function NotificationSettings() {
  const [notifications, setNotifications] = useState({ email: true, push: false });
  return (
    <Card className="max-w-2xl">
      <CardHeader>
        <CardTitle>Notificações</CardTitle>
        <CardDescription>Escolha quais notificações deseja receber.</CardDescription>
      </CardHeader>
      <CardContent className="space-y-0">
        <div className="flex items-center justify-between py-4">
          <div className="space-y-0.5">
            <Label className="text-base">Notificações por e-mail</Label>
            <p className="text-sm text-muted-foreground">Receba atualizações no seu e-mail</p>
          </div>
          <Switch checked={notifications.email}
            onCheckedChange={() => setNotifications(p => ({ ...p, email: !p.email }))} />
        </div>
        <Separator />
      </CardContent>
      <CardFooter className="flex justify-end">
        <Button onClick={() => toast.success('Preferências de notificação salvas.')}>Salvar</Button>
      </CardFooter>
    </Card>
  );
}

function ApiKeySettings() {
  const { geminiApiKey, setGeminiApiKey } = useApiKey();
  const [localKey, setLocalKey] = useState(geminiApiKey);
  return (
    <Card className="max-w-2xl">
      <CardHeader>
        <CardTitle>Chaves de API</CardTitle>
        <CardDescription>Configure as chaves de API para os serviços integrados.</CardDescription>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="space-y-2">
          <Label htmlFor="gemini-key">Google Gemini API Key</Label>
          <Input id="gemini-key" type="password" value={localKey}
            onChange={e => setLocalKey(e.target.value)} placeholder="AIza..." />
        </div>
      </CardContent>
      <CardFooter className="flex justify-end">
        <Button onClick={() => { setGeminiApiKey(localKey); toast.success('API key salva com sucesso.'); }}>
          Salvar chave
        </Button>
      </CardFooter>
    </Card>
  );
}
```

**Erros comuns / o que evitar (regras explícitas do documento):**
- Um `Card` por grupo de settings, sempre com `CardHeader`/`CardContent`/`CardFooter`.
- `Switch` para configurações booleanas — nunca `Checkbox` nesse contexto.
- Sempre exibir toast após salvar (`toast.success`/`toast.error` de `sonner`).
- `Tabs` para categorias quando houver mais de 2 — não usar sidebar customizada.
- Campos de senha sempre `type="password"`.
- Chaves de API sempre via `useApiKey()` de `xertica-ui/hooks` — nunca em state de componente ou `localStorage` direto.

**Componentes relacionados:** `PageHeader`, `Card`, `Tabs`, `Form`, `Switch`, `Select`, `Input`, `Button`, `Separator`, `Sonner`.

---

### Padrão: Wizard (Formulário Multi-Etapas)

**Quando usar:** formulários com mais de 4-5 campos que se beneficiam de divisão em seções lógicas, quando cada etapa exige validação própria antes de avançar, quando o usuário precisa revisar um resumo antes do envio final, ou quando o processo tem sequência linear clara (onboarding, checkout, configuração).

**Estrutura/composição:**
- `Stepper` no topo, controlado por `currentStep` (nunca construir indicador customizado com `div`s cruas).
- Um único `Card` por etapa atual: `CardHeader` com o título da etapa, `CardContent` renderizando condicionalmente o componente da etapa, `CardFooter` com botões Voltar/Próximo (ou Confirmar na última etapa) — nunca múltiplos cards nem `Tabs` como substituto do `Stepper`.
- Etapa final de revisão é **somente leitura** — resumo dos dados coletados, sem campos editáveis.
- Validação por etapa via `react-hook-form` (`trigger()`) antes de `setCurrentStep`.
- Botão "Voltar" nunca desabilitado exceto na primeira etapa.
- Progresso pode ser persistido em `localStorage` ou query param de URL.

```tsx
import { useState } from 'react';
import { Stepper, Button, Card, CardContent, CardFooter, CardHeader, CardTitle } from 'xertica-ui/ui';

const STEPS = [
  { id: 'info', label: 'Informações Básicas' },
  { id: 'address', label: 'Endereço' },
  { id: 'review', label: 'Revisão' },
];

export function WizardExample() {
  const [currentStep, setCurrentStep] = useState(0);
  const [formData, setFormData] = useState({ name: '', email: '', street: '', city: '' });
  const isLastStep = currentStep === STEPS.length - 1;

  const handleNext = () => (!isLastStep ? setCurrentStep(s => s + 1) : handleSubmit());
  const handleBack = () => currentStep > 0 && setCurrentStep(s => s - 1);
  const handleSubmit = () => console.log('Submitted:', formData);

  return (
    <div className="max-w-2xl mx-auto p-6 space-y-6">
      <Stepper steps={STEPS} currentStep={currentStep} onStepClick={setCurrentStep} />

      <Card>
        <CardHeader><CardTitle>{STEPS[currentStep].label}</CardTitle></CardHeader>
        <CardContent>
          {currentStep === 0 && <StepBasicInfo data={formData} onChange={setFormData} />}
          {currentStep === 1 && <StepAddress data={formData} onChange={setFormData} />}
          {currentStep === 2 && <StepReview data={formData} />}
        </CardContent>
        <CardFooter className="flex justify-between">
          <Button variant="outline" onClick={handleBack} disabled={currentStep === 0}>Voltar</Button>
          <Button onClick={handleNext}>{isLastStep ? 'Confirmar' : 'Próximo'}</Button>
        </CardFooter>
      </Card>
    </div>
  );
}

// Validação por etapa (opcional, produção):
// import { useForm } from 'react-hook-form';
// import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from 'xertica-ui/ui';
```

**Erros comuns / o que evitar (regras explícitas do documento):**
- Usar `Stepper` para o indicador — nunca construir um customizado com `div`s.
- Um `Card` por etapa — não usar múltiplos cards nem `Tabs` como substituto do `Stepper`.
- Sempre validar os campos da etapa atual antes de avançar (usar `trigger()` do `react-hook-form`).
- Etapa de revisão deve ser somente leitura — nunca conter campos editáveis.
- Botão "Voltar" nunca desabilitado em `currentStep > 0` — só desabilitar no primeiro passo.

**Componentes relacionados:** `Stepper`, `Card`, `Button`, `Form`, `Input`, `PageHeader`.

---

## 16. Referência Completa de Componentes (A–Z)

> Documentação de cada componente exportado pela xertica-ui, em ordem alfabética: import, propósito, props/variantes, exemplo de uso e notas importantes (o que a IA deve/não deve fazer). Fonte: `docs/components/*.md` cruzado com o código-fonte dos componentes.

---

### Accordion
**Import:** `import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from 'xertica-ui/ui'`
**Propósito:** Conjunto de seções empilhadas verticalmente que podem ser expandidas/colapsadas por um cabeçalho clicável. Ideal para FAQs, painéis de configuração e árvores de navegação densas.

**Props/variantes principais:**

*Accordion*
| Prop | Tipo | Default | Obrigatório |
|---|---|---|---|
| `type` | `'single' \| 'multiple'` | — | **Sim** |
| `collapsible` | `boolean` | `false` | Não (quando `type="single"`, permite fechar o item aberto clicando novamente) |
| `defaultValue` | `string \| string[]` | — | Não |
| `value` | `string \| string[]` | — | Não |
| `onValueChange` | `(v) => void` | — | Não |

*AccordionItem*
| Prop | Tipo | Obrigatório |
|---|---|---|
| `value` | `string` | **Sim** — identificador único do item |

**Exemplo de uso:**
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from 'xertica-ui/ui';

<Accordion type="single" collapsible className="w-full">
  <AccordionItem value="q-1">
    <AccordionTrigger>Esta lib é acessível?</AccordionTrigger>
    <AccordionContent>
      Sim. Construída inteiramente sobre Radix UI com suporte ARIA completo.
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

**Notas importantes:** `AccordionItem` exige `value` único e não-vazio — sem ele o rastreamento de aberto/fechado quebra. O ícone `ChevronDown` animado já vem embutido no `AccordionTrigger` — não adicionar outro. `type` é obrigatório, nunca omitir. Use `type="single" collapsible"` para FAQs e `type="multiple"` para painéis de configuração com múltiplas seções abertas simultaneamente. Não usar para alternar entre views principais (usar `Tabs`). Relacionado: `Collapsible` (seção única), `Tabs`.

---

### AlertDialog
**Import:** `import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from 'xertica-ui/ui'`
**Propósito:** Modal especializado para **ações destrutivas irreversíveis** (deletar, revogar, resetar). Diferente de `Dialog`, exige confirmação explícita — clicar fora não dispensa o modal.

**Props/variantes principais:**
| Componente | Prop | Tipo | Descrição |
|---|---|---|---|
| `AlertDialog` | `open` | `boolean` | Estado controlado |
| `AlertDialog` | `onOpenChange` | `(open: boolean) => void` | Handler de estado |
| `AlertDialogTrigger` | `asChild` | `boolean` | Renderiza como elemento filho |
| `AlertDialogAction` | `className` | `string` | Estilo do botão de ação (tipicamente destrutivo) |
| `AlertDialogAction` | `onClick` | `() => void` | Ação executada ao confirmar |

**Exemplo de uso:**
```tsx
import {
  AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
  AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
  AlertDialogTrigger, Button,
} from 'xertica-ui/ui';

<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button variant="destructive">Excluir Conta</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Você tem certeza absoluta?</AlertDialogTitle>
      <AlertDialogDescription>
        Esta ação não pode ser desfeita. Isso excluirá permanentemente sua conta.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancelar</AlertDialogCancel>
      <AlertDialogAction
        className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
        onClick={handleDelete}
      >
        Excluir
      </AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>
```

**Notas importantes:** `AlertDialogTitle` e `AlertDialogDescription` são **obrigatórios** — omiti-los gera erro de acessibilidade. `AlertDialogCancel` deve sempre vir **antes** de `AlertDialogAction` no footer (esquerda → direita: Cancelar → Confirmar). Estilizar `AlertDialogAction` com `className="bg-destructive text-destructive-foreground hover:bg-destructive/90"` para ações destrutivas. Clicar fora do dialog **não** o dispensa (intencional). Nunca omitir a opção Cancelar. Para confirmações não-destrutivas use `Dialog`.

---

### Alert
**Import:** `import { Alert, AlertTitle, AlertDescription } from 'xertica-ui/ui'`
**Propósito:** Banner de mensagem inline persistente para comunicar status, feedback ou informação contextual sem interromper o fluxo. Diferente do `Sonner` (toast), é sempre visível. Renderiza automaticamente um ícone semântico baseado no `variant`.

**Props/variantes principais:**
| Variant | Cor | Ícone padrão | Uso |
|---|---|---|---|
| `default` | Neutro | Info | Informação geral |
| `info` | Azul | Info | Contexto informativo |
| `success` | Verde | CheckCircle | Confirmação/sucesso |
| `warning` | Âmbar | AlertTriangle | Informação cautelar |
| `destructive` | Vermelho | XCircle | Erros, avisos críticos |

| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `variant` | `'default' \| 'info' \| 'success' \| 'warning' \| 'destructive'` | `'default'` | Estilo visual e ícone automático |
| `icon` | `ReactNode` | — | Sobrescreve o ícone padrão; `null` oculta a área do ícone |
| `className` | `string` | — | Classes CSS adicionais |

**Exemplo de uso:**
```tsx
import { Alert, AlertDescription, AlertTitle } from 'xertica-ui/ui';

<Alert variant="warning">
  <AlertTitle>Atenção Necessária</AlertTitle>
  <AlertDescription>
    Sua assinatura expira em 3 dias. Renove para evitar interrupção do serviço.
  </AlertDescription>
</Alert>
```

**Notas importantes:** Não adicionar ícones manualmente dentro de `<Alert>` — deixe o componente gerenciá-los via `variant` ou `icon`. Sempre incluir `AlertTitle` para hierarquia clara. Use `destructive` para erros e falhas críticas. Posicione perto do conteúdo relevante (ex.: topo de um formulário). Relacionado: `Sonner` (toasts transitórios), `AlertDialog` (confirmações destrutivas em modal).

---

### AspectRatio
**Import:** `import { AspectRatio } from 'xertica-ui/ui'`
**Propósito:** Container que mantém uma proporção largura/altura fixa independentemente do espaço disponível. Evita layout shift ao carregar imagens, vídeos ou gráficos.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `ratio` | `number` | `1` | Proporção largura/altura (ex.: `16/9`) |
| `className` | `string` | — | Aplicado ao container |

**Exemplo de uso:**
```tsx
import { AspectRatio } from 'xertica-ui/ui';

<div className="w-full max-w-lg">
  <AspectRatio ratio={16 / 9}>
    <img src="https://example.com/hero.jpg" alt="Hero" className="rounded-md object-cover w-full h-full" />
  </AspectRatio>
</div>
```

**Notas importantes:** O container externo precisa ter largura definida — `AspectRatio` calcula a altura automaticamente a partir de `ratio`. O conteúdo filho deve usar `w-full h-full` para preencher o container. Nunca usar sem uma restrição de largura explícita no elemento pai.

---

### AudioPlayer
**Import:** `import { AudioPlayer } from 'xertica-ui/media'` (hook headless: `import { useAudioPlayer } from 'xertica-ui/hooks'`)
**Propósito:** Player de mídia unificado e premium, para prévias rápidas de áudio e podcasts longos. Suporta layout embutido (`card`) e barra global (`bar`), com transições fluidas e responsividade inteligente.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `variant` | `'card' \| 'bar'` | `'card'` | Player embutido em card ou barra fixa |
| `colorVariant` | `'default' \| 'primary'` | `'default'` | Estilo visual (`primary` para conteúdo com foco de marca) |
| `isOpen` | `boolean` | `true` | (apenas `bar`) se a barra está aberta ou fechada |
| `enableAutoFloat` | `boolean` | `true` | (apenas `card`) flutuar ao rolar para fora da viewport |
| `title` | `string` | — | Título da faixa |
| `artist` | `string` | — | Autor/subtítulo |
| `src` | `string` | — | URL do arquivo de áudio |
| `duration` | `number` | — | Duração total em segundos |

**Exemplo de uso:**
```tsx
<AudioPlayer src="url/to/audio.mp3" title="Entrevista Exclusiva" artist="Xertica News" variant="card" />

<AudioPlayer
  src="url/to/audio.mp3"
  title="Podcast Semanal"
  variant="bar"
  colorVariant="primary"
  isOpen={true}
  onClose={() => setOpen(false)}
/>
```

**Notas importantes:** A responsividade é dinâmica — em telas médias/pequenas, controles como volume e velocidade migram automaticamente para o menu secundário (`...`) em vez de apenas se esconderem. Suporta velocidades `0.5x`, `1x`, `1.5x`, `2x`. A variante `bar` detecta o estado do Sidebar e do Assistant de IA via `LayoutContext` para ajustar margens. Use `colorVariant="primary"` para conteúdo de marca/podcasts; padrão para áudios utilitários. O hook `useAudioPlayer` é headless (expõe `audioRef`, `containerRef`, `isPlaying`, `togglePlay`, `currentTime`, `duration`, `formatTime`, `onPlay`, `onPause`, `onEnded`, `onTimeUpdate`, `onLoadedMetadata`) e usa internamente o padrão "latest-ref" para evitar stale closures ao alternar entre modo flutuante/embutido — não é necessário replicar esse padrão. Use `<AudioPlayer>` diretamente para reprodução padrão; só use o hook para UI 100% customizada. Sempre forneça `title` descritivo (acessibilidade).

---

### Avatar
**Import:** `import { Avatar, AvatarImage, AvatarFallback } from 'xertica-ui/ui'`
**Propósito:** Exibe a identidade de um usuário — foto de perfil ou fallback com iniciais. Usado em headers, sidebars, threads de comentários e linhas de tabela.

**Props/variantes principais:**
| Componente | Prop | Tipo | Descrição |
|---|---|---|---|
| `Avatar` | `className` | `string` | Ajuste de tamanho/forma (default: `h-10 w-10 rounded-full`) |
| `AvatarImage` | `src` | `string` | URL da imagem |
| `AvatarImage` | `alt` | `string` | Texto alternativo acessível |
| `AvatarFallback` | `className` | `string` | Estilo do fundo/texto do fallback |
| `AvatarFallback` | `children` | `ReactNode` | Iniciais ou ícone de fallback |
| `AvatarFallback` | `delayMs` | `number` | Atraso antes do fallback aparecer (ms) |

**Exemplo de uso:**
```tsx
import { Avatar, AvatarImage, AvatarFallback } from 'xertica-ui/ui';

<Avatar>
  <AvatarImage src="https://github.com/johndoe.png" alt="John Doe" />
  <AvatarFallback>JD</AvatarFallback>
</Avatar>
```

**Notas importantes:** Sempre incluir `<AvatarFallback>` — nunca renderizar `<AvatarImage>` sozinho (fica em branco se a imagem falhar). O fallback deve ser iniciais (`user.name.charAt(0)`) ou um ícone `lucide-react` — nunca outra imagem externa. Tamanhos padrão: `className="h-8 w-8"` (pequeno) ou `h-10 w-10` (default). Relacionado: `Header`, `Sidebar`.

---

### Badge
**Import:** `import { Badge } from 'xertica-ui/ui'`
**Propósito:** Rótulo compacto e inline, não-interativo por padrão, usado para status, categoria ou contagem.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `variant` | `'default' \| 'secondary' \| 'outline' \| 'destructive' \| 'success' \| 'info' \| 'warning'` | `'default'` | Estilo visual |
| `asChild` | `boolean` | `false` | Renderiza como elemento filho |
| `className` | `string` | — | Classes CSS adicionais |

**Exemplo de uso:**
```tsx
import { Badge } from 'xertica-ui/ui';

<Badge>Ativo</Badge>
<Badge variant="secondary">Rascunho</Badge>
<Badge variant="destructive">Erro</Badge>
<Badge variant="success">Concluído</Badge>
```

**Notas importantes:** Rótulos de status em tabelas e listas devem **sempre** usar `<Badge>` — nunca texto plano ou `<span>` colorido manualmente. Use variantes semânticas: `success` (positivo/concluído), `info` (informativo), `warning` (cautela), `destructive` (erro). Nunca inventar uma variante custom com cores cruas — usar apenas as 7 variantes documentadas; para cor de marca específica, aplicar `className` com tokens semânticos (ex.: `bg-primary/10 text-primary border-primary/20`). Relacionado: `Button` (quando o rótulo precisa ser clicável), `NotificationBadge` (indicadores overlay de ponto/contagem).

---

### Breadcrumb
**Import:** `import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from 'xertica-ui/ui'`
**Propósito:** Trilha de navegação hierárquica que mostra a posição atual na estrutura de páginas. Normalmente renderizada dentro do `Header` via prop `breadcrumbs`.

**Props/variantes principais (sub-componentes):**
| Componente | Descrição |
|---|---|
| `Breadcrumb` | Wrapper raiz (`<nav aria-label="breadcrumb">`) |
| `BreadcrumbList` | Lista ordenada de itens |
| `BreadcrumbItem` | Item individual |
| `BreadcrumbLink` | Item de link clicável |
| `BreadcrumbPage` | Item de página atual, não-linkado |
| `BreadcrumbSeparator` | Separador visual entre itens (default: `/`) |
| `BreadcrumbEllipsis` | Indicador de itens colapsados em cadeias longas |

**Exemplo de uso:**
```tsx
// Via Header (recomendado)
<Header
  breadcrumbs={[
    { label: 'Dashboard', href: '/dashboard', icon: <Home className="w-4 h-4" /> },
    { label: 'Usuários', href: '/users' },
    { label: 'Ariel Santos' }, // sem href = página atual
  ]}
/>
```

**Notas importantes:** O último item da cadeia é sempre a página atual e deve usar `<BreadcrumbPage>` (não `<BreadcrumbLink>`). Prefira a prop `breadcrumbs` do `<Header>` — ela compõe tudo automaticamente. Sempre adicionar `<BreadcrumbSeparator>` entre cada dois itens. Relacionado: `Header`, `NavigationMenu`.

---

### Button
**Import:** `import { Button } from 'xertica-ui/ui'`
**Propósito:** Gatilho de interação primário para qualquer ação iniciada pelo usuário: submissão de formulários, navegação, abertura de diálogos, confirmações destrutivas.

**Props/variantes principais:**

Variantes: `default` · `secondary` · `outline` · `ghost` · `destructive` · `link` · `success` · `info` · `warning`
Tamanhos: `default` (`h-9`) · `sm` (`h-8`) · `lg` (`h-10`) · `icon` (`h-9 w-9`)

| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `variant` | `'default' \| 'secondary' \| 'outline' \| 'ghost' \| 'destructive' \| 'link' \| 'success' \| 'info' \| 'warning'` | `'default'` | Estilo visual |
| `size` | `'default' \| 'sm' \| 'lg' \| 'icon'` | `'default'` | Preset de tamanho |
| `loading` | `boolean` | `false` | Desabilita e mostra spinner preservando a largura |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `asChild` | `boolean` | `false` | Renderiza como componente filho (composição com Link) |
| `className` | `string` | — | Classes CSS adicionais |
| `onClick` | `() => void` | — | Handler de clique |
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Tipo HTML do botão |

Todos os demais atributos nativos de `button` são encaminhados.

**Exemplo de uso:**
```tsx
import { Button } from 'xertica-ui/ui';
import { Link } from 'react-router-dom';

<Button variant="destructive">Excluir Conta</Button>

<Button asChild>
  <Link to="/dashboard">Ir para Dashboard</Link>
</Button>

<Button variant="ghost" size="icon" aria-label="Abrir configurações">
  <Settings className="size-4" />
</Button>
```

**Notas importantes:** Nunca usar `<button>` nativo — sempre `<Button>` de `xertica-ui`. Nunca inventar novas variantes além das 9 documentadas. Nunca colocar dois botões `variant="default"` lado a lado (um deve ser `ghost`, `outline` ou `secondary`). Em submissão de formulário, sempre `type="submit"`. `size="icon"` requer conter apenas um ícone. Ações destrutivas devem usar `variant="destructive"` e sempre confirmar com `<AlertDialog>` antes. Relacionado: `AlertDialog`, `Dialog`, `Form`, `DropdownMenu`.

---

### Calendar
**Import:** `import { Calendar } from 'xertica-ui/ui'` (usado junto com `Popover`)
**Propósito:** Date picker em grid mensal (construído sobre `react-day-picker`), quase sempre dentro de um `<Popover>`. Suporta seleção única, múltipla e por intervalo (range).

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `mode` | `'single' \| 'multiple' \| 'range'` | `'single'` | Modo de seleção |
| `selected` | `Date \| Date[] \| DateRange` | — | Data(s) selecionada(s) |
| `onSelect` | `(date) => void` | — | Handler de mudança |
| `captionLayout` | `'label' \| 'dropdown' \| 'dropdown-months' \| 'dropdown-years'` | `'label'` | Como o cabeçalho mês/ano é renderizado; `'dropdown'` renderiza ambos como selects interativos |
| `buttonVariant` | `ButtonVariant` | `'ghost'` | Variante dos botões de navegação prev/next |
| `initialFocus` | `boolean` | `false` | Foca o calendário ao abrir (necessário dentro de Popover) |
| `disabled` | `Matcher \| Matcher[]` | — | Desabilita datas específicas |
| `fromDate` | `Date` | — | Data mínima selecionável |
| `toDate` | `Date` | — | Data máxima selecionável |
| `showOutsideDays` | `boolean` | `true` | Mostra dias de meses adjacentes |
| `className` | `string` | — | Classes CSS adicionais |

> Demais props de `react-day-picker` são encaminhadas.

**Exemplo de uso:**
```tsx
import { Popover, PopoverContent, PopoverTrigger, Calendar, Button } from 'xertica-ui/ui';
import { format } from 'date-fns';

<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline" className="w-[240px] justify-start gap-2">
      <CalendarIcon className="size-4" />
      {date ? format(date, 'PPP') : 'Escolha uma data'}
    </Button>
  </PopoverTrigger>
  <PopoverContent className="w-auto p-0" align="start">
    <Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
  </PopoverContent>
</Popover>
```

**Notas importantes:** Sempre usar dentro de `<Popover>` com `<PopoverContent className="w-auto p-0">`. Sempre passar `initialFocus` quando dentro do Popover (essencial para UX de teclado/acessibilidade). Usar `date-fns` para formatação (`format(date, 'PPP')`). Não existe prop `size` no `Calendar` — tamanho e border-radius do trigger devem ser aplicados diretamente no `<Button>` trigger. Use `captionLayout="dropdown"` quando o usuário precisa navegar rapidamente até um mês/ano distante (ex.: data de nascimento). Relacionado: `Popover`, `Select` (para seleção de período sem datas exatas).

---

### Card
**Import:** `import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter } from 'xertica-ui/ui'`
**Propósito:** Container estrutural primário para todos os blocos de conteúdo — fornece elevação, tokens de background/border e padding interno consistente. É a única forma correta de criar painéis de conteúdo — nunca recriar com `<div>` cru.

**Props/variantes principais (sub-componentes):**
| Componente | Descrição |
|---|---|
| `Card` | Container raiz com background, border, shadow, border-radius |
| `CardHeader` | Seção superior (`p-6 pb-0`); usa CSS grid para acomodar `CardAction` |
| `CardTitle` | Título principal — renderiza como `<h3>` |
| `CardDescription` | Subtítulo/descrição em texto muted |
| `CardAction` | Slot alinhado à direita dentro de `CardHeader`, posicionado automaticamente pelo grid |
| `CardContent` | Área de conteúdo principal (`p-6 pt-0`) |
| `CardFooter` | Seção inferior com botões de ação (default: `flex items-center`) |

Todos os sub-componentes aceitam `className` e encaminham atributos HTML de `div`.

**Exemplo de uso:**
```tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button } from 'xertica-ui/ui';

<Card className="w-[350px]">
  <CardHeader>
    <CardTitle>Detalhes do Projeto</CardTitle>
    <CardDescription>Gerencie as configurações do seu workspace.</CardDescription>
  </CardHeader>
  <CardContent>
    <p>Conteúdo interno aqui.</p>
  </CardContent>
  <CardFooter className="flex justify-between">
    <Button variant="outline">Descartar</Button>
    <Button>Salvar</Button>
  </CardFooter>
</Card>
```

**Notas importantes:** Nunca criar superfícies com Tailwind cru (`<div className="bg-white rounded-lg shadow border">`). Sempre importar de `xertica-ui/ui`. Use `CardAction` (não `flex justify-between` manual em `CardHeader`) para controles alinhados à direita. `CardHeader` e `CardFooter` são opcionais. `CardFooter` com dois botões (cancelar + confirmar) usa `className="flex justify-between"`; com um único botão, `className="justify-end"`. `CardTitle` renderiza `<h3>` — não colocar `h1`/`h2` dentro dele. Em stat cards compactos: `CardTitle` com `className="text-sm font-medium"` e valor em `CardContent` com `className="text-2xl font-bold"`. Para padrões pré-compostos (ActivityCard, ProjectCard, etc.), ver `card-patterns`. Relacionado: `StatsCard`, `Dialog`, `Form`.

---

### Card Patterns (FeatureCard, ActivityCard, ProfileCard, ProjectCard, QuickActionCard, NotificationCard)
**Import:** `import { ActivityCard, ProjectCard, FeatureCard, ProfileCard, QuickActionCard, NotificationCard } from 'xertica-ui'` (barrel raiz) ou `from 'xertica-ui/blocks'`
**Propósito:** Blocos de card pré-compostos para dashboards, gestão de projetos e páginas de funcionalidades, construídos exclusivamente a partir de primitivos `ui/` (sem dependências externas). Usar quando o padrão é recorrente e reutilizado em várias páginas — evite para layouts únicos de uma única página.

**Props/variantes principais:**

*FeatureCard* — ícone com fundo colorido, título, badge opcional, descrição, botão de ação.
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `title` | `string` | — | Título do card |
| `description` | `string` | — | Texto do corpo |
| `icon` | `ReactNode` | — | Elemento de ícone |
| `color` | `FeatureCardColor` | `'primary'` | Token de cor de fundo do ícone (`primary`, `chart-1`…`chart-5`, `success`, `info`, `warning`, `destructive`) |
| `badge` | `string` | — | Rótulo de badge opcional |
| `badgeVariant` | `BadgeVariant` | `'default'` | Variante de cor do badge |
| `actionLabel` | `string` | — | Texto do botão (omitir esconde o botão) |
| `actionVariant` | `ButtonVariant` | `'outline'` | Variante do botão |
| `onAction` | `() => void` | — | Handler de clique |

*ActivityCard* — feed cronológico de ações com avatar, descrição, timestamp, badge de tipo.
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `title` | `string` | `'Atividade Recente'` | Título |
| `items` | `ActivityItem[]` | — | Entradas do feed |
| `action` | `ReactNode` | — | Ação no header (alinhada à direita) |
| `maxItems` | `number` | `5` | Máximo de itens visíveis |

`ActivityItem`: `{ id, user: { name, initials, avatar? }, action, target, time, type?: 'create'|'update'|'delete'|'comment'|'deploy' }`

*ProfileCard* — card de usuário/membro com avatar, badge de status, linha de stats, ações.
| Prop | Tipo | Descrição |
|---|---|---|
| `name`, `role`, `department` | `string` | Dados textuais |
| `initials` | `string` | Fallback do avatar |
| `avatar` | `string` | URL da imagem |
| `status` | `'online' \| 'offline' \| 'away' \| 'busy'` | Badge de presença |
| `stats` | `{ label: string; value: string \| number }[]` | Até 3 stats |
| `primaryAction` / `secondaryAction` | `{ label: string; onClick?: () => void }` | Botões |

*ProjectCard* — status de projeto com progress bar, avatar stack, data de vencimento.
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `title`, `description` | `string` | — | Textos |
| `status` | `ProjectStatus` (`active`\|`review`\|`done`\|`paused`\|`at-risk`) | — | Cor do badge/progresso |
| `progress` | `number` | — | 0–100 |
| `dueDate` | `string` | — | Data de vencimento |
| `members` | `ProjectMember[]` | `[]` | Avatar stack |
| `maxMembers` | `number` | `4` | Máx. avatares antes do contador de overflow |
| `action` | `ReactNode` | — | Ação extra junto ao badge de status |

*QuickActionCard* — tile de ação com ícone em caixa colorida e botão full-width.
| Prop | Tipo | Default |
|---|---|---|
| `title`, `description`, `icon` | — | — |
| `badge` | `string` | — |
| `badgeVariant` | `BadgeVariant` | `'secondary'` |
| `actionLabel` | `string` | — |
| `actionVariant` | `ButtonVariant` | `'default'` |
| `onAction` | `() => void` | — |
| `disabled` | `boolean` | `false` |

*NotificationCard* — lista de notificações com indicador de não lidas, badges de tipo, ação de marcar-tudo-como-lido.
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `title` | `string` | `'Notificações'` | — |
| `items` | `NotificationItem[]` | — | Entradas |
| `unreadCount` | `number` | contado automaticamente | Sobrescreve o badge de contagem |
| `onMarkAllRead` | `() => void` | — | Mostra botão "Marcar todas como lidas" |
| `onViewAll` | `() => void` | — | Mostra botão de rodapé "Ver todas" |
| `maxItems` | `number` | `4` | — |

`NotificationItem`: `{ id, title, message, time, read?, type?: 'info'|'warning'|'success'|'error'|'default', user? }`

**Exemplo de uso:**
```tsx
import { QuickActionCard, ActivityCard, ActivityCardSkeleton } from 'xertica-ui';
import { Briefcase } from 'lucide-react';

<QuickActionCard title="Novo Projeto" icon={<Briefcase />} actionLabel="Criar" />

function ActivityFeed() {
  const { data: items, isLoading } = useActivityItems();
  if (isLoading) return <ActivityCardSkeleton rows={5} />;
  return <ActivityCard items={items!} />;
}
```

**Notas importantes:** Importar sempre do barrel `xertica-ui` (não fazem fetch interno — dados sempre vêm via props). Manter `maxItems` entre 4–6 para evitar overflow em containers de altura fixa. Para painéis de gráfico, usar `ChartCard` de `xertica-ui/ui` (não é um block component) como filho. O badge do `FeatureCard` quebra linha quando título+badge não cabem — comportamento intencional, não forçar largura. **Cada card tem um companion `*Skeleton`** (`ActivityCardSkeleton`, `ProfileCardSkeleton`, `ProjectCardSkeleton`, `NotificationCardSkeleton`, `QuickActionCardSkeleton`, `FeatureCardSkeleton`, além de `StatsCardSkeleton` em `ui/stats-card`) que deve ser usado durante o carregamento em vez de spinner/container vazio — passar `rows={maxItems}` para que a altura do placeholder corresponda ao estado carregado. Skeletons renderizam `<div>`s simples via primitivo `Skeleton` (`bg-accent animate-pulse`), sem os subcomponentes reais de Card. Relacionado: `Card`, `StatsCard`, `Skeleton`, `Chart`.

---

### Carousel
**Import:** `import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext } from 'xertica-ui/ui'`
**Propósito:** Slider horizontal de conteúdo (construído sobre `embla-carousel-react`) que permite navegar por painéis/cards um de cada vez, com botões Previous/Next e indicadores opcionais.

**Props/variantes principais:** Sem tabela de props formal na fonte — a customização é feita via `className` em `CarouselItem` (para controlar quantos itens ficam visíveis) e no `Carousel` (largura do container).

**Exemplo de uso:**
```tsx
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Card, CardContent } from 'xertica-ui/ui';

<Carousel className="w-full max-w-sm mx-auto">
  <CarouselContent>
    {items.map(item => (
      <CarouselItem key={item.id} className="md:basis-1/2 lg:basis-1/3">
        <Card>
          <CardContent>{item.name}</CardContent>
        </Card>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>
```

**Notas importantes:** Controle a quantidade de cards visíveis com `className="md:basis-1/2 lg:basis-1/3"` em `<CarouselItem>` — não limitando o array de dados. `<CarouselPrevious />`/`<CarouselNext />` são posicionados de forma absoluta (o `<Carousel>` já cuida do `relative` no pai). Nunca envolver `<Carousel>` em containers com `overflow-hidden` que possam cortar os botões de navegação. Relacionado: `Card` (conteúdo mais comum dos itens).

---

### Chart
**Import:** `import { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartCard, DashboardBarChart, DashboardLineChart, HorizontalBarChart, InteractiveTimeSeriesChart, ComboMetricChart, DonutBreakdownChart, SparklineChart, RadarMetricChart, PieMetricChart, RadialBarMetricChart, GaugeChart, type ChartConfig, type GaugeChartThreshold } from 'xertica-ui/ui'`
**Propósito:** Sistema de gráficos construído sobre **Recharts**, com `ChartContainer` injetando variáveis CSS de cor por tema (dark-mode completo). Inclui **11 wrappers "dashboard-ready"** que tratam automaticamente estados de loading, vazio e erro, exigindo peer dependency `recharts` (`npm install recharts`).

**Props/variantes principais:**

Tokens de cor: `--chart-1` a `--chart-8` (theme-aware). `ChartConfig`: `{ [key]: { label?, icon?, color?, theme?: { light, dark } } }`.

Wrappers dashboard-ready: `DashboardBarChart` (barras agrupadas/empilhadas), `DashboardLineChart` (linhas multi-série), `HorizontalBarChart` (ranking horizontal), `InteractiveTimeSeriesChart` (área com tabs de métrica/período), `ComboMetricChart` (barra+linha+área combinados), `DonutBreakdownChart` (donut/pizza interativo), `SparklineChart` (mini área/linha para KPIs), `RadarMetricChart`, `PieMetricChart`, `RadialBarMetricChart`, `GaugeChart`.

*RadarMetricChart:* `data: DashboardChartDatum[]`, `labelKey: string`, `series: DashboardChartSeries[]`, `colors` (auto), `filled: boolean` (default `true`), `fillOpacity: number` (default `0.25`), `showDots: boolean` (default `false`), `showGrid: boolean` (default `true`), `showLegend: boolean` (auto), `valueFormatter`.

*PieMetricChart:* `data`, `nameKey: string`, `valueKey: string`, `colors`, `outerRadius: number|string` (default `"80%"`), `innerRadius: number|string` (default `0`, >0 = donut), `showLabels: boolean` (default `false`), `showLegend: boolean` (default `true`), `explodeIndex: number`, `explodeOffset: number` (default `12`), `valueFormatter`.

*RadialBarMetricChart:* `data`, `dataKey: string` (default `"value"`), `nameKey: string` (default `"name"`), `colors`, `innerRadius` (default `"30%"`), `outerRadius` (default `"100%"`), `startAngle: number` (default `90`), `endAngle: number` (default `-270`), `showBackground: boolean` (default `true`), `showLegend: boolean` (default `true`), `valueFormatter`.

*GaugeChart* (SVG puro, sem Recharts): `value: number` (obrigatório), `min` (default `0`), `max` (default `100`), `thresholds: GaugeChartThreshold[]` (`{ value, color, label? }`), `label: ReactNode`, `valueFormatter: (value, percent) => string`, `showNeedle: boolean` (default `true`), `className`.

Estados assíncronos comuns (`isLoading`, `error`, `onRetry`, `retryLabel`, `emptyTitle`, `emptyDescription`, `errorTitle`, `errorDescription`, `loadingLabel`, `stateClassName`) disponíveis em: `DashboardBarChart`, `DashboardLineChart`, `HorizontalBarChart`, `InteractiveTimeSeriesChart`, `ComboMetricChart`, `DonutBreakdownChart`, `RadarMetricChart`, `PieMetricChart`, `RadialBarMetricChart` (não em `GaugeChart`).

`barSize`: `'sm' | 'md' | 'lg' | 'xl' | number` — disponível em `DashboardBarChart`, `HorizontalBarChart` e nas séries `bar` de `ComboMetricChart`.

**Exemplo de uso:**
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from 'xertica-ui/ui';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from 'recharts';

const chartConfig: ChartConfig = {
  revenue: { label: 'Revenue', color: 'var(--chart-1)' },
};

<ChartContainer config={chartConfig} className="h-[300px]">
  <BarChart data={data}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="month" tickLine={false} axisLine={false} />
    <ChartTooltip content={<ChartTooltipContent />} />
    <Bar dataKey="revenue" fill="var(--color-revenue)" radius={4} />
  </BarChart>
</ChartContainer>

// Dashboard-ready
<DashboardBarChart
  data={analyticsData}
  indexKey="date"
  config={config}
  stacked
  isLoading={isLoading}
  error={error}
  onRetry={refetch}
/>
```

**Notas importantes:** Sempre usar `<ChartContainer config={config}>` — nunca `<ResponsiveContainer>` diretamente (já embutido). Nunca hex cru em `fill` — sempre `fill="var(--color-keyName)"` ou tokens `--chart-N`. Importar primitivos Recharts (`BarChart`, `Bar`, `Line`...) diretamente de `'recharts'` — não são re-exportados. Definir altura via `className="h-[300px]"` no `ChartContainer`. `RadarMetricChart`, `PieMetricChart` e `RadialBarMetricChart` constroem o `ChartConfig` internamente — não é preciso passar `config`. Em `stacked` no `DashboardBarChart`, a **última** entrada do array `series` fica no topo e recebe `radius={[4,4,0,0]}` automaticamente; as demais recebem `radius={[0,0,0,0]}` — ordene a série visualmente superior por último. `RadialBarMetricChart` renderiza a legenda como HTML fora do SVG (evita bug de posicionamento de legenda polar do Recharts). Em apps FSD, mantenha fetch de dados na camada feature/model e passe apenas `data`, `isLoading`, `error`, `onRetry` para os wrappers.

---

### Checkbox
**Import:** `import { Checkbox, Label } from 'xertica-ui/ui'`
**Propósito:** Alternância binária para selecionar/desselecionar uma opção única, ou marcar múltiplas opções independentes em uma lista.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `id` | `string` | — | Associa com `<Label htmlFor="...">` |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Variante de tamanho |
| `checked` | `boolean` | — | Estado controlado |
| `defaultChecked` | `boolean` | `false` | Estado inicial não-controlado |
| `onCheckedChange` | `(checked: boolean) => void` | — | Handler de mudança |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `className` | `string` | — | Classes CSS adicionais |

**Exemplo de uso:**
```tsx
<FormField
  control={form.control}
  name="acceptTerms"
  render={({ field }) => (
    <FormItem className="flex items-center space-x-2">
      <FormControl>
        <Checkbox checked={field.value} onCheckedChange={field.onChange} />
      </FormControl>
      <FormLabel>Aceitar Termos</FormLabel>
      <FormMessage />
    </FormItem>
  )}
/>
```

**Notas importantes:** Não usar spread `{...field}` no `<Checkbox>` — usar `checked={field.value}` e `onCheckedChange={field.onChange}` explicitamente. Sempre parear com um `<Label>` conectado via `htmlFor`. Para escolhas mutuamente exclusivas, usar `<RadioGroup>`; para um toggle único proeminente, usar `<Switch>`.

---

### CodeBlock
**Import:** `import { CodeBlock } from 'xertica-ui/assistant'`
**Propósito:** Exibição de código com syntax highlighting e botão de copiar de um clique. Projetado para uso em mensagens de chat de IA e painéis de documentação.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `code` | `string` | *(obrigatório)* | String de código-fonte a exibir |
| `language` | `string` | `'text'` | Identificador de linguagem para highlighting (`'typescript'`, `'python'`, `'bash'`...) |
| `className` | `string` | — | Classes CSS adicionais do container externo |

**Exemplo de uso:**
```tsx
import { CodeBlock } from 'xertica-ui/assistant';

<CodeBlock
  language="typescript"
  code={`function greet(name: string): string {
  return \`Hello, \${name}!\`;
}`}
/>
```

**Notas importantes:** Usa `react-syntax-highlighter` (engine Prism) com tema baseado em tokens do design system (keywords em `--chart-1`, strings em `--chart-2`, números/booleanos em `--chart-5`, propriedades em `--chart-4`, comentários em `--muted-foreground`; fundo transparente, herdando do card pai) — adapta-se automaticamente a claro/escuro. O botão de copiar troca o ícone `Copy` por `Check` por 2 segundos após o clique. Sempre fornecer `language` corretamente (ex.: `'typescript'`, não `'ts'`) — sem isso, cai em texto plano. Não envolver em `<pre>`/`<code>` — o componente já renderiza seu próprio `<pre>`, o que quebraria o layout. Otimizado para painéis estreitos (~400px, ex.: sidebar do Assistant); para exibição full-page, adicionar `className="w-full"`.

---

### Collapsible
**Import:** `import { Collapsible, CollapsibleTrigger, CollapsibleContent } from 'xertica-ui/ui'`
**Propósito:** Seção única expansível/colapsável com um trigger. Diferente do `<Accordion>`, é um elemento independente, sem irmãos nem comportamento de "apenas um aberto".

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `open` | `boolean` | — | Estado controlado |
| `defaultOpen` | `boolean` | `false` | Padrão não-controlado |
| `onOpenChange` | `(open: boolean) => void` | — | Handler de mudança |
| `disabled` | `boolean` | `false` | Impede alternância |

**Exemplo de uso:**
```tsx
import { Collapsible, CollapsibleContent, CollapsibleTrigger, Button } from 'xertica-ui/ui';
import { ChevronsUpDown } from 'lucide-react';

<Collapsible open={isOpen} onOpenChange={setIsOpen}>
  <div className="flex items-center justify-between">
    <h4 className="text-sm font-semibold">Configurações Avançadas</h4>
    <CollapsibleTrigger asChild>
      <Button variant="ghost" size="icon">
        <ChevronsUpDown className="size-4" />
      </Button>
    </CollapsibleTrigger>
  </div>
  <CollapsibleContent className="space-y-2 mt-4">
    <p className="text-sm text-muted-foreground">Opções de configuração avançada aparecem aqui.</p>
  </CollapsibleContent>
</Collapsible>
```

**Notas importantes:** Para múltiplas seções colapsáveis relacionadas, usar `<Accordion>`. `<CollapsibleTrigger asChild>` é obrigatório ao envolver um `<Button>`. `CollapsibleContent` já é animado por padrão — não adicionar transições de altura customizadas.

---

### Command
**Import:** `import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from 'xertica-ui/ui'`
**Propósito:** Paleta de comandos para buscar e executar ações via teclado, acionada por `⌘K`/`Ctrl+K`. Fornece lista pesquisável/filtrável de comandos, navegação e ações rápidas. Construída sobre `cmdk`.

**Props/variantes principais:** Sem tabela de props formal na fonte; a composição segue a anatomia:
```
<CommandDialog open={open} onOpenChange={setOpen}>
  <CommandInput placeholder="..." />
  <CommandList>
    <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
    <CommandGroup heading="Sugestões">
      <CommandItem>...</CommandItem>
    </CommandGroup>
  </CommandList>
</CommandDialog>
```

**Exemplo de uso:**
```tsx
export function CommandPalette() {
  const [open, setOpen] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setOpen(prev => !prev);
      }
    };
    document.addEventListener('keydown', down);
    return () => document.removeEventListener('keydown', down);
  }, []);

  return (
    <CommandDialog open={open} onOpenChange={setOpen}>
      <CommandInput placeholder="Buscar comandos..." />
      <CommandList>
        <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
        <CommandGroup heading="Navegação">
          <CommandItem onSelect={() => { navigate('/dashboard'); setOpen(false); }}>
            Dashboard
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  );
}
```

**Notas importantes:** Sempre registrar o atalho de teclado (`⌘K`/`Ctrl+K`) — sem isso o componente fica invisível/inacessível. `CommandEmpty` é obrigatório (mostrado quando a busca não retorna resultados). Fechar a paleta (`setOpen(false)`) após executar qualquer ação em `onSelect`. Usar `CommandDialog` para a variante modal; usar `Command` diretamente para embutir em um painel customizado.

---

### ContextMenu
**Import:** `import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent } from 'xertica-ui/ui'`
**Propósito:** Menu que aparece ao clicar com o botão direito (ou pressionar-e-segurar em mobile) sobre uma área trigger. Padrão de menu contextual nativo de SO, para interações de usuários avançados. Construído sobre Radix UI.

**Props/variantes principais:** Sem tabela de props formal na fonte; composição via anatomia:
```
<ContextMenu>
  <ContextMenuTrigger>...</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem />
    <ContextMenuSeparator />
    <ContextMenuSub>
      <ContextMenuSubTrigger />
      <ContextMenuSubContent><ContextMenuItem /></ContextMenuSubContent>
    </ContextMenuSub>
  </ContextMenuContent>
</ContextMenu>
```

**Exemplo de uso:**
```tsx
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger } from 'xertica-ui/ui';

<ContextMenu>
  <ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed">
    Clique com o botão direito aqui
  </ContextMenuTrigger>
  <ContextMenuContent className="w-48">
    <ContextMenuItem>Ver</ContextMenuItem>
    <ContextMenuItem>Editar</ContextMenuItem>
    <ContextMenuSeparator />
    <ContextMenuItem className="text-destructive">Excluir</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>
```

**Notas importantes:** Itens destrutivos usam `className="text-destructive"`. Menus de contexto complementam (não substituem) botões de ação visíveis. `ContextMenuTrigger` não requer `asChild` — renderiza como `<span>` por padrão; classes de layout são aplicadas diretamente nele. Não usar para ações primárias descobríveis (usar `Button`/`DropdownMenu`) nem em UIs mobile-first (usar `Sheet`). Relacionado: `DropdownMenu`, `AlertDialog`.

---

### Dialog
**Import:** `import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter } from 'xertica-ui/ui'`
**Propósito:** Overlay modal que interrompe o fluxo da página para capturar atenção do usuário em confirmações críticas, entrada de dados ou visualizações detalhadas. Construído sobre Radix UI Dialog com focus trap, escape via teclado e fechamento ao clicar no backdrop.

**Props/variantes principais:**
| Componente | Prop | Tipo | Default | Descrição |
|---|---|---|---|---|
| `Dialog` | `open` | `boolean` | — | Estado controlado |
| `Dialog` | `onOpenChange` | `(open: boolean) => void` | — | Handler de abertura/fechamento |
| `Dialog` | `defaultOpen` | `boolean` | — | Estado inicial não-controlado |
| `DialogContent` | `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| '3xl' \| '4xl' \| '5xl' \| 'full'` | `'lg'` | Preset de largura (`full` ocupa a viewport) |
| `DialogContent` | `showClose` | `boolean` | `true` | Exibe o botão de fechar `×` (sempre fixo, nunca rola) |
| `DialogContent` | `className` | `string` | — | Classes CSS adicionais |
| `DialogContent` | `onPointerDownOutside` | `event => void` | — | Intercepta clique fora |
| `DialogContent` | `onEscapeKeyDown` | `event => void` | — | Intercepta tecla Escape |
| `DialogBody` | `className` | `string` | — | Wrapper opcional para conteúdo longo — mantém header/footer fixos, só o body rola |
| `DialogTrigger` | `asChild` | `boolean` | — | Renderiza como elemento filho (usar com `<Button>`) |

**Exemplo de uso:**
```tsx
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Button, Input, Label } from 'xertica-ui/ui';

<Dialog>
  <DialogTrigger asChild>
    <Button><Plus className="size-4 mr-2" /> Novo Membro</Button>
  </DialogTrigger>
  <DialogContent size="md">
    <DialogHeader>
      <DialogTitle>Adicionar Membro à Equipe</DialogTitle>
      <DialogDescription>Preencha os dados do novo membro abaixo.</DialogDescription>
    </DialogHeader>
    <div className="grid gap-4 py-2">
      <div className="grid gap-2">
        <Label htmlFor="name">Nome Completo</Label>
        <Input id="name" placeholder="João Silva" />
      </div>
    </div>
    <DialogFooter>
      <Button type="submit">Salvar</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

**Notas importantes:** Sempre usar `<DialogTrigger asChild>` envolvendo um `<Button>` — nunca renderizar um `<button>` cru como trigger. `DialogTitle` é **obrigatório** (erro de acessibilidade se omitido). Usar a prop `size` para controlar a largura — não sobrescrever com `className="max-w-..."`. Para conteúdo longo/rolável, envolver em `<DialogBody>` para manter header/footer fixos. Para ações irreversíveis (deletar, revogar, resetar), usar `<AlertDialog>` em vez de `Dialog`. Com react-hook-form, colocar `<form onSubmit={...}>` dentro de `<DialogContent>` e o botão de submit em `<DialogFooter>`. Relacionado: `AlertDialog`, `Sheet` (painel lateral mais largo), `Drawer` (painel mobile), `Form`.

---

### Drawer
**Import:** `import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose } from 'xertica-ui/ui'`
**Propósito:** Painel que desliza a partir da borda inferior da tela — equivalente mobile-otimizado do `<Sheet>`. Padrão de interação padrão para painéis em mobile.

**Props/variantes principais:**
| Prop | Tipo | Descrição |
|---|---|---|
| `open` | `boolean` | Estado controlado |
| `onOpenChange` | `(open: boolean) => void` | Handler de estado |

**Exemplo de uso:**
```tsx
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, Button } from 'xertica-ui/ui';

<Drawer>
  <DrawerTrigger asChild>
    <Button variant="outline">Abrir Filtros</Button>
  </DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Filtros</DrawerTitle>
      <DrawerDescription>Refine seus resultados.</DrawerDescription>
    </DrawerHeader>
    <div className="p-4">{/* Controles de filtro */}</div>
    <DrawerFooter>
      <Button>Aplicar Filtros</Button>
      <DrawerClose asChild>
        <Button variant="outline">Cancelar</Button>
      </DrawerClose>
    </DrawerFooter>
  </DrawerContent>
</Drawer>
```

**Notas importantes:** `DrawerTitle` é obrigatório para acessibilidade. `DrawerClose asChild` envolvendo um `<Button>` fecha o drawer automaticamente. Para aplicações desktop, prefira `<Sheet side="right">` em vez de `<Drawer>`. Relacionado: `Sheet`, `Dialog`.

---

### DropdownMenu
**Import:** `import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent } from 'xertica-ui/ui'`
**Propósito:** Menu contextual acionado por clique, ancorado abaixo (ou ao lado) de um trigger. Usado em ações de linha de tabela, menus de perfil no Header e opções agrupadas. Construído sobre Radix UI com navegação por teclado completa.

**Props/variantes principais:**
| Componente | Prop | Tipo | Default | Descrição |
|---|---|---|---|---|
| `DropdownMenu` | `open` | `boolean` | — | Estado controlado |
| `DropdownMenu` | `onOpenChange` | `(open: boolean) => void` | — | Handler de estado |
| `DropdownMenuContent` | `align` | `'start' \| 'center' \| 'end'` | `'start'` | Alinhamento horizontal ao trigger |
| `DropdownMenuContent` | `sideOffset` | `number` | `4` | Offset vertical do trigger |
| `DropdownMenuContent` | `className` | `string` | — | Sobrescreve largura/estilo |
| `DropdownMenuItem` | `onClick` | `() => void` | — | Handler de ação |
| `DropdownMenuItem` | `className` | `string` | — | Classes adicionais (`text-destructive` para itens perigosos) |
| `DropdownMenuItem` | `disabled` | `boolean` | — | Desabilita o item |

**Exemplo de uso:**
```tsx
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Button } from 'xertica-ui/ui';
import { MoreHorizontal } from 'lucide-react';

<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="ghost" size="icon" className="size-8">
      <MoreHorizontal className="size-4" />
    </Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent align="end">
    <DropdownMenuLabel>Ações</DropdownMenuLabel>
    <DropdownMenuSeparator />
    <DropdownMenuItem onClick={() => handleEdit(record)}>Editar</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem className="text-destructive focus:text-destructive" onClick={() => handleDelete(record)}>
      Excluir
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

**Notas importantes:** Sempre usar `<DropdownMenuTrigger asChild>` quando o trigger for um `<Button>`. Itens destrutivos (Excluir, Revogar, Desabilitar) devem usar `className="text-destructive focus:text-destructive"`. Ao clicar em item destrutivo, deve-se abrir um `<AlertDialog>` — nunca executar a ação diretamente. Usar `<DropdownMenuSeparator>` para agrupar visualmente; itens destrutivos sempre separados dos seguros. `align="end"` é padrão para menus de linha de tabela. Relacionado: `ContextMenu` (variante clique-direito), `AlertDialog`, `Button`.

---

### Empty
**Import:** `import { Empty, EmptyIcon, EmptyImage, EmptyTitle, EmptyDescription, EmptyAction } from 'xertica-ui/ui'`
**Propósito:** Container de estado vazio estruturado, renderizado quando uma lista, tabela ou busca não retorna resultados. Usa um padrão composable (`Empty + EmptyIcon + EmptyTitle + EmptyDescription + EmptyAction`).

**Props/variantes principais (sub-componentes):**
| Componente | Descrição |
|---|---|
| `Empty` | Container raiz com borda tracejada, layout flex centralizado e animação de fade-in |
| `EmptyIcon` | Container circular de ícone com fundo muted |
| `EmptyImage` | Elemento de imagem opcional com opacidade reduzida |
| `EmptyTitle` | Título principal (`<h3>`) com texto semibold |
| `EmptyDescription` | Texto de suporte muted (`<p>`) |
| `EmptyAction` | Área de CTA — flex row no desktop, empilhado em mobile |

Todos os sub-componentes aceitam `className` e encaminham atributos HTML de `div`/`p`/`h3`.

**Exemplo de uso:**
```tsx
import { Empty, EmptyIcon, EmptyTitle, EmptyDescription, EmptyAction, Button } from 'xertica-ui/ui';
import { Users } from 'lucide-react';

<Empty>
  <EmptyIcon>
    <Users className="size-10 text-muted-foreground" />
  </EmptyIcon>
  <EmptyTitle>Nenhum membro ainda</EmptyTitle>
  <EmptyDescription>Comece adicionando seu primeiro membro de equipe.</EmptyDescription>
  <EmptyAction>
    <Button onClick={() => setDialogOpen(true)}>Adicionar Membro</Button>
  </EmptyAction>
</Empty>
```

**Notas importantes:** Sempre usar o padrão composable (`Empty > EmptyIcon > EmptyTitle`) — não passar props flat como `title=""` ou `icon={}`. Ícones dentro de `<EmptyIcon>` usam `className="size-10 text-muted-foreground"` (ou `size-8` em contextos compactos de tabela). `<EmptyAction>` é opcional — só incluir quando há uma próxima ação clara. Dentro de uma tabela, envolver em `<TableCell colSpan={totalColumns}>`. `Empty` renderiza com `min-h-[400px]` — em linhas compactas de tabela, sobrescrever com `className="min-h-[200px]"`. Para estados de carregamento usar `<Skeleton>`; para erros de fetch usar `<Alert variant="destructive">`. Relacionado: `Skeleton`, `Alert`, `Table`.

---

### ErrorBoundary
**Import:** `import { AppErrorBoundary, PageErrorBoundary, SectionErrorBoundary } from '@/components/shared/error-boundary'` (wrappers pré-configurados); `import { ErrorBoundary } from '@/components/shared/error-boundary'` e `import type { FallbackProps } from '@/components/shared/error-boundary'` (classe base para fallbacks customizados) — **atenção:** este componente reside no código local do projeto (`@/components/shared/error-boundary`), não em um subpath do pacote `xertica-ui`.
**Propósito:** React Error Boundaries capturam erros JavaScript na árvore de componentes, os registram e renderizam uma UI de fallback em vez de derrubar a aplicação. A Xertica UI fornece uma única classe `ErrorBoundary` reutilizável com três wrappers pré-configurados cobrindo diferentes granularidades de proteção.

**Props/variantes principais:**

| Variante | Fallback UI | Posicionamento | Propósito |
|---|---|---|---|
| `AppErrorBoundary` | Full-screen, estilizado inline | Fora do `QueryClientProvider`, envolve o `App` inteiro | Captura crashes de provider/contexto; funciona mesmo se Tailwind/CSS falhar |
| `PageErrorBoundary` | Meia-altura, estilizado com Tailwind | Envolve `<Routes>`/`<AuthGuard>` dentro do router | Captura falhas de carregamento de lazy-chunk e erros de render de página |
| `SectionErrorBoundary` | Badge inline compacto | Envolve seções individuais (tabela, gráfico, assistente) | Captura erros isolados de renderização de dados |

| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `children` | `ReactNode` | — | **Obrigatório.** Conteúdo protegido |
| `onError` | `(error: Error, info: React.ErrorInfo) => void` | — | Chamado quando um erro é capturado (integrar com Sentry, Datadog etc.) |
| `resetKeys` | `unknown[]` | — | Quando qualquer valor muda, o boundary reseta automaticamente (ex.: `[location.pathname]`) |
| `fallback` (apenas `ErrorBoundary` base) | `React.ComponentType<FallbackProps>` | — | **Obrigatório.** Componente renderizado quando um erro é capturado |

`FallbackProps`: `{ error: Error; reset: () => void }`

**Exemplo de uso:**
```tsx
import { AppErrorBoundary, PageErrorBoundary, SectionErrorBoundary } from '@/components/shared/error-boundary';

<AppErrorBoundary onError={(error, info) => Sentry.captureException(error, { extra: info })}>
  <QueryClientProvider client={queryClient}>
    <Router>
      <PageErrorBoundary resetKeys={[location.pathname]}>
        <Suspense fallback={null}>
          <Routes>{/* ... */}</Routes>
        </Suspense>
      </PageErrorBoundary>
    </Router>
  </QueryClientProvider>
</AppErrorBoundary>

function TeamSection() {
  return (
    <SectionErrorBoundary>
      <TeamDataTable />
    </SectionErrorBoundary>
  );
}
```

**Notas importantes:** `<AppErrorBoundary>` deve ser sempre o elemento **mais externo** em `App.tsx`, antes do `QueryClientProvider` (se o próprio query client quebrar, algo ainda deve aparecer). `<PageErrorBoundary>` deve envolver `<Routes>`/`<AuthGuard>` — proteção primária contra falhas de lazy-chunk (erros de rede, chunk não encontrado). Usar `<SectionErrorBoundary>` em qualquer seção que faça fetch de dados ou renderize conteúdo complexo (tabelas, gráficos, painel do assistente, mapas embutidos). `<AppErrorBoundary>` **nunca** deve ficar dentro do `<Router>` — seu fallback usa `window.location.assign`, não `useNavigate`. `try/catch` não substitui `ErrorBoundary` (não captura erros em tempo de render). Passar `onError` em produção para enviar erros a um serviço de observabilidade. Passar `resetKeys={[location.pathname]}` para resetar o boundary automaticamente na navegação. Error boundaries **não** capturam: erros em event handlers, erros assíncronos fora do render (usar `onError` do React Query), erros de SSR, nem erros lançados dentro do próprio boundary.

---

### FileUpload
**Import:** `import { FileUpload, useFileUpload } from 'xertica-ui/ui'`
**Propósito:** Input de arquivo drag-and-drop que aceita um ou múltiplos arquivos, com feedback visual ao arrastar, exibição dos nomes selecionados e validação de tipos MIME e tamanho. Também expõe o hook headless `useFileUpload` para UIs de dropzone totalmente customizadas.

**Props/variantes principais:**

*FileUpload*
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `accept` | `string` | `'*'` | Tipos MIME aceitos (ex.: `'image/*,.pdf'`) |
| `multiple` | `boolean` | `false` | Permite seleção múltipla |
| `maxSize` | `number` | — | Tamanho máximo em bytes |
| `onFilesChange` | `(files: File[]) => void` | — | Chamado quando a seleção muda |
| `disabled` | `boolean` | `false` | Desabilita a dropzone |
| `className` | `string` | — | Classes CSS adicionais |

*useFileUpload* (props)
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `maxFiles` | `number` | `1` | Número máximo de arquivos |
| `maxSize` | `number` | `5242880` (5MB) | Tamanho máximo em bytes |
| `onFilesChange` | `(files: File[]) => void` | — | Chamado com a lista aceita a cada mudança |
| `onError` | `(rejected: File[], reason: 'size' \| 'count') => void` | — | Chamado quando arquivos são rejeitados |
| `disabled` | `boolean` | `false` | Desabilita a área de upload |

*useFileUpload* (retorno): `files: File[]`, `dragActive: boolean`, `errorMessage: string | null`, `inputRef: RefObject<HTMLInputElement>`, `handleFiles`, `handleDrag`, `handleDrop`, `handleChange`, `removeFile: (index: number) => void`, `openFileDialog: () => void`.

**Exemplo de uso:**
```tsx
import { FileUpload } from 'xertica-ui/ui';

<FileUpload
  accept="image/*"
  maxSize={5 * 1024 * 1024}
  onFilesChange={files => handleAvatarUpload(files[0])}
/>
```

**Notas importantes:** Nunca usar `<input type="file">` nativo — sempre `<FileUpload>` ou `useFileUpload` de `xertica-ui`. `maxSize` é em bytes (calcular com `MB * 1024 * 1024`). Em formulários, usar `onFilesChange` para chamar `field.onChange` — não `{...field}`. Para upload de avatar, usar `accept="image/*"` e `multiple={false}`. Ao usar `useFileUpload`, anexar `handleDrag` às **três** eventos de drag: `onDragEnter`, `onDragOver` e `onDragLeave`. Sempre anexar `inputRef` a um `<input type="file">` oculto — `openFileDialog()` depende dele. `removeFile(index)` remove pelo índice do array, não pelo nome do arquivo.
### FloatingMediaWrapper
**Import:** `import { FloatingMediaWrapper } from 'xertica-ui/media'`
**Propósito:** Um wrapper para players de mídia que gerencia estado flutuante, posicionamento e transições.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `isFloating` | `boolean` | — | Controla o estado flutuante |
| `setIsFloating` | `(v: boolean) => void` | — | Setter do estado flutuante |
| `title` | `string` | — | Título no cabeçalho flutuante |
| `aspectRatio` | `number` | `16 / 9` | Mantém a proporção durante o redimensionamento |
| `minWidth` | `number` | `320` | Largura mínima permitida |
| `colorVariant` | `'default' \| 'primary'` | `'default'` | Estilo visual do cabeçalho |
| `playerId` | `string` | `'default'` | Chave única para armazenar posição |

**Exemplo de uso:**
```tsx
<FloatingMediaWrapper
  isFloating={isFloating}
  setIsFloating={setIsFloating}
  title="Sample Media"
>
  <video src="..." />
</FloatingMediaWrapper>
```
**Notas importantes:** Componente usado internamente por players de vídeo e áudio para comportamento flutuante. Usa localStorage para persistir posição e tamanho.

### Form
**Import:** `import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage, FormDescription } from 'xertica-ui/ui'`
**Propósito:** Integração entre react-hook-form e componentes Xertica UI para formulários com validação.
**Props/variantes principais:**
Subcomponentes principais:
- `Form`: Wrapper com contexto do react-hook-form
- `FormField`: Conecta campo ao estado do formulário
- `FormItem`: Container para label + controle + mensagem
- `FormLabel`: Label acessível
- `FormControl`: Wrapper com atributos ARIA
- `FormMessage`: Mensagens de erro
- `FormDescription`: Texto de ajuda

**Exemplo de uso:**
```tsx
<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)}>
    <FormField
      control={form.control}
      name="email"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email</FormLabel>
          <FormControl>
            <Input {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  </form>
</Form>
```
**Notas importantes:** Sempre usar com zod para validação. Não implementar estado manualmente. Usar {...field} no componente de input interno.

### FormattedDocument
**Import:** `import { FormattedDocument } from 'xertica-ui/assistant'`
**Propósito:** Renderizador leve de Markdown para HTML com preview colapsável, ideal para documentos gerados por IA.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `content` | `string` | _(required)_ | String Markdown para renderizar |
| `maxPreviewLength` | `number` | `500` | Caracteres antes do toggle "Ver mais" |
| `className` | `string` | `''` | Classes CSS adicionais |

**Exemplo de uso:**
```tsx
<FormattedDocument 
  content="# Report\n\nThis is a **bold** statement." 
  maxPreviewLength={300} 
/>
```
**Notas importantes:** Não suporta tabelas, links ou blocos de código. Usar MarkdownMessage para mensagens de chat. Checkbox são somente leitura.

### GoogleMapsLoader
**Import:** `import { useGoogleMapsLoader } from 'xertica-ui'`
**Propósito:** Hook para carregamento lazy e inicialização da API JavaScript do Google Maps.
**Props/variantes principais:** 
Hook retorna:
- `isLoaded`: boolean indicando se a API está carregada
- `loadError`: erro de carregamento, se houver

**Exemplo de uso:**
```tsx
const { isLoaded, loadError } = useGoogleMapsLoader();

if (loadError) return <div>Error loading maps</div>;
if (!isLoaded) return <div>Loading maps...</div>;
```
**Notas importantes:** Gerenciado automaticamente pelo XerticaProvider. Verificar isLoaded antes de renderizar componentes que usam window.google.

### Header
**Import:** `import { Header } from 'xertica-ui/ui'`
**Propósito:** Barra superior da aplicação com breadcrumbs, navegação e controles do sistema.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `title` | `string` | — | Título para páginas standalone |
| `breadcrumbs` | `BreadcrumbType[]` | — | Trilha de navegação (recomendado) |
| `breadcrumbSlot` | `ReactNode` | — | Elemento customizado após breadcrumbs |
| `renderLink` | `(href, props) => ReactNode` | — | Renderer customizado para links SPA |
| `showLanguageSelector` | `boolean` | `true` | Mostrar seletor de idioma |
| `showThemeToggle` | `boolean` | `true` | Mostrar toggle de tema |
| `user` | `HeaderUser` | — | Informações do usuário |
| `actions` | `HeaderAction[]` | — | Botões de ação global |
| `showSettings` | `boolean` | `false` | Ícone de configurações |
| `showLogout` | `boolean` | `false` | Ícone de logout |

**Exemplo de uso:**
```tsx
<Header
  breadcrumbs={[
    { label: 'Home', href: '/', icon: <Home /> },
    { label: 'Settings', href: '/settings' },
    { label: 'Team Management' },
  ]}
/>
```
**Notas importantes:** Preferir breadcrumbs em vez de title. Usar renderLink para navegação SPA. Integrar com LayoutContext.

### Hooks
**Import:** `import { useTheme, useLanguage, useBrandColors, useAssistente, useApiKey, useLayout, useAudioPlayer, useLayoutShortcuts, useIsMobile } from 'xertica-ui/hooks'`
**Propósito:** Conjunto de hooks React para acessar contextos globais e utilitários da Xertica UI.

**useTheme:**
- Retorna: { theme, setTheme, toggleTheme }
- Temas: 'light' | 'dark' | 'system'

**useLanguage:**
- Retorna: { language, setLanguage, availableLanguages, isMonolingual }
- Gerencia preferência de idioma e i18n

**useBrandColors:**
- Retorna: { colors, setBrandColor, currentTheme }
- Gerencia cores da marca via CSS custom properties

**useAssistente:**
- Retorna: { conversas, conversaAtual, setConversaAtual, sugestoes, setSugestoes }
- Contexto do assistente de IA

**useApiKey:**
- Retorna: { apiKey, setApiKey, geminiApiKey, setGeminiApiKey, isApiKeyValid, googleMapsApiKey, setGoogleMapsApiKey, isGoogleMapsKeyValid, reloadMapsApi }
- Gerencia chaves de API

**useLayout:**
- Retorna: { sidebarExpanded, sidebarWidth, setSidebarWidth, toggleSidebar, assistenteExpanded, toggleAssistente, toggleAssistenteWithTab }
- Controla estado do layout global

**useAudioPlayer:**
- Hook headless para player de áudio
- Retorna refs, estado de playback, handlers e utilitários

**useLayoutShortcuts:**
- Registra atalhos de teclado (Ctrl+B para sidebar)
- Chamar apenas uma vez no layout raiz

**useIsMobile:**
- Retorna boolean indicando viewport mobile

**Exemplo de uso:**
```tsx
const { theme, toggleTheme } = useTheme();
const { language, setLanguage } = useLanguage();
```
**Notas importantes:** Todos os hooks de contexto requerem XerticaProvider. useLayoutShortcuts é singleton. Preferir componentes prontos ao hook headless.

### HoverCard
**Import:** `import { HoverCard, HoverCardContent, HoverCardTrigger } from 'xertica-ui/ui'`
**Propósito:** Painel flutuante que aparece ao passar o mouse, para preview de informações adicionais.
**Props/variantes principais:** 
Componentes:
- `HoverCard`: Container raiz
- `HoverCardTrigger`: Elemento que dispara o card
- `HoverCardContent`: Conteúdo do card

**Exemplo de uso:**
```tsx
<HoverCard>
  <HoverCardTrigger asChild>
    <Button variant="link">@username</Button>
  </HoverCardTrigger>
  <HoverCardContent className="w-80">
    <div>Preview content</div>
  </HoverCardContent>
</HoverCard>
```
**Notas importantes:** Usar asChild no trigger. Não colocar controles interativos no conteúdo. Usar className="w-80" para conteúdo rico.

### ImageWithFallback
**Import:** `import { ImageWithFallback } from 'xertica-ui/ui'`
**Propósito:** Substituição para <img> que mostra placeholder em caso de erro no carregamento.
**Props/variantes principais:** 
Aceita todas as props padrão de <img>:
- `src`: URL da imagem
- `alt`: Texto alternativo (obrigatório)
- `className`: Classes CSS

**Exemplo de uso:**
```tsx
<ImageWithFallback
  src="https://example.com/photo.jpg"
  alt="User profile photo"
  className="w-32 h-32 rounded-lg object-cover"
/>
```
**Notas importantes:** Sempre fornecer alt para acessibilidade. Usar para imagens externas que podem falhar. Não usar para avatares (preferir componente Avatar).

### Input
**Import:** `import { Input } from 'xertica-ui/ui'`
**Propósito:** Campo de texto padrão para entradas de linha única.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `type` | `string` | `'text'` | Tipo do input |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Tamanho |
| `placeholder` | `string` | — | Placeholder |
| `disabled` | `boolean` | `false` | Desabilita interação |

**Exemplo de uso:**
```tsx
<Input placeholder="Enter your name" />
```
**Notas importantes:** Sempre parear com Label. Usar aria-label quando não há label visual. Nunca usar <input> nativo diretamente.

### InputOTP
**Import:** `import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'xertica-ui/ui'`
**Propósito:** Input para códigos OTP/PIN com slots individuais.
**Props/variantes principais:**
Componentes:
- `InputOTP`: Container principal
  - `maxLength`: número total de caracteres
- `InputOTPSlot`: Slot individual
  - `index`: posição (0-indexed)
  - `size`: 'sm' | 'md' | 'lg'

**Exemplo de uso:**
```tsx
<InputOTP maxLength={6} value={code} onChange={setCode}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>
```
**Notas importantes:** maxLength deve corresponder ao número total de slots. Cada slot requer index correto. Usar pattern="[0-9]*" para códigos numéricos.

### Label
**Import:** `import { Label } from 'xertica-ui/ui'`
**Propósito:** Label acessível para campos de formulário.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `htmlFor` | `string` | — | ID do input associado |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Tamanho |

**Exemplo de uso:**
```tsx
<Label htmlFor="username">Username</Label>
<Input id="username" />
```
**Notas importantes:** Sempre associar com htmlFor. Usar FormLabel dentro de FormField. Obrigatoriedade para acessibilidade.

### Map
**Import:** `import { Map } from 'xertica-ui/ui'`
**Propósito:** Componente Google Map interativo com markers e overlays.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `center` | `{ lat: number; lng: number }` | Sao Paulo | Centro inicial |
| `zoom` | `number` | `12` | Nível de zoom |
| `markers` | `MapMarker[]` | `[]` | Markers a exibir |
| `layers` | `MapLayersConfig` | `{}` | Overlays nativos |
| `height` | `string` | `"400px"` | Altura do container |

**Exemplo de uso:**
```tsx
<Map
  center={{ lat: -23.5505, lng: -46.6333 }}
  zoom={12}
  height="420px"
  markers={[{ position: { lat: -23.5505, lng: -46.6333 }, title: 'Xertica' }]}
  layers={{ traffic: true }}
/>
```
**Notas importantes:** Requer Google Maps API key. Usar { lat, lng } para coordenadas. Altura deve ser definida explicitamente.

### MapLayers
**Import:** `import { useMapLayers } from 'xertica-ui/ui'`
**Propósito:** Hook para gerenciar overlays nativos do Google Maps (tráfego, trânsito, ciclovias).
**Props/variantes principais:**
Configuração:
```typescript
interface MapLayersConfig {
  traffic?: boolean;
  transit?: boolean;
  bicycling?: boolean;
}
```

**Exemplo de uso:**
```tsx
useMapLayers(mapInstance, {
  traffic: showTraffic,
  transit: showTransit,
});
```
**Notas importantes:** Usar via prop layers do componente Map quando possível. Requer Google Maps API carregada. Passar null quando mapa não estiver disponível.

### Menubar
**Import:** `import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger } from 'xertica-ui/ui'`
**Propósito:** Barra de menu horizontal estilo aplicação desktop.
**Props/variantes principais:** 
Componentes:
- `Menubar`: Container raiz
- `MenubarMenu`: Menu individual
- `MenubarTrigger`: Elemento que dispara o menu
- `MenubarContent`: Conteúdo do menu
- `MenubarItem`: Item de menu
- `MenubarSeparator`: Separador
- `MenubarShortcut`: Atalho de teclado

**Exemplo de uso:**
```tsx
<Menubar>
  <MenubarMenu>
    <MenubarTrigger>File</MenubarTrigger>
    <MenubarContent>
      <MenubarItem>
        New Tab <MenubarShortcut>⌘T</MenubarShortcut>
      </MenubarItem>
    </MenubarContent>
  </MenubarMenu>
</Menubar>
```
**Notas importantes:** Usar MenubarShortcut para atalhos. Para itens destrutivos, usar className="text-destructive". Pouco comum em dashboards.

### NavigationMenu
**Import:** `import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger } from 'xertica-ui/ui'`
**Propósito:** Navegação horizontal para seções de aplicação/top-level.
**Props/variantes principais:** 
Componentes:
- `NavigationMenu`: Container raiz
- `NavigationMenuList`: Lista de itens
- `NavigationMenuItem`: Item individual
- `NavigationMenuTrigger`: Trigger para submenu
- `NavigationMenuContent`: Conteúdo do submenu
- `NavigationMenuLink`: Link simples

**Exemplo de uso:**
```tsx
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Products</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div>Dropdown content</div>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>
```
**Notas importantes:** Para navegação primária usar Sidebar. Conteúdo renderiza em portal. Usar NavigationMenuLink para links simples.

### NotificationBadge
**Import:** `import { NotificationBadge } from 'xertica-ui/ui'`
**Propósito:** Indicador overlay para mostrar contagem ou atenção em elementos.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `count` | `number` | `0` | Número a exibir |
| `max` | `number` | `99` | Máximo antes de mostrar {max}+ |
| `dot` | `boolean` | `false` | Mostra ponto em vez de número |
| `showZero` | `boolean` | `false` | Mostra quando count é 0 |
| `variant` | `'default' \| 'secondary' \| 'destructive' \| 'outline' \| 'success' \| 'info' \| 'warning'` | `'destructive'` | Variante de cor |

**Exemplo de uso:**
```tsx
<NotificationBadge count={3} variant="destructive">
  <Button variant="ghost" size="icon">
    <Bell />
  </Button>
</NotificationBadge>
```
**Notas importantes:** Sempre envolver o elemento alvo. Não mostrar quando count é 0 (a menos que showZero=true). Usar Badge para labels standalone.

### PageHeader
**Import:** `import { PageHeader, PageHeaderHeading, PageHeaderDescription } from 'xertica-ui/ui'`
**Propósito:** Cabeçalho de conteúdo para contexto específico de página/seção.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `title` | `string` | — | Título H1 da página |
| `subtitle` | `string` | — | Descrição secundária |
| `backHref` | `string` | — | URL para navegação de volta |
| `onBack` | `() => void` | — | Handler para botão de voltar |
| `actions` | `React.ReactNode` | — | Ações primárias da página |

**Exemplo de uso:**
```tsx
<PageHeader
  title="Users"
  subtitle="Manage and invite members"
  actions={<Button size="sm"><Plus /> Add User</Button>}
/>
```
**Notas importantes:** Não confundir com Header global. Sempre fornecer título significativo. Usar backHref/onBack para sub-views. Colocar imediatamente após Header global.

### Pages
**Import:** `import { LoginPage, ForgotPasswordPage, ResetPasswordPage, VerifyEmailPage, HomePage, HomeContent, TemplatePage, TemplateContent } from 'xertica-ui/pages'`
**Propósito:** Templates de página prontos para autenticação, dashboard e showcase.
**Props/variantes principais:** 
Templates de autenticação:
- `LoginPage`: Formulário de login com validação
- `ForgotPasswordPage`: Recuperação de senha
- `ResetPasswordPage`: Nova senha
- `VerifyEmailPage`: Confirmação de email

Templates de dashboard:
- `HomePage`: Layout completo com Sidebar + HomeContent + XerticaAssistant
- `HomeContent`: Área de conteúdo principal
- `TemplatePage`: Shell idêntico a HomePage
- `TemplateContent`: Showcase completo de componentes

**Exemplo de uso:**
```tsx
// Páginas de autenticação
<LoginPage onLogin={(email, password) => {}} />

// Páginas de dashboard (requer XerticaProvider e AuthProvider)
<HomePage />
<TemplatePage />
```
**Notas importantes:** HomePage/TemplatePage consomem user/onLogout via useAuth(). Não passar essas props. Usar TemplatePage como referência para novas páginas. Requer react-router-dom.

### Pagination
**Import:** `import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, usePagination } from 'xertica-ui/ui'`
**Propósito:** Controle de navegação para dados paginados.
**Props/variantes principais:** 
Componentes:
- `Pagination`: Container raiz
- `PaginationContent`: Container flex
- `PaginationItem`: Item individual
- `PaginationLink`: Link de página
- `PaginationPrevious`: Botão anterior
- `PaginationNext`: Botão próximo
- `PaginationEllipsis`: Indicador "..."

Hook usePagination:
- `totalItems`: número total de itens
- `pageSize`: itens por página
- `page`: página controlada
- `onPageChange`: handler de mudança

**Exemplo de uso:**
```tsx
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious disabled={!canGoPrev} />
    </PaginationItem>
    {items.map(item => 
      item.type === 'ellipsis' ? (
        <PaginationItem key={item.key}>
          <PaginationEllipsis />
        </PaginationItem>
      ) : (
        <PaginationItem key={item.page}>
          <PaginationLink isActive={item.page === currentPage}>
            {item.page}
          </PaginationLink>
        </PaginationItem>
      )
    )}
    <PaginationItem>
      <PaginationNext disabled={!canGoNext} />
    </PaginationItem>
  </PaginationContent>
</Pagination>
```
**Notas importantes:** Colocar abaixo de tabelas. Usar disabled em vez de aria-disabled. isActive para página atual. Usar usePagination para lógica headless.

### Popover
**Import:** `import { Popover, PopoverContent, PopoverTrigger } from 'xertica-ui/ui'`
**Propósito:** Painel flutuante ancorado a elemento trigger com conteúdo interativo.
**Props/variantes principais:** 
Componentes:
- `Popover`: Container raiz
  - `open`: estado controlado
  - `onOpenChange`: handler
- `PopoverTrigger`: Elemento que dispara o popover
- `PopoverContent`: Conteúdo
  - `align`: alinhamento horizontal
  - `side`: posição
  - `sideOffset`: distância do trigger

**Exemplo de uso:**
```tsx
<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline">Filters</Button>
  </PopoverTrigger>
  <PopoverContent className="w-64 p-4">
    <div>Filter controls</div>
  </PopoverContent>
</Popover>
```
**Notas importantes:** Usar asChild no trigger quando é Button. Conteúdo pode ser interativo. Para date pickers, usar className="w-auto p-0".

### Progress
**Import:** `import { Progress } from 'xertica-ui/ui'`
**Propósito:** Indicador visual de completude ou porcentagem.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `value` | `number` | — | Valor (0-100) |
| `variant` | `'default' \| 'success' \| 'info' \| 'warning' \| 'destructive'` | `'default'` | Variante de cor |

**Exemplo de uso:**
```tsx
<Progress value={65} variant="success" />
```
**Notas importantes:** Valor entre 0-100. Sempre parear com label visível. Usar variantes para estados semânticos. Não confundir com Slider.

### RadioGroup
**Import:** `import { RadioGroup, RadioGroupItem } from 'xertica-ui/ui'`
**Propósito:** Conjunto de opções mutuamente exclusivas.
**Props/variantes principais:** 
Componentes:
- `RadioGroup`: Container
  - `value`: valor controlado
  - `defaultValue`: valor inicial
  - `onValueChange`: handler
  - `orientation`: direção
- `RadioGroupItem`: Item individual
  - `value`: valor da opção
  - `id`: associação com Label
  - `size`: tamanho ('sm' | 'md' | 'lg')

**Exemplo de uso:**
```tsx
<RadioGroup defaultValue="editor">
  <div className="flex items-center space-x-2">
    <RadioGroupItem value="admin" id="admin" />
    <Label htmlFor="admin">Administrator</Label>
  </div>
</RadioGroup>
```
**Notas importantes:** Cada item requer value e id. Label htmlFor deve corresponder. Para formulários, usar onValueChange e defaultValue. Mais de 6 opções usar Select.

### Rating
**Import:** `import { Rating } from 'xertica-ui/ui'`
**Propósito:** Input de avaliação por estrelas.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `value` | `number` | — | Valor atual |
| `defaultValue` | `number` | `0` | Valor inicial |
| `onChange` | `(value: number) => void` | — | Handler |
| `max` | `number` | `5` | Máximo de estrelas |
| `readonly` | `boolean` | `false` | Modo somente leitura |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Tamanho |

**Exemplo de uso:**
```tsx
<Rating value={rating} onChange={setRating} readonly />
```
**Notas importantes:** Usar readonly para display. Em formulários, usar value e onChange (não {...field}). Valores decimais apenas em modo readonly.

### Resizable
**Import:** `import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from 'xertica-ui/ui'`
**Propósito:** Painéis redimensionáveis por arrasto.
**Props/variantes principais:** 
Componentes:
- `ResizablePanelGroup`: Container
  - `direction`: 'horizontal' | 'vertical'
- `ResizablePanel`: Painel
  - `defaultSize`: tamanho inicial (%)
  - `minSize`: tamanho mínimo
  - `maxSize`: tamanho máximo
- `ResizableHandle`: Handle de arrasto
  - `withHandle`: mostra grip visível

**Exemplo de uso:**
```tsx
<ResizablePanelGroup direction="horizontal">
  <ResizablePanel defaultSize={30}>
    <div>Side Panel</div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={70}>
    <div>Main Panel</div>
  </ResizablePanel>
</ResizablePanelGroup>
```
**Notas importantes:** direction é obrigatório. defaultSize deve somar 100%. Container pai precisa de altura definida. Usar withHandle para handles visíveis.

### RichTextEditor
**Import:** `import { RichTextEditor, useRichTextEditor } from 'xertica-ui/ui'`
**Propósito:** Editor WYSIWYG leve com toolbar nativa.
**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|------|------|---------|-----------|
| `value` | `string` | — | Conteúdo HTML inicial |
| `onChange` | `(value: string) => void` | — | Handler de mudança |
| `placeholder` | `string` | — | Placeholder |
| `allowSearch` | `boolean` | `true` | Mostrar busca |
| `allowLinks` | `boolean` | `true` | Mostrar links |
| `allowUndoRedo` | `boolean` | `true` | Mostrar undo/redo |
| `disabled` | `boolean` | `false` | Desabilitar edição |
| `readOnly` | `boolean` | `false` | Modo somente leitura |

**Exemplo de uso:**
```tsx
<RichTextEditor
  value={content}
  onChange={setContent}
  placeholder="Escreva algo..."
/>
```
**Notas importantes:** Usar useRichTextEditor para UI customizada. value é apenas para montagem inicial. Chamar handleInput em eventos input. Não re-renderizar durante digitação.
### RouteMap
**Import:** `import { RouteMap } from 'xertica-ui';`
**Propósito:** Componente especializado do Google Maps que calcula e renderiza uma rota entre origem e destino (com waypoints intermediários opcionais), usando o Google Directions Service. Use `<Map>` para mapas genéricos e `<RouteMap>` quando precisar exibir uma rota navegável (rastreamento de entregas, logística, roteiros multi-parada). Requer chave do Google Maps com **Directions API** habilitada (via `XerticaProvider googleMapsApiKey` ou prop `apiKey`).

**Props/variantes principais:**
| Prop | Tipo | Default | Obrigatório |
|---|---|---|---|
| `origin` | `{ lat: number; lng: number }` | — | Sim |
| `destination` | `{ lat: number; lng: number }` | — | Sim |
| `waypoints` | `{ lat: number; lng: number }[]` | `[]` | Não |
| `travelMode` | `'DRIVING' \| 'WALKING' \| 'BICYCLING' \| 'TRANSIT'` | `'DRIVING'` | Não |
| `height` | `string` | `'450px'` | Não |
| `mapId` | `string` | — | Não |
| `apiKey` | `string` | — | Não |
| `onRouteCalculated` | `(distance: string, duration: string) => void` | — | Não |
| `disableDefaultUI` | `boolean` | `false` | Não |
| `zoomControl` | `boolean` | `true` | Não |
| `streetViewControl` | `boolean` | `false` | Não |
| `mapTypeControl` | `boolean` | `false` | Não |
| `fullscreenControl` | `boolean` | `true` | Não |
| `className` | `string` | — | Não |

**Exemplo de uso:**
```tsx
import { RouteMap } from 'xertica-ui';

<RouteMap
  origin={{ lat: -12.971, lng: -38.501 }}
  destination={{ lat: -13.025, lng: -38.478 }}
  waypoints={[{ lat: -12.99, lng: -38.49 }]}
  travelMode="DRIVING"
  height="500px"
  onRouteCalculated={(distance, duration) => console.log(distance, duration)}
/>;
```

**Notas importantes:**
- Sempre defina `height` explicitamente — sem ele o mapa renderiza com altura 0.
- `origin`/`destination` usam `{ lat, lng }` — não `{ latitude, longitude }`.
- A chave requer a **Directions API** além da Maps JavaScript API; chaves só com Maps API falham no cálculo da rota.
- `onRouteCalculated` recebe strings já formatadas (`"12.4 km"`, `"1h 23min"` ou `"45 min"`) — não valores numéricos brutos.
- Se `waypoints` estiver vazio, omita a prop em vez de passar array vazio.
- Componente relacionado: `Map` (mapa genérico sem rota).

---

### ScrollArea
**Import:** `import { ScrollArea } from 'xertica-ui/ui';`
**Propósito:** Container com rolagem customizada e cross-browser, com scrollbar estilizada de acordo com o design system. Use no lugar de `overflow-y-auto` nativo quando a scrollbar precisa combinar com o tema da aplicação (painéis de altura fixa, listas de chat, áreas de navegação da sidebar).

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `className` | `string` | — | Define altura/largura do container |
| `type` | `'auto' \| 'always' \| 'scroll' \| 'hover'` | `'hover'` | Quando a scrollbar fica visível |

**Exemplo de uso:**
```tsx
import { ScrollArea } from 'xertica-ui/ui';

<ScrollArea className="h-[300px] w-full rounded-md border p-4">
  {items.map(item => (
    <div key={item.id} className="py-2 text-sm">{item.name}</div>
  ))}
</ScrollArea>;
```

**Notas importantes:**
- Sempre defina uma altura explícita (`h-[300px]` ou `h-full`) — `ScrollArea` exige altura limitada para rolar.
- Usado internamente na `Sidebar` para a área de navegação rolável.
- Não usar para rolagem de página inteira — isso é gerenciado pelo `overflow-y-auto` do container principal.

---

### Search
**Import:** `import { Search } from 'xertica-ui/ui';`
**Propósito:** Campo de busca pré-construído com ícone de lupa e botão de limpar, sobre o `<Input>`. Usado em headers, filtros de sidebar e linhas de filtro de CRUD, evitando composição manual de ícone + input.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `value` | `string` | — | Valor controlado |
| `onChange` | `React.ChangeEventHandler<HTMLInputElement>` | — | Handler nativo de change |
| `onSearch` | `(value: string) => void` | — | Chamado a cada tecla digitada, com o valor string |
| `onClear` | `() => void` | — | Chamado ao clicar no × ou pressionar Escape |
| `placeholder` | `string` | — | Texto de placeholder |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Altura (h-8/h-10/h-12), igual à escala de Input/Select |
| `containerClassName` | `string` | — | Classes CSS do wrapper externo |
| `className` | `string` | — | Classes CSS do elemento input |

**Exemplo de uso:**
```tsx
import { Search } from 'xertica-ui/ui';
import { useState } from 'react';

const [query, setQuery] = useState('');

<Search
  value={query}
  onSearch={setQuery}
  onClear={() => setQuery('')}
  placeholder="Buscar membros..."
  className="w-full max-w-sm"
/>;
```

**Notas importantes:**
- Botão de limpar (×) só aparece quando há conteúdo digitado; `Escape` também dispara `onClear`.
- Use `onSearch` para callback simples de string; use `onChange` quando precisar do evento completo.
- Mantenha o mesmo `size` entre `Input`, `SelectTrigger`, `Textarea` e `Search` para alinhamento visual consistente.
- Alternativa manual (quando `Search` não está disponível): `<Input>` com ícone `lucide-react` posicionado via `relative`/`absolute` + `pl-8`.

---

### Select
**Import:** `import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'xertica-ui/ui';`
**Propósito:** Dropdown estilizado para seleção de um único valor entre uma lista, construído sobre Radix UI com navegação por teclado e ARIA completo. Sempre usar no lugar do `<select>` nativo. Ideal para 5+ opções, seleção de papel/categoria/status, seletores de período.

**Props/variantes principais:**

*Select:* `value: string` (controlado) · `defaultValue: string` (não controlado) · `onValueChange: (value: string) => void` · `disabled: boolean`

*SelectTrigger:* `size: 'sm' | 'md' | 'lg'` (default `'md'`, escala h-8/h-10/h-12) · `className: string` · `placeholder: string` (repassado ao `SelectValue`)

*SelectItem:* `value: string` (**obrigatório**, não pode ser vazio ou numérico)

**Exemplo de uso:**
```tsx
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'xertica-ui/ui';

const [role, setRole] = useState('editor');

<Select value={role} onValueChange={setRole}>
  <SelectTrigger className="w-[200px]">
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="admin">Administrator</SelectItem>
    <SelectItem value="editor">Editor</SelectItem>
    <SelectItem value="viewer">Viewer</SelectItem>
  </SelectContent>
</Select>;
```

**Notas importantes:**
- Nunca usar `<select>` nativo — sempre a composição completa `Select`/`SelectTrigger`/`SelectContent`/`SelectItem`.
- Em formulários (react-hook-form), envolva `SelectTrigger` em `<FormControl>`; o spread do `field` vai no `<Select>` (`onValueChange={field.onChange}` `defaultValue={field.value}`), não no trigger.
- Para valor padrão, use `defaultValue` no `<Select>` — não em `<SelectItem>`.
- Para menos de 4 opções sempre visíveis, prefira `RadioGroup`; para múltipla seleção, `Checkbox`; para escolha binária, `Switch`/`RadioGroup`.

---

### Separator
**Import:** `import { Separator } from 'xertica-ui/ui';`
**Propósito:** Linha fina (horizontal ou vertical) que divide visualmente seções de conteúdo, semanticamente um `<hr>`. Usado entre seções de página, grupos de ações em menus/rodapés, e para separar cabeçalho do corpo em painéis/sidebars.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da linha |
| `decorative` | `boolean` | `true` | Quando `true`, oculto da árvore de acessibilidade |
| `className` | `string` | — | Classes adicionais |

**Exemplo de uso:**
```tsx
import { Separator } from 'xertica-ui/ui';

<div className="flex items-center gap-2">
  <Button variant="ghost">Bold</Button>
  <Separator orientation="vertical" className="h-6" />
  <Button variant="ghost">Italic</Button>
</div>;
```

**Notas importantes:**
- Adicione `className="my-6"` (ou `my-8`) para espaçamento vertical entre seções.
- Em `orientation="vertical"`, sempre defina uma classe `h-*` fixa (a linha não tem altura por padrão).
- Mantenha `decorative={true}` (default) a menos que a separação tenha significado estrutural para leitores de tela.

---

### Sheet
**Import:** `import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SheetClose } from 'xertica-ui/ui';`
**Propósito:** Painel deslizante ancorado a uma borda da tela (top/right/bottom/left) que sobrepõe a página atual. Similar ao `Dialog`, mas mais adequado para formulários largos, painéis de detalhe/preview, filtros deslizantes pela direita, ou navegação mobile pela esquerda.

**Props/variantes principais:**

*Sheet:* `open: boolean` (controlado) · `onOpenChange: (open: boolean) => void`

*SheetContent:* `side: 'top' | 'right' | 'bottom' | 'left'` (default `'right'`) · `className: string` (override de largura, ex.: `sm:max-w-md`)

Subcomponentes: `SheetTrigger`, `SheetHeader`, `SheetTitle`, `SheetDescription`, `SheetFooter`, `SheetClose`.

**Exemplo de uso:**
```tsx
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SheetClose, Button } from 'xertica-ui/ui';

<Sheet>
  <SheetTrigger asChild>
    <Button variant="ghost" size="sm">Edit</Button>
  </SheetTrigger>
  <SheetContent className="sm:max-w-md">
    <SheetHeader>
      <SheetTitle>Edit Member</SheetTitle>
      <SheetDescription>Update member information below.</SheetDescription>
    </SheetHeader>
    <div className="py-6">{/* campos do formulário */}</div>
    <SheetFooter>
      <SheetClose asChild>
        <Button variant="ghost">Cancel</Button>
      </SheetClose>
      <Button onClick={handleSave}>Save Changes</Button>
    </SheetFooter>
  </SheetContent>
</Sheet>;
```

**Notas importantes:**
- `SheetTitle` é **obrigatório** para acessibilidade.
- Use `side="right"` para painéis de edição/filtro e `side="left"` para drawers de navegação.
- O default é estreito — sobrescreva com `className="sm:max-w-md"`/`sm:max-w-lg` no `SheetContent`.
- `<SheetClose asChild>` envolvendo um `<Button>` fecha automaticamente o sheet.
- Sempre `<SheetTrigger asChild>` envolvendo um `<Button>` — nunca um botão bruto.
- Para confirmações curtas use `Dialog`; para confirmações destrutivas, `AlertDialog`; para painéis inferiores mobile, `Drawer`.

---

### Sidebar
**Import:** `import { Sidebar, useSidebar } from 'xertica-ui/layout';`
**Propósito:** Painel de navegação vertical primário do shell da aplicação. Gerencia a hierarquia de rotas (listas simples, grupos, sub-itens) e um modo avançado "Assistant" para interfaces orientadas a IA/dados. É responsivo (expandido ou colapsado em tira de ícones) e integrado ao `LayoutContext`.

Oferece **três padrões de uso**:
| Padrão | Quando usar |
|---|---|
| API Monolítica (`<Sidebar />`) | Uso padrão — passa props, recebe sidebar completa |
| API de Componentes Compostos (`<Sidebar.Root>` + subcomponentes) | Layouts customizados — compõe só as partes necessárias |
| Hook Headless (`useSidebar()`) | Controle total — gerencia estado e renderiza livremente |

**Variantes:**
- `default`: lista de navegação estruturada; suporta `routes` (lista plana) ou `navigationGroups` (agrupada); cada rota pode ter `children` (sub-rotas via botão `ChevronRight`).
- `assistant`: busca embutida, grupos com label/descrição/menu de ações, área fixa no topo ("Nova Conversa").

**Props/variantes principais:**

*`<Sidebar />` (API Monolítica):*
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `expanded` | `boolean` | `auto` (LayoutContext ou estado local) | Estado controlado |
| `onToggle` | `() => void` | `auto` | Callback de toggle |
| `variant` | `'default' \| 'assistant'` | `'default'` | Layout/features internas |
| `routes` | `RouteConfig[]` | — | Itens de navegação planos |
| `navigationGroups` | `RouteGroup[]` | — | Itens agrupados com labels (ambas variantes) |
| `fixedArea` | `SidebarFixedAreaConfig` | — | Botão fixo no topo (variante assistant) |
| `search` | `SidebarSearchConfig` | — | Config da barra de busca (variante assistant) |
| `logo` | `ReactNode` | — | Logo exibido quando expandida |
| `logoCollapsed` | `ReactNode` | — | Logo exibido quando colapsada |
| `width` | `number` | `320` | Largura expandida em px |
| `footer` | `SidebarFooterConfig` | — | Flags de visibilidade do rodapé (`showUser`, `showSettings`, `showLogout`) |
| `showFooter` | `boolean` | `true` (default) / `false` (assistant) | Renderiza a seção de rodapé |

*`<Sidebar.Root />` (Compound):* `expanded: boolean` · `onToggle: () => void` · `navigate: (path: string) => void` (default `window.location.href`) · `location: { pathname: string }` (default `window.location`) · `width: number` (default `320`) · `children`: `Sidebar.Header`, `Sidebar.Nav`, `Sidebar.Footer`, `Sidebar.Search`.

*`useSidebar({ defaultExpanded, navigationGroups })` retorna:* `expanded`, `setExpanded`, `toggleExpanded`, `isMobileViewport`, `hasOverflow`, `visibleItems`, `overflowItems`, `openSubmenu`, `setOpenSubmenu`, `isFilterOpen`, `setIsFilterOpen`, `navRef`, `navigationGroups`.

*`RouteConfig`:* `path: string`, `label: string`, `icon: ComponentType`, `children: RouteConfig[]`, `actions: ActionMenuItem[]`, `description: ReactNode`.

*`RouteGroup`:* `id: string`, `label: string`, `icon: ComponentType`, `items: RouteConfig[]`, `actions: ActionMenuItem[]`.

**Exemplo de uso:**
```tsx
import { Sidebar } from 'xertica-ui/layout';
import { useLayout } from 'xertica-ui/hooks';
import { useLocation, useNavigate } from 'react-router-dom';
import { Home, BarChart, Users, Settings } from 'lucide-react';

export function MySidebar() {
  const { sidebarExpanded, toggleSidebar, sidebarWidth } = useLayout();
  const location = useLocation();
  const navigate = useNavigate();

  return (
    <Sidebar
      expanded={sidebarExpanded}
      onToggle={toggleSidebar}
      width={sidebarWidth}
      location={location}
      navigate={navigate}
      navigationGroups={[
        {
          id: 'main',
          label: 'Principal',
          items: [
            { path: '/home', label: 'Início', icon: Home },
            {
              path: '/dashboard',
              label: 'Dashboard',
              icon: BarChart,
              children: [
                { path: '/dashboard/overview', label: 'Visão Geral' },
                { path: '/dashboard/reports', label: 'Relatórios' },
              ],
            },
          ],
        },
        {
          id: 'admin',
          label: 'Administração',
          items: [
            { path: '/users', label: 'Usuários', icon: Users },
            { path: '/settings', label: 'Configurações', icon: Settings },
          ],
        },
      ]}
    />
  );
}
```

**Notas importantes:**
- **Gerenciamento de estado**: sempre use `useLayout()` para manter a Sidebar sincronizada com o padding do conteúdo principal; evite estado local de `expanded` exceto no padrão Headless Hook.
- **Ícones**: passe o componente do ícone (ex.: `HomeIcon`), não um elemento já renderizado.
- **Grupos vs rotas planas**: use `navigationGroups` para navegação estruturada com labels; use `routes` para listas planas.
- **Sub-itens (`children`)**: no **desktop** (≥768px), o `ChevronRight` abre um `DropdownMenu` lateral posicionado à direita da sidebar. No **mobile** (<768px), o mesmo botão alterna um **acordeão inline** que expande os itens filhos abaixo do item pai, com animação de rotação no ícone. Não aninhar mais de 2 níveis.
- **Modo Assistant**: use `fixedArea` para botões de "Criar" de alta prioridade que devem permanecer visíveis independente do scroll.
- **Compound Components**: use `<Sidebar.Root>` + subcomponentes quando precisar injetar conteúdo customizado entre seções ou reordenar o layout.
- **Headless Hook**: use `useSidebar()` apenas quando precisar de UI totalmente customizada, sem nenhuma renderização padrão.
- **prop `location`**: sempre passe `location={useLocation()}` do `react-router-dom` dentro de um Router — uma referência estática de `window.location` impede a atualização do estado ativo na navegação.
- **Acessibilidade (v2.1.9+)**: aplicada automaticamente na variante `default` — botão de toggle com `aria-expanded={expanded}` e `aria-controls="sidebar-nav"`; `<nav id="sidebar-nav" aria-label="Navegação principal">`. Não requer configuração manual.
- Componente relacionado: `Header` (dispara a sidebar mobile); `useLayout` (provider de estado); `TreeView` (padrão usado para navegação aninhada).

---

### SimpleMap
**Import:** `import { SimpleMap } from 'xertica-ui';`
**Propósito:** Wrapper simplificado do `<Map>` para o caso de uso mais comum: um único marcador com configuração básica (contato, localização de loja única). Use `Map` completo apenas quando precisar de polígonos, círculos ou múltiplos marcadores dinâmicos.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `center` | `{ lat: number, lng: number }` | *(obrigatório)* | Coordenadas de centralização do mapa |
| `address` | `string` | — | Atalho que define `markerTitle` e `markerInfo` ao mesmo tempo |
| `markerTitle` | `string` | — | Tooltip/título do marcador |
| `markerInfo` | `string` | — | String HTML-safe exibida no InfoWindow ao clicar |
| `showMarker` | `boolean` | `true` | Exibe ou não o marcador no centro |
| `zoom` | `number` | `15` | Nível de zoom inicial |
| `height` | `string` | `"350px"` | Altura do container do mapa |

**Exemplo de uso:**
```tsx
import { SimpleMap } from 'xertica-ui';

<SimpleMap
  center={{ lat: -23.5505, lng: -46.6333 }}
  markerTitle="Head Office"
  markerInfo="Paulista Avenue, 1000"
  zoom={15}
/>;
```

**Notas importantes:**
- Sempre forneça `center` válido com `lat`/`lng`.
- `SimpleMap` cobre ~90% dos casos de negócio (páginas de contato, loja única); só use `Map` base para necessidades avançadas.
- Herda todas as props não avançadas do componente base `<Map>`.

---

### Skeleton
**Import:** `import { Skeleton } from 'xertica-ui/ui';`
**Propósito:** Placeholder de carregamento que imita a forma/dimensão do conteúdo enquanto ele é buscado, evitando espaço em branco. Usado em cards, tabelas e áreas de perfil durante fetch assíncrono (>~300ms).

**Props/variantes principais:**
| Prop | Tipo | Descrição |
|---|---|---|
| `className` | `string` | **Obrigatório** — define largura, altura e forma; sem ele o componente não renderiza nada visível |

**Exemplo de uso:**
```tsx
import { Skeleton, Card, CardContent, CardHeader } from 'xertica-ui/ui';

<Card>
  <CardHeader>
    <Skeleton className="h-5 w-[200px]" />
    <Skeleton className="h-4 w-[150px] mt-2" />
  </CardHeader>
  <CardContent className="space-y-3">
    <Skeleton className="h-4 w-full" />
    <Skeleton className="h-4 w-4/5" />
  </CardContent>
</Card>;
```

**Notas importantes:**
- Sempre especifique dimensões via `className` — é um box de pulso animado, dimensionado inteiramente pelo consumidor.
- Avatares/círculos: `className="h-12 w-12 rounded-full"`; linhas de texto: `className="h-4 w-full"` ou frações (`w-3/4`, `w-1/2`).
- Sempre iguale aproximadamente a forma do conteúdo real substituído.
- Não usar para loading de página inteira (usar um layout de skeleton dedicado) nem para estados de erro (usar `Alert`/`Empty`).
- Componente relacionado: `Empty` (zero-state pós-carregamento sem resultados).

---

### Slider
**Import:** `import { Slider } from 'xertica-ui/ui';`
**Propósito:** Controle de intervalo numérico onde o usuário arrasta um thumb ao longo de uma trilha. Usado para seletores de quantidade, volume, brilho, porcentagem e faixas de preço.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `value` | `number[]` | — | Valor(es) controlado(s) — sempre um array |
| `defaultValue` | `number[]` | — | Default não controlado |
| `onValueChange` | `(values: number[]) => void` | — | Handler de mudança |
| `min` | `number` | `0` | Valor mínimo |
| `max` | `number` | `100` | Valor máximo |
| `step` | `number` | `1` | Incremento |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `className` | `string` | — | Classes adicionais |

**Exemplo de uso:**
```tsx
import { Slider } from 'xertica-ui/ui';
import { useState } from 'react';

const [value, setValue] = useState([50]);

<div className="space-y-2">
  <div className="flex justify-between text-sm">
    <span>Value</span>
    <span>{value[0]}%</span>
  </div>
  <Slider value={value} onValueChange={setValue} min={0} max={100} step={1} />
</div>;
```

**Notas importantes:**
- `value`/`defaultValue` são **sempre arrays** (`[50]`, nunca `50`), mesmo em sliders de um único thumb — acesse via `value[0]`.
- Use `onValueChange`, não `onChange`.
- Sempre exiba o valor atual visualmente ao lado do slider.
- Para entrada numérica exata use `Input type="number"`; para seleções categóricas, `RadioGroup`/`Select`.
- Componente relacionado: `Progress` (exibição de valor sem interação do usuário).

---

### Sonner (Toast)
**Import:** `import { toast } from 'sonner';`
**Propósito:** Sistema de notificações toast — mensagens efêmeras de feedback, posicionadas fixas na tela (top-right por padrão), com auto-dismiss. Usado para confirmar ações, exibir erros de API e oferecer undo em ações não destrutivas.

**Props/variantes principais (API do `toast`):**
| Método | Descrição |
|---|---|
| `toast(message)` | Toast neutro padrão |
| `toast.success(message)` | Toast verde de sucesso |
| `toast.error(message)` | Toast vermelho de erro |
| `toast.warning(message)` | Toast âmbar de aviso |
| `toast.info(message)` | Toast azul informativo |
| `toast.loading(message)` | Toast com spinner (persiste até ser dispensado) |
| `toast.promise(promise, { loading, success, error })` | Atualiza o toast automaticamente conforme o estado da Promise |

Opções adicionais: `description` (texto complementar), `action: { label, onClick }` (botão de ação, ex. Undo).

**Exemplo de uso:**
```tsx
import { toast } from 'sonner';

toast.success('Member saved successfully');
toast.error('Save failed', { description: 'Please check your connection and try again.' });

toast.promise(fetch('/api/save', { method: 'POST', body: JSON.stringify(data) }), {
  loading: 'Saving...',
  success: 'Saved successfully!',
  error: 'Failed to save. Please try again.',
});

toast('Member deactivated', {
  action: { label: 'Undo', onClick: () => handleReactivate(memberId) },
});
```

**Notas importantes:**
- **`<Toaster>` é injetado automaticamente pelo `<XerticaProvider>`** (com `position="top-right" richColors`) — **nunca renderizá-lo manualmente**.
- Sempre use `toast.error()` para erros — nunca `toast()` com className modificada para simular erro.
- Para ações que modificam dados (salvar, deletar, adicionar), sempre forneça feedback via toast.
- Para chamadas de API, prefira `toast.promise()` — cobre loading + success + error automaticamente.
- Não usar toasts para erros de validação de formulário — isso vai em `FormMessage` dentro de `FormField`.
- Para avisos persistentes/críticos use `Alert` inline; para confirmações destrutivas, `AlertDialog`.

---

### StatsCard
**Import:** `import { StatsCard } from 'xertica-ui/ui';`
**Propósito:** Card de KPI pré-construído para páginas de overview de dashboard. Renderiza título da métrica, valor grande, indicador de tendência opcional (ícone up/down/neutral automático) e ícone opcional. Construído sobre `<Card>`.

**Props/variantes principais:**
| Prop | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `title` | `string` | Sim | Label curto da métrica |
| `value` | `string \| number` | Sim | Valor da métrica exibido (já formatado, ex.: `"$45,231"`) |
| `description` | `string` | Não | Texto complementar abaixo do valor |
| `trend` | `{ value: number; label?: string }` | Não | `value` numérico (positivo = alta, negativo = queda); `label` sobrescreve o texto de `description` |
| `icon` | `ReactNode` | Não | Ícone (ex.: `lucide-react`) |
| `iconColor` | `string` | Não | Classe Tailwind da cor do ícone (default `text-muted-foreground`) |
| `iconBg` | `string` | Não | Classe Tailwind do fundo do ícone (default `bg-muted`) |
| `className` | `string` | Não | Classes adicionais |

`trend.value > 0` → ícone `TrendingUp` verde; `< 0` → `TrendingDown` vermelho (destructive); `=== 0` → `Minus` muted. Exibido sempre como `Math.abs(trend.value)%`.

**Exemplo de uso:**
```tsx
import { StatsCard } from 'xertica-ui/ui';
import { DollarSign } from 'lucide-react';

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  <StatsCard
    title="Total Revenue"
    value="$45,231.89"
    trend={{ value: 20.1, label: 'from last month' }}
    icon={<DollarSign className="size-5" />}
  />
</div>;
```

**Notas importantes:**
- `trend.value` é um número bruto (`20.1`, não `"20.1%"`) — o componente formata automaticamente. `value`, ao contrário, já vem pré-formatado como string.
- `trend.label` tem precedência sobre `description` para o texto abaixo do valor.
- Use `size-5` no ícone; personalize fundo/cor via `iconBg`/`iconColor`.
- Sempre usar em grid responsivo de 4 colunas para overviews de dashboard.
- Para conteúdo arbitrário ou layout muito customizado, componha diretamente com `Card`.

---

### Stepper
**Import:** `import { Stepper, Step, useStepper } from 'xertica-ui/ui';`
**Propósito:** Indicador visual de progresso multi-etapas. `<Stepper>` envolve um ou mais `<Step>`, inferindo o total de passos automaticamente. Ideal para formulários multi-step (checkout, cadastro, onboarding) e wizards de configuração sequencial (2-7 etapas). Também exporta o hook headless `useStepper` para UIs de navegação totalmente customizadas, incluindo guarda de validação assíncrona antes de avançar.

**Props/variantes principais:**

*Stepper:* `currentStep: number` (**obrigatório**, 1-indexado) · `orientation: 'horizontal' | 'vertical'` (default `'horizontal'`) · `className: string`. Renderiza `role="list"` e `aria-label="Progresso: etapa N de M"` automaticamente.

*Step:* `step: number` (**obrigatório**, 1-indexado, deve casar com a posição sequencial) · `label: string` (**obrigatório**) · `description: string` · `error: boolean` (estado de erro/vermelho) · `className: string`. Renderiza `role="listitem"`, `aria-current="step"` (só na etapa ativa) e `aria-label="Etapa N: Label[, concluída | , atual]"` automaticamente.

*`useStepper({ totalSteps, initialStep, step, onStepChange, onBeforeNext })`* — `totalSteps: number` (obrigatório) · `initialStep: number` (default `1`, auto-clamped em `[1, totalSteps]`) · `step: number` (modo controlado) · `onStepChange: (step: number) => void` · `onBeforeNext: (currentStep: number) => boolean | Promise<boolean>` (retornar `false` bloqueia o avanço). Retorna: `currentStep`, `totalSteps`, `isFirstStep`, `isLastStep`, `canGoPrev`, `canGoNext`, `next: () => Promise<void>`, `prev: () => void`, `goTo: (step: number) => void`, `reset: () => void`.

**Exemplo de uso:**
```tsx
import { useStepper, Stepper, Step, Button } from 'xertica-ui/ui';

function OnboardingWizard() {
  const { currentStep, isFirstStep, isLastStep, next, prev } = useStepper({ totalSteps: 3 });

  return (
    <div className="space-y-8">
      <Stepper currentStep={currentStep}>
        <Step step={1} label="Account" />
        <Step step={2} label="Plan" />
        <Step step={3} label="Confirm" />
      </Stepper>
      <div className="flex justify-between">
        <Button variant="outline" onClick={prev} disabled={isFirstStep}>Previous</Button>
        <Button onClick={next}>{isLastStep ? 'Finish' : 'Next'}</Button>
      </div>
    </div>
  );
}
```

**Notas importantes:**
- Steps antes do `currentStep` renderizam como concluídos (check verde); o step igual a `currentStep`, ativo (preenchido primary); os seguintes, pendentes (muted).
- `next()` é assíncrono (executa a guarda `onBeforeNext` se fornecida) — use diretamente como `onClick`.
- Em modo controlado, passe `step` **e** `onStepChange` — omitir `onStepChange` torna o hook somente-leitura.
- Não adicione `role`, `aria-current` ou `aria-label` manualmente — aplicados automaticamente desde v2.1.9.
- Use `reset()` para voltar ao passo 1 após submissão bem-sucedida.
- Não usar para mais de 7 etapas (reestruturar) nem para fluxos de 2 passos simples (usar Dialog com Next/Back).

---

### Switch
**Import:** `import { Switch, Label } from 'xertica-ui/ui';`
**Propósito:** Controle de alternância binária (on/off), mais expressivo visualmente que um `<Checkbox>`. Usado para toggles de features, preferências de notificação, toggles de tema e configurações booleanas em destaque.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `id` | `string` | — | Associa com `<Label htmlFor="...">` |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Dimensões da trilha e do thumb |
| `checked` | `boolean` | — | Estado controlado |
| `defaultChecked` | `boolean` | `false` | Estado inicial não controlado |
| `onCheckedChange` | `(checked: boolean) => void` | — | Handler de mudança |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `className` | `string` | — | Classes adicionais |

**Exemplo de uso:**
```tsx
import { Switch, Label } from 'xertica-ui/ui';
import { useState } from 'react';

const [enabled, setEnabled] = useState(false);

<div className="flex items-center space-x-2">
  <Switch id="notifications" checked={enabled} onCheckedChange={setEnabled} />
  <Label htmlFor="notifications">Email notifications</Label>
</div>;
```

**Notas importantes:**
- Em react-hook-form use `checked={field.value}` + `onCheckedChange={field.onChange}` — **não** `{...field}`.
- Sempre parear com `<Label>` para acessibilidade; garanta que o `size` combine com o do `Label`.
- Para listas de configurações, use `justify-between` na linha para empurrar o switch à direita.
- Para toggles imediatos e irreversíveis destrutivos, use `Button` + `AlertDialog`; para múltipla seleção independente, `Checkbox`; para escolha única entre opções, `RadioGroup`.

---

### Table
**Import:** `import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell, TableFooter, TableCaption } from 'xertica-ui/ui';`
**Propósito:** Exibição de dados tabulares estruturados via elementos HTML `<table>` semânticos, estilizados e acessíveis. Usado em páginas de listagem CRUD, relatórios e views densas onde linhas = registros e colunas = atributos.

**Props/variantes principais (subcomponentes):**
| Componente | Descrição |
|---|---|
| `Table` | Envolve `<table>` com scroll horizontal em overflow |
| `TableHeader` | Envolve `<thead>` |
| `TableBody` | Envolve `<tbody>` |
| `TableFooter` | Envolve `<tfoot>` (opcional — totais, resumos) |
| `TableRow` | Envolve `<tr>` com estado de hover |
| `TableHead` | Envolve `<th>` com texto muted |
| `TableCell` | Envolve `<td>` |
| `TableCaption` | Legenda abaixo da tabela |

**Exemplo de uso:**
```tsx
import {
  Table, TableHeader, TableRow, TableHead, TableBody, TableCell,
  Badge, Button, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
} from 'xertica-ui/ui';
import { MoreHorizontal } from 'lucide-react';

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Email</TableHead>
      <TableHead className="hidden md:table-cell">Role</TableHead>
      <TableHead>Status</TableHead>
      <TableHead className="text-right">Actions</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {records.map(record => (
      <TableRow key={record.id}>
        <TableCell className="font-medium">{record.name}</TableCell>
        <TableCell>{record.email}</TableCell>
        <TableCell className="hidden md:table-cell">{record.role}</TableCell>
        <TableCell><Badge variant="outline">{record.status}</Badge></TableCell>
        <TableCell className="text-right">
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="ghost" size="icon" className="size-8">
                <MoreHorizontal className="size-4" />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem>Edit</DropdownMenuItem>
              <DropdownMenuItem className="text-destructive">Delete</DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>;
```

**Padrão com Pagination:** envolva `<Table>` e a linha de filtros (busca, dropdowns) dentro de um único `<Card>`; adicione `<Pagination>` abaixo da tabela para paginar grandes conjuntos de registros — não crie cards separados para tabela e paginação.

**Notas importantes:**
- Valores de status sempre em `<Badge>` — nunca texto simples.
- Coluna de ações é sempre a última, com `className="text-right"`, e ações usam `<DropdownMenu>` — nunca botões inline soltos para mais de uma ação.
- Use `className="hidden md:table-cell"` em colunas secundárias para ocultação responsiva.
- A coluna identificadora primária (nome/título) usa `className="font-medium"`.
- Para chave-valor (detalhes de perfil), use `Card` com lista de definição em vez de tabela.
- Componentes relacionados: `Badge`, `DropdownMenu`, `Pagination`, `Card`.

---

### Tabs
**Import:** `import { Tabs, TabsList, TabsTrigger, TabsContent } from 'xertica-ui/ui';`
**Propósito:** Alternador de conteúdo que organiza views relacionadas sob triggers rotulados, com uma aba ativa por vez. Usado em dashboards multi-view, páginas de analytics e seções de configurações relacionadas.

**Props/variantes principais:**

*Tabs:* `defaultValue: string` (não controlado) · `value: string` (controlado) · `onValueChange: (value: string) => void` · `orientation: 'horizontal' | 'vertical'` (default `'horizontal'`)

*TabsTrigger:* `value: string` (**obrigatório**, deve casar com `TabsContent`) · `disabled: boolean`

*TabsContent:* `value: string` (**obrigatório**, deve casar com `TabsTrigger`)

**Exemplo de uso:**
```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'xertica-ui/ui';

<Tabs defaultValue="general">
  <TabsList>
    <TabsTrigger value="general">General</TabsTrigger>
    <TabsTrigger value="performance">Performance</TabsTrigger>
    <TabsTrigger value="reports">Reports</TabsTrigger>
  </TabsList>
  <TabsContent value="general" className="mt-6">{/* ... */}</TabsContent>
  <TabsContent value="performance" className="mt-6">{/* ... */}</TabsContent>
  <TabsContent value="reports" className="mt-6">{/* ... */}</TabsContent>
</Tabs>;
```

**Notas importantes:**
- Cada `TabsTrigger` deve ter `value` idêntico ao `TabsContent` correspondente.
- Adicione `className="mt-6"` (ou `mt-4`) em `TabsContent` para espaçamento.
- Deixe `TabsList` preencher a largura disponível (não restrinja a `auto`) salvo exigência de design.
- Não usar para navegação entre páginas separadas (usar `Sidebar`/router) nem para exibir/ocultar uma única seção (usar `Collapsible`/`Accordion`).
- Componentes relacionados: `Accordion`, `Card` (conteúdo das tabs frequentemente vive dentro de um Card).

---

### Textarea
**Import:** `import { Textarea } from 'xertica-ui/ui';`
**Propósito:** Input de texto multi-linha para conteúdo mais longo: descrições, notas, mensagens, campos de texto rico. Nunca usar `<textarea>` nativo.

**Props/variantes principais:**
Todos os atributos HTML nativos de `<textarea>` são repassados. Adicionalmente:
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Padding e fonte (mesma escala do Input) — **não** controla altura |
| `placeholder` | `string` | — | Texto de placeholder |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `className` | `string` | — | Classes adicionais |
| `rows` | `number` | — | Altura inicial em linhas |

**Exemplo de uso:**
```tsx
import { Textarea } from 'xertica-ui/ui';

<Textarea className="resize-none" rows={4} placeholder="Your message..." />;
```

**Notas importantes:**
- `size` controla apenas padding/fonte — use `rows` para controlar a altura vertical.
- Em grids de formulário, `Textarea` sempre usa `className="col-span-full"` (largura total).
- Use `className="resize-none"` na maioria dos contextos corporativos — resize livre pode quebrar layouts de card/painel.
- Sempre espalhe `{...field}` quando usado dentro de `FormField` do react-hook-form.
- Para inputs de uma linha, use `Input` em vez de `Textarea`.

---

### Timeline
**Import:** `import { Timeline, TimelineItem, TimelineDot, TimelineContent, TimelineHeading, TimelineTime, TimelineDescription } from 'xertica-ui/ui';`
**Propósito:** Sequência vertical de eventos exibidos cronologicamente, cada um com timestamp, título e descrição opcional. Usado em logs de atividade, trilhas de auditoria, históricos de casos e feeds de notificação.

**Props/variantes principais:**

*TimelineDot* `variant`: `'default'` (muted, eventos neutros) · `'primary'` (marca, eventos destacados) · `'success'` (verde, ações concluídas) · `'info'` (azul, eventos informativos) · `'warning'` (âmbar, cautela) · `'destructive'` (vermelho, erros/falhas) · `'outline'` (só borda, eventos pendentes/futuros). Aceita prop `icon`.

Subcomponentes: `Timeline` (`<ol>` raiz com linha de borda esquerda), `TimelineItem` (`<li>`), `TimelineDot`, `TimelineContent`, `TimelineHeading` (`<h3>`), `TimelineTime` (`<time>`), `TimelineDescription`.

**Exemplo de uso:**
```tsx
import {
  Timeline, TimelineItem, TimelineDot, TimelineContent,
  TimelineHeading, TimelineTime, TimelineDescription,
} from 'xertica-ui/ui';
import { CheckCircle, LogIn } from 'lucide-react';

<Timeline>
  <TimelineItem>
    <TimelineDot variant="default" icon={<LogIn className="size-4" />} />
    <TimelineContent>
      <TimelineHeading>Login</TimelineHeading>
      <TimelineTime>Today, 09:00</TimelineTime>
      <TimelineDescription>User logged in from Chrome on macOS</TimelineDescription>
    </TimelineContent>
  </TimelineItem>
  <TimelineItem>
    <TimelineDot variant="success" icon={<CheckCircle className="size-4" />} />
    <TimelineContent>
      <TimelineHeading>Password Changed</TimelineHeading>
      <TimelineTime>Yesterday, 16:32</TimelineTime>
    </TimelineContent>
  </TimelineItem>
</Timeline>;
```

**Notas importantes:**
- Sempre compor: `Timeline > TimelineItem > (TimelineDot + TimelineContent)`.
- `TimelineTime` renderiza um `<time>` — passe uma string já formatada, não um objeto `Date`.
- Use `TimelineDot variant="outline"` para eventos pendentes/futuros; passe `icon` para dots grandes com ícone, omita para dots pequenos coloridos.
- Para timelines longas, envolva em `<ScrollArea className="h-[400px]">`.

---

### ToggleGroup
**Import:** `import { ToggleGroup, ToggleGroupItem } from 'xertica-ui/ui';`
**Propósito:** Conjunto de botões `<Toggle>` relacionados que operam juntos, permitindo seleção única (`type="single"`) ou múltipla (`type="multiple"`). Usado em alternadores de modo de visualização, controles de alinhamento de texto e chips de filtro multi-seleção em toolbars compactas.

**Props/variantes principais:**

*ToggleGroup:* `type: 'single' | 'multiple'` (**obrigatório**) · `value: string | string[]` (controlado) · `defaultValue: string | string[]` · `onValueChange: (value) => void`

*ToggleGroupItem:* `value: string` (**obrigatório**, identificador único) · `aria-label: string` (**obrigatório para itens somente-ícone**)

**Exemplo de uso:**
```tsx
import { ToggleGroup, ToggleGroupItem } from 'xertica-ui/ui';
import { LayoutGrid, LayoutList } from 'lucide-react';

<ToggleGroup type="single" defaultValue="list">
  <ToggleGroupItem value="list" aria-label="List view">
    <LayoutList className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="grid" aria-label="Grid view">
    <LayoutGrid className="size-4" />
  </ToggleGroupItem>
</ToggleGroup>;
```

**Notas importantes:**
- `type` é obrigatório — nunca omitir.
- Sempre fornecer `aria-label` para itens somente-ícone.
- Para toolbars de formatação de texto (bold/italic/underline), use `type="multiple"` para permitir formatações simultâneas.
- Componentes relacionados: `Toggle` (toggle isolado), `RadioGroup` (alternativa textual para escolha única).

---

### Toggle
**Import:** `import { Toggle } from 'xertica-ui/ui';`
**Propósito:** Botão pressionável que mantém visualmente um estado on/off, como uma tecla de teclado que permanece pressionada. Usado em toolbars de formatação, alternância de modo de visualização e botões de ativação de filtro.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `pressed` | `boolean` | — | Estado controlado |
| `defaultPressed` | `boolean` | `false` | Estado inicial não controlado |
| `onPressedChange` | `(pressed: boolean) => void` | — | Handler de mudança |
| `variant` | `'default' \| 'outline'` | `'default'` | Estilo visual |
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Preset de tamanho |
| `disabled` | `boolean` | `false` | Desabilita interação |
| `aria-label` | `string` | — | **Obrigatório** para toggles somente-ícone |

**Exemplo de uso:**
```tsx
import { Toggle } from 'xertica-ui/ui';
import { Bold } from 'lucide-react';

const [bold, setBold] = useState(false);

<Toggle pressed={bold} onPressedChange={setBold} aria-label="Bold">
  <Bold className="size-4" />
</Toggle>;
```

**Notas importantes:**
- Toggles somente-ícone exigem `aria-label` — nunca renderizar sem nome acessível.
- Não usar para ações imediatas (usar `Button`) nem para configurações (usar `Switch`/`Checkbox`, com semântica on/off mais clara).
- Para conjuntos mutuamente exclusivos, usar `ToggleGroup` em vez de gerenciar múltiplos `Toggle` manualmente.
- Nunca emular estado pressionável com `<button>` bruto.

---

### Tooltip
**Import:** `import { Tooltip, TooltipContent, TooltipTrigger } from 'xertica-ui/ui';`
**Propósito:** Rótulo flutuante pequeno que aparece ao passar o mouse (ou foco) sobre um elemento trigger, fornecendo explicação contextual curta — tipicamente para botões somente-ícone ou texto abreviado.

**Props/variantes principais:**

*Tooltip:* `open: boolean` (controlado) · `defaultOpen: boolean` (default `false`) · `onOpenChange: (open: boolean) => void` · `delayDuration: number` (default `700` ms)

*TooltipTrigger:* `asChild: boolean` (renderiza como elemento filho)

*TooltipContent:* `side: 'top' | 'right' | 'bottom' | 'left'` (default `'top'`) · `sideOffset: number` (default `4`) · `className: string`

**Exemplo de uso:**
```tsx
import { Tooltip, TooltipContent, TooltipTrigger, Button } from 'xertica-ui/ui';
import { Settings } from 'lucide-react';

<Tooltip>
  <TooltipTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Settings">
      <Settings className="size-4" />
    </Button>
  </TooltipTrigger>
  <TooltipContent>
    <p>Settings</p>
  </TooltipContent>
</Tooltip>;
```

**Notas importantes:**
- `TooltipProvider` **não é necessário** manualmente — já injetado pelo `<XerticaProvider>`.
- Sempre use `<TooltipTrigger asChild>` ao envolver um `<Button>` — sem `asChild` o DOM renderiza um botão dentro de outro botão.
- Conteúdo deve ser uma frase curta (2-4 palavras) — não instruções longas.
- Adicione `aria-label` também no botão trigger somente-ícone — tooltip não substitui acessibilidade semântica.
- Para previews ricos com imagens, use `HoverCard`; para conteúdo persistente, use `Popover`. Tooltips são ocultos por padrão — não colocar informação crítica obrigatória.

---

### TreeView
**Import:** `import { TreeView, useTreeView } from 'xertica-ui/ui';`
**Propósito:** Componente de árvore hierárquica interativo para exibir/navegar estruturas aninhadas — sistemas de arquivos, organogramas, árvores de categorias, menus de navegação recursivos. Suporta navegação por teclado (setas, Home, End, Space), expandir/colapsar e seleção de nó único. Exporta também o hook headless `useTreeView` para UIs de árvore totalmente customizadas.

**Props/variantes principais:**

*TreeView:* `data: TreeNode[]` (**obrigatório**) · `defaultExpanded: string[]` · `selectedNodeId: string` (controlado) · `onNodeClick: (node: TreeNode) => void` · `onNodeSelect: (node: TreeNode) => void` · `ariaLabel: string` (default `"Navegação em árvore"`) · `className: string`

*TreeNode:* `{ id: string; label: string; icon?: ReactNode; children?: TreeNode[] }`

*`useTreeView({ data, defaultExpanded, selectedNodeId, onNodeClick, onNodeSelect })`* retorna: `expanded: Set<string>`, `effectiveSelectedId`, `nodeRefs`, `getNodeRef(nodeId)`, `toggleExpand(nodeId)`, `handleSelect(node)`, `handleKeyDown(e, node)`, `getVisibleNodes()`.

**Navegação por teclado** (WAI-ARIA Tree Pattern 1.2): `ArrowDown`/`ArrowUp` movem foco entre nós visíveis · `ArrowRight` expande nó de ramo (ou vai ao primeiro filho se já expandido) · `ArrowLeft` colapsa (ou vai ao pai se já colapsado) · `Home`/`End` vão ao primeiro/último nó visível · `Space`: em nós de ramo alterna expand/collapse sem selecionar, em folhas seleciona · `Enter`: sempre seleciona e também alterna expand/collapse se tiver filhos (v2.1.9+).

**Exemplo de uso:**
```tsx
import { TreeView } from 'xertica-ui/ui';
import { Folder, File } from 'lucide-react';

const tree = [
  {
    id: 'src', label: 'src', icon: <Folder className="size-4" />,
    children: [
      { id: 'button', label: 'Button.tsx', icon: <File className="size-4" /> },
    ],
  },
];

<TreeView data={tree} onNodeSelect={node => console.log('Selected:', node.id)} />;
```

**Notas importantes:**
- Cada `TreeNode` precisa de `id` único (usado para expand/collapse e seleção). Nós folha (sem `children`) não exibem seta de expansão.
- **Roving tabindex (v2.1.9+)**: apenas um nó tem `tabIndex={0}` por vez (`effectiveSelectedId ?? data[0]?.id`); `Tab` sai da árvore inteiramente, navegação interna é por setas. Não defina `tabIndex` manualmente ao usar `<TreeView>`.
- Ao usar `useTreeView`, é obrigatório: anexar `getNodeRef(node.id)` como `ref` de cada botão (necessário para mover o foco) e anexar `handleKeyDown` a `onKeyDown` em todo botão de nó — omitir quebra a acessibilidade por teclado.
- `expanded` é um `Set<string>` — use `expanded.has(nodeId)`.
- Sempre passe `ariaLabel` customizado — o default `"Navegação em árvore"` está em português e deve ser sobrescrito em apps em outro idioma.
- Usado como padrão de navegação aninhada na variante assistant da `Sidebar`.

---

### useMobile / useIsMobile
**Import:** `import { useMobile } from 'xertica-ui/ui';` (hook; alias: `useIsMobile`)
**Propósito:** Hooks que detectam se o viewport atual está em largura mobile. Úteis para renderizar layouts condicionais, trocar componentes (ex.: `Drawer` vs `Sheet`) ou mostrar/ocultar conteúdo baseado no tamanho de tela quando a árvore de componentes React precisa ramificar (não apenas CSS).

**Props/variantes principais:**
- `useMobile(): boolean` — retorna `true` quando a largura do viewport está abaixo do breakpoint mobile (`768px` / `md`); `false` no desktop.
- `useIsMobile()` — alias funcionalmente idêntico a `useMobile()`; escolher um por preferência de nomenclatura, não usar os dois no mesmo componente.
- Ambos são reativos — atualizam automaticamente no resize da janela.

**Exemplo de uso:**
```tsx
import { useMobile, Sheet, SheetContent, Drawer, DrawerContent } from 'xertica-ui/ui';

function FilterPanel({ open, onClose }) {
  const isMobile = useMobile();

  if (isMobile) {
    return (
      <Drawer open={open} onOpenChange={onClose}>
        <DrawerContent>{/* Filter controls */}</DrawerContent>
      </Drawer>
    );
  }

  return (
    <Sheet open={open} onOpenChange={onClose}>
      <SheetContent side="right">{/* Filter controls */}</SheetContent>
    </Sheet>
  );
}
```

**Notas importantes:**
- Breakpoint fixo: `768px` (equivalente ao `md` do Tailwind) — abaixo disso é considerado mobile.
- Prefira classes responsivas do Tailwind (`className="hidden md:table-cell"`) para diferenças puramente visuais/CSS; reserve o hook para quando a árvore de componentes React precisa ser estruturalmente diferente (ex.: trocar `Drawer` por `Sheet`).
- Não é um substituto de media queries CSS — é para lógica React condicional.
- Componentes relacionados: `Sheet` (painel desktop), `Drawer` (painel mobile).

---

### VideoPlayer
**Import:** `import { VideoPlayer } from 'xertica-ui/media';`
**Propósito:** Player de vídeo completo com controles customizados, modo flutuante automático e gerenciamento de estado integrado ao design system Xertica UI. Ideal para conteúdo em vídeo de formato longo onde o usuário pode querer continuar assistindo enquanto navega pela página.

**Props/variantes principais:**
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
| `src` | `string` | — | URL de origem do vídeo |
| `poster` | `string` | — | URL de imagem exibida antes do vídeo iniciar |
| `title` | `string` | — | Título do conteúdo do vídeo |
| `autoPlay` | `boolean` | `false` | Se o vídeo inicia automaticamente |
| `enableAutoFloat` | `boolean` | `true` | Se o vídeo flutua ao sair da área visível durante o scroll |

**Recursos:** controles customizados (play/pause, barra de progresso, volume, mute, Picture-in-Picture, fullscreen); modo flutuante automático ao sair do viewport; controles ocultam após 2.5s de inatividade durante reprodução; design responsivo mobile/desktop.

**Exemplo de uso:**
```tsx
import { VideoPlayer } from 'xertica-ui/media';

<VideoPlayer
  src="https://example.com/video.mp4"
  poster="https://example.com/poster.jpg"
  title="Featured Video"
/>;
```

**Notas importantes:**
- Sempre forneça `poster` para melhor UX, especialmente em vídeos que demoram para carregar.
- O modo Auto-Float é ideal para conteúdo longo onde o usuário navega por outras seções enquanto assiste.
- Componentes relacionados: `FloatingMediaWrapper` (wrapper subjacente do comportamento flutuante), `AudioPlayer` (equivalente para áudio).

---

## 17. Divergências Conhecidas e Notas de Precisão

Esta seção existe para que o assistente de IA **não contradiga a si mesmo** ao responder perguntas sobre pontos onde a documentação oficial da xertica-ui diverge internamente, está desatualizada em relação ao código-fonte, ou usa números de marketing em vez de números auditados. Sempre que possível, prefira a versão marcada como "mais confiável" abaixo — mas se o usuário citar explicitamente uma das outras fontes, reconheça a divergência em vez de simplesmente contradizê-lo.

### 17.1 Contagem total de componentes

Três números diferentes circulam na documentação oficial:

| Fonte | Número | Detalhamento |
|---|---|---|
| `docs/llms.md` (auditado em 2026-06-25) | **84** | 60 UI + 5 Assistant + 6 Brand + 3 Media + 2 Layout + 8 Page Templates |
| `guidelines/Guidelines.md` | "97" | Sem detalhamento por categoria |
| `README.md` (raiz) | "100+" | Número de marketing/arredondado |

**Recomendação:** use **84** como referência — é o único número auditado e detalhado por categoria (`docs/doc-audit.md`). Se perguntado sobre "quantos componentes tem a xertica-ui", responda "cerca de 84 componentes documentados e auditados (o README fala em '100+' de forma arredondada/marketing)".

### 17.2 Subpath exports do pacote

`docs/architecture.md` lista **7 subpaths** de import, mas omite `xertica-ui/pages`, que existe tanto no `README.md` quanto no `package.json` (`exports`). Confirmado via leitura direta do `package.json`: os subpaths JS reais são **8**: `xertica-ui/ui`, `xertica-ui/blocks`, `xertica-ui/assistant`, `xertica-ui/layout`, `xertica-ui/brand`, `xertica-ui/media`, `xertica-ui/hooks`, `xertica-ui/pages` — mais o barrel raiz `xertica-ui` e o CSS `xertica-ui/style.css` (não contados como "subpath de componentes").

### 17.3 Estrutura de arquivos de locale (i18n)

A estrutura **atual e recomendada** (desde a v2.2.0) é uma pasta por idioma, dividida em arquivos por categoria (`src/locales/<lang>/common.json`, `src/locales/<lang>/home.json`, etc.), auto-descoberta via `import.meta.glob`. A estrutura **flat** (`src/locales/<lang>.json`, um único arquivo por idioma) é **legada** (pré-2.2.0) e ainda aparece em alguma documentação/exemplos mais antigos. Projetos novos escafoldados pela CLI já usam a estrutura em pastas.

### 17.4 `isSidebarOpen` não existe

O `README.md` raiz menciona uma prop/estado chamado `isSidebarOpen` em exemplos de código. **Isso não existe na API real.** O nome correto, exportado por `useLayout()` (`xertica-ui/hooks`), é **`sidebarExpanded`** (com o setter `setSidebarExpanded` e o helper `toggleSidebar`). Sempre corrija esse nome ao gerar código.

### 17.5 `xertica` vs `xertica-original` — são temas distintos

Um erro comum é assumir que "tema xertica" e "tema xertica-original" são a mesma coisa ou sinônimos. **Não são.** São dois valores distintos de `defaultColorTheme`/`ColorTheme`:

- **`xertica-original`** — tema **padrão (DEFAULT)** da biblioteca, identidade "clássica" com gradiente `#FDB0F2 → #72CDFD`.
- **`xertica`** — a nova identidade visual "serigrafia": fundo off-white (`#FFFEF8`), cor primária "Negro Xertica" (`#1E1E1E`), botões em formato pílula com contorno, amarelo `#FAF338` restrito ao `Button` variante `default` e ao estado marcado do `Switch`, **sem gradientes**.

A troca automática do logo/mark de marca para a família **Isotype** nas páginas de autenticação (Login, Forgot Password, Reset Password, Verify Email) só ocorre quando `currentTheme === 'xertica'` **estritamente** — nunca quando é `'xertica-original'` ou qualquer um dos outros 8 temas. Isso foi confirmado por citação exata de código-fonte em `XerticaOrbe.tsx`, `LoginPage.tsx`, `ForgotPasswordPage.tsx`, `ResetPasswordPage.tsx` e `VerifyEmailPage.tsx` (ver Seção 8).

### 17.6 `ThemeToggle` não é 100% "self-contained"

`docs/components/theme-toggle.md` descreve o `<ThemeToggle>` como um componente autocontido, plugue-e-use. Na prática, ele chama `useTheme()` internamente, que **lança um erro se não houver um `<ThemeProvider>` (ou `<XerticaProvider>`) ancestral** na árvore de componentes. Ele não gerencia estado "do zero" de forma isolada — depende do contexto de tema já estar montado.

### 17.7 `streamResponseGenerator` — capacidade de streaming não documentada

O hook `useAssistant` (por trás de `<XerticaAssistant>`) aceita uma prop `streamResponseGenerator?: (message) => AsyncGenerator<Partial<Message>, void, unknown>` que permite respostas em streaming (token a token), com a **prioridade mais alta** entre as estratégias de geração de resposta disponíveis. Essa capacidade **não está documentada** em nenhum arquivo `docs/components/assistant.md` ou similar — foi encontrada apenas por leitura direta do código-fonte (branch `feat/fdm-integration`). Se o usuário perguntar como fazer streaming de respostas do assistente, esta é a resposta correta, mesmo que a documentação oficial ainda não a mencione.

### 17.8 Pitch de marketing do README vs números auditados

O `README.md` raiz usa linguagem de marketing ("100+ componentes", diferenciais de alto nível) que é apropriada para atrair adotantes, mas não deve ser tratada como fonte técnica precisa quando o usuário precisar de números exatos ou comportamento detalhado de API — para isso, prefira sempre `docs/llms.md`, `docs/components/*.md` e o próprio código-fonte.

---

*Fim da Base de Conhecimento xertica-ui v3.0.1.*
