Files
ariki-website/public/consent.js
T
jeremy aa3dd9f1fb
Deploy arikigame.com / deploy (push) Successful in 21s
Add privacy policy page and cookie consent banner
The site was loading Google Analytics 4 unconditionally with no consent
gate — non-essential cookies set before consent (PECR reg. 6), and no
privacy policy was published at all.

- Add /privacy — full Privacy and Cookie Policy from tinqs/legal,
  styled with the site's existing design tokens, no tracking on the page
- Gate GA4 behind opt-in consent (consent.js / consent.css); nothing
  loads until the visitor accepts, and declining clears any _ga cookies
- Add Privacy Policy + Cookie Settings links to the footer, patched in
  the footer component so they survive React hydration
- Cookie Settings reopens the banner so consent can be withdrawn as
  easily as it was given (UK GDPR Art. 7(3))
- Add /privacy to sitemap.xml

Verified with Playwright: zero googletagmanager requests before consent,
GA loads only after Accept, choice persists across reloads, withdrawal
works, no horizontal scroll on mobile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 14:24:17 -07:00

101 lines
3.7 KiB
JavaScript

/* Cookie consent — gates Google Analytics behind opt-in (PECR reg. 6 / UK GDPR).
No analytics script loads until the visitor actively chooses "Accept".
Choice is stored in localStorage under "ariki-consent" as "accepted" | "declined". */
(function () {
var KEY = 'ariki-consent';
var GA_ID = 'G-CMZ6T2LJ0W';
function stored() {
try { return localStorage.getItem(KEY); } catch (e) { return null; }
}
function remember(v) {
try { localStorage.setItem(KEY, v); } catch (e) {}
}
function loadAnalytics() {
if (window.__arikiGaLoaded) return;
window.__arikiGaLoaded = true;
var s = document.createElement('script');
s.async = true;
s.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_ID;
document.head.appendChild(s);
window.dataLayer = window.dataLayer || [];
function gtag() { window.dataLayer.push(arguments); }
window.gtag = gtag;
gtag('js', new Date());
gtag('config', GA_ID, { anonymize_ip: true });
}
function clearAnalyticsCookies() {
var host = location.hostname.replace(/^www\./, '');
document.cookie.split(';').forEach(function (c) {
var name = c.split('=')[0].trim();
if (!/^(_ga|_gid|_gat)/.test(name)) return;
['/', location.pathname].forEach(function (p) {
['', '.' + host, host].forEach(function (d) {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=' + p +
(d ? '; domain=' + d : '');
});
});
});
}
function decide(choice) {
remember(choice);
if (choice === 'accepted') loadAnalytics();
else clearAnalyticsCookies();
var b = document.getElementById('cookie-banner');
if (b) b.parentNode.removeChild(b);
}
function banner() {
var wrap = document.createElement('div');
wrap.id = 'cookie-banner';
wrap.setAttribute('role', 'dialog');
wrap.setAttribute('aria-live', 'polite');
wrap.setAttribute('aria-label', 'Cookie consent');
wrap.innerHTML =
'<div class="cookie-banner__inner">' +
'<p class="cookie-banner__text">We use analytics cookies to understand how the site is used. ' +
'They are optional — the site works fine without them. ' +
'See our <a href="/privacy">Privacy Policy</a>.</p>' +
'<div class="cookie-banner__actions">' +
'<button type="button" class="cookie-banner__btn cookie-banner__btn--ghost" data-consent="declined">Decline</button>' +
'<button type="button" class="cookie-banner__btn cookie-banner__btn--solid" data-consent="accepted">Accept</button>' +
'</div>' +
'</div>';
wrap.addEventListener('click', function (e) {
var t = e.target.closest('[data-consent]');
if (t) decide(t.getAttribute('data-consent'));
});
document.body.appendChild(wrap);
var first = wrap.querySelector('button');
if (first) first.focus({ preventScroll: true });
}
function start() {
var choice = stored();
if (choice === 'accepted') { loadAnalytics(); return; }
if (choice === 'declined') return;
banner();
/* The landing page is a hydrated React app: hydration can discard nodes that
were appended to <body> beforehand. Re-insert once if that happens. */
setTimeout(function () {
if (!stored() && !document.getElementById('cookie-banner')) banner();
}, 1200);
}
/* Let other pages re-open the banner (e.g. a "Cookie settings" link). */
window.arikiCookieSettings = function () {
try { localStorage.removeItem(KEY); } catch (e) {}
if (!document.getElementById('cookie-banner')) banner();
};
/* Wait for full load so React hydration has settled before we touch <body>. */
if (document.readyState === 'complete') {
start();
} else {
window.addEventListener('load', start);
}
})();