Restyle Klaro banner to brand + add persistent reopen button

Was full-width ('wide' theme) with Klaro's default green/dark
palette, neither matching the brand. Now: compact bottom-left corner
notice (~380px), brand colors via Klaro's CSS-var overrides
(dark1/light1 etc. — confusingly named, they're background/text, not
a dark-mode switch), neutral gray decline button instead of red.

Also adds a persistent bottom-left cookie icon (KlaroConsentManager.tsx)
so a visitor can reopen the consent manager any time via Klaro.show(),
not just on first visit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-08-01 21:07:26 +00:00
parent 43ab5f2407
commit 72b7bc629c
3 changed files with 56 additions and 12 deletions
+28 -2
View File
@@ -1,6 +1,6 @@
"use client";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import "klaro/dist/klaro.css";
import type { TrackingCode } from "../lib/payload";
import { buildKlaroConfig } from "../lib/klaroConfig";
@@ -21,6 +21,12 @@ import { loadTrackingCode } from "../lib/loadTrackingCode";
// dynamic import()), paired with the "-no-css" JS build so the stylesheet
// isn't loaded twice.
export function KlaroConsentManager({ codes }: { codes: TrackingCode[] }) {
// Kept in state (not just called once) so the persistent reopen button
// below can call Klaro.show() itself, any time after the initial
// accept/reject decision — a visitor must always be able to revisit
// their choice, not just on first load.
const [klaroModule, setKlaroModule] = useState<typeof import("klaro/dist/klaro-no-css") | null>(null);
useEffect(() => {
// Nothing to ask consent for — don't even load/render Klaro. A cookie
// banner with zero services to list would just be visual noise.
@@ -31,6 +37,7 @@ export function KlaroConsentManager({ codes }: { codes: TrackingCode[] }) {
if (cancelled) return;
const config = buildKlaroConfig(codes, loadTrackingCode);
Klaro.setup(config);
setKlaroModule(Klaro);
});
return () => {
cancelled = true;
@@ -41,5 +48,24 @@ export function KlaroConsentManager({ codes }: { codes: TrackingCode[] }) {
// meant to be called more than once per page load anyway.
}, []);
return null;
if (!klaroModule) return null;
return (
<button
type="button"
onClick={() => klaroModule.show()}
aria-label="Cookie-Einstellungen öffnen"
title="Cookie-Einstellungen"
className="fixed bottom-5 left-5 z-40 flex h-11 w-11 items-center justify-center rounded-full border border-border bg-bg-base shadow-md hover:border-brand transition-colors"
>
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.6" />
<circle cx="9" cy="9.5" r="1.1" fill="currentColor" />
<circle cx="14" cy="8.5" r="1" fill="currentColor" />
<circle cx="15" cy="13.5" r="1.1" fill="currentColor" />
<circle cx="10.5" cy="14.5" r="1" fill="currentColor" />
<circle cx="12" cy="11" r="0.9" fill="currentColor" />
</svg>
</button>
);
}
+25 -10
View File
@@ -56,20 +56,35 @@ export function buildKlaroConfig(codes: TrackingCode[], onAccept: (code: Trackin
return {
version: 1,
elementID: "klaro",
// 'light' (not 'dark'), 'bottom' (matches the previous custom
// CookieBanner.tsx's own position), 'wide' (roomier than Klaro's
// narrow default notice, closer to the old banner's proportions).
// 'light' + 'bottom' + 'left' — a compact corner notice (Klaro's own
// default max-width, ~400px), not a full-width bar. 'wide' (tried
// first) stretched it across nearly the whole viewport, which read as
// too dominant compared to a typical bottom-left corner CMP.
// Extra keys beyond `theme` override individual CSS custom properties
// (Klaro's injectStyles() applies any non-'theme' `styling` key as a
// `--<key>` var) — green1 is what colors the "Alle akzeptieren"
// button (.cm-btn-success), so this is how the accept button gets the
// site's actual brand color instead of Klaro's default green.
// `--<key>` var, see node_modules/klaro's own utils/styling.js) — this
// is the actual brand-color pass: dark1/light1 are (confusingly
// named) the notice's background/text colors, not a dark-mode toggle;
// green1 colors the accept button, red1 the decline button (set
// neutral gray, not alarming red — this site's own secondary buttons
// are plain-bordered, never red, see NotifyMeForm.tsx).
styling: {
theme: ["light", "bottom", "wide"],
green1: "#f6a701", // --color-brand
"button-text-color": "#1a1a18", // --color-text-primary — dark text reads better on the brand orange than Klaro's default white
"border-radius": "6px",
theme: ["light", "bottom", "left"],
dark1: "#fffdf8", // --color-bg-paper — notice/modal background
dark2: "#e5e0d8", // --color-border
dark3: "#f3efe9",
light1: "#1a1a18", // --color-text-primary — notice/modal text
light2: "#6b6b69", // --color-text-muted
light3: "#1a1a18",
green1: "#f6a701", // --color-brand — accept button
green2: "#d99000",
red1: "#e5e0d8", // decline button — neutral bordered look, not red
red2: "#d1cabf",
"button-text-color": "#1a1a18",
"border-radius": "10px",
"border-width": "1px",
"font-family": "inherit",
"notice-max-width": "380px",
},
storageMethod: "cookie",
cookieName: "klaro-consent",
+3
View File
@@ -5,6 +5,9 @@
// codebase relies on).
declare module "klaro/dist/klaro-no-css" {
export function setup(config: unknown): void;
// Re-opens the consent modal on demand — used by KlaroConsentManager.tsx's
// persistent bottom-left reopen button.
export function show(config?: unknown, modal?: boolean): void;
}
declare module "klaro/dist/klaro.css";