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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user