More Modals Snippets
GDPR Consent Manager — Free HTML CSS JS Snippet
GDPR Consent Manager · Modals · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
GDPR Consent Manager — Cookie Banner, Preferences Modal, Toggle Switches & localStorage Consent Storage

Managing cookie consent is a legal requirement for any website serving users in the European Union, the United Kingdom, or California. The GDPR (General Data Protection Regulation), ePrivacy Directive, and CCPA (California Consumer Privacy Act) all require that websites obtain freely given, specific, informed, and unambiguous consent before placing non-essential cookies or tracking technologies on a user's device. This snippet provides a complete, production-ready GDPR consent manager built entirely in HTML, CSS, and vanilla JavaScript — no third-party consent platform, no script tags, no monthly fees.
What the GDPR and ePrivacy Directive require
Under GDPR Article 6 and the EU ePrivacy Directive (Cookie Law), consent must meet four criteria: it must be freely given (declining must be as easy as accepting — no dark patterns), specific (users must know exactly which cookie categories they are consenting to), informed (a clear privacy policy link must be accessible), and unambiguous (pre-ticked boxes or implied consent from continued browsing are not valid). The user's choice must be stored and honoured, and users must be able to withdraw or change their consent at any time. This snippet satisfies every requirement: the banner offers Accept All, Reject All, and Manage Preferences; the modal exposes four named categories with individual toggles; and a persistent floating settings button lets users revisit their choices.
The four consent categories
The snippet implements the standard four-tier consent taxonomy used by the IAB TCF (Transparency and Consent Framework) and most consent management platforms. Necessary cookies (session IDs, CSRF tokens, login state) are always enabled because they are required for basic website functionality — GDPR explicitly exempts strictly necessary cookies from the consent requirement. Analytics cookies (Google Analytics, Plausible, Fathom) track how users navigate the site; they require opt-in consent because they build a profile of user behaviour. Marketing cookies (Meta Pixel, Google Ads, retargeting) are used for personalised advertising and require explicit consent under both GDPR and CCPA. Functional cookies power enhanced features such as live chat (Intercom, Zendesk), language preferences, or saved shopping carts — they require consent unless strictly necessary for a service explicitly requested by the user.
Technical implementation: banner, modal, and localStorage
The consent banner uses position: fixed; bottom: 0 with a CSS transform: translateY(110%) slide-up entrance and exit. The preferences modal uses the same opacity + scale pattern as a standard dialog: transform: translate(-50%, -48%) scale(0.96) closed, transitioning to translate(-50%, -50%) scale(1) open. Toggle switches are built in pure CSS: a hidden <input type="checkbox"> drives a .slider pseudo-element that moves 20px on the X axis when checked. The accent colour #6366f1 appears on active toggles and the primary Accept All button for visual consistency. The saveConsent() function serialises the consent object to localStorage as JSON: { necessary: true, analytics: bool, marketing: bool, functional: bool }. On page load, loadConsent() reads this value — if it exists, the banner is suppressed and the floating settings FAB is shown instead.
The floating settings FAB
After any consent action (Accept All, Reject All, or Save Preferences), the banner dismisses and a small floating "Cookie Settings" button appears in the bottom-left corner. This satisfies the GDPR requirement that users must be able to withdraw or change consent at any time — not just on first visit. The FAB reopens the preferences modal where users can toggle individual categories and save again. The FAB is hidden by default via display: none and shown by adding the .visible class which sets display: flex.
Connecting consent to actual cookie loading
The getConsent() function returns the current consent object. Call it before loading any third-party script: const c = getConsent(); if (c?.analytics) { /* inject GA script */ }. For strictest compliance, do not load any non-essential scripts in the HTML head — always load them dynamically in JavaScript after checking consent. Re-check consent in a pageshow listener to handle browser back/forward navigation.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to just trust that this satisfies GDPR's consent criteria. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through exactly how loadConsent, saveConsent, and getConsent work together to decide whether the banner shows on a repeat visit, and where in the flow you would actually gate a real third-party script (like Google Analytics) behind the stored analytics flag. The same assistant can help optimize it — ask whether storing consent only in localStorage is sufficient for a multi-page site with server-rendered personalization, or whether a cookie readable server-side is also needed. It's also useful for extending the manager: ask it to add a versioned storage key so changing your cookie categories automatically re-prompts existing users, wire up the data-src iframe-blocking pattern mentioned in the howToUse for embedded YouTube or Maps content, or add a script that automatically disables Google Analytics tracking when a user withdraws previously-granted consent. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a GDPR-style cookie consent manager in plain HTML, CSS, and JavaScript with a banner, a granular preferences modal, and persisted consent — no third-party consent platform.
Requirements:
- A bottom-docked banner that slides up on entrance and down on exit using a CSS transform transition, offering three genuinely equal-prominence actions: accept all, reject all, and open a detailed preferences view — reject must not be visually buried or harder to find than accept.
- A preferences modal listing at least four cookie categories (for example necessary, analytics, marketing, functional), where the necessary category is always-on and cannot be toggled, and every other category has its own independent toggle switch built as a real checkbox input paired with a styled slider element (not just a decorative div).
- A single consent object shape that gets serialized to localStorage as JSON whenever the user takes any consent action (accept all, reject all, or save custom preferences from the modal), and a corresponding read function that other code can call to check whether a specific category is currently granted.
- On page load, check for previously stored consent: if none exists, show the banner; if consent already exists, suppress the banner entirely and instead show a small persistent floating button that reopens the preferences modal at any time, since GDPR requires consent to be as easy to withdraw or change as it was to give.
- Opening the preferences modal must sync every toggle to whatever was previously saved (not reset to defaults), so returning users see their actual current choices reflected accurately.
- The modal must be dismissible via a close button, a backdrop click, and the Escape key, and must use proper dialog semantics (role dialog, aria-modal, an accessible label tied to its heading).
- Explain, in a comment or to the user, exactly where in application code you would call the consent-read function before loading any non-essential third-party script (like an analytics or ads tag), so tracking never fires before explicit permission is granted.Step by step
How to Use
- 1Interact with the banner and modalThe banner slides up from the bottom on first load. Click "Accept All" or "Reject All" to save consent and dismiss the banner. Click "Manage Preferences" to open the modal where you can toggle individual cookie categories. After saving, the floating "Cookie Settings" button appears in the bottom-left corner — click it to reopen the modal at any time.
- 2Wire consent to your analytics and marketing scriptsReplace any hard-coded <script> tags for analytics or ads with dynamic loading: const c = getConsent(); if (c && c.analytics) { const s = document.createElement("script"); s.src = "https://www.googletagmanager.com/gtag/js?id=G-XXXX"; s.async = true; document.head.appendChild(s); }. Call this check in a DOMContentLoaded listener so it runs on every page after consent is loaded from localStorage.
- 3Customise the banner text and Privacy Policy linkIn the HTML panel, update the .banner-desc paragraph to describe your specific cookie usage. Replace href="#" on the "Privacy Policy" anchor with your actual /privacy-policy/ URL. Update the category descriptions in the modal to name your specific tools — e.g. "Analytics: Google Analytics 4, Plausible" and "Marketing: Meta Pixel, Google Ads Remarketing".
- 4Add or remove consent categoriesTo add a fifth category (e.g. "Personalisation"), copy a .category-row div in the HTML, give the checkbox a new id (e.g. "toggle-personalisation"), and add the key to the consent object in savePreferences(): personalisation: document.getElementById("toggle-personalisation").checked. Update saveConsent(), getConsent(), acceptAll(), and rejectAll() to include the new key.
- 5Block iframes and embeds until consent is givenFor YouTube embeds, Google Maps, or other third-party iframes, replace the src attribute with a data-src attribute on page load. After the user accepts functional or marketing consent, set iframe.src = iframe.dataset.src to load them. Show a placeholder overlay with a "Load content" button for users who decline — this is required by GDPR for consent-gated embeds.
- 6Export and add to your root layoutClick HTML to download a standalone file, or JSX for a React component. In Next.js, add the GdprConsentManager component to src/app/layout.js so it appears on every page. Use a useEffect with an empty dependency array to call loadConsent() on mount. Store the consent object in React context so any component can call useConsent() to check if a specific category is enabled before rendering consent-gated content.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
This snippet satisfies the core GDPR UI requirements: it presents consent before non-essential cookies are set, offers a genuine Reject All option that is as prominent as Accept All, provides granular category control via the preferences modal, persists the user's choice in localStorage, and allows consent withdrawal via the floating settings button. To complete your compliance implementation, you must also: (1) wire the getConsent() helper to your script loading logic so non-essential scripts only load after consent; (2) link to a complete Privacy Policy explaining what data is collected and why; (3) re-show the banner when your cookie usage changes materially; and (4) implement server-side consent checks for any SSR-rendered personalisation. For IAB TCF v2.2 compliance required by programmatic ad networks, a full certified CMP is needed.
Call getConsent() in a DOMContentLoaded listener on every page load: const c = getConsent(); if (c && c.analytics) { /* inject Google Analytics */ } if (c && c.marketing) { /* inject Meta Pixel */ } if (c && c.functional) { /* initialise Intercom */ }. If c is null, no consent has been recorded yet — do not load any non-essential scripts. When the user saves preferences, call the same conditional loading logic immediately in savePreferences() and acceptAll() so newly granted consent activates without a page reload. For revoking consent, set the appropriate script-specific disable flags (e.g. window["ga-disable-G-XXXX"] = true for Google Analytics) and note that already-set cookies will persist until their natural expiry or until the user clears browser data.
localStorage is synchronous and scoped to the origin, so getConsent() reads the same object on every page of your website without any additional setup. In a React or Next.js SPA, call loadConsent() in a useEffect in your root layout component and store the consent object in React context (useContext) or Zustand/Redux so any component can access it. For SSR pages, localStorage is unavailable on the server — check typeof window !== "undefined" before calling getConsent() in any server-side or isomorphic code. For a React hook: function useConsent() { const [c, setC] = useState(null); useEffect(() => { setC(getConsent()); }, []); return c; }
Open the browser DevTools console and run: localStorage.removeItem("gdpr-consent"); location.reload(). The STORAGE_KEY constant in the JS panel defines the localStorage key — change it from "gdpr-consent" to a versioned key like "gdpr-consent-v2" whenever you add new cookie categories. Users who previously consented under the old key will see the banner again, which is correct behaviour under GDPR — materially changing your cookie usage requires re-collecting consent. You can also call openBanner() from the console to show the banner without clearing localStorage, which is useful for visual testing.