Restyle Klaro to actually match the brand, not just its accent colors

CSS-var overrides alone (colors, corner position) still left Klaro's
own border/shadow/spacing/typography, which read as an obviously
bolted-on library widget next to this site's hand-designed
components. New KlaroTheme component overrides Klaro's real DOM
classnames directly (confirmed against kiprotect/klaro's own scss
source) — kills the default border, adds this site's own soft-shadow
card look, restyles every button, uses the site's actual serif/sans
font pairing. Also warms up the notice/modal copy.

Updates both READMEs with the tracking-codes/Klaro/back-in-stock/
settings-search/DHL-shipment-label work from this session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-08-01 21:20:28 +00:00
parent a8ab3e4f44
commit f6b001dd11
3 changed files with 153 additions and 34 deletions
+58
View File
@@ -1796,6 +1796,64 @@ session (low-stock digest, stale-unverified-accounts report, weekly revenue
report, expired-discount-code cleanup) needed no monitor changes of their
own; see the Payload README's "Jobs Queue" section.
## Cookie consent, tracking codes & back-in-stock notifications
**Cookie consent** (added 2026-08-01) uses **Klaro** (`kiprotect/klaro`,
npm, self-hosted, open source) — a first hand-rolled `CookieBanner.tsx`/
`useConsent.ts`/`TrackingScripts.tsx` trio shipped the same day, then was
replaced with Klaro after real per-service consent lists and bundled
legally-grade German translations were judged worth the swap over
building all of that by hand. `no @types/klaro` package exists —
`types/klaro.d.ts` declares only the small surface actually used
(`setup`/`show`).
- `app/lib/klaroConfig.ts` — builds Klaro's config object dynamically
from the Payload backend's `tracking-codes` collection (see that
repo's README): one Klaro "service" per row, `purposes: [consentCategory]`.
Brand colors applied via Klaro's CSS-custom-property `styling`
overrides (`green1`/`red1`/`blue1`/`dark1`/`light1` etc. — confusingly
named: `dark1`/`light1` are the notice's background/text colors, not a
dark-mode switch; `blue1` colors the modal's "Einstellungen" button and
is easy to miss since it only shows once the modal itself opens, not
just the notice). Theme: compact bottom-left corner notice
(`theme: ["light", "bottom", "left"]`, `notice-max-width: 380px`), not
Klaro's full-width `wide` default.
- `app/lib/loadTrackingCode.ts` — the actual `<script>` injection side
effect (GA4/Facebook Pixel/GTM fixed loader snippets, `other` renders
the admin-supplied raw script), wired via each Klaro service's
`callback(consent)` in `klaroConfig.ts`, not declaratively rendered.
- `app/components/KlaroConsentManager.tsx` — mounts Klaro client-side
(dynamic `import()`, not a static import — Klaro touches
`window`/`document` at module-eval time, not SSR-safe) inside
`app/layout.tsx`. Renders nothing itself if there are zero active
`tracking-codes` rows (no banner needed with nothing to consent to).
Also renders a **persistent bottom-left cookie icon button** (fixed
position, calls `Klaro.show()`) so a visitor can reopen the consent
manager any time, not just on first visit.
- **Testing note**: a hard reload does NOT clear the `klaro-consent`
cookie (180-day expiry) — that's correct/expected behavior, not a bug.
To re-trigger the first-visit notice while testing, use the reopen
button, delete the cookie in DevTools, or an incognito window.
**Back-in-stock notifications**`NotifyMeForm.tsx` replaces the
disabled Add-to-cart button's slot entirely (not stacked below it) once a
product/variant is out of stock, POSTing to `/api/stock-notifications`
`app/lib/stockNotifications.ts` → the backend's `stock-notifications`
collection. Always shows the email input + submit button directly (no
collapse-to-reveal step). `AddToCartButton`/`AddToCartInlineButton` both
carry a `numericId` prop (`product.numericId`, the real Payload id — NOT
their own `id`/`productId` props, which are the commerce slug) threaded
through for this, same split `WishlistButton` already used.
`ProductGrid.tsx`/`MerklisteGrid.tsx`/`RelatedProducts.tsx` all use
`items-start` on their grid (not CSS Grid's stretch default) so an
out-of-stock card being taller than its siblings doesn't push their own
buttons out of alignment.
The email itself (`renderBackInStockHtml` in `app/lib/emailTemplates.ts`)
has its own dedicated Live Preview renderer, not the shared generic
order-status one — there's no order for this email type, so no fake
"Bestellnummer" line; CTA is "Zum Produkt", not "Bestellung ansehen".
## Tests
`npm run test:unit` (Vitest, `node` environment, no jsdom/Next.js runtime