@c15t/nextjs 2.0.0-rc.6 → 2.0.0-rc.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ @import "@c15t/react/iab/styles.tw3.css";
@@ -0,0 +1 @@
1
+ @import "@c15t/react/styles.tw3.css";
package/dist/version.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_)=>{for(var r in _)__webpack_require__.o(_,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:_[r]})},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{version:()=>version});const version="2.0.0-rc.6";for(var __rspack_i in exports.version=__webpack_exports__.version,__webpack_exports__)-1===["version"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
1
+ "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_)=>{for(var r in _)__webpack_require__.o(_,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:_[r]})},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{version:()=>version});const version="2.0.0-rc.7";for(var __rspack_i in exports.version=__webpack_exports__.version,__webpack_exports__)-1===["version"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- let e="2.0.0-rc.6";export{e as version};
1
+ let e="2.0.0-rc.7";export{e as version};
@@ -1 +1 @@
1
- export declare const version = "2.0.0-rc.6";
1
+ export declare const version = "2.0.0-rc.7";
@@ -4,6 +4,9 @@ description: Build policy-aware custom consent components in Next.js using the h
4
4
  ---
5
5
  Building headless components is easier now because c15t exposes policy-aware primitives instead of forcing you to reconstruct banner rules by hand.
6
6
 
7
+ > ⚠️ **Warning:**
8
+ > Headless is the last step in the customization ladder. Use this guide only when pre-built components, tokens, slots, compound components, and noStyle are no longer sufficient.
9
+
7
10
  The headless stack is:
8
11
 
9
12
  * `useHeadlessConsentUI()` for policy-aware banner/dialog actions, ordering, layout, and primary actions hints
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: ConsentBanner
3
- description: A pre-built consent banner that appears when user consent is needed. Supports custom layout, button arrangement, and full compound component composition.
3
+ description: A pre-built consent banner that appears when user consent is needed. Supports policy-aware layout, theming, and advanced composition when markup must change.
4
4
  ---
5
5
  `ConsentBanner` is a ready-to-use consent banner that appears automatically in **opt-in jurisdictions** (like GDPR) where explicit consent is required before tracking. In opt-out jurisdictions (like CCPA), the banner won't appear — users get an opt-out mechanism instead. It includes reject, accept, and customize buttons with configurable layout.
6
6
 
@@ -18,20 +18,6 @@ export function ConsentManager() {
18
18
  }
19
19
  ```
20
20
 
21
- ## Customizing Content
22
-
23
- Override the default title, description, and button labels:
24
-
25
- ```tsx
26
- <ConsentBanner
27
- title="We value your privacy"
28
- description="We use cookies to enhance your browsing experience and analyze site traffic."
29
- acceptButtonText="Accept all"
30
- rejectButtonText="Reject all"
31
- customizeButtonText="Manage preferences"
32
- />
33
- ```
34
-
35
21
  ## Button Layout
36
22
 
37
23
  The `layout` prop controls button arrangement. Each item is either a button ID or an array of button IDs (which groups them together):
@@ -86,6 +72,37 @@ Use the provider `theme` prop to control how stock consent actions look:
86
72
 
87
73
  Policy packs control grouping, ordering, and direction. The theme controls button appearance.
88
74
 
75
+ ## Styling First
76
+
77
+ > ℹ️ **Info:**
78
+ > For pure theming, stay inside the pre-built banner. Start with layout props, theme.consentActions, design tokens, and theme.slots before reaching for compound components. See Styling Overview.
79
+
80
+ The stock banner maps common visual changes to the theme system:
81
+
82
+ * Card background -> `theme.colors.surface`
83
+ * Footer background -> `theme.colors.surfaceHover`
84
+ * Card, footer, and title tweaks -> `theme.slots.consentBannerCard`, `consentBannerFooter`, and `consentBannerTitle`
85
+
86
+ ```tsx
87
+ <ConsentManagerProvider
88
+ options={{
89
+ theme: {
90
+ colors: {
91
+ surface: '#fffdf8',
92
+ surfaceHover: '#f6f3ee',
93
+ },
94
+ slots: {
95
+ consentBannerCard: 'rounded-[28px] shadow-xl',
96
+ consentBannerFooter: 'border-t border-black/10 px-6',
97
+ consentBannerTitle: 'tracking-tight',
98
+ },
99
+ },
100
+ }}
101
+ >
102
+ <ConsentBanner />
103
+ </ConsentManagerProvider>
104
+ ```
105
+
89
106
  ### Primary Button
90
107
 
91
108
  Highlight specific button(s) as the primary action:
@@ -116,9 +133,40 @@ Control which legal links appear in the banner description:
116
133
  > ℹ️ **Info:**
117
134
  > Legal link URLs are configured in the ConsentManagerProvider options via the legalLinks prop, not on the banner itself.
118
135
 
119
- ## Compound Components
136
+ ## Customizing Copy
137
+
138
+ Prefer provider `i18n` when you want to rename the stock banner content:
139
+
140
+ ```tsx
141
+ <ConsentManagerProvider
142
+ options={{
143
+ i18n: {
144
+ locale: 'en',
145
+ messages: {
146
+ en: {
147
+ cookieBanner: {
148
+ title: 'We value your privacy',
149
+ description: 'We use cookies to improve the site and measure performance.',
150
+ },
151
+ common: {
152
+ acceptAll: 'Accept all',
153
+ rejectAll: 'Reject all',
154
+ customize: 'Manage preferences',
155
+ },
156
+ },
157
+ },
158
+ },
159
+ }}
160
+ >
161
+ <ConsentBanner />
162
+ </ConsentManagerProvider>
163
+ ```
164
+
165
+ Direct text props such as `title`, `description`, and `acceptButtonText` are still supported for one-off overrides, but `i18n` is the preferred path for copy changes.
120
166
 
121
- Build fully custom banner layouts using sub-components:
167
+ ## Advanced: Compound Components
168
+
169
+ Use compound components only when the stock banner structure is no longer enough and you need to rearrange existing c15t primitives:
122
170
 
123
171
  ```tsx
124
172
  <ConsentBanner.Root>
@@ -142,7 +190,7 @@ Build fully custom banner layouts using sub-components:
142
190
  * `ConsentBanner.Root` — Outermost container, provides theme context
143
191
  * `ConsentBanner.Card` — Main content card with optional focus trapping
144
192
  * `ConsentBanner.Header` — Contains title and description
145
- * `ConsentBanner.Title` — Heading, defaults to translation `consentBanner.title`
193
+ * `ConsentBanner.Title` — Heading, defaults to translation `cookieBanner.title`
146
194
  * `ConsentBanner.Description` — Description text, supports `legalLinks` prop
147
195
  * `ConsentBanner.Footer` — Action buttons container
148
196
  * `ConsentBanner.FooterSubGroup` — Groups related buttons together
@@ -151,6 +199,8 @@ Build fully custom banner layouts using sub-components:
151
199
  * `ConsentBanner.AcceptButton` — Accepts all consent
152
200
  * `ConsentBanner.Overlay` — Optional backdrop overlay
153
201
 
202
+ If you only need styling changes, stay with tokens and slots instead of rebuilding the banner layout.
203
+
154
204
  ## Props
155
205
 
156
206
  ### ConsentBannerProps
@@ -81,9 +81,36 @@ Hide the c15t branding in the dialog footer:
81
81
  <ConsentDialog hideBranding />
82
82
  ```
83
83
 
84
- ## Compound Components
84
+ ## Styling First
85
85
 
86
- Build fully custom dialog layouts using sub-components:
86
+ > ℹ️ **Info:**
87
+ > If you are only changing visuals, stay with the stock dialog and use the theme system first. Start with tokens and slots such as consentDialogCard, consentDialogHeader, and consentDialogFooter. See Styling Overview.
88
+
89
+ ```tsx
90
+ <ConsentManagerProvider
91
+ options={{
92
+ theme: {
93
+ colors: {
94
+ surface: '#fffdf8',
95
+ surfaceHover: '#f6f3ee',
96
+ },
97
+ slots: {
98
+ consentDialogCard: 'rounded-[32px] shadow-xl',
99
+ consentDialogHeader: 'gap-3',
100
+ consentDialogFooter: 'border-t border-black/10 px-6',
101
+ },
102
+ },
103
+ }}
104
+ >
105
+ <ConsentDialog />
106
+ </ConsentManagerProvider>
107
+ ```
108
+
109
+ Dialog copy should be changed through `ConsentManagerProvider.options.i18n`, not by rebuilding the dialog structure.
110
+
111
+ ## Advanced: Compound Components
112
+
113
+ Use compound components only when you need custom dialog markup while still keeping c15t primitives:
87
114
 
88
115
  ```tsx
89
116
  <ConsentDialog.Root>
@@ -118,6 +145,8 @@ For a quick pre-composed layout, use the shorthand card:
118
145
  </ConsentDialog.Root>
119
146
  ```
120
147
 
148
+ If the stock dialog structure still works, prefer tokens, slots, and provider configuration instead.
149
+
121
150
  ## Props
122
151
 
123
152
  ### ConsentDialogProps
@@ -35,9 +35,36 @@ export function PrivacySettingsPage() {
35
35
 
36
36
  Each consent category is rendered as an expandable accordion item. Clicking the category header expands it to show a description and any associated services. Users can toggle individual categories on or off using the switch control. The `necessary` category is always enabled and cannot be toggled.
37
37
 
38
- ## Compound Components
38
+ ## Styling First
39
39
 
40
- Build fully custom widget layouts using sub-components:
40
+ > ℹ️ **Info:**
41
+ > Most widget customization should stay in the stock component. Use theme tokens and slots such as consentWidgetAccordion, consentWidgetFooter, and toggle before reaching for compound components. See Styling Overview.
42
+
43
+ ```tsx
44
+ <ConsentManagerProvider
45
+ options={{
46
+ theme: {
47
+ colors: {
48
+ surface: '#fffdf8',
49
+ surfaceHover: '#f6f3ee',
50
+ },
51
+ slots: {
52
+ consentWidgetAccordion: 'rounded-3xl border border-black/10',
53
+ consentWidgetFooter: 'border-t border-black/10 px-6',
54
+ toggle: 'shadow-sm',
55
+ },
56
+ },
57
+ }}
58
+ >
59
+ <ConsentWidget />
60
+ </ConsentManagerProvider>
61
+ ```
62
+
63
+ Widget copy should be changed through `ConsentManagerProvider.options.i18n` so the inline UI stays aligned with the rest of the consent experience.
64
+
65
+ ## Advanced: Compound Components
66
+
67
+ Use compound components only when you need to rearrange the widget's existing primitives:
41
68
 
42
69
  ```tsx
43
70
  <ConsentWidget.Root>
@@ -68,6 +95,8 @@ Build fully custom widget layouts using sub-components:
68
95
  * `ConsentWidget.RejectButton` — Rejects all consent
69
96
  * `ConsentWidget.SaveButton` — Saves custom selections
70
97
 
98
+ If the stock widget structure is already correct, stay with tokens and slots instead of rebuilding the layout.
99
+
71
100
  ## Props
72
101
 
73
102
  ### ConsentWidgetProps
package/docs/headless.md CHANGED
@@ -4,11 +4,12 @@ description: Build fully custom consent UI using only hooks - no pre-built compo
4
4
  ---
5
5
  c15t's headless mode means using the hooks (`useConsentManager`, `useTranslations`, etc.) without any pre-built UI components. This gives you complete control over the consent experience.
6
6
 
7
- There are three levels of customization:
7
+ Before you go headless, walk the customization ladder in order:
8
8
 
9
- 1. **Props** - Use pre-built components with custom text and configuration
10
- 2. **noStyle** - Use pre-built components with their structure but strip all styles
11
- 3. **Headless** - Use only hooks and build the entire UI yourself
9
+ 1. **Pre-built components** - Use provider options, component props, tokens, slots, and `theme.consentActions`
10
+ 2. **Compound components** - Rearrange c15t primitives when the markup order must change
11
+ 3. **`noStyle`** - Keep c15t structure but replace its styling
12
+ 4. **Headless** - Use only hooks and build the entire UI yourself
12
13
 
13
14
  ## When to Go Headless
14
15
 
@@ -18,10 +19,15 @@ Go headless when:
18
19
  * You need a consent flow that doesn't fit the banner/dialog pattern
19
20
  * You want to embed consent choices inline rather than as overlays
20
21
 
21
- Use `noStyle` instead when:
22
+ Use a lower-power tool instead when:
22
23
 
23
- * The component structure works but the styling doesn't
24
- * You want to apply your own CSS/Tailwind without fighting defaults
24
+ * The component structure works but the styling doesn't -> use tokens, slots, or `noStyle`
25
+ * You only need to rearrange existing c15t parts -> use compound components
26
+ * You want to change copy -> use `ConsentManagerProvider.options.i18n`
27
+ * You only need to restyle stock actions -> use `theme.consentActions`
28
+
29
+ > ⚠️ **Warning:**
30
+ > Headless mode is not the first answer for pure theming. If you are still trying to debug why a banner footer color did not change, stay in the styling system and verify the token-to-component mapping before you rebuild the UI.
25
31
 
26
32
  > ℹ️ **Info:**
27
33
  > Need a policy-aware implementation guide? See Building Headless Components.
@@ -2,15 +2,11 @@
2
2
  title: Class Names
3
3
  description: Style consent components using className props and per-slot className targeting via the theme.
4
4
  ---
5
- ## Component className
5
+ ## Prefer Slots for Stock Components
6
6
 
7
- All consent components accept a `className` prop:
7
+ There is no single top-level `className` contract across every pre-built consent component.
8
8
 
9
- ```tsx
10
- <ConsentBanner className="my-banner" />
11
- <ConsentDialog className="my-dialog" />
12
- <ConsentWidget className="my-widget" />
13
- ```
9
+ For the stock `ConsentBanner`, `ConsentDialog`, and `ConsentWidget`, prefer `theme.slots` first. That keeps the markup intact and lets you target the exact part you need.
14
10
 
15
11
  ## Per-Slot className
16
12
 
@@ -59,9 +55,19 @@ const theme = {
59
55
  }
60
56
  ```
61
57
 
62
- ## noStyle Mode
58
+ ## When to Use Raw className
59
+
60
+ Use raw className-level styling when:
61
+
62
+ * your styling system is already class-driven
63
+ * tokens are too broad for the change
64
+ * slots already identify the correct element
63
65
 
64
- Use `noStyle` when you want to remove defaults and style from scratch:
66
+ If the request is "make the banner footer darker", prefer `theme.colors.surfaceHover` first. If the request is "add a border and spacing only to the footer", prefer `theme.slots.consentBannerFooter`.
67
+
68
+ ## Advanced: `noStyle`
69
+
70
+ Use `noStyle` only when you want to remove defaults and style from scratch while still keeping c15t's component structure:
65
71
 
66
72
  ```tsx
67
73
  {/* Remove all styles from a specific component */}
@@ -81,4 +87,6 @@ const theme = {
81
87
  } satisfies Theme;
82
88
  ```
83
89
 
90
+ Treat `noStyle` as an advanced escape hatch. Do not jump to it just because a token or slot needs debugging.
91
+
84
92
  For full custom markup and behavior, continue to [Headless Mode](../headless).
@@ -2,24 +2,34 @@
2
2
  title: Styling Overview
3
3
  description: Five approaches for theming consent components — design tokens, component slots, CSS variables, className, and noStyle mode.
4
4
  ---
5
- c15t's theming system gives you multiple levels of control, from high-level design tokens to complete style removal.
5
+ c15t's theming system gives you multiple levels of control, but most customization should stay inside the pre-built components.
6
6
 
7
- The flow:
7
+ Start with the lowest-power tool that solves the problem:
8
8
 
9
- 1. **Define** tokens (colors, spacing, radius, etc.) in JavaScript
10
- 2. Tokens are **injected** as CSS custom properties (`--c15t-*`) at runtime
11
- 3. Components **consume** these variables in their default styles
12
- 4. You **override** at any level: tokens, slots, CSS variables, or raw classNames
9
+ 1. **Pre-built component APIs** provider options and component props such as `layout`, `direction`, `primaryButton`, `legalLinks`, and `theme.consentActions`
10
+ 2. **Design tokens** global colors, typography, spacing, radius, shadows, and motion
11
+ 3. **Slots** targeted styling for specific parts such as the banner card, footer, or title
12
+ 4. **CSS variables or className-level overrides** when you need to integrate with external CSS systems
13
+ 5. **Compound components** — when you must rearrange markup while still using c15t primitives
14
+ 6. **`noStyle`** — when you want c15t structure but you need to own all visual styling
15
+ 7. **Headless** — when you want fully custom markup and behavior
16
+
17
+ Keep styling and escalation as separate decisions:
18
+
19
+ * If you are still using the stock banner, dialog, or widget, stay with props, tokens, and slots.
20
+ * Escalate to compound components, `noStyle`, or headless only when the structure or behavior itself must change.
13
21
 
14
22
  ## Styling Approaches
15
23
 
16
24
  |Approach|Control|Use When|
17
25
  |--|--|--|
26
+ |**Component and provider APIs**|High|Reordering actions, changing button emphasis, configuring links, hiding branding, changing copy via `i18n`|
18
27
  |**Tokens**|High|Changing global colors, typography, spacing, radius, shadows, or motion|
19
- |**Slots**|Medium|Targeting specific component parts (e.g., the banner title, dialog footer)|
20
- |**CSS Variables**|Medium|Overriding `--c15t-*` variables from external CSS|
21
- |**className**|Medium|Passing class names to components or slots|
22
- |**noStyle**|Full|Removing all default styles, building from scratch|
28
+ |**Slots**|Medium|Targeting specific component parts (for example `consentBannerFooter` or `consentDialogCard`)|
29
+ |**CSS variables / className**|Medium|Integrating with an existing stylesheet or utility classes after tokens and slots|
30
+ |**Compound components**|Structure|Rearranging existing c15t primitives without going fully custom|
31
+ |**noStyle**|Full visuals|Keeping c15t structure but replacing all visual defaults|
32
+ |**Headless**|Full|Replacing both markup and behavior|
23
33
 
24
34
  ## Quick Start
25
35
 
@@ -58,9 +68,25 @@ export function ConsentManager({ children }) {
58
68
  }
59
69
  ```
60
70
 
61
- ## Styling Paths
71
+ ## Styling Inside Pre-Built Components
72
+
73
+ Start here before you consider compound components or headless mode.
74
+
75
+ ### 1. Provider and component configuration
76
+
77
+ Use the stock APIs first:
78
+
79
+ * `layout`, `direction`, and `primaryButton` for banner action arrangement
80
+ * `legalLinks` for link visibility
81
+ * `hideBranding` and `showTrigger` for dialog and widget behavior
82
+ * `theme.consentActions` for stock banner and dialog button treatment
83
+ * `i18n` on `ConsentManagerProvider` for copy changes
84
+
85
+ ```tsx
86
+ <ConsentBanner layout={['customize', ['reject', 'accept']]} primaryButton="accept" />
87
+ ```
62
88
 
63
- ### 1. Design tokens
89
+ ### 2. Design tokens
64
90
 
65
91
  Set global values for colors, typography, spacing, radius, shadows, and motion:
66
92
 
@@ -68,55 +94,140 @@ Set global values for colors, typography, spacing, radius, shadows, and motion:
68
94
  options={{ theme: { colors: { primary: '#6366f1' } } }}
69
95
  ```
70
96
 
71
- ### 2. Component slots
97
+ Use tokens first when the change is semantic:
98
+
99
+ * Banner card background -> `theme.colors.surface`
100
+ * Banner footer background -> `theme.colors.surfaceHover`
101
+ * Shared copy color -> `theme.colors.text` and `theme.colors.textMuted`
102
+
103
+ ```tsx
104
+ options={{
105
+ theme: {
106
+ colors: {
107
+ surface: '#ffffff',
108
+ surfaceHover: '#f6f3ee',
109
+ },
110
+ },
111
+ }}
112
+ ```
113
+
114
+ ### 3. Component slots
72
115
 
73
116
  Target specific component parts via the `slots` object:
74
117
 
75
118
  ```tsx
76
- options={{ theme: { slots: { consentBannerTitle: 'text-2xl font-bold' } } }}
119
+ options={{
120
+ theme: {
121
+ slots: {
122
+ consentBannerCard: 'rounded-[28px] shadow-xl',
123
+ consentBannerFooter: 'border-t border-black/10',
124
+ consentBannerTitle: 'tracking-tight',
125
+ },
126
+ },
127
+ }}
77
128
  ```
78
129
 
79
- ### 3. CSS variables
130
+ Use slots when the component part is right but the local styling needs adjustment.
80
131
 
81
- Override `--c15t-*` custom properties in your stylesheet.
132
+ ### 4. CSS variables and className-level overrides
82
133
 
83
- ### 4. className prop
134
+ Override `--c15t-*` custom properties in your stylesheet or attach classes through slots when your app styling is driven externally.
84
135
 
85
- Pass className directly to components:
136
+ Reach for this after tokens and slots, not before.
86
137
 
87
138
  ```tsx
88
- <ConsentBanner className="my-custom-banner" />
139
+ options={{
140
+ theme: {
141
+ slots: {
142
+ consentBannerFooter: 'bg-[var(--banner-footer)]',
143
+ },
144
+ },
145
+ }}
89
146
  ```
90
147
 
91
- ### 5. noStyle prop
148
+ ## Escalating Beyond Pre-Built Components
149
+
150
+ Only move up this ladder when the lower rung cannot satisfy the request.
92
151
 
93
- Strip all default styles and build from scratch (best paired with [Headless Mode](../headless)):
152
+ ### 5. Compound components
153
+
154
+ Use compound components when you need to rearrange existing c15t primitives:
155
+
156
+ ```tsx
157
+ <ConsentBanner.Root>
158
+ <ConsentBanner.Card>
159
+ <ConsentBanner.Header>
160
+ <ConsentBanner.Title />
161
+ <ConsentBanner.Description />
162
+ </ConsentBanner.Header>
163
+ <ConsentBanner.Footer>
164
+ <ConsentBanner.CustomizeButton />
165
+ <ConsentBanner.FooterSubGroup>
166
+ <ConsentBanner.RejectButton />
167
+ <ConsentBanner.AcceptButton />
168
+ </ConsentBanner.FooterSubGroup>
169
+ </ConsentBanner.Footer>
170
+ </ConsentBanner.Card>
171
+ </ConsentBanner.Root>
172
+ ```
173
+
174
+ ### 6. `noStyle`
175
+
176
+ Use `noStyle` only when the c15t structure is still correct but you want to replace all visual defaults:
94
177
 
95
178
  ```tsx
96
179
  <ConsentBanner noStyle />
97
180
  ```
98
181
 
182
+ ### 7. Headless
183
+
184
+ Go headless only when you are replacing both markup and behavior. For that path, continue to [Headless Mode](../headless).
185
+
99
186
  ## Common Styling Tasks
100
187
 
101
- ### Change brand color globally
188
+ ### Change the banner footer background
102
189
 
103
190
  ```tsx
104
- options={{ theme: { colors: { primary: '#0ea5e9', primaryHover: '#0284c7' } } }}
191
+ options={{
192
+ theme: {
193
+ colors: {
194
+ surfaceHover: '#f6f3ee',
195
+ },
196
+ },
197
+ }}
105
198
  ```
106
199
 
107
- ### Make the banner card more compact
200
+ Use `theme.colors.surfaceHover` before trying raw CSS.
201
+
202
+ ### Change the banner card background
108
203
 
109
204
  ```tsx
110
- options={{ theme: { spacing: { md: '0.75rem', lg: '1rem' } } }}
205
+ options={{
206
+ theme: {
207
+ colors: {
208
+ surface: '#fffdf8',
209
+ },
210
+ },
211
+ }}
111
212
  ```
112
213
 
113
- ### Round primary/secondary buttons
214
+ Use `theme.colors.surface` before overriding banner CSS variables directly.
215
+
216
+ ### Tweak the banner card, footer, or title styling without changing markup
114
217
 
115
218
  ```tsx
116
- options={{ theme: { slots: { buttonPrimary: 'rounded-full', buttonSecondary: 'rounded-full' } } }}
219
+ options={{
220
+ theme: {
221
+ slots: {
222
+ consentBannerCard: 'rounded-[28px] shadow-xl',
223
+ consentBannerFooter: 'border-t border-black/10 px-6',
224
+ consentBannerTitle: 'text-xl tracking-tight',
225
+ },
226
+ },
227
+ }}
117
228
  ```
118
229
 
119
- ### Change consent action button styles semantically
230
+ ### Change stock consent action button styles semantically
120
231
 
121
232
  ```tsx
122
233
  options={{
@@ -132,6 +243,29 @@ options={{
132
243
 
133
244
  Use `theme.consentActions` when you want to change the stock banner/dialog button treatment without rewriting the component layout. Policy packs still control action arrangement and primary-action hints. The theme controls whether those actions render as `stroke`, `filled`, `ghost`, or `lighter`.
134
245
 
246
+ ### Change banner copy without replacing the component
247
+
248
+ ```tsx
249
+ options={{
250
+ i18n: {
251
+ locale: 'en',
252
+ messages: {
253
+ en: {
254
+ cookieBanner: {
255
+ title: 'We value your privacy',
256
+ description: 'We use cookies to improve the site and measure performance.',
257
+ },
258
+ common: {
259
+ acceptAll: 'Accept all',
260
+ rejectAll: 'Reject all',
261
+ customize: 'Manage preferences',
262
+ },
263
+ },
264
+ },
265
+ },
266
+ }}
267
+ ```
268
+
135
269
  ### Enable dark mode safely
136
270
 
137
271
  ```tsx
@@ -144,8 +278,11 @@ options={{
144
278
  }}
145
279
  ```
146
280
 
281
+ > ℹ️ **Info:**
282
+ > If a token change does not show up where you expect, check how that component maps tokens to CSS variables before escalating. For example, the stock banner footer background comes from colors.surfaceHover, not a separate footer token.
283
+ >
147
284
  > ⚠️ **Warning:**
148
- > noStyle: true removes layout and visual defaults. Use it only when you want full control.Use either tokens/slots or raw CSS variable overrides intentionally to avoid conflicting style sources.For dark mode, c15t supports .dark and .c15t-dark.
285
+ > Do not jump to CSS overrides or !important because a token did not appear to work at first glance.noStyle: true removes layout and visual defaults. Treat it as an advanced opt-out, not a normal theming step.Headless mode is for replacing markup and behavior, not for styling-only requests.Use either tokens/slots or raw CSS variable overrides intentionally to avoid conflicting style sources.For dark mode, c15t supports .dark and .c15t-dark.
149
286
 
150
287
  ## API Reference
151
288
 
@@ -4,7 +4,19 @@ description: Target individual component parts with styles using the slot system
4
4
  ---
5
5
  ## What are Slots?
6
6
 
7
- Slots let you target specific parts of consent components with styles. Each component is built from named "slots" (e.g., `consentBannerTitle`, `consentDialogFooter`) that you can style individually.
7
+ Slots let you target specific parts of consent components with styles. Each component is built from named slots such as `consentBannerTitle` and `consentDialogFooter`.
8
+
9
+ Use slots after the stock component APIs and design tokens:
10
+
11
+ * If the change is semantic, prefer tokens first. For example, the stock banner footer background comes from `theme.colors.surfaceHover`.
12
+ * If the component part is correct but you need a local tweak, use a slot.
13
+
14
+ Common banner slot choices:
15
+
16
+ * `consentBannerCard` for card radius, shadow, width, and local background treatment
17
+ * `consentBannerFooter` for spacing, borders, and local footer styling
18
+ * `consentBannerTitle` for title typography
19
+ * `buttonPrimary` and `buttonSecondary` for shared button classes
8
20
 
9
21
  ## Using Slots
10
22
 
@@ -13,8 +25,8 @@ Pass slot styles in the theme's `slots` object:
13
25
  ```tsx
14
26
  const theme = {
15
27
  slots: {
16
- // String value = className
17
- consentBannerTitle: 'text-xl font-bold text-gray-900',
28
+ consentBannerFooter: 'border-t border-black/10 px-6',
29
+ consentBannerTitle: 'text-xl font-bold tracking-tight',
18
30
 
19
31
  // Object value = className + inline styles
20
32
  consentBannerCard: {
@@ -34,13 +46,31 @@ Each slot accepts either a `string` (treated as className) or a `SlotStyle` obje
34
46
  consentBannerTitle: 'my-custom-class'
35
47
 
36
48
  // Object: className + style + noStyle
37
- consentBannerTitle: {
38
- className: 'my-custom-class',
39
- style: { color: 'red', fontSize: '1.5rem' },
40
- noStyle: false, // Set true to remove default styles for this slot
49
+ consentBannerFooter: {
50
+ className: 'border-t border-black/10',
51
+ style: { paddingBlock: '1rem' },
52
+ noStyle: false, // Set true only when you want to remove this slot's default styling
41
53
  }
42
54
  ```
43
55
 
56
+ `noStyle` on a slot is an advanced escape hatch. Start with className and style overrides first.
57
+
58
+ ## Example: Style the stock banner without changing markup
59
+
60
+ ```tsx
61
+ const theme = {
62
+ colors: {
63
+ surface: '#fffdf8',
64
+ surfaceHover: '#f6f3ee',
65
+ },
66
+ slots: {
67
+ consentBannerCard: 'rounded-[28px] shadow-xl',
68
+ consentBannerFooter: 'border-t border-black/10 px-6',
69
+ consentBannerTitle: 'tracking-tight',
70
+ },
71
+ } satisfies Theme;
72
+ ```
73
+
44
74
  ## Available Slots
45
75
 
46
76
  Use the typed API reference below for the full slot list and descriptions. It stays in sync with the actual component slot surface.
@@ -18,40 +18,32 @@ import '@c15t/nextjs/styles.css';
18
18
 
19
19
  ### Tailwind v4
20
20
 
21
- Tailwind v4 automatically scans your source files. Declare c15t's layer order once in your global CSS so utilities stay last:
21
+ Tailwind v4 automatically scans your source files. Import Tailwind normally. c15t component styles join Tailwind's `components` layer automatically, so no extra c15t-specific layer declaration is needed:
22
22
 
23
23
  ```css
24
- @layer theme, base, components, c15t, utilities;
25
24
  @import "tailwindcss";
26
25
  ```
27
26
 
28
27
  ### Tailwind v3
29
28
 
30
- Keep Tailwind's directives in your app stylesheet, but wrap `@tailwind base` in a native `base` layer so c15t's `@layer c15t` rules can sit above Preflight:
29
+ Import the Tailwind 3-compatible c15t stylesheet before your app Tailwind globals, then keep your standard Tailwind directives in the app stylesheet:
31
30
 
32
- ```css title="app/globals.css"
33
- @layer base, components, c15t;
31
+ ```tsx title="app/layout.tsx"
32
+ import '@c15t/react/styles.tw3.css';
33
+ import './globals.css';
34
+ ```
34
35
 
35
- @layer base {
36
- @tailwind base;
37
- }
36
+ ```tsx title="app/layout.tsx"
37
+ import '@c15t/nextjs/styles.tw3.css';
38
+ import './globals.css';
39
+ ```
38
40
 
41
+ ```css title="app/globals.css"
42
+ @tailwind base;
39
43
  @tailwind components;
40
44
  @tailwind utilities;
41
45
  ```
42
46
 
43
- Then add c15t's component paths to your `content` array:
44
-
45
- ```js title="tailwind.config.js"
46
- module.exports = {
47
- content: [
48
- './src/**/*.{js,ts,jsx,tsx}',
49
- './node_modules/@c15t/react/**/*.{js,mjs}',
50
- './node_modules/@c15t/nextjs/**/*.{js,mjs}',
51
- ],
52
- };
53
- ```
54
-
55
47
  ## Using Tailwind with Slots
56
48
 
57
49
  Apply Tailwind classes via the theme's `slots` object:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c15t/nextjs",
3
- "version": "2.0.0-rc.6",
3
+ "version": "2.0.0-rc.7",
4
4
  "description": "Developer-first CMP for Next.js: cookie banner, consent manager, preferences centre. GDPR ready with minimal setup and rich customization.",
5
5
  "keywords": [
6
6
  "nextjs",
@@ -33,7 +33,9 @@
33
33
  "type": "module",
34
34
  "exports": {
35
35
  "./styles.css": "./dist/styles.css",
36
+ "./styles.tw3.css": "./src/styles.tw3.css",
36
37
  "./iab/styles.css": "./dist/iab/styles.css",
38
+ "./iab/styles.tw3.css": "./src/iab/styles.tw3.css",
37
39
  "./headless": {
38
40
  "types": "./dist-types/headless.d.ts",
39
41
  "import": "./dist/headless.js",
@@ -57,7 +59,11 @@
57
59
  "dist",
58
60
  "docs",
59
61
  "dist-types",
60
- "client"
62
+ "client",
63
+ "src/styles.css",
64
+ "src/styles.tw3.css",
65
+ "src/iab/styles.css",
66
+ "src/iab/styles.tw3.css"
61
67
  ],
62
68
  "scripts": {
63
69
  "prebuild": "genversion --esm --semi src/version.ts",
@@ -72,7 +78,7 @@
72
78
  "test:watch": "bun prebuild && vitest --passWithNoTests"
73
79
  },
74
80
  "dependencies": {
75
- "@c15t/react": "2.0.0-rc.6",
81
+ "@c15t/react": "2.0.0-rc.7",
76
82
  "@c15t/translations": "2.0.0-rc.5",
77
83
  "c15t": "2.0.0-rc.6"
78
84
  },
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @c15t/nextjs — IAB TCF component styles.
3
+ *
4
+ * Import this stylesheet when using IAB consent components in Next.js.
5
+ * Self-contained — includes shared primitives, no need for base styles.
6
+ *
7
+ * Usage:
8
+ * import '@c15t/nextjs/iab/styles.css';
9
+ */
10
+ @import "@c15t/react/iab/styles.css";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @c15t/nextjs/iab — Tailwind 3-compatible IAB component styles.
3
+ *
4
+ * Import this stylesheet before your app Tailwind globals so utility classes
5
+ * can come after the c15t base rules.
6
+ *
7
+ * Usage:
8
+ * import '@c15t/nextjs/iab/styles.tw3.css';
9
+ */
10
+ @import "@c15t/react/iab/styles.tw3.css";
package/src/styles.css ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @c15t/nextjs — Non-IAB prebuilt component styles.
3
+ *
4
+ * Import this stylesheet once in your root layout when using
5
+ * prebuilt (styled) consent components.
6
+ *
7
+ * Usage:
8
+ * import '@c15t/nextjs/styles.css';
9
+ */
10
+ @import "@c15t/react/styles.css";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @c15t/nextjs — Tailwind 3-compatible prebuilt component styles.
3
+ *
4
+ * Import this stylesheet before your app Tailwind globals so utility classes
5
+ * can come after the c15t base rules.
6
+ *
7
+ * Usage:
8
+ * import '@c15t/nextjs/styles.tw3.css';
9
+ */
10
+ @import "@c15t/react/styles.tw3.css";