react-lgpd-consent - v0.5.4
    Preparing search index...

    🚀 Quick Start - Desenvolvimento (Monorepo)# 🚀 Guia de Início Rápido

    Para usuários finais: Veja packages/react-lgpd-consent/QUICKSTART.mdEste guia fornece tudo o que você precisa para integrar rapidamente a biblioteca react-lgpd-consent em seu projeto React.

    Este guia é para desenvolvedores que querem contribuir ou fazer build local do monorepo.## 📦 Instalação

    npm install react-lgpd-consent

    
    react-lgpd-consent/yarn add react-lgpd-consent
    
    ├── packages/```
    
    │   ├── core/                    # @react-lgpd-consent/core (headless)
    
    │   ├── mui/                     # @react-lgpd-consent/mui (componentes)### Dependências Peer
    
    │   └── react-lgpd-consent/      # react-lgpd-consent (agregador)
    
    ├── examples/```bash
    
    │   ├── vite/                    # Exemplo Vitenpm install @mui/material @mui/icons-material @emotion/react @emotion/styled
    
    │   └── next-app-router/         # Exemplo Next.js```
    
    └── docs/                        # TypeDoc + Storybook (gh-pages)
    
    ```> ℹ️ **Modularização (v0.5.0+)**
    
    >
    
    ## 🛠️ Setup Inicial> - `react-lgpd-consent` continua sendo o pacote principal publicado.
    
    > - `@react-lgpd-consent/core` expõe apenas contextos, hooks e utilitários (sem UI).
    
    ### Pré-requisitos> - `@react-lgpd-consent/mui` publica os componentes baseados em Material-UI (atualmente um proxy).
    
    - Node.js >= 20 (veja `.nvmrc`)> - Use os novos subpaths (`react-lgpd-consent/core`, `react-lgpd-consent/mui`) conforme a necessidade.
    
    - pnpm >= 10
    
    ## 🎯 Uso Básico (30 segundos)
    
    ### Instalação
    
    ````tsx
    
    ```bashimport React from 'react'
    
    # Clonar repositórioimport { ConsentProvider } from 'react-lgpd-consent'
    
    git clone https://github.com/lucianoedipo/react-lgpd-consent.git
    
    cd react-lgpd-consentfunction App() {
    
      return (
    
    # Instalar dependências (workspace completo)    
    
          
    # Rodar testes em todos os pacotes

    Minha Aplicação

    pnpm test {/* Seu conteúdo aqui */} ```
    ## 📝 Comandos Principais ) ### Build & Dev## 🧭 Storybook — quick note ```bash # Build de todos os pacotesThis repository ships an interactive Storybook playground used for manual testing and visual exploration of components. Quick commands: pnpm run build - Run locally (development): # Watch mode (rebuild automático) pnpm run dev```bash npm run storybook # Build apenas do core```` pnpm --filter @react-lgpd-consent/core run build - Build static Storybook (for publishing to GitHub Pages): # Build apenas do mui pnpm --filter @react-lgpd-consent/mui run build```bash ```npm run build-storybook
    
    # Rodar todos os testes
    
    pnpm test- The Storybook preview (`.storybook/preview.tsx`) applies a clean environment between stories (removes consent cookie and performs defensive DOM cleanup). Check that file when creating stories that rely on a clean initial state.
    
    
    
    # Watch mode}
    
    pnpm test -- --watch
    
    export default App
    
    # Coverage
    
    pnpm test -- --coverage````
    
    
    
    # Mutation testing (Stryker)## ⚡ Quickstarts: Next.js (App Router) e Vite
    
    pnpm run mutation
    
    ```Os exemplos a seguir integram GTM/GA4 com Consent Mode v2 e garantem que nenhum script de tracking rode antes do consentimento. Eles também mostram como usar `ConsentScriptLoader` e sincronizar os sinais do Consent Mode via `gtag('consent', ...)`.
    
    
    
    ### Qualidade de Código- Exemplos completos: `examples/next-app-router/*`, `examples/vite/*`
    
    ```bash
    
    # Type-check (todos os pacotes)### Next.js 14/15 — App Router (SSR-safe)
    
    pnpm run type-check
    
    1) Criar app Next e instalar deps
    
    # Linting
    
    pnpm run lint```bash
    
    npm create next-app@latest my-app --ts --eslint --src-dir --app --no-tailwind --no-experimental-app
    
    # Formataçãocd my-app
    
    pnpm run formatnpm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
    
    

    Size-check (bundle size)

    pnpm run size-check2) Variáveis públicas no .env.local

    
    
    
    # Storybook (playground interativo)```
    
    pnpm run storybook
    
    3) Copiar os arquivos do exemplo e ajustar imports
    
    # Build Storybook
    
    pnpm run build-storybook- De `examples/next-app-router/app/layout.tsx` → `app/layout.tsx`
    
    - De `examples/next-app-router/app/page.tsx` → `app/page.tsx`
    
    # Gerar TypeDoc- De `examples/next-app-router/components/ClientConsent.tsx` → `app/components/ClientConsent.tsx`
    
    pnpm run docs:generate
    
    ```Observação: nos arquivos copiados, troque imports relativos para `import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'`.
    
    
    
    ### Exemplos4) O que esse setup faz
    
    ```bash
    
    # Rodar exemplo Vite- `ClientConsent` é um componente client-only (via `dynamic(..., { ssr: false })` no layout) que:
    
    cd examples/vite  - Injeta um stub de `dataLayer/gtag` e define `consent default = denied` para todos os sinais (ad_storage, ad_user_data, ad_personalization, analytics_storage).
    
    pnpm dev  - Sincroniza as mudanças do consentimento com `gtag('consent','update', ...)` mapeando as categorias: `analytics → analytics_storage`, `marketing → ad_*`.
    
      - Usa `ConsentScriptLoader` para carregar GTM/GA4 somente quando as categorias permitirem. Antes disso, nenhum script de tracking é carregado.
    
    # Rodar exemplo Next.js
    
    cd examples/next-app-router5) Rodar
    
    pnpm dev
    
    ``````bash
    
    npm run dev
    
    ## 📤 Publicação (CI/CD)```
    
    
    
    A publicação é **automática via CI** ao criar uma tag:Validação rápida:
    
    - Acesse em aba anônima: a rede não carrega `gtm.js`/`gtag/js` até aceitar preferências.
    
    ```bash- Ao aceitar `analytics`, o GA4 é carregado; ao aceitar `marketing`, os sinais `ad_*` são atualizados como granted.
    
    # 1. Atualizar versões nos packages (manualmente)
    
    # packages/core/package.json - version: "0.5.1"### Vite (CSR)
    
    # packages/mui/package.json - version: "0.5.1"
    
    # packages/react-lgpd-consent/package.json - version: "0.5.3"1) Criar app Vite e instalar deps
    
    
    
    # 2. Commitar mudanças```bash
    
    git add .npm create vite@latest my-app -- --template react-ts
    
    git commit -m "chore: bump versions to 0.5.x"cd my-app
    
    npm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
    
    # 3. Criar tag (formato: vX.Y.Z)```
    
    git tag v0.5.3
    
    2) Variáveis no `.env`
    
    # 4. Push com tags
    
    git push --tags```
    
    VITE_GA_ID=G-XXXXXXXXXX
    
    # CI vai automaticamente:VITE_GTM_ID=GTM-XXXXXXX
    
    # - Publicar @react-lgpd-consent/core@0.5.1```
    
    # - Publicar @react-lgpd-consent/mui@0.5.1  
    
    # - Publicar react-lgpd-consent@0.5.33) Copiar os arquivos do exemplo e ajustar imports
    
    
    • De examples/vite/index.htmlindex.html (não adicione scripts do GA/GTM aqui)

    Workflow: .github/workflows/publish-github-packages.yml- De examples/vite/src/main.tsxsrc/main.tsx

    • De examples/vite/src/App.tsxsrc/App.tsx
    • Headless (sem UI)

    • Context, hooks, lógica de consentimento4) Rodar

    • Target: Desenvolvedores que querem criar UI própria

    • Size: ~86 KB```bash

    npm run dev

    • Componentes Material-UI

    • CookieBanner, PreferencesModal, FloatingButtonValidação rápida:

    • Depende de @react-lgpd-consent/core- Ao abrir a app (em nova sessão), nenhum script de tracking é baixado até que o usuário consinta.

    • Target: Desenvolvedores que querem UI pronta- Preferências atualizam gtag('consent','update', ...) corretamente por categoria.

    • Size: ~104 KB

    • Re-exporta tudo do @react-lgpd-consent/mui

    • Melhor DX (developer experience)## 🍪 Categorias: definição, uso e exemplos

    • Target: Maioria dos usuários finais

    • Size: ~104 KBFonte única de verdade

    • Defina as categorias do seu projeto SOMENTE na prop categories do ConsentProvider.

    Usar Conventional Commits:- Apenas a categoria necessary é obrigatória (e já é sempre incluída automaticamente).

    • feat: - Nova funcionalidade- Todas as demais (analytics, marketing, functional, etc.) são opcionais e dependem do seu caso de negócio. Se você não usa analytics/ads/chat, simplesmente não habilite essas categorias.

    • fix: - Correção de bug

    • docs: - DocumentaçãoComo “esconder” categorias que não uso?

    • test: - Testes- Basta não incluí-las em enabledCategories e não declará-las em customCategories. A UI não exibirá toggles para categorias ausentes.

    • chore: - Manutenção

    • refactor: - RefatoraçãoExemplo A — Somente necessários (mínimo, comum para apps internos/governo sem tracking)


    ### Códigoimport { ConsentProvider } from 'react-lgpd-consent'

    - **Linguagem**: TypeScript (ESM)

    - **Formatação**: Prettier (no semicolons, single quotes, width 100)export default function App() {

    - **Linting**: ESLint flat config return (

    - **Testes**: Jest + Testing Library <ConsentProvider

    categories={{ enabledCategories: [] }}

    ### Documentação (TSDoc) texts={{ bannerMessage: 'Usamos apenas cookies necessários para funcionamento.' }}

    - `@category` - Categoria do export >

    - `@component` - Tag para componentes React <YourApp />

    - `@example` - Exemplos de uso </ConsentProvider>

    - `@param` - Parâmetros de função )

    - `@returns` - Tipo de retorno}

    - `@since` - Versão introduzida```



    ## 🐛 DebuggingExemplo B — Conjunto completo (site com analytics e marketing)

    ```tsx

    ### Problemas com peer dependenciesimport { ConsentProvider } from 'react-lgpd-consent'

    ```bash

    pnpm install --legacy-peer-depsexport default function App() {

    ``` return (

    <ConsentProvider

    ### Limpar build categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }}

    ```bash >

    pnpm run clean <YourApp />

    pnpm install </ConsentProvider>

    pnpm run build )

    ```}
    
    cd packages/react-lgpd-consent- Sempre passe `categories` explicitamente. Em DEV, a biblioteca avisa quando `categories` não foi configurado para evitar ambiguidades.
    
    pnpm run size-check- Não classifique scripts de analytics/ads como “necessary” — use `ConsentScriptLoader` e categorias adequadas.
    
    ```- Em dúvidas, comece com “somente necessários” e evolua quando o negócio exigir outras categorias.
    
    
    
    ## 📖 Documentação Completa### 🔎 Validação de configuração (DEV)
    
    
    
    - **API Reference**: https://lucianoedipo.github.io/react-lgpd-consentEm desenvolvimento, a biblioteca valida a configuração e mostra mensagens amigáveis no console. Nada disso impacta produção (onde só ocorre uma sanitização leve).
    
    - **Storybook**: https://lucianoedipo.github.io/react-lgpd-consent/storybook
    
    - **Contributing**: [`DEVELOPMENT.md`](DEVELOPMENT.md)Avisos comuns e como corrigir:
    
    - **Architecture**: [`AGENTS.md`](AGENTS.md)- `Prop 'categories' não fornecida...` — defina `categories.enabledCategories` de forma explícita; exemplo mínimo: `categories={{ enabledCategories: [] }}`.
    
    - `'necessary' é sempre incluída automaticamente` — remova `'necessary'` de `enabledCategories` (ela já é incluída por padrão).
    
    ## 🤝 Contribuindo- `IDs de categoria duplicados detectados` — revise `enabledCategories` e `customCategories` para garantir que não há IDs repetidos.
    
    - `enabledCategories contém valores inválidos` — verifique se todos os itens são strings não vazias (IDs de categoria).
    
    1. Fork o repositório- `customCategories: ... — ... deve ser uma string não vazia` — preencha `id`, `name` e `description` das categorias customizadas.
    
    2. Crie uma branch: `git checkout -b feat/minha-feature`
    
    3. Faça as mudanças e **teste localmente**:Notas:
    
       ```bash- Validação detalhada roda apenas em `NODE_ENV !== 'production'`.
    
       pnpm run type-check- Em produção, a lib não carrega o validador; somente remove `'necessary'` se vier por engano, mantendo o comportamento seguro.
    
       pnpm test
    
       pnpm run lint## 🧱 SSR/Next.js (App Router) — Padrões seguros
    
       pnpm run build
    
       ```Objetivo: evitar hydration mismatch, hooks em Server Components e vazamento de scripts.
    
    4. Commit: `git commit -m "feat: adiciona nova feature"`
    
    5. Push: `git push origin feat/minha-feature`Padrões recomendados
    
    6. Abra um Pull Request- Envolva o app com o `ConsentProvider` apenas no cliente.
    
    - Use `dynamic(() => import('./ClientConsent'), { ssr: false })` no `RootLayout` (Server Component) e mova hooks e efeitos para o componente cliente.
    
    ## 📄 Licença- Nenhum acesso a `window`/`document` no topo de módulo; use apenas dentro de `useEffect`.
    
    - Inicialize Consent Mode v2 com `gtag('consent','default', denied)` antes de carregar GTM/GA4; depois, atualize sinais na mudança de preferências.
    
    MIT - [@lucianoedipo](https://github.com/lucianoedipo)
    
    Exemplo de RootLayout (Server) + Client wrapper
    
    ```tsx
    // app/layout.tsx (Server Component)
    import dynamic from 'next/dynamic'
    
    const ClientConsent = dynamic(() => import('./components/ClientConsent'), { ssr: false })
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        
          
            {children}
          
        
      )
    }
    
    // app/components/ClientConsent.tsx (Client Component)
    'use client'
    import React from 'react'
    import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'
    import { COMMON_INTEGRATIONS } from 'react-lgpd-consent'
    import { useConsent } from 'react-lgpd-consent'

    function BootstrapConsentMode() {
    React.useEffect(() => {
    const w = window as any
    w.dataLayer = w.dataLayer ?? []
    w.gtag = w.gtag ?? ((...args: any[]) => w.dataLayer.push(args))
    w.gtag('consent', 'default', {
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    analytics_storage: 'denied',
    })
    }, [])
    return null
    }

    function SyncConsentMode() {
    const { consented, preferences } = useConsent()
    React.useEffect(() => {
    if (!consented) return
    const w = window as any
    w.gtag?.('consent', 'update', {
    analytics_storage: preferences.analytics ? 'granted' : 'denied',
    ad_storage: preferences.marketing ? 'granted' : 'denied',
    ad_user_data: preferences.marketing ? 'granted' : 'denied',
    ad_personalization: preferences.marketing ? 'granted' : 'denied',
    })
    }, [consented, preferences])
    return null
    }

    export default function ClientConsent({ children }: { children: React.ReactNode }) {
    const GA = process.env.NEXT_PUBLIC_GA_ID!
    const GTM = process.env.NEXT_PUBLIC_GTM_ID!
    return (
    <ConsentProvider categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }} blocking>
    <BootstrapConsentMode />
    <SyncConsentMode />
    <ConsentScriptLoader
    integrations={[
    COMMON_INTEGRATIONS.googleAnalytics({ measurementId: GA }),
    COMMON_INTEGRATIONS.googleTagManager({ containerId: GTM }),
    ]}
    />
    {children}
    </ConsentProvider>
    )
    }

    Ordem de provedores e estilos (MUI/Emotion)

    • Preferência de ordem recomendada:
      • CacheProvider (Emotion) ou StyledEngineProvider com injectFirst
      • ThemeProvider (MUI)
      • CssBaseline
      • ConsentProvider (sem criar tema por padrão)
    • Motivo: garante injeção de estilos do MUI antes de CSS da app e evita desalinhamento visual; os componentes da lib herdam o tema quando presente.

    Z-index e Portals

    • Componentes MUI usam o zIndex do tema; modals/portals padrão usam zIndex.modal = 1300.
    • O overlay bloqueante do Provider usa z-index: 1299; o Modal/Banner usa camadas ≥ 1300.
    • Em caso de conflito com headers fixos, ajuste o theme.zIndex (ex.: appBar: 1200, modal: 1300+) ou os designTokens conforme a necessidade.

    Checklist SSR (evite hydration mismatch)

    • [ ] Hooks somente em Client Components ('use client' no topo).
    • [ ] Nada de window/document/localStorage no topo de módulo (apenas em useEffect).
    • [ ] dynamic(..., { ssr: false }) para wrappers que usam hooks e efeitos do consentimento.
    • [ ] GTM/GA4 carregados apenas após consentimento (via ConsentScriptLoader).
    • [ ] Sem <script> de GTM/GA4 em head/body; todo carregamento vem do loader.

    No modo bloqueante, o banner usa um backdrop para focar a atenção do usuário. Você pode controlar via design tokens:

    <ConsentProvider
    categories={{ enabledCategories: ['analytics'] }}
    designTokens={{
    layout: {
    // false: transparente | 'auto': ajusta ao tema | string: cor custom (ex.: '#00000088')
    backdrop: 'auto',
    },
    colors: {
    // Se omitido, usa o palette do tema MUI (background.paper, text.primary)
    // background: '#1e1e1e',
    // text: '#ffffff',
    },
    }}
    >
    <App />
    </ConsentProvider>

    Se colors.background ou colors.text não forem fornecidos, a lib usa automaticamente theme.palette.background.paper e theme.palette.text.primary do MUI, garantindo compatibilidade com dark mode.

    Durante o desenvolvimento, o console exibe um guia com:

    • Avisos quando a configuração padrão é usada; sugestões para explicitar categorias
    • Lista de categorias ativas e quais exigem toggle
    • Detecção de integrações que requerem categorias, com sugestão para habilitá-las
    • Boas práticas LGPD (Brasil) e alertas de UX (categorias demais)
    • Silenciado automaticamente em produção; SSR-safe

    Adicione categorias específicas do seu projeto (ex.: chat de suporte, players de vídeo, AB testing):

    <ConsentProvider
    categories={{
    enabledCategories: ['analytics'],
    customCategories: [
    { id: 'chat', name: 'Chat de Suporte', description: 'Widget de chat' },
    { id: 'video', name: 'Vídeo', description: 'Players incorporados' },
    { id: 'abTesting', name: 'A/B Testing', description: 'Experimentos de interface' },
    ],
    }}
    >
    <App />
    </ConsentProvider>
    import { useConsent } from 'react-lgpd-consent'

    function MyComponent() {
    const { consent } = useConsent()

    // Verificar se o usuário consentiu com categorias específicas
    const canShowChat = consent?.preferences?.chat === true
    const canLoadVideos = consent?.preferences?.video === true
    const canRunABTests = consent?.preferences?.abTesting === true

    return (
    <div>
    {canShowChat && <ChatWidget />}
    {canLoadVideos && <VideoPlayer src="..." />}
    {canRunABTests && <ABTestVariant />}
    </div>
    )
    }
    Prop Tipo Obrigatória Padrão Descrição
    categories ProjectCategoriesConfig Sim - Define as categorias de cookies do projeto
    texts Partial<ConsentTexts> ❌ Não Textos padrão PT-BR Customiza textos da interface
    theme any ❌ Não Tema padrão Tema Material-UI para os componentes
    designTokens DesignTokens ❌ Não Tokens padrão Tokens de design para customização avançada
    blocking boolean ❌ Não false Exibe overlay bloqueando interação até decisão
    blockingStrategy 'auto' | 'provider' ❌ Não 'auto' Estratégia de renderização do overlay
    hideBranding boolean ❌ Não false Oculta branding "fornecido por"
    onConsentGiven (state: ConsentState) => void ❌ Não - Callback na primeira vez que usuário consente
    onPreferencesSaved (prefs: ConsentPreferences) => void ❌ Não - Callback quando preferências são salvas
    disableDeveloperGuidance boolean ❌ Não false Desativa orientações no console
    disableFloatingPreferencesButton boolean ❌ Não false Desabilita botão flutuante de preferências
    CookieBannerComponent React.ComponentType<CustomCookieBannerProps> ❌ Não Banner padrão Componente de banner customizado
    PreferencesModalComponent React.ComponentType<CustomPreferencesModalProps> ❌ Não Modal padrão Componente de modal customizado
    FloatingPreferencesButtonComponent React.ComponentType<CustomFloatingPreferencesButtonProps> ❌ Não Botão padrão Componente de botão flutuante customizado
    cookieBannerProps object ❌ Não {} Props adicionais para o banner
    preferencesModalProps object ❌ Não {} Props adicionais para o modal
    floatingPreferencesButtonProps object ❌ Não {} Props adicionais para o botão flutuante
    initialState ConsentState ❌ Não - Estado inicial para hidratação SSR
    cookie Partial<ConsentCookieOptions> ❌ Não Opções padrão Configurações do cookie de consentimento
    import React from 'react'
    import { ConsentProvider, type CustomCookieBannerProps } from 'react-lgpd-consent'

    // Componente de banner customizado
    const MeuBannerCustomizado: React.FC<CustomCookieBannerProps> = ({
    consented,
    acceptAll,
    rejectAll,
    openPreferences,
    texts,
    blocking,
    }) => {
    return (
    <div
    style={{
    position: 'fixed',
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: blocking ? 'red' : 'blue',
    color: 'white',
    padding: '1rem',
    zIndex: 1000,
    }}
    >
    <p>{texts.bannerMessage}</p>
    <div style={{ display: 'flex', gap: '1rem', marginTop: '1rem' }}>
    <button onClick={acceptAll}>{texts.acceptAll}</button>
    <button onClick={rejectAll}>{texts.declineAll}</button>
    <button onClick={openPreferences}>{texts.preferences}</button>
    </div>
    </div>
    )
    }

    // Usando o banner customizado
    function App() {
    return (
    <ConsentProvider
    categories={{ enabledCategories: ['analytics'] }}
    CookieBannerComponent={MeuBannerCustomizado}
    blocking={true}
    >
    <main>Minha App</main>
    </ConsentProvider>
    )
    }

    A biblioteca react-lgpd-consent não injeta um ThemeProvider global por conta própria. Ela foi projetada para herdar o tema do app quando um ThemeProvider do MUI está presente. Se você precisa garantir um tema de fallback apenas para os componentes da biblioteca, use a fábrica exportada createDefaultConsentTheme() e passe pelo prop theme do ConsentProvider:

    import { ConsentProvider, createDefaultConsentTheme } from 'react-lgpd-consent'
    ;<ConsentProvider
    theme={createDefaultConsentTheme()}
    categories={{ enabledCategories: ['analytics'] }}
    >
    <App />
    </ConsentProvider>

    Isso evita alterações indesejadas no contexto do MUI do seu app e problemas de SSR.

    import React from 'react'
    import { ConsentProvider, type CustomPreferencesModalProps } from 'react-lgpd-consent'

    const MeuModalCustomizado: React.FC<CustomPreferencesModalProps> = ({
    preferences,
    setPreferences,
    closePreferences,
    isModalOpen,
    texts,
    }) => {
    if (!isModalOpen) return null

    return (
    <div
    style={{
    position: 'fixed',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    backgroundColor: 'white',
    border: '2px solid #ccc',
    borderRadius: '8px',
    padding: '2rem',
    zIndex: 2000,
    minWidth: '400px',
    }}
    >
    <h2>{texts.modalTitle}</h2>
    <p>{texts.modalIntro}</p>

    {/* Lista de categorias */}
    <div style={{ margin: '1rem 0' }}>
    {Object.entries(preferences).map(([category, enabled]) => (
    <label key={category} style={{ display: 'block', marginBottom: '0.5rem' }}>
    <input
    type="checkbox"
    checked={enabled}
    onChange={(e) =>
    setPreferences({
    ...preferences,
    [category]: e.target.checked,
    })
    }
    disabled={category === 'necessary'}
    />{' '}
    {category === 'necessary' ? texts.necessaryAlwaysOn : `Cookies ${category}`}
    </label>
    ))}
    </div>

    <div style={{ display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}>
    <button onClick={closePreferences}>Cancelar</button>
    <button onClick={closePreferences}>{texts.save}</button>
    </div>
    </div>
    )
    }

    function App() {
    return (
    <ConsentProvider
    categories={{ enabledCategories: ['analytics', 'marketing'] }}
    PreferencesModalComponent={MeuModalCustomizado}
    >
    <main>Minha App</main>
    </ConsentProvider>
    )
    }

    Para casos mais avançados onde você precisa exibir informações detalhadas sobre cada cookie (nome, finalidade, duração, provedor), use getCookiesInfoForCategory junto com useCategories:

    import React from 'react'
    import {
    ConsentProvider,
    useCategories,
    getCookiesInfoForCategory,
    type CustomPreferencesModalProps,
    type CookieDescriptor,
    } from 'react-lgpd-consent'

    const ModalComDetalhesCookies: React.FC<CustomPreferencesModalProps> = ({
    preferences,
    setPreferences,
    closePreferences,
    isModalOpen,
    texts,
    }) => {
    const { allCategories } = useCategories()

    if (!isModalOpen) return null

    // Simula integrações usadas no projeto (normalmente você teria isso em contexto)
    const integracoesUsadas = ['google-analytics', 'google-tag-manager', 'mixpanel']

    return (
    <div
    style={{
    position: 'fixed',
    top: 0,
    left: 0,
    width: '100vw',
    height: '100vh',
    backgroundColor: 'rgba(0, 0, 0, 0.7)',
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 2000,
    }}
    >
    <div
    style={{
    backgroundColor: 'white',
    borderRadius: '12px',
    padding: '2rem',
    maxWidth: '800px',
    maxHeight: '80vh',
    overflow: 'auto',
    boxShadow: '0 10px 25px rgba(0, 0, 0, 0.25)',
    }}
    >
    <h2 style={{ marginBottom: '1rem', color: '#333' }}>{texts.modalTitle}</h2>
    <p style={{ marginBottom: '2rem', color: '#666' }}>{texts.modalIntro}</p>

    {/* Lista de categorias com detalhes dos cookies */}
    <div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
    {allCategories.map((categoria) => {
    const cookiesDetalhados: CookieDescriptor[] = getCookiesInfoForCategory(
    categoria.id as any,
    integracoesUsadas,
    )

    return (
    <div
    key={categoria.id}
    style={{
    border: '1px solid #e0e0e0',
    borderRadius: '8px',
    padding: '1.5rem',
    backgroundColor: '#fafafa',
    }}
    >
    <div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
    <input
    type="checkbox"
    id={`categoria-${categoria.id}`}
    checked={preferences[categoria.id] || false}
    onChange={(e) =>
    setPreferences({
    ...preferences,
    [categoria.id]: e.target.checked,
    })
    }
    disabled={categoria.essential}
    style={{ marginRight: '0.75rem', transform: 'scale(1.2)' }}
    />
    <label
    htmlFor={`categoria-${categoria.id}`}
    style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#333' }}
    >
    {categoria.name}
    {categoria.essential && (
    <span style={{ fontSize: '0.8rem', color: '#888', marginLeft: '0.5rem' }}>
    (sempre ativo)
    </span>
    )}
    </label>
    </div>

    <p style={{ marginBottom: '1rem', color: '#666', fontSize: '0.95rem' }}>
    {categoria.description}
    </p>

    {/* Lista de cookies desta categoria */}
    {cookiesDetalhados.length > 0 && (
    <details style={{ marginTop: '1rem' }}>
    <summary
    style={{
    cursor: 'pointer',
    fontWeight: '500',
    color: '#4f46e5',
    marginBottom: '0.5rem',
    }}
    >
    Ver cookies desta categoria ({cookiesDetalhados.length})
    </summary>
    <div style={{ marginTop: '0.75rem', paddingLeft: '1rem' }}>
    {cookiesDetalhados.map((cookie, index) => (
    <div
    key={`${cookie.name}-${index}`}
    style={{
    backgroundColor: 'white',
    border: '1px solid #e5e5e5',
    borderRadius: '6px',
    padding: '1rem',
    marginBottom: '0.75rem',
    }}
    >
    <h4
    style={{ margin: '0 0 0.5rem 0', color: '#333', fontSize: '0.95rem' }}
    >
    <code
    style={{
    backgroundColor: '#f3f4f6',
    padding: '2px 6px',
    borderRadius: '4px',
    fontFamily: 'monospace',
    }}
    >
    {cookie.name}
    </code>
    </h4>
    {cookie.purpose && (
    <p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
    <strong>Finalidade:</strong> {cookie.purpose}
    </p>
    )}
    {cookie.duration && (
    <p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
    <strong>Duração:</strong> {cookie.duration}
    </p>
    )}
    {cookie.provider && (
    <p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
    <strong>Provedor:</strong> {cookie.provider}
    </p>
    )}
    </div>
    ))}
    </div>
    </details>
    )}

    {/* Fallback para categorias sem cookies catalogados */}
    {cookiesDetalhados.length === 0 &&
    categoria.cookies &&
    categoria.cookies.length > 0 && (
    <div style={{ marginTop: '1rem', fontSize: '0.9rem', color: '#666' }}>
    <strong>Padrões de cookies:</strong>{' '}
    {categoria.cookies.map((pattern, i) => (
    <code
    key={i}
    style={{
    backgroundColor: '#f3f4f6',
    padding: '2px 4px',
    borderRadius: '3px',
    marginRight: '0.5rem',
    fontFamily: 'monospace',
    fontSize: '0.8rem',
    }}
    >
    {pattern}
    </code>
    ))}
    </div>
    )}
    </div>
    )
    })}
    </div>

    {/* Botões de ação */}
    <div
    style={{
    display: 'flex',
    gap: '1rem',
    justifyContent: 'flex-end',
    marginTop: '2rem',
    paddingTop: '1rem',
    borderTop: '1px solid #e0e0e0',
    }}
    >
    <button
    onClick={closePreferences}
    style={{
    padding: '0.75rem 1.5rem',
    backgroundColor: '#6b7280',
    color: 'white',
    border: 'none',
    borderRadius: '6px',
    cursor: 'pointer',
    fontSize: '0.95rem',
    }}
    >
    Cancelar
    </button>
    <button
    onClick={closePreferences}
    style={{
    padding: '0.75rem 1.5rem',
    backgroundColor: '#4f46e5',
    color: 'white',
    border: 'none',
    borderRadius: '6px',
    cursor: 'pointer',
    fontSize: '0.95rem',
    }}
    >
    {texts.save}
    </button>
    </div>
    </div>
    </div>
    )
    }

    function AppComModalAvancado() {
    return (
    <ConsentProvider
    categories={{
    enabledCategories: ['analytics', 'marketing', 'functional'],
    }}
    PreferencesModalComponent={ModalComDetalhesCookies}
    // Especifique as integrações para obter informações detalhadas dos cookies
    scriptIntegrations={[
    { id: 'google-analytics', config: { measurementId: 'GA_MEASUREMENT_ID' } },
    { id: 'google-tag-manager', config: { containerId: 'GTM-XXXXXXX' } },
    { id: 'mixpanel', config: { token: 'MIXPANEL_TOKEN' } },
    ]}
    >
    <main>Minha App com Modal Avançado</main>
    </ConsentProvider>
    )
    }
    • useCategories(): Hook que retorna informações sobre todas as categorias ativas
    • getCookiesInfoForCategory(categoryId, integrations): Função que retorna detalhes completos dos cookies
    • CookieDescriptor: Interface TypeScript com name, purpose, duration, provider
    1. Informações Detalhadas: Cada cookie mostra nome, finalidade, duração e provedor
    2. Organização por Categoria: Cookies agrupados logicamente
    3. Interface Expansível: Detalhes dos cookies ficam em <details> expansível
    4. Fallback Inteligente: Mostra padrões básicos quando detalhes não estão disponíveis
    5. Acessibilidade: Labels apropriados e navegação por teclado
    6. Design Responsivo: Layout que se adapta a diferentes tamanhos de tela
    import React from 'react'
    import { useOpenPreferencesModal, useConsent } from 'react-lgpd-consent'

    function MeuComponente() {
    const openPreferences = useOpenPreferencesModal()
    const { preferences, acceptAll, rejectAll } = useConsent()

    return (
    <div>
    <h3>Status atual: {preferences.analytics ? 'Analytics ativo' : 'Analytics inativo'}</h3>

    <button onClick={openPreferences}>⚙️ Gerenciar Preferências</button>

    <button onClick={acceptAll}>✅ Aceitar Todos</button>

    <button onClick={rejectAll}>❌ Recusar Todos</button>
    </div>
    )
    }
    <!-- Em templates HTML, emails ou widgets externos -->
    <button onclick="window.openPreferencesModal?.()">Configurar Cookies</button>

    <script>
    // Ou em JavaScript puro
    function abrirConfiguracoesCookies() {
    if (window.openPreferencesModal) {
    window.openPreferencesModal()
    } else {
    console.warn('Sistema de consentimento não carregado')
    }
    }

    // Verificar se função está disponível
    if (typeof window.openPreferencesModal === 'function') {
    console.log('✅ Sistema de consentimento disponível')
    }
    </script>
    import { setDebugLogging, LogLevel } from 'react-lgpd-consent'

    // Ativar todos os logs (desenvolvimento)
    setDebugLogging(true, LogLevel.DEBUG)

    // Apenas logs importantes (staging)
    setDebugLogging(true, LogLevel.INFO)

    // Apenas erros (produção)
    setDebugLogging(true, LogLevel.ERROR)

    // Desativar completamente
    setDebugLogging(false)
    // Em desenvolvimento, você verá logs como:
    // [🍪 LGPD-CONSENT] 🔧 Categorias Ativas (para UI customizada)
    // [🍪 LGPD-CONSENT] ℹ️ User accepted all cookies
    // [🍪 LGPD-CONSENT] 🐛 Category preference changed: analytics = true
    import { useConsent } from 'react-lgpd-consent'

    function DebugPanel() {
    const { preferences, consented } = useConsent()

    // Apenas mostrar em desenvolvimento
    if (process.env.NODE_ENV !== 'development') return null

    return (
    <div
    style={{
    position: 'fixed',
    top: 0,
    right: 0,
    background: 'rgba(0,0,0,0.8)',
    color: 'white',
    padding: '1rem',
    fontSize: '12px',
    fontFamily: 'monospace',
    }}
    >
    <h4>🍪 Debug LGPD</h4>
    <p>Consentimento: {consented ? '✅' : '❌'}</p>
    <pre>{JSON.stringify(preferences, null, 2)}</pre>
    </div>
    )
    }
    import React from 'react'
    import { ThemeProvider, createTheme } from '@mui/material/styles'
    import CssBaseline from '@mui/material/CssBaseline'
    import { ConsentProvider } from 'react-lgpd-consent'

    // Seu tema personalizado
    const meuTema = createTheme({
    palette: {
    primary: {
    main: '#1976d2',
    },
    secondary: {
    main: '#dc004e',
    },
    },
    typography: {
    fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
    },
    })

    // Tema específico para componentes de consentimento (opcional)
    const temaConsentimento = createTheme({
    ...meuTema,
    components: {
    MuiPaper: {
    styleOverrides: {
    root: {
    borderRadius: 12,
    },
    },
    },
    MuiButton: {
    styleOverrides: {
    root: {
    textTransform: 'none',
    fontWeight: 600,
    },
    },
    },
    },
    })

    function App() {
    return (
    <ThemeProvider theme={meuTema}>
    <CssBaseline />
    <ConsentProvider
    categories={{
    enabledCategories: ['analytics', 'marketing', 'advertising'],
    }}
    theme={temaConsentimento} // Tema específico para consentimento
    texts={{
    bannerMessage: 'Utilizamos cookies para personalizar sua experiência.',
    acceptAll: 'Aceitar Todos',
    declineAll: 'Apenas Necessários',
    preferences: 'Personalizar',
    }}
    onConsentGiven={(state) => {
    console.log('✅ Consentimento dado:', state.preferences)
    }}
    >
    <main>
    <h1>Minha Aplicação com Material-UI</h1>
    {/* Seus componentes aqui */}
    </main>
    </ConsentProvider>
    </ThemeProvider>
    )
    }

    export default App
    import { ConsentProvider, type DesignTokens } from 'react-lgpd-consent'

    const meusTokens: DesignTokens = {
    colors: {
    primary: '#6366f1',
    secondary: '#f59e0b',
    background: '#ffffff',
    text: '#1f2937',
    border: '#e5e7eb',
    },
    layout: {
    borderRadius: '12px',
    spacing: '1rem',
    backdrop: 'rgba(0, 0, 0, 0.6)', // ou false para transparente
    },
    typography: {
    fontFamily: '"Inter", system-ui, sans-serif',
    fontSize: '14px',
    fontWeight: '500',
    },
    }

    function App() {
    return (
    <ConsentProvider
    categories={{ enabledCategories: ['analytics'] }}
    designTokens={meusTokens}
    blocking={true}
    blockingStrategy="provider"
    >
    <main>Minha App</main>
    </ConsentProvider>
    )
    }
    import { ConsentProvider } from 'react-lgpd-consent'

    function App() {
    return (
    <ConsentProvider
    categories={{ enabledCategories: ['analytics'] }}
    cookie={{
    name: 'meu_app_consent', // Nome customizado
    maxAge: 365 * 24 * 60 * 60, // 1 ano em segundos
    domain: '.meudominio.com.br', // Cookie compartilhado entre subdomínios
    secure: true, // Apenas HTTPS
    sameSite: 'Lax', // Política SameSite
    }}
    >
    <main>Minha App</main>
    </ConsentProvider>
    )
    }
    <ConsentProvider
    categories={{
    enabledCategories: ['analytics', 'marketing', 'advertising'],
    }}
    texts={{
    bannerMessage:
    'Usamos cookies para melhorar sua experiência de compra e exibir ofertas personalizadas.',
    acceptAll: 'Aceitar e continuar',
    declineAll: 'Apenas essenciais',
    }}
    onConsentGiven={(state) => {
    // Inicializar ferramentas baseado no consentimento
    if (state.preferences.analytics) {
    // gtag('config', 'GA_MEASUREMENT_ID')
    }
    if (state.preferences.marketing) {
    // fbq('init', 'FACEBOOK_PIXEL_ID')
    }
    }}
    >
    {/* Sua loja */}
    </ConsentProvider>
    <ConsentProvider
    categories={{
    enabledCategories: ['analytics'],
    }}
    disableFloatingPreferencesButton={false}
    hideBranding={true}
    >
    {/* Seu conteúdo */}
    </ConsentProvider>
    <ConsentProvider
    categories={{
    enabledCategories: ['analytics', 'functional'],
    }}
    blocking={true}
    blockingStrategy="provider"
    disableDeveloperGuidance={false}
    onPreferencesSaved={(prefs) => {
    // Log de auditoria
    console.log('Audit: User preferences updated', prefs)
    }}
    >
    {/* Sua app corporativa */}
    </ConsentProvider>
    // ❌ Errado - hook usado fora do provider
    function MeuComponente() {
    const { preferences } = useConsent() // Erro!
    return <div>...</div>
    }

    function App() {
    return (
    <div>
    <MeuComponente /> {/* useConsent usado aqui falhará */}
    <ConsentProvider categories={{ enabledCategories: ['analytics'] }}>
    <main>App</main>
    </ConsentProvider>
    </div>
    )
    }

    // ✅ Correto - hook usado dentro do provider
    function App() {
    return (
    <ConsentProvider categories={{ enabledCategories: ['analytics'] }}>
    <div>
    <MeuComponente /> {/* Agora funciona */}
    <main>App</main>
    </div>
    </ConsentProvider>
    )
    }
    1. Verificar se não há consentimento salvo no cookie:
    // Limpar cookie para teste
    document.cookie = 'cookieConsent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
    1. Verificar se hideBranding e outras configs estão corretas
    2. Conferir se o z-index não está sendo sobrescrito por outros elementos
    // Se você tiver problemas com tipos, adicione ao tsconfig.json:
    {
    "compilerOptions": {
    "moduleResolution": "bundler", // ou "node"
    "skipLibCheck": true
    }
    }

    💡 Dica: Use setDebugLogging(true, LogLevel.DEBUG) durante o desenvolvimento para ver logs detalhados do comportamento da biblioteca.