Bonus
Cookie consent
Bottom privacy banner
Bottom privacy banner
Build a cookie-consent banner with Tailwind CSS: a fixed bottom bar with a short privacy message and a link, plus "Accept all" and "Reject" buttons. Keep it unobtrusive with a soft shadow and rounded corners. Then add the JavaScript that gives it meaning: record the choice and the timestamp in localStorage, hide the banner with a slide-down transition once a choice is made, skip showing it at all on later visits, wrap every storage call in try/catch so private-mode browsers do not throw, and dispatch a custom cookie-consent event carrying the decision so analytics or embeds can subscribe rather than being hard-wired into the banner. <div id="cookieBar" class="fixed inset-x-0 bottom-0 translate-y-0 p-4 transition-transform duration-300">
<div class="mx-auto flex max-w-3xl flex-col items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white p-4 shadow-lg sm:flex-row">
<p class="text-sm text-neutral-600">
We use cookies to improve your experience. Read our <a href="#" class="font-medium text-neutral-900 underline">privacy policy</a>.
</p>
<div class="flex shrink-0 gap-2">
<button data-consent="rejected" class="rounded-lg border border-neutral-200 px-4 py-2 text-sm font-medium text-neutral-700 hover:bg-neutral-50">Reject</button>
<button data-consent="accepted" class="rounded-lg bg-neutral-900 px-4 py-2 text-sm font-medium text-white hover:bg-neutral-800">Accept all</button>
</div>
</div>
</div> var STORAGE_KEY = "cookie-consent";
var bar = document.getElementById("cookieBar");
// Storage access throws in private mode and in sandboxed frames — never let a
// consent banner take the page down with it.
function read(key) {
try { return localStorage.getItem(key); } catch (e) { return null; }
}
function write(key, value) {
try { localStorage.setItem(key, value); } catch (e) {}
}
function decide(choice) {
write(STORAGE_KEY, JSON.stringify({ choice: choice, at: new Date().toISOString() }));
// Let the rest of the page react instead of wiring analytics in here.
document.dispatchEvent(new CustomEvent("cookie-consent", { detail: { choice: choice } }));
bar.classList.add("translate-y-full");
setTimeout(function () { bar.remove(); }, 300);
}
if (read(STORAGE_KEY)) {
bar.remove();
} else {
[].slice.call(bar.querySelectorAll("[data-consent]")).forEach(function (button) {
button.addEventListener("click", function () {
decide(button.getAttribute("data-consent"));
});
});
}
// Example subscriber — replace with your own loader.
document.addEventListener("cookie-consent", function (e) {
if (e.detail.choice === "accepted") console.log("Analytics may load now.");
});