npm package: @meui-creative/cookies
Description: GDPR-compliant cookie consent management for React with centralized Payload CMS configuration
Author: MEUI Creative
Latest version: 6.0.1

# @meui-creative/cookies

GDPR-compliant cookie consent management for React and Next.js with 5 preset layouts, script blocking, built-in integrations, and full Google Consent Mode V2 support.

## Features

- **5 Preset Layouts** — From minimal bars to full sidebars
- **Fully Customizable** — Override colors, position, spacing, typography, animations
- **Multi-language** — Built-in translations (EN, CS, DE)
- **Google Consent Mode V2** — Automatic setup with all required parameters
- **Script Blocking** — Blocks scripts/iframes until consent via `data-cookie-category`
- **Built-in Integrations** — Google Analytics, GTM, Facebook Pixel, Hotjar, Clarity
- **Remote Config** — Optional Payload CMS plugin for centralized management
- **Headless Mode** — Use the hook only, build your own UI (zero UI dependencies)
- **TypeScript** — Full type safety
- **SSR-safe** — Portal-based rendering, `"use client"` directive for Next.js App Router
- **GDPR Compliant** — Strict mode, consent versioning, audit-ready export

## Installation

```bash
npm install @meui-creative/cookies framer-motion lucide-react
```

`framer-motion` and `lucide-react` are only needed if using the built-in component/presets. For headless usage (hook only), install just the main package.

## Entry Points

| Import | What you get |
|--------|-------------|
| `@meui-creative/cookies` | Full library: `CookieConsent` component + `useCookieConsent` hook + API |
| `@meui-creative/cookies/headless` | Hook + API only (no framer-motion/lucide-react dependency) |
| `@meui-creative/cookies/presets` | Individual preset components: `BannerDefault`, `BannerMinimal`, `BannerCard`, `BannerSidebar`, `BannerMeui` |
| `@meui-creative/cookies/styles` | CSS stylesheet |
| `@meui-creative/cookies/payload` | Payload CMS plugin |

---

## Quick Start

```tsx
import { CookieConsent } from '@meui-creative/cookies'
import '@meui-creative/cookies/styles'

export default function Layout({ children }) {
  return (
    <>
      {children}
      <CookieConsent
        preset="default"
        language="en"
        version="1.0.0"
        mode="strict"
        links={{
          privacyPolicy: '/privacy',
          cookiePolicy: '/cookies',
        }}
        onAccept={(categories) => console.log('Accepted:', categories)}
        onDecline={() => console.log('Declined')}
      />
    </>
  )
}
```

The component renders via a React Portal, checks localStorage for existing consent on mount, shows/hides the banner automatically, and handles script blocking + Google Consent Mode out of the box.

---

## Integrations

Pass integration IDs and the library loads them automatically when the user consents to the mapped category:

```tsx
<CookieConsent
  integrations={{
    googleAnalytics: 'G-XXXXXXXXXX',    // category: analytics
    googleTagManager: 'GTM-XXXXXXX',    // category: analytics
    facebookPixel: '123456789',          // category: marketing
    hotjar: '1234567',                   // category: analytics
    clarity: 'xxxxxxxxxx',              // category: analytics
  }}
/>
```

### Important: Remove existing Google tags

If you have Google Analytics or GTM script tags in your `<head>`, remove them and use the `integrations` prop instead. The library ensures the correct order: consent defaults (denied) -> load tag -> config -> consent update.

```tsx
// Don't do this — bypasses consent
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXX"></script>

// Do this instead
<CookieConsent integrations={{ googleAnalytics: 'G-XXX' }} />
```

---

## Script & Iframe Blocking

For scripts not covered by built-in integrations, mark them with `type="text/plain"` and `data-cookie-category`:

```html
<!-- Only runs when "analytics" category is consented -->
<script type="text/plain" data-cookie-category="analytics" src="https://example.com/analytics.js"></script>

<!-- Inline scripts work too -->
<script type="text/plain" data-cookie-category="marketing">
  console.log('Runs only after marketing consent');
</script>

<!-- Iframes — src is auto-stripped until consent -->
<iframe data-cookie-category="marketing" src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
```

The library also uses a `MutationObserver` to handle dynamically added scripts/iframes.

**Categories:** `strictly-necessary` (always on), `functional`, `analytics`, `marketing`

---

## Google Consent Mode V2

The library automatically manages Google Consent Mode V2. No extra configuration needed.

**On page load:** all signals set to `denied`. **On consent:** signals updated to `granted` for consented categories.

| Category | Consent Mode signals |
|----------|---------------------|
| `strictly-necessary` | `functionality_storage` |
| `functional` | `functionality_storage`, `personalization_storage` |
| `analytics` | `analytics_storage` |
| `marketing` | `ad_storage`, `ad_user_data`, `ad_personalization` |
| *(always)* | `security_storage` |

### Optional configuration

```tsx
<CookieConsent
  consentModeOptions={{
    region: ['EU', 'US-CA'],     // Geographic regions
    urlPassthrough: true,         // Pass ad click info through URLs
    adsDataRedaction: true,       // Redact ads data when consent denied
  }}
/>
```

### Verify in GTM Preview

1. Open GTM Preview Mode
2. Click the "Consent" tab
3. Before accept: all signals show **Denied**
4. After accept: consented signals show **Granted**

```javascript
// Check in browser console
window.dataLayer.filter(e => e[0] === 'consent')
```

---

## Design Presets

Choose from 5 professionally designed layouts, each with unique UX:

### `default` - Classic Card

**Color:** Blue (#3b82f6) | **Position:** Bottom-left

Clean, professional design with large cookie icon. Perfect for corporate websites.

```tsx
<CookieConsent />
```

---

### `meui` - Compact with Inline Settings

**Color:** Green (#4A6953) | **Position:** Bottom-left

Original Meui design with expandable settings directly in banner. Saves modal interactions.

```tsx
<CookieConsent preset="meui" />
```

---

### `minimal` - Bottom Bar

**Color:** Black (#000) | **Position:** Full-width bottom

Horizontal bar spanning full width. Non-intrusive, great for blogs and content sites.

```tsx
<CookieConsent preset="minimal" />
```

---

### `card` - Accent Header

**Color:** Violet (#8b5cf6) | **Position:** Bottom-right

Modern card with colored header stripe and shield icon. Bold and eye-catching.

```tsx
<CookieConsent preset="card" />
```

---

### `sidebar` - Full-Height Panel

**Color:** Emerald (#10b981) | **Position:** Right sidebar

Full-height sidebar with detailed information and trust indicators. Best for complex consent needs.

```tsx
<CookieConsent preset="sidebar" />
```

---

## Customization

Every preset can be customized. Override color, position, language, or any style property:

### Change Color

```tsx
<CookieConsent
  preset="default"
  accentColor="#ec4899" // Pink
/>
```

### Change Position

```tsx
<CookieConsent
  preset="card"
  style={{
    position: 'top-center', // Move to top
  }}
/>
```

### Change Language

```tsx
<CookieConsent
  preset="meui"
  language="cs" // Czech
/>
```

### Multiple Overrides

```tsx
<CookieConsent
  preset="minimal"
  accentColor="#f97316"
  language="de"
  style={{
    position: 'top-center',
    animation: {
      direction: 'down',
      duration: 0.5,
    },
  }}
/>
```

### Full Style Override

```tsx
<CookieConsent
  preset="default"
  accentColor="#ff6600"
  borderRadius="sharp"
  style={{
    position: 'top-right',
    maxWidth: 'lg',              // 'sm' | 'md' | 'lg' | 'xl' or custom string
    spacing: 'comfortable',      // 'compact' | 'normal' | 'comfortable'
    background: '#1a1a1a',
    shadow: '2xl',               // 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
    border: { width: '2', color: '#333' },
    fontSize: { heading: 'lg', body: 'sm' },
    button: { shape: 'pill', size: 'lg' },
    modal: { shadow: 'xl' },
    animation: { direction: 'right', duration: 0.4, enabled: true },
  }}
/>
```

### Position Options

All presets support 6 positions:

- `bottom-left` | `bottom-center` | `bottom-right`
- `top-left` | `top-center` | `top-right`

---

## All Configuration Options

```tsx
<CookieConsent
  // Design
  preset="default"             // 'default' | 'meui' | 'minimal' | 'card' | 'sidebar'
  accentColor="#3b82f6"        // Any CSS color
  borderRadius="rounded"       // 'rounded' | 'sharp'
  style={{ ... }}              // See Full Style Override above

  // Content & Language
  language="en"                // 'en' | 'cs' | 'de'
  descriptionMode="short"     // 'short' | 'long'

  // Behavior
  mode="strict"                // 'strict' (closing = decline) | 'soft' (closing = nothing)
  showSettingsButton={true}    // Show settings gear button in banner
  closeButton={true}           // Show X close button
  showRejectLink={true}        // Show reject all link
  version="1.0.0"             // Bump to force re-consent
  ttl={365}                    // Consent expiry in days
  domain=".example.com"       // Cookie domain for subdomain sharing

  // Legal Links
  links={{
    privacyPolicy: '/privacy',
    cookiePolicy: '/cookies',
  }}

  // Custom Categories (replaces defaults entirely)
  customCategories={[
    { id: 'essential', label: 'Essential', description: '...', required: true, enabled: true },
    { id: 'analytics', label: 'Analytics', description: '...', required: false, enabled: false },
  ]}

  // Integrations
  integrations={{
    googleAnalytics: 'G-XXXXXXXXXX',
    googleTagManager: 'GTM-XXXXXXX',
    facebookPixel: '123456789',
    hotjar: '1234567',
    clarity: 'xxxxxxxxxx',
  }}

  // Google Consent Mode V2
  consentModeOptions={{
    region: ['EU'],
    urlPassthrough: true,
    adsDataRedaction: true,
  }}

  // Remote Config (Payload CMS)
  apiUrl="https://your-cms.com/api/cookie-config"

  // Callbacks
  onAccept={(choices) => console.log('Accepted:', choices)}
  onDecline={() => console.log('Declined')}
  onChange={(choices) => console.log('Changed:', choices)}
  onReady={() => console.log('Consent loaded from storage')}
  onBlock={(src) => console.log('Script unblocked:', src)}
  onVersionChange={(prev) => console.log('Version changed from:', prev)}

  // Debug
  debug={false}
/>
```

---

## Headless Mode

Import from `@meui-creative/cookies/headless` to use the hook without any UI dependencies:

```tsx
import { useCookieConsent } from '@meui-creative/cookies/headless'

function CustomCookieBanner() {
  const {
    consent,        // { 'strictly-necessary': true, analytics: false, ... }
    hasConsent,     // Has the user made any choice?
    isReady,        // Initialization complete?
    categories,     // CategoryDefinition[]
    acceptAll,
    declineAll,
    setConsent,     // Granular: setConsent({ analytics: true, marketing: false })
    revokeConsent,
    exportConsent,
    showSettings,   // Dispatches event to open settings modal
  } = useCookieConsent({
    version: '1.0.0',
    language: 'en',
    integrations: { googleAnalytics: 'G-XXXXXXXXXX' },
  })

  if (!isReady || hasConsent) return null

  return (
    <div className="my-custom-banner">
      <h2>We use cookies</h2>
      <button onClick={acceptAll}>Accept All</button>
      <button onClick={declineAll}>Decline</button>
    </div>
  )
}
```

The hook handles all core logic — storage, script blocking, integrations, Google Consent Mode — you just provide the UI.

---

## Programmatic API

```tsx
import { showCookieSettings, revokeConsent, exportConsent } from '@meui-creative/cookies'

// Open settings modal from anywhere (e.g., footer link)
showCookieSettings()

// Revoke all consent
revokeConsent()

// Export consent record for GDPR data subject requests
const record = exportConsent()
// { version, timestamp, choices, userAgent, language, consentMethod, sourceUrl }
```

### "Manage cookies" footer link

```tsx
import { showCookieSettings } from '@meui-creative/cookies'

function Footer() {
  return <button onClick={showCookieSettings}>Cookie Settings</button>
}
```

---

## Version Management

Set `version` on the component. When you bump it, all users are prompted to re-consent:

```tsx
<CookieConsent version="2.0.0" />
```

Bump the version when you:
- Add or remove cookie categories
- Add a new integration that requires consent
- Change what data a category covers
- Need to comply with updated regulations

Consent also expires automatically after `ttl` days (default: 365).

---

## Events

The library dispatches DOM events on `window` for integration with non-React code:

```javascript
window.addEventListener('meui:consent:given', (e) => {
  console.log('Consent given:', e.detail.consent)
})

window.addEventListener('meui:consent:revoked', () => {
  console.log('Consent revoked')
})

window.addEventListener('meui:consent:version-changed', (e) => {
  console.log('Re-consent needed, previous version:', e.detail.previousVersion)
})
```

Events: `meui:consent:given`, `meui:consent:revoked`, `meui:consent:changed`, `meui:consent:version-changed`, `meui:settings:opened`

---

## Remote Configuration (Payload CMS)

Manage consent texts and categories through a Payload CMS admin panel instead of hardcoding them.

### 1. Install plugin

```ts
// payload.config.ts
import { cookieConsentPlugin } from '@meui-creative/cookies/payload'

export default buildConfig({
  plugins: [
    cookieConsentPlugin({
      enabled: true,
      defaultVersion: '1.0.0',
    }),
  ],
})
```

This adds a `cookie-config` collection (version, texts in en/cs/de, categories, legal links) and a public `GET /api/cookie-config` endpoint.

### 2. Use in frontend

```tsx
<CookieConsent
  preset="default"
  apiUrl="https://your-cms.com/api/cookie-config"
/>
```

The component fetches the remote config (cached for 24h with background refresh), using it for texts and categories. Styling remains controlled locally via props.

---

## Storage

Consent is stored in two places:

- **localStorage** (`meui_cookie_consent`) — Full record: version, timestamp, choices, userAgent, language, consentMethod, sourceUrl
- **Cookie** (`meui_cookie_consent`) — Simplified comma-separated list of consented category IDs (for server-side reading)

Use `exportConsent()` to retrieve the full record for GDPR Article 7(1) compliance.

---

## License

MIT