consent-banner

Cookie and privacy consent banner. Non-modal (bottom/top) or modal (center) dialog backed by localStorage.

Overview

The <consent-banner> component wraps a native <dialog> to handle cookie and privacy consent. It manages localStorage persistence, expiry, and provides a trigger attribute to let users re-open their preferences from anywhere on the page.

The author controls all content inside the dialog — the component handles the plumbing: show/hide, localStorage read/write, and event dispatch.

Simple Banner

The minimal setup: a <dialog> with a paragraph and footer containing accept/reject buttons. The value attribute on each button drives the consent logic.

<consent-banner> <dialog> <p>We use cookies to improve your experience. <a href="/privacy">Privacy Policy</a></p> <footer> <button value="reject" class="secondary">Reject All</button> <button value="accept">Accept All</button> </footer> </dialog> </consent-banner>

Granular Preferences

Add checkboxes for per-category control. The value="save" button stores whatever the user has checked. The value="accept" button marks everything true; value="reject" only keeps disabled checkboxes (typically Necessary) as true.

<consent-banner persist="cookie-prefs" trigger="#manage-cookies"> <dialog> <header><h2>Cookie Preferences</h2></header> <section> <p>We use cookies to personalize content and analyse traffic. <a href="/privacy">Learn more</a></p> <fieldset class="minimal"> <label> <input type="checkbox" name="necessary" checked disabled> Necessary </label> <label> <input type="checkbox" name="analytics"> Analytics </label> <label> <input type="checkbox" name="marketing"> Marketing </label> </fieldset> </section> <footer> <button value="reject" class="secondary">Reject All</button> <button value="save" class="secondary">Save Preferences</button> <button value="accept">Accept All</button> </footer> </dialog> </consent-banner> <!-- Elsewhere on the page --> <button id="manage-cookies">Manage Cookies</button>

Position Variants

The position attribute controls where the banner appears and whether it is modal.

Value Behavior Dialog API
bottom (default) Fixed to viewport bottom, page remains interactive dialog.show()
top Fixed to viewport top, page remains interactive dialog.show()
center Centered modal with backdrop — user must choose (ESC prevented) dialog.showModal()
<consent-banner position="top"> <dialog> <p>This site uses cookies. <a href="/privacy">Learn more</a></p> <footer> <button value="reject" class="secondary">Reject</button> <button value="accept">Accept</button> </footer> </dialog> </consent-banner> <consent-banner position="center"> <dialog> <header><h2>Before you continue</h2></header> <section> <p>We need your consent to use cookies. You must make a choice to proceed.</p> </section> <footer> <button value="reject" class="secondary">Reject All</button> <button value="accept">Accept All</button> </footer> </dialog> </consent-banner>

Re-opening Preferences

Set trigger to a CSS selector pointing at a Manage Cookies button (or link) elsewhere on the page. When clicked, the dialog re-opens with previously saved checkbox states restored.

The trigger uses document-level event delegation, so the target element can exist anywhere in the DOM — even if it loads after the banner.

Events

The component dispatches a single event when the user makes a consent choice.

Event Detail Description
consent-banner:change { preferences: Object, action: string } Fired when the user clicks accept, reject, or save. preferences maps checkbox names to booleans. action is the button’s value.
// Listen for consent changes document.addEventListener('consent-banner:change', (e) => { const { preferences, action } = e.detail; if (preferences.analytics) initAnalytics(); if (preferences.marketing) initMarketing(); console.log(`User chose: ${action}`, preferences); });

Static API

The ConsentBanner class exposes static methods for reading and clearing consent from external code.

Method Parameters Returns Description
ConsentBanner.getConsent(key?) key: string (default: 'consent-banner') { preferences, action, timestamp } | null Read stored consent. Use on page load to check if analytics/marketing scripts should run.
ConsentBanner.reset(key?) key: string (default: 'consent-banner') void Clear stored consent. The banner reappears on next page load.
import { ConsentBanner } from '/src/web-components/consent-banner/logic.js'; // Check stored consent on page load const consent = ConsentBanner.getConsent('cookie-prefs'); if (consent?.preferences?.analytics) { initAnalytics(); } // Clear consent (banner reappears on next load) ConsentBanner.reset('cookie-prefs');

Attributes Reference

AttributeValuesDefaultDescription
persist string consent-banner localStorage key for storing consent
position "bottom", "top", "center" bottom Banner position. Center uses modal dialog.
trigger string CSS selector for a re-open button (e.g. #manage-cookies)
expires string 365 Days until consent expires. 0 or 'never' disables expiry.

Required Structure

ElementRequiredDescription
<dialog> yes Banner container — shown until consent is given
<button value="accept"> yes Accept-all action button
<button value="reject"> no Reject-all action button (optional)
<input type="checkbox"> no Granular preference toggles (optional, for save mode)
<button value="save"> no Save granular preferences button (optional, used with checkboxes)

Child Attributes

AttributeOnValuesDescription
value button "accept", "reject", "save" Action button type — accept all, reject all, or save granular preferences
name input[type=checkbox] string Preference category name stored in the consent cookie

localStorage Format

Consent is stored as JSON under the configured key.

{ "preferences": { "necessary": true, "analytics": true, "marketing": false }, "action": "save", "timestamp": 1709913600000 }

The timestamp field enables expiry. When expires days have elapsed since the timestamp, the banner reappears on next page load.

Accessibility

Dialog Behavior

  • Bottom / Top: Uses dialog.show() (non-modal). The page remains interactive — users can scroll and navigate freely.
  • Center: Uses dialog.showModal() (modal). Focus is trapped inside the dialog. ESC is prevented to enforce a consent choice.

Keyboard

Key Action
Tab Move between buttons and checkboxes
Enter / Space Activate button or toggle checkbox
Escape Prevented in center (modal) position; no effect in bottom/top (non-modal)

Reduced Motion

When prefers-reduced-motion: reduce is set, the slide-in animation is disabled. The banner appears instantly.

No JavaScript

The consent-banner:not(:defined) CSS rule hides the banner until the component is defined. Without JavaScript, the banner never appears and no consent is required — a safe progressive enhancement default.

Scope

This is a client-side consent UI helper built around native <dialog>. It stores the user's decision in localStorage. If your project requires a broader compliance workflow (server-side enforcement, cookie-less tracking prevention, or jurisdiction-specific legal requirements), those concerns live outside this component.