First draft.

This commit is contained in:
2026-06-14 11:14:34 +02:00
parent c1b8e161fc
commit cb07db14dc
10 changed files with 269 additions and 1 deletions
+83 -1
View File
@@ -1,3 +1,85 @@
# hugo-cookie-consent # hugo-cookie-consent
Hugo module that implements a cookie-consent message and functionality to change the cookie-consent decision. **Hugo module that implements a cookie-consent message and functionality to change
the cookie-consent decision.**
The purpose of this module is to help make a site [GDPR][] compliant. It can
optionally insert tracking code that calls a [Matomo][] instance. This will only
happen after visitors have accepted the use of a cookie -- no logging will take
place before visitors have interacted with the cookie information message. Their
decision is stored in the browser's Local Storage, and it is _not_ personal data
as defined by the GDPR.
This Hugo Module requires Hugo [v0.146.0][]+ which uses the new layout directory
structure with `_partials` and `_shortcode` directories with leading underscores.
[Anthropic Claude][] helped in creating this module (paid subscription).
## Installation in your Hugo site
1. Turn the Hugo site into a Hugo module:
```bash
hugo mod init <identifier>
```
Where `<identifier>` may be the path to the site's Git repository or anything
else; see [Hugo's docs][mod-doc].
2. Add the hugo-cookie-consent module by adding this `module.imports.path` to your
site configuration file:
```yaml
# config/_default/hugo.yaml
module:
imports:
- path: git.bovender.de/daniel/hugo-cookie-consent
```
3. Add configuration keys (see [below](#configuration)).
4. If your site's language is neither English nor German, add a few terms in the
desired language to the language tables.
5. Make your pages load the JavaScript code, e. g. by adding this to a `script.html`
partial:
```html
{{- $js := resources.Get "js/cookie-consent.js" | minify -}}
<script src="{{ $js.RelPermalink }}" defer></script>
{{- $js := resources.Get "js/tracker-code.js" | minify -}}
<script src="{{ $js.RelPermalink }}" defer></script>
```
6. Optionally, insert a `cookie-settings` partial somewhere on your site to enable
visitors to review and revise their choice. The privacy statement page might
be a good place for this.
```md
{{< cookie-settings >}}
```
## Configuration
You can configure the module by defining site parameters in your
`hugo.yaml` or `hugo.toml` configuration file. The following
snippet shows the default values.
```yaml
# config/_default/params.yaml
hugo_cookie_consent:
privacy_policy_url:
enable_matomo: false
matomo_host:
matomo_site_id:
```
| Parameter | Description |
|-----------|-------------|
| `privacy_policy_url` | URL of the privacy policy page
[anthropic claude]: https://claude.ai
[gdpr]: https://en.wikipedia.org/wiki/GDPR
[matomo]: https://matomo.org
[mod-doc]: https://gohugo.io/hugo-modules/use-modules/#import
[v0.146.0]: https://github.com/gohugoio/hugo/releases/tag/v0.146.0
+51
View File
@@ -0,0 +1,51 @@
(function () {
var KEY = 'cookie_consent';
var banner = document.getElementById('cookie-banner');
if (!banner) return;
updateStatus();
var consent = localStorage.getItem(KEY);
if (!consent) banner.style.display = 'flex';
function dismiss() {
banner.classList.add('fade-out');
setTimeout(function () { banner.style.display = 'none'; }, 500);
}
function updateStatus() {
var el = document.getElementById('cookie-status');
if (!el) return;
var consent = localStorage.getItem(KEY);
el.textContent = consent === 'accepted' ? el.dataset.accepted
: consent === 'declined' ? el.dataset.declined
: el.dataset.none;
}
document.getElementById('cookie-accept').onclick = function () {
localStorage.setItem(KEY, 'accepted');
updateStatus();
dismiss();
window.dispatchEvent(new Event('cookieAccepted'));
};
document.getElementById('cookie-decline').onclick = function () {
localStorage.setItem(KEY, 'declined');
updateStatus();
dismiss();
};
window.reopenCookieBanner = function () {
localStorage.removeItem(KEY);
updateStatus();
banner.classList.remove('fade-out');
banner.style.display = 'flex';
};
window.addEventListener('cookieAccepted', function () {
if (typeof window.trackVisit === 'function') window.trackVisit();
});
if (localStorage.getItem(KEY) === 'accepted') {
if (typeof window.trackVisit === 'function') window.trackVisit();
}
})();
+24
View File
@@ -0,0 +1,24 @@
(function () {
function trackVisit() {
var banner = document.getElementById('cookie_banner');
if (!banner) return;
var matomoUrl = banner.dataset.matomoUrl;
var siteId = banner.dataset.matomoSiteId;
if (!matomoUrl || !siteId) return;
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
_paq.push(['setTrackerUrl', matomoUrl + 'matomo.php']);
_paq.push(['setSiteId', siteId]);
var d = document, g = d.createElement('script'),
s = d.getElementsByTagName('script')[0];
g.async = true; g.src = matomoUrl + 'matomo.js';
s.parentNode.insertBefore(g, s);
}
window.addEventListener('cookieAccepted', trackVisit);
if (localStorage.getItem('cookie_consent') === 'accepted') {
trackVisit();
}
})();
+39
View File
@@ -0,0 +1,39 @@
#cookie-banner {
position: fixed;
top: 2.5rem;
left: 50%;
transform: translateX(-50%);
width: min(480px, calc(100vw - 2rem));
background: #fff;
border: 1px solid #e2e2e2;
border-radius: 12px;
padding: 1rem 1.25rem;
box-shadow: 0 4px 24px rgba(0,0,0,.08);
display: flex;
align-items: center;
gap: 1rem;
z-index: 9999;
transition: opacity 0.5s ease;
}
#cookie-banner.fade-out {
opacity: 0;
pointer-events: none;
}
#cookie-banner p { margin: 0; font-size: 14px; flex: 1; }
#cookie-banner button {
margin: 0.1rem 0;
padding: .45rem 1rem;
border-radius: 8px;
border: 1px solid #ccc;
cursor: pointer;
font-size: 13px;
white-space: nowrap;
}
#cookie-accept {
background: #0b913a; color: #fff; border-color: #1a1a1a;
}
+5
View File
@@ -0,0 +1,5 @@
hugo_cookie_consent:
privacy_policy_url:
enable_matomo: false
matomo_host:
matomo_site_id:
View File
+18
View File
@@ -0,0 +1,18 @@
site_wants_cookie:
other: Diese Website möchte ein Cookie speichern.
cookie_more_info:
other: Mehr darüber.
cookie_button_accept:
other: Akzeptieren
cookie_button_decline:
other: Ablehnen
cookie_status:
other: Aktueller Status
cookie_accepted:
other: Akzeptiert
cookie_declined:
other: Abgelehnt
cookie_none:
other: Noch keine Entscheidung
cookie_change:
other: Cookie-Einstellungen ändern
+18
View File
@@ -0,0 +1,18 @@
site_wants_cookie:
other: This site would like to store a cookie.
cookie_more_info:
other: More about this.
cookie_button_accept:
other: Accept
cookie_button_decline:
other: Decline
cookie_status:
other: Current status
cookie_accepted:
other: Accepted
cookie_declined:
other: Declined
cookie_none:
other: No decision yet
cookie_change:
other: Change cookie preferences
+19
View File
@@ -0,0 +1,19 @@
<div id="cookie-banner" style="display:none" role="dialog"
aria-label="Cookie consent" aria-live="polite"
data-matomo-url="{{ .Site.Params.hugo_cookie_consent.matomo_host }}"
data-matomo-site-id="{{ .Site.Params.hugo_cookie_consent_matomo_site_id }}" >
<p>
{{ i18n "site_wants_cookie" }}
<a href="{{ .Site.Params.hugo_cookie_consent.privacy_policy_url }}">
{{ i18n "cookie_more_info" }}
</a>
</p>
<div>
<button id="cookie-accept">
{{ i18n "cookie_button_accept" }}
</button>
<button id="cookie-decline">
{{ i18n "cookie_button_decline" }}
</button>
</div>
</div>
+12
View File
@@ -0,0 +1,12 @@
<p>
{{ i18n "cookie_status" }}:
<strong id="cookie_status"
data-accepted="{{ i18n "cookie_accepted" }}"
data-declined="{{ i18n "cookie_declined" }}"
data-none="{{ i18n "cookie_none" }}">
&mdash;
</strong>
</p>
<button onclick="reopenCookieBanner()">
{{ i18n "cookie_change" }}
</button>