22
33import type React from "react"
44import { useEffect , useCallback } from "react"
5- import { ConsentManagerProvider , useConsentManager } from "@c15t/nextjs/client"
6- import { CookieBanner , ConsentManagerDialog } from "@c15t/nextjs"
5+ import {
6+ ConsentBanner ,
7+ ConsentDialog ,
8+ ConsentManagerProvider ,
9+ type ConsentManagerOptions ,
10+ type Theme ,
11+ useConsentManager ,
12+ } from "@c15t/nextjs"
13+ import "@c15t/nextjs/styles.css"
714
815const backendURL = process . env . NEXT_PUBLIC_C15T_URL
916
10- const darkButton = {
11- "--button-background-color" : "hsl(212 30% 15%)" ,
12- "--button-background-color-hover" : "hsl(212 30% 20%)" ,
13- "--button-text" : "hsl(0 0% 97%)" ,
14- "--button-text-hover" : "hsl(0 0% 97%)" ,
15- "--button-border-color" : "hsl(213 19% 25%)" ,
16- "--button-border-radius" : "0.625rem" ,
17- "--button-neutral" : "hsl(213 19% 25%)" ,
18- "--button-neutral-hover" : "hsl(213 19% 30%)" ,
19- "--button-neutral-soft" : "hsl(212 30% 15%)" ,
20- "--button-neutral-soft-dark" : "hsl(212 30% 15%)" ,
21- "--button-shadow" : "none" ,
22- "--button-shadow-dark" : "none" ,
23- "--button-shadow-neutral" : "none" ,
24- "--button-shadow-neutral-dark" : "none" ,
25- "--button-shadow-neutral-hover" : "none" ,
26- "--button-shadow-neutral-hover-dark" : "none" ,
27- "--button-shadow-neutral-focus" : "0 0 0 2px hsl(163 69% 47%)" ,
28- "--button-shadow-neutral-focus-dark" : "0 0 0 2px hsl(163 69% 47%)" ,
29- "--button-focus-ring" : "0 0 0 2px hsl(163 69% 47%)" ,
30- } as const
31-
32- const acceptButton = {
33- "--button-background-color" : "hsl(163 69% 47%)" ,
34- "--button-background-color-dark" : "hsl(163 69% 47%)" ,
35- "--button-text" : "hsl(204 100% 3%)" ,
36- "--button-text-dark" : "hsl(204 100% 3%)" ,
37- "--button-text-hover" : "hsl(204 100% 3%)" ,
38- "--button-text-hover-dark" : "hsl(204 100% 3%)" ,
39- "--button-border-color" : "hsl(163 69% 47%)" ,
40- "--button-border-radius" : "0.625rem" ,
41- "--button-neutral-soft" : "hsl(163 69% 47%)" ,
42- "--button-neutral-soft-dark" : "hsl(163 69% 47%)" ,
43- "--button-shadow" : "none" ,
44- "--button-shadow-dark" : "none" ,
45- "--button-shadow-neutral" : "none" ,
46- "--button-shadow-neutral-dark" : "none" ,
47- "--button-shadow-neutral-hover" : "none" ,
48- "--button-shadow-neutral-hover-dark" : "none" ,
49- "--button-shadow-neutral-focus" : "0 0 0 2px hsl(163 69% 47%)" ,
50- "--button-shadow-neutral-focus-dark" : "0 0 0 2px hsl(163 69% 47%)" ,
51- "--button-focus-ring" : "0 0 0 2px hsl(163 69% 47%)" ,
52- } as const
53-
54- const customizeButton = {
55- "--button-background-color" : "hsl(212 30% 15%)" ,
56- "--button-background-color-dark" : "hsl(212 30% 15%)" ,
57- "--button-text" : "hsl(213 14% 70%)" ,
58- "--button-text-dark" : "hsl(213 14% 70%)" ,
59- "--button-border-color" : "hsl(213 19% 25%)" ,
60- "--button-border-radius" : "0.625rem" ,
61- "--button-primary" : "hsl(213 14% 70%)" ,
62- "--button-primary-dark" : "hsl(213 14% 70%)" ,
63- "--button-primary-hover" : "hsl(213 14% 80%)" ,
64- "--button-primary-hover-dark" : "hsl(213 14% 80%)" ,
65- "--button-shadow" : "none" ,
66- "--button-shadow-dark" : "none" ,
67- "--button-shadow-primary" : "inset 0 0 0 1px hsl(213 19% 25%)" ,
68- "--button-shadow-primary-dark" : "inset 0 0 0 1px hsl(213 19% 25%)" ,
69- "--button-shadow-primary-hover" : "inset 0 0 0 1px hsl(213 19% 30%)" ,
70- "--button-shadow-primary-hover-dark" : "inset 0 0 0 1px hsl(213 19% 30%)" ,
71- "--button-shadow-primary-focus" : "0 0 0 2px hsl(163 69% 47%)" ,
72- "--button-shadow-primary-focus-dark" : "0 0 0 2px hsl(163 69% 47%)" ,
73- "--button-focus-ring" : "0 0 0 2px hsl(163 69% 47%)" ,
74- } as const
75-
76- const saveButton = {
77- "--button-background-color" : "hsl(163 69% 47%)" ,
78- "--button-background-color-dark" : "hsl(163 69% 47%)" ,
79- "--button-text" : "hsl(204 100% 3%)" ,
80- "--button-text-dark" : "hsl(204 100% 3%)" ,
81- "--button-border-color" : "hsl(163 69% 47%)" ,
82- "--button-border-radius" : "0.625rem" ,
83- "--button-primary" : "hsl(204 100% 3%)" ,
84- "--button-primary-dark" : "hsl(204 100% 3%)" ,
85- "--button-primary-hover" : "hsl(204 100% 3%)" ,
86- "--button-primary-hover-dark" : "hsl(204 100% 3%)" ,
87- "--button-shadow" : "none" ,
88- "--button-shadow-dark" : "none" ,
89- "--button-shadow-primary" : "none" ,
90- "--button-shadow-primary-dark" : "none" ,
91- "--button-shadow-primary-hover" : "none" ,
92- "--button-shadow-primary-hover-dark" : "none" ,
93- "--button-shadow-primary-focus" : "0 0 0 2px hsl(163 69% 47%)" ,
94- "--button-shadow-primary-focus-dark" : "0 0 0 2px hsl(163 69% 47%)" ,
95- "--button-focus-ring" : "0 0 0 2px hsl(163 69% 47%)" ,
17+ /**
18+ * Dark palette carried over from the c15t 1.x CSS-variable theme.
19+ *
20+ * 2.x replaced the per-slot `--banner-*` / `--button-*` / `--dialog-*` variables
21+ * with semantic design tokens that resolve to `--c15t-*` custom properties, so
22+ * the old values are re-expressed as tokens here.
23+ */
24+ const palette = {
25+ primary : "hsl(163 69% 47%)" ,
26+ primaryHover : "hsl(163 69% 47%)" ,
27+ textOnPrimary : "hsl(204 100% 3%)" ,
28+ surface : "hsl(212 36% 10%)" ,
29+ surfaceHover : "hsl(212 36% 10%)" ,
30+ border : "hsl(213 19% 25%)" ,
31+ borderHover : "hsl(213 19% 30%)" ,
32+ text : "hsl(0 0% 97%)" ,
33+ textMuted : "hsl(213 14% 70%)" ,
34+ overlay : "rgba(0,0,0,0.6)" ,
35+ switchTrack : "hsl(213 19% 25%)" ,
36+ switchTrackActive : "hsl(163 69% 47%)" ,
9637} as const
9738
39+ // The app renders a single dark theme and sets no `.dark` class, so the same
40+ // palette is applied to both schemes and the scheme is pinned to dark.
9841const theme = {
99- "banner.card" : {
100- style : {
101- "--banner-background-color" : "hsl(212 36% 10%)" ,
102- "--banner-border-color" : "hsl(213 19% 25%)" ,
103- "--banner-border-radius" : "0.875rem" ,
104- "--banner-shadow" : "0 8px 32px rgba(0,0,0,0.4)" ,
105- } ,
106- } ,
107- "banner.header.title" : {
108- style : { "--banner-title-color" : "hsl(0 0% 97%)" } ,
109- } ,
110- "banner.header.description" : {
111- style : { "--banner-description-color" : "hsl(213 14% 70%)" } ,
42+ colors : palette ,
43+ dark : palette ,
44+ typography : { fontFamily : "var(--font-body), sans-serif" } ,
45+ radius : { md : "0.625rem" , lg : "0.875rem" } ,
46+ shadows : { sm : "none" , md : "none" , lg : "0 8px 32px rgba(0,0,0,0.4)" } ,
47+ consentActions : {
48+ accept : { variant : "primary" , mode : "filled" } ,
49+ reject : { variant : "neutral" , mode : "stroke" } ,
50+ customize : { variant : "neutral" , mode : "stroke" } ,
11251 } ,
113- "banner.footer" : {
114- style : { "--banner-footer-background-color" : "hsl(212 36% 10%)" } ,
115- } ,
116- "banner.footer.accept-button" : { style : acceptButton } ,
117- "banner.footer.reject-button" : { style : darkButton } ,
118- "banner.footer.customize-button" : { style : customizeButton } ,
119- "banner.overlay" : {
120- style : { "--banner-overlay-background-color" : "rgba(0,0,0,0.6)" } ,
121- } ,
122- "dialog.root" : {
123- style : {
124- "--dialog-font-family" : "var(--font-body), sans-serif" ,
125- "--dialog-background-color" : "hsl(212 36% 10%)" ,
126- "--dialog-foreground-color" : "hsl(0 0% 97%)" ,
127- "--dialog-border-color" : "hsl(213 19% 25%)" ,
128- "--dialog-stroke-color" : "hsl(213 19% 25%)" ,
129- "--dialog-muted-color" : "hsl(213 14% 70%)" ,
130- "--dialog-card-radius" : "0.875rem" ,
131- "--dialog-card-shadow" : "0 8px 32px rgba(0,0,0,0.4)" ,
132- "--dialog-overlay-background-color" : "rgba(0,0,0,0.6)" ,
133- "--dialog-link-text-color" : "hsl(0 0% 97%)" ,
134- "--widget-background-color" : "hsl(212 36% 10%)" ,
135- "--widget-border-color" : "hsl(213 19% 25%)" ,
136- "--widget-text-color" : "hsl(0 0% 97%)" ,
137- "--widget-text-muted-color" : "hsl(213 14% 70%)" ,
138- "--widget-link-text-color" : "hsl(0 0% 97%)" ,
139- "--widget-footer-background-color" : "hsl(212 36% 10%)" ,
140- "--widget-accordion-background-color" : "hsl(213 27% 18%)" ,
141- "--widget-accordion-background-hover" : "hsl(213 27% 22%)" ,
142- "--widget-accordion-border-color" : "hsl(213 19% 25%)" ,
143- "--widget-accordion-text-color" : "hsl(0 0% 97%)" ,
144- "--widget-accordion-content-color" : "hsl(213 14% 70%)" ,
145- "--widget-accordion-icon-color" : "hsl(213 14% 70%)" ,
146- "--widget-accordion-arrow-color" : "hsl(213 14% 70%)" ,
147- "--widget-accordion-focus-ring" : "hsl(163 69% 47%)" ,
148- "--widget-accordion-radius" : "0.625rem" ,
52+ slots : {
53+ // Preserves the 1.x accordion shades, which have no semantic token, by
54+ // scoping the surface variables to the accordion subtree.
55+ consentWidgetAccordion : {
56+ style : {
57+ "--c15t-surface" : "hsl(213 27% 18%)" ,
58+ "--c15t-surface-hover" : "hsl(213 27% 22%)" ,
59+ } ,
14960 } ,
15061 } ,
151- "widget.accordion" : {
152- style : {
153- "--accordion-background-color" : "hsl(213 27% 18%)" ,
154- "--accordion-background-color-dark" : "hsl(213 27% 18%)" ,
155- "--accordion-background-hover" : "hsl(213 27% 22%)" ,
156- "--accordion-background-hover-dark" : "hsl(213 27% 22%)" ,
157- "--accordion-border-color" : "hsl(213 19% 25%)" ,
158- "--accordion-border-color-dark" : "hsl(213 19% 25%)" ,
159- "--accordion-text-color" : "hsl(0 0% 97%)" ,
160- "--accordion-text-color-dark" : "hsl(0 0% 97%)" ,
161- "--accordion-content-color" : "hsl(213 14% 70%)" ,
162- "--accordion-content-color-dark" : "hsl(213 14% 70%)" ,
163- "--accordion-icon-color" : "hsl(213 14% 70%)" ,
164- "--accordion-icon-color-dark" : "hsl(213 14% 70%)" ,
165- "--accordion-arrow-color" : "hsl(213 14% 70%)" ,
166- "--accordion-arrow-color-dark" : "hsl(213 14% 70%)" ,
167- "--accordion-focus-ring" : "hsl(163 69% 47%)" ,
168- "--accordion-focus-ring-dark" : "hsl(163 69% 47%)" ,
169- "--accordion-focus-shadow" : "0 0 0 2px hsl(163 69% 47%)" ,
170- "--accordion-focus-shadow-dark" : "0 0 0 2px hsl(163 69% 47%)" ,
171- "--accordion-radius" : "0.625rem" ,
172- } ,
173- } ,
174- "widget.accordion.item" : {
175- style : {
176- "--accordion-background-color" : "hsl(213 27% 18%)" ,
177- "--accordion-background-color-dark" : "hsl(213 27% 18%)" ,
178- "--accordion-background-hover" : "hsl(213 27% 22%)" ,
179- "--accordion-background-hover-dark" : "hsl(213 27% 22%)" ,
180- "--accordion-border-color" : "hsl(213 19% 25%)" ,
181- "--accordion-border-color-dark" : "hsl(213 19% 25%)" ,
182- "--accordion-radius" : "0.625rem" ,
183- } ,
184- } ,
185- "widget.switch.track" : {
186- style : {
187- "--switch-background-color" : "hsl(213 19% 25%)" ,
188- "--switch-background-color-checked" : "hsl(163 69% 47%)" ,
189- } ,
190- } ,
191- "widget.footer.accept-button" : { style : acceptButton } ,
192- "widget.footer.reject-button" : { style : darkButton } ,
193- "widget.footer.save-button" : { style : saveButton } ,
194- "widget.branding" : {
195- style : { display : "none" } ,
196- } ,
197- "dialog.footer" : {
198- style : { display : "none" } ,
199- } ,
200- } as const
62+ } satisfies Theme
20163
202- const options = backendURL
203- ? {
204- mode : "c15t" as const ,
205- backendURL,
206- legalLinks : { privacyPolicy : { href : "/privacy" } , termsOfService : { href : "/terms" } } ,
207- }
208- : {
209- mode : "offline" as const ,
210- legalLinks : { privacyPolicy : { href : "/privacy" } , termsOfService : { href : "/terms" } } ,
211- }
64+ const uiOptions = {
65+ theme,
66+ colorScheme : "dark" ,
67+ legalLinks : { privacyPolicy : { href : "/privacy" } , termsOfService : { href : "/terms" } } ,
68+ } satisfies Partial < ConsentManagerOptions >
69+
70+ const options : ConsentManagerOptions = backendURL
71+ ? { mode : "hosted" , backendURL, ...uiOptions }
72+ : { mode : "offline" , ...uiOptions }
21273
21374function DialogDismissHandler ( ) {
214- const { isPrivacyDialogOpen, setIsPrivacyDialogOpen, setShowPopup } = useConsentManager ( )
75+ const { activeUI, setActiveUI } = useConsentManager ( )
76+ const isPrivacyDialogOpen = activeUI === "dialog"
21577
78+ // 1.x used setIsPrivacyDialogOpen(false) + setShowPopup(true); in 2.x a single
79+ // setActiveUI("banner") closes the dialog and re-shows the banner.
21680 const dismiss = useCallback ( ( ) => {
217- setIsPrivacyDialogOpen ( false )
218- setShowPopup ( true )
219- } , [ setIsPrivacyDialogOpen , setShowPopup ] )
81+ setActiveUI ( "banner" )
82+ } , [ setActiveUI ] )
22083
22184 useEffect ( ( ) => {
22285 if ( ! isPrivacyDialogOpen ) return
@@ -230,7 +93,7 @@ function DialogDismissHandler() {
23093
23194 const handleClick = ( e : MouseEvent ) => {
23295 const target = e . target as HTMLElement
233- if ( target . dataset . testid === "consent-manager- dialog-overlay" ) {
96+ if ( target . dataset . testid === "consent-dialog-overlay" ) {
23497 dismiss ( )
23598 }
23699 }
@@ -250,8 +113,8 @@ export function ConsentProvider({ children }: { children: React.ReactNode }) {
250113 return (
251114 < ConsentManagerProvider options = { options } >
252115 { children }
253- < CookieBanner theme = { theme } />
254- < ConsentManagerDialog theme = { theme } />
116+ < ConsentBanner hideBranding />
117+ < ConsentDialog hideBranding />
255118 < DialogDismissHandler />
256119 </ ConsentManagerProvider >
257120 )
0 commit comments