Component Library

← Back to gallery

Cookie consent banner

Category: Feedback · Tags: cookies, consent, privacy, banner, settings

How to use it

Use when a site sets optional cookies that need consent before they load; place it fixed at the bottom of the viewport and keep it until a choice is made. If you set only strictly necessary cookies, you do not need a consent banner. This demo stores nothing: connect the choice handler to your own consent record and do not load analytics or marketing scripts until they are accepted.

Accessibility

The banner is a <section> labelled by its heading, not a modal, so it never traps focus or hides the page. Accept all, Reject all and the Settings <summary> share one style and size, so no choice is visually favoured, and Settings is a native <details> disclosure that opens with Enter or Space and announces expanded or collapsed. The optional categories are checkboxes in a <fieldset> with a <legend>, and the outcome is written to a role="status" line. Text is #0f172a and #334155 on white (17.85:1, 10.35:1), the three choices #1d4ed8 on white (6.70:1, 8.01:1 hovered as #1e40af on #eff6ff), Save choices white on #1d4ed8 (6.70:1), all with a 2px #1d4ed8 focus outline.

Source (copy-ready)

<section class="dcl-cookie" aria-labelledby="dcl-cookie-title">
  <h2 class="dcl-cookie__title" id="dcl-cookie-title">Cookies on this site</h2>
  <p class="dcl-cookie__text">We use necessary cookies to make this site work. With your permission we would also use analytics cookies to learn which pages help people most, and marketing cookies to measure our campaigns.</p>
  <div class="dcl-cookie__choices">
    <button type="button" class="dcl-cookie__choice" data-choice="accept">Accept all</button>
    <button type="button" class="dcl-cookie__choice" data-choice="reject">Reject all</button>
    <details class="dcl-cookie__settings">
      <summary class="dcl-cookie__choice">Settings</summary>
      <fieldset class="dcl-cookie__fieldset">
        <legend class="dcl-cookie__legend">Choose optional cookies</legend>
        <label class="dcl-cookie__option"><input type="checkbox" checked disabled> Necessary (always on)</label>
        <label class="dcl-cookie__option"><input class="dcl-cookie__optional" type="checkbox" value="analytics"> Analytics</label>
        <label class="dcl-cookie__option"><input class="dcl-cookie__optional" type="checkbox" value="marketing"> Marketing</label>
        <button type="button" class="dcl-cookie__save" data-choice="save">Save choices</button>
      </fieldset>
    </details>
  </div>
  <p class="dcl-cookie__status" role="status">No choice made yet. This demo stores nothing, so it asks again on every visit.</p>
</section>
<style>
.dcl-cookie { max-width: 40rem; padding: 1rem 1.1rem; border: 1px solid #94a3b8; border-radius: 0.5rem; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-cookie__title { margin: 0 0 0.4rem; font-size: 1.1rem; color: #0f172a; background-color: #ffffff; }
.dcl-cookie__text { margin: 0 0 0.85rem; color: #334155; background-color: #ffffff; }
.dcl-cookie__choices { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.5rem; }
.dcl-cookie__choice { display: inline-block; box-sizing: border-box; min-width: 7.5rem; padding: 0.5rem 1rem; border: 2px solid #1d4ed8; border-radius: 6px; font: inherit; font-weight: 600; text-align: center; cursor: pointer; color: #1d4ed8; background-color: #ffffff; }
.dcl-cookie__choice:hover { color: #1e40af; background-color: #eff6ff; }
.dcl-cookie__choice:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-cookie__settings { min-width: 0; }
.dcl-cookie__settings[open] { flex-basis: 100%; }
.dcl-cookie__fieldset { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.6rem 0 0; padding: 0.75rem; border: 1px solid #cbd5e1; border-radius: 6px; color: #0f172a; background-color: #ffffff; }
.dcl-cookie__legend { padding: 0 0.3rem; font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-cookie__option { display: flex; align-items: center; gap: 0.5rem; color: #0f172a; background-color: #ffffff; }
.dcl-cookie__option input:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-cookie__save { align-self: flex-start; padding: 0.45rem 0.9rem; border: none; border-radius: 6px; font: inherit; font-weight: 600; cursor: pointer; color: #ffffff; background-color: #1d4ed8; }
.dcl-cookie__save:hover { color: #ffffff; background-color: #1e40af; }
.dcl-cookie__save:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-cookie__status { margin: 0.85rem 0 0; font-size: 0.9rem; color: #334155; background-color: #ffffff; }
</style>
<script>
(function () {
  document.querySelectorAll(".dcl-cookie").forEach(function (root) {
    if (root.dataset.dclBound) { return; }
    root.dataset.dclBound = "true";
    var status = root.querySelector(".dcl-cookie__status");
    var optional = root.querySelectorAll(".dcl-cookie__optional");
    var details = root.querySelector(".dcl-cookie__settings");
    function report(text) { if (status) { status.textContent = text + " Nothing has been stored."; } }
    root.querySelectorAll("button[data-choice]").forEach(function (button) {
      button.addEventListener("click", function () {
        var choice = button.dataset.choice;
        if (choice === "accept" || choice === "reject") {
          optional.forEach(function (box) { box.checked = choice === "accept"; });
        }
        var accepted = [];
        optional.forEach(function (box) { if (box.checked) { accepted.push(box.value); } });
        if (details) { details.open = false; if (choice === "save") { var summary = details.querySelector("summary"); if (summary) { summary.focus(); } } }
        report(accepted.length ? "Choice: necessary and " + accepted.join(" and ") + " cookies." : "Choice: necessary cookies only.");
      });
    });
  });
})();
</script>

Was this useful? Send feedback.