Cookie Consent

wb-cookie-consent is the reusable public-site consent pattern for bottom banners, floating cards, and one shared wb-modal preference center. It stores consent state only; host projects still decide when analytics or marketing scripts actually load.

Pattern boundary Cookie Consent is not a primitive and not a separate overlay system. It is a reusable pattern composed from wb-card, layout primitives, buttons, form controls, and one shipped wb-modal.

Use this pattern when a public site, CMS surface, or product landing page needs one consistent consent flow that can be reopened later from a footer or settings link. Keep the markup explicit, keep the copy legal-reviewable, and keep script loading decisions in the host project.

Close does not save consent. Accept, Reject, or Save preferences stores the user choice.

Live demo

This page ships one live consent demo: the bottom banner plus one shared preference modal. Use the footer-style button below after you save consent.

When to use

  • Public websites that need a reusable consent banner
  • Products that need one shared preference center modal
  • Projects that must reopen cookie settings from the footer
  • Teams that want consent state stored consistently in localStorage
What it does not do This runtime does not decide whether Google Analytics, Meta Pixel, or other third-party code should execute. It only stores and exposes consent state so the host project can gate those integrations correctly.

Static previews

Static previews are inert; only the Live demo section contains active consent hooks. Copy the markup examples below when you need the real hooks.

Bottom banner preview

Recommended default for most public pages.

Floating card preview

Compact alternative for calmer marketing pages.

1. Bottom banner + preference modal

This is the recommended default. The banner owns the first decision, while one shared modal handles category-level preferences.

<div class="wb-cookie-consent wb-cookie-consent-banner"
     data-wb-cookie-consent
     hidden>
  <section class="wb-card wb-cookie-consent-card" aria-label="Cookie consent">
    <div class="wb-card-header wb-cluster wb-cluster-between wb-cluster-2 wb-items-start">
      <div class="wb-stack-1">
        <strong>We use cookies</strong>
      </div>
      <button class="wb-btn wb-btn-secondary wb-btn-icon wb-btn-sm" type="button" data-wb-cookie-consent-close aria-label="Close cookie settings"><i class="wb-icon wb-icon-x" aria-hidden="true"></i></button>
    </div>
    <div class="wb-card-body">
      <div class="wb-cluster wb-cluster-between wb-cluster-3 wb-items-center">
        <p class="wb-text-sm wb-text-muted wb-m-0">
          Necessary cookies keep the site running. Optional cookies help us remember preferences and understand usage.
        </p>
        <div class="wb-cluster wb-cluster-2">
          <button class="wb-btn wb-btn-secondary wb-btn-sm" type="button" data-wb-cookie-consent-reject>Reject</button>
          <button class="wb-btn wb-btn-secondary wb-btn-sm" type="button" data-wb-cookie-consent-open data-wb-target="#siteCookiePreferences">Customize</button>
          <button class="wb-btn wb-btn-primary wb-btn-sm" type="button" data-wb-cookie-consent-accept>Accept all</button>
        </div>
      </div>
    </div>
  </section>
</div>

<div class="wb-modal wb-cookie-consent-modal" id="siteCookiePreferences" data-wb-cookie-consent role="dialog" aria-modal="true" aria-labelledby="siteCookiePreferencesTitle">
  <div class="wb-modal-dialog">
    <div class="wb-modal-header">
      <h2 class="wb-modal-title" id="siteCookiePreferencesTitle">Cookie preferences</h2>
      <button class="wb-btn wb-btn-secondary wb-btn-icon wb-btn-sm" type="button" data-wb-cookie-consent-close aria-label="Close cookie settings"><i class="wb-icon wb-icon-x" aria-hidden="true"></i></button>
    </div>
    <div class="wb-modal-body">
      <div class="wb-stack-1">
        <p class="wb-text-sm wb-text-muted">
          Necessary cookies are always on. Optional categories can be changed at any time.
        </p>
      </div>
      <div class="wb-stack-3">
        <div class="wb-card wb-card-muted">
          <div class="wb-card-body wb-cluster wb-cluster-between wb-cluster-3 wb-items-center">
            <div class="wb-stack-1">
            <strong>Necessary</strong>
              <p class="wb-text-sm wb-text-muted wb-m-0">Required for core site behavior.</p>
            </div>
            <label class="wb-switch">
              <input type="checkbox" data-wb-cookie-category="necessary" data-wb-cookie-required="true" checked disabled>
              <span class="wb-switch-track"></span>
              <span>Always on</span>
            </label>
          </div>
        </div>
        <div class="wb-card wb-card-muted">
          <div class="wb-card-body wb-cluster wb-cluster-between wb-cluster-3 wb-items-center">
            <div class="wb-stack-1">
            <strong>Preferences</strong>
              <p class="wb-text-sm wb-text-muted wb-m-0">Remember language and interface choices.</p>
            </div>
            <label class="wb-switch">
              <input type="checkbox" data-wb-cookie-category="preferences">
              <span class="wb-switch-track"></span>
              <span>Allow</span>
            </label>
          </div>
        </div>
        <div class="wb-card wb-card-muted">
          <div class="wb-card-body wb-cluster wb-cluster-between wb-cluster-3 wb-items-center">
            <div class="wb-stack-1">
            <strong>Analytics</strong>
              <p class="wb-text-sm wb-text-muted wb-m-0">Measure usage and content performance.</p>
            </div>
            <label class="wb-switch">
              <input type="checkbox" data-wb-cookie-category="analytics">
              <span class="wb-switch-track"></span>
              <span>Allow</span>
            </label>
          </div>
        </div>
        <div class="wb-card wb-card-muted">
          <div class="wb-card-body wb-cluster wb-cluster-between wb-cluster-3 wb-items-center">
            <div class="wb-stack-1">
            <strong>Marketing</strong>
              <p class="wb-text-sm wb-text-muted wb-m-0">Support campaign attribution and ad measurement.</p>
            </div>
            <label class="wb-switch">
              <input type="checkbox" data-wb-cookie-category="marketing">
              <span class="wb-switch-track"></span>
              <span>Allow</span>
            </label>
          </div>
        </div>
      </div>
    </div>
    <div class="wb-modal-footer">
      <button class="wb-btn wb-btn-secondary" type="button" data-wb-cookie-consent-reject>Reject all</button>
      <div class="wb-cluster wb-cluster-2">
        <button class="wb-btn wb-btn-secondary" type="button" data-wb-cookie-consent-save>Save preferences</button>
        <button class="wb-btn wb-btn-primary" type="button" data-wb-cookie-consent-accept>Accept all</button>
      </div>
    </div>
  </div>
</div>

2. Floating card + preference modal

Use the same modal and behavior hooks, but swap the entry surface to a smaller floating card for promotional or editorial pages.

<div class="wb-cookie-consent wb-cookie-consent-floating"
     data-wb-cookie-consent
     hidden>
  <section class="wb-card wb-cookie-consent-card" aria-label="Cookie settings">
    <div class="wb-card-header wb-cluster wb-cluster-between wb-cluster-2 wb-items-start">
      <div class="wb-stack-1">
          <strong>Cookie settings</strong>
          <p class="wb-text-sm wb-text-muted wb-m-0">
            Necessary cookies keep the site working. Optional cookies help personalize and measure the experience.
          </p>
      </div>
      <button class="wb-btn wb-btn-secondary wb-btn-icon wb-btn-sm" type="button" data-wb-cookie-consent-close aria-label="Close cookie settings"><i class="wb-icon wb-icon-x" aria-hidden="true"></i></button>
    </div>
    <div class="wb-card-body wb-stack-3">
      <div class="wb-cluster wb-cluster-2">
        <button class="wb-btn wb-btn-secondary wb-btn-sm" type="button" data-wb-cookie-consent-reject>Reject</button>
        <button class="wb-btn wb-btn-secondary wb-btn-sm" type="button" data-wb-cookie-consent-open data-wb-target="#marketingCookiePreferences">Customize</button>
        <button class="wb-btn wb-btn-primary wb-btn-sm" type="button" data-wb-cookie-consent-accept>Accept all</button>
      </div>
    </div>
  </section>
</div>

<div class="wb-modal wb-cookie-consent-modal" id="marketingCookiePreferences" data-wb-cookie-consent role="dialog" aria-modal="true" aria-labelledby="marketingCookiePreferencesTitle" hidden>
  <div class="wb-modal-dialog">
    <div class="wb-modal-header">
      <h2 class="wb-modal-title" id="marketingCookiePreferencesTitle">Cookie preferences</h2>
      <button class="wb-btn wb-btn-secondary wb-btn-icon wb-btn-sm" type="button" data-wb-cookie-consent-close aria-label="Close cookie settings"><i class="wb-icon wb-icon-x" aria-hidden="true"></i></button>
    </div>
    <div class="wb-modal-body">...same category rows...</div>
  </div>
</div>

Close dismisses the card or modal for the current view only when no consent has been saved yet.

Storage keys and emitted event

Stored keys

wb-cookie-consent

Stores accepted, rejected, or custom.

wb-cookie-consent-preferences

Stores a JSON object with necessary, preferences, analytics, and marketing.

Change event

Whenever consent changes, WebBlocks emits wb:cookie-consent:change on document.documentElement.

document.documentElement.addEventListener('wb:cookie-consent:change', function (event) {
  var state = event.detail;

  if (state.preferences.analytics) {
    // Load analytics here.
  }
});

Public API

WBCookieConsent.open()
WBCookieConsent.close()
WBCookieConsent.get()
WBCookieConsent.set({
  necessary: true,
  preferences: true,
  analytics: false,
  marketing: false
})
WBCookieConsent.clear()
WBCookieConsent.hasConsent()

Integration guidance

  • Necessary cookies cannot be disabled
  • Site owners must adapt the legal copy for their own jurisdiction and policy text
  • Use layout primitives such as wb-stack, wb-cluster, wb-grid, and wb-split inside the pattern
  • Use the shipped modal primitive for the preference center instead of creating a second overlay family
Testing tip During development, call WBCookieConsent.clear() from the console to remove the stored keys and force the first-entry consent UI to appear again.