# Cookie Consent

JavaScript cookie consent banner with customizable UI, local storage, i18n, configurable script loading, and runtime APIs.

## Install

```bash
npm install @bato-web-agency/cookie-consent
```

## Quick Start

```js
import CookieConsent from '@bato-web-agency/cookie-consent';
import '@bato-web-agency/cookie-consent/style.css';

await CookieConsent.init({
  mode: 'gdpr',
  policyVersion: '2026-05-19',
  language: 'browser',
  fallbackLanguage: 'en',
  layout: 'floating',
  policyLinks: [{ label: 'Cookie policy', href: '/cookie-policy' }],
  scripts: [
    {
      id: 'analytics',
      category: 'analytics',
      callback: () => {
        window.analyticsLoaded = true;
      }
    }
  ]
});
```

## Browser Script

When you serve the built package files yourself, use the UMD build and compiled CSS:

```html
<link rel="stylesheet" href="/dist/style.css" />
<script src="/dist/index.umd.js"></script>
<script>
  window.CookieConsent.init({
    defaultLanguage: 'uk',
    policyVersion: '1'
  });
</script>
```

For CDN usage with jsDelivr:

```html
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@bato-web-agency/cookie-consent@1.2.0/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/@bato-web-agency/cookie-consent@1.2.0"></script>
<script>
  window.CookieConsent.init({
    defaultLanguage: 'uk',
    policyVersion: '1'
  });
</script>
```

For CDN usage with unpkg:

```html
<link
  rel="stylesheet"
  href="https://unpkg.com/@bato-web-agency/cookie-consent@1.2.0/dist/style.css"
/>
<script src="https://unpkg.com/@bato-web-agency/cookie-consent@1.2.0/dist/index.umd.js"></script>
<script>
  window.CookieConsent.init({
    defaultLanguage: 'uk',
    policyVersion: '1'
  });
</script>
```

The `/dist/...` paths above are local/static hosting paths, not CDN URLs. Examples and plain HTML templates inside this repository should use the built files:

```html
<link rel="stylesheet" href="./dist/style.css" />
<script type="module">
  import CookieConsent from './dist/index.es.js';
</script>
```

For source-level development, edit `src/styles/index.scss` and run `npm run build`.

## Runtime API

- `CookieConsent.init(config)`
- `CookieConsent.getConsent()`
- `CookieConsent.hasConsent(category)`
- `CookieConsent.setConsent({ categories })`
- `CookieConsent.acceptCategory(category)`
- `CookieConsent.resetConsent()`
- `CookieConsent.openBanner()`
- `CookieConsent.openPreferences()`
- `CookieConsent.close()`
- `CookieConsent.on(event, handler)`
- `CookieConsent.off(event, handler)`
- `CookieConsent.registerScript(script)`
- `CookieConsent.loadCategory(category)`
- `CookieConsent.registerEmbed(embed)`
- `CookieConsent.loadEmbed(id)`
- `CookieConsent.loadEmbedsByCategory(category)`

Use `getConsent()` and `hasConsent(category)` from arbitrary site JavaScript to branch behaviour after the user choice:

```js
if (CookieConsent.hasConsent('analytics')) {
  window.siteAnalyticsFeatureEnabled = true;
}
```

## Consent Mode

`mode` controls when tagged scripts are loaded:

- `gdpr` is the default. Registered scripts and blocked script selectors are loaded only after the user accepts the required category.
- `eprivacy` loads registered scripts and blocked script selectors automatically after initialization or registration.

```js
CookieConsent.init({
  mode: 'eprivacy',
  scripts: [
    {
      id: 'analytics',
      category: 'analytics',
      selector: 'script[type="text/plain"][data-cookie-category="analytics"]'
    }
  ]
});
```

In `eprivacy` mode, automatic script loading does not mark optional categories as accepted. Use `getConsent()` and `hasConsent(category)` for the user's saved preference state.

## YouTube And Vimeo Embeds

Embedded videos are managed separately from JavaScript scripts because YouTube and Vimeo cookies can be created when an iframe loads. Use `data-cc-src`, not `src`, for compliance-safe markup in `gdpr` mode:

```html
<div
  data-cc-embed="youtube"
  data-cc-id="homepage-video"
  data-cc-src="https://www.youtube.com/watch?v=VIDEO_ID"
  data-cc-title="Product video"
></div>
```

```js
CookieConsent.init({
  mode: 'gdpr',
  embeds: {
    enabled: true,
    category: 'marketing',
    autoScan: true,
    providers: {
      youtube: {
        enabled: true,
        privacyMode: true
      },
      vimeo: {
        enabled: true,
        dnt: true
      }
    }
  }
});
```

In `gdpr`, the library renders a local placeholder until the user allows the configured category. In `eprivacy`, embeds load automatically with privacy-friendly URL normalization. YouTube URLs are converted to `youtube-nocookie.com` when `privacyMode` is enabled, and Vimeo URLs receive `dnt=1` when `dnt` is enabled.

Regular `<iframe src="...">` markup can load before the consent library runs, so use `data-cc-src` or config-based embeds instead.

## Customization

Use CSS variables for fast styling:

```css
:root {
  --cc-font-size: 1rem;
  --cc-color-primary: #0f6cbd;
  --cc-color-primary-hover: #115ea3;
  --cc-color-primary-active: #0f548c;
  --cc-color-secondary: #f5f5f5;
  --cc-color-focus: #000;
  --cc-duration: 160ms;
  --cc-easing: cubic-bezier(0.33, 0, 0.67, 1);
  --cc-font-family: 'Segoe UI', 'Segoe UI Variable', system-ui, sans-serif;
  --cc-radius: 4px;
  --cc-shadow: 0 0.5em 1em rgb(0 0 0 / 14%), 0 0 0.125em rgb(0 0 0 / 12%);
}
```

Animations are controlled by `--cc-duration` and respect `prefers-reduced-motion`.

Hide UI parts through `components`. The logo is included in this list:

```js
CookieConsent.init({
  components: {
    logo: false
  }
});
```

Supported starter languages: `en`, `en-gb`, `en-uk`, `de`, `fr`, `es`, `ca-es`, `it`, `sv`, `no`, `nl`, `pt`, `fi`, `hu`, `cs`, `hr`, `da`, `sk`, `sl`, `pl`, `el`, `he`, `mk`, `uk`, `ro`, `sr`, `et`, `lt`, `lv`, `ru`, `bg`, `cy`, `ja`, `ar`, and `tr`.
Built-in language copy is stored as one JSON file per locale in `src/i18n/locales`.
Every label can be overridden with `translations`.

Language resolution is controlled by `language`:

- `browser` uses `navigator.language` and is the default.
- `auto` uses the site language from the root `<html lang="...">` attribute.
- any language code, for example `he` or `en-uk`, forces that language.

Category content supports a short `description` and expandable `details` text. Override `details` through `content` or `translations`; category config is for category metadata such as `title`, `description`, `required`, and `defaultValue`.

```js
CookieConsent.init({
  content: {
    en: {
      embeds: {
        placeholder: {
          title: 'Video content',
          description: 'Allow marketing cookies to play this video.',
          buttonText: 'Allow and play'
        }
      },
      categories: {
        analytics: {
          description: 'Short analytics summary.',
          details: 'Long analytics explanation shown after clicking Read More.'
        }
      }
    }
  }
});
```

Editable popup text supports safe inline formatting such as links and emphasis:

```js
CookieConsent.init({
  providedBy: 'Provided by <a href="https://bato.dev">Bato Web Agency</a>',
  content: {
    en: {
      banner: {
        description: 'We use <strong>cookies</strong> to improve the site.'
      }
    }
  }
});
```

Formatted text allows these tags: `a`, `abbr`, `b`, `br`, `code`, `em`, `i`, `mark`, `small`, `span`, `strong`, `sub`, `sup`, and `u`. Supported attributes are `href`, `target`, `rel`, and `title` on links; `title` on `abbr`; and `class` or `title` on `span`.

The packaged stylesheet is available as compiled CSS and a source SCSS entrypoint:

```js
import '@bato-web-agency/cookie-consent/style.css';
import '@bato-web-agency/cookie-consent/style.scss';
```

The SCSS source is split into internal partial files for maintenance, but `@bato-web-agency/cookie-consent/style.scss` is the public SCSS import path.

See [docs/API.md](docs/API.md) and [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for the full configuration reference.
