@c15t/nextjs 2.0.0-rc.8 → 2.0.0
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.
- package/README.md +3 -3
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist-types/libs/initial-data.d.ts +7 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/building-headless-components.md +122 -16
- package/docs/components/consent-banner.md +1 -30
- package/docs/components/consent-dialog.md +4 -3
- package/docs/components/consent-manager-provider.md +11 -11
- package/docs/components/consent-widget.md +1 -28
- package/docs/concepts/client-modes.md +1 -1
- package/docs/concepts/policy-packs.md +1 -1
- package/docs/hooks/use-consent-manager/overview.md +18 -2
- package/docs/iab/consent-banner.md +6 -4
- package/docs/iab/consent-dialog.md +6 -4
- package/docs/iab/overview.md +12 -11
- package/docs/iab/use-gvl-data.md +9 -197
- package/docs/internationalization.md +1 -1
- package/docs/optimization.md +42 -23
- package/docs/policy-packs.md +1 -1
- package/docs/quickstart.md +8 -8
- package/docs/server-side.md +10 -5
- package/docs/styling/color-scheme.md +1 -1
- package/docs/styling/css-variables.md +1 -1
- package/docs/styling/overview.md +11 -4
- package/docs/styling/slots.md +7 -3
- package/docs/styling/tokens.md +3 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
[](https://github.com/c15t/c15t)
|
|
13
13
|
[](https://github.com/c15t/c15t/actions/workflows/ci.yml)
|
|
14
|
-
[](https://github.com/c15t/c15t/blob/main/LICENSE.md)
|
|
15
15
|
[](https://c15t.link/discord)
|
|
16
16
|
[](https://www.npmjs.com/package/@c15t/nextjs)
|
|
17
17
|
[](https://github.com/c15t/c15t)
|
|
@@ -133,8 +133,8 @@ Our preference is that you make use of GitHub's private vulnerability reporting
|
|
|
133
133
|
|
|
134
134
|
## License
|
|
135
135
|
|
|
136
|
-
[
|
|
136
|
+
[Apache License 2.0](https://github.com/c15t/c15t/blob/main/LICENSE.md)
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|
|
140
|
-
**Built
|
|
140
|
+
**Built by [Inth](https://inth.com?utm_source=github&utm_medium=repopage_%40c15t%2Fnextjs)**
|
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
|
|
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";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
|
|
1
|
+
let e="2.0.0";export{e as version};
|
|
@@ -21,7 +21,13 @@ import type { FetchInitialDataOptions } from '../types';
|
|
|
21
21
|
* backendURL: '/api/consent',
|
|
22
22
|
* debug: process.env.NODE_ENV === 'development'
|
|
23
23
|
* });
|
|
24
|
-
* return
|
|
24
|
+
* return (
|
|
25
|
+
* <ConsentManagerProvider
|
|
26
|
+
* options={{ mode: 'hosted', backendURL: '/api/consent', ssrData: initialData }}
|
|
27
|
+
* >
|
|
28
|
+
* {children}
|
|
29
|
+
* </ConsentManagerProvider>
|
|
30
|
+
* )
|
|
25
31
|
* ```
|
|
26
32
|
*/
|
|
27
33
|
export declare function fetchInitialData(options: FetchInitialDataOptions): Promise<SSRInitialData | undefined>;
|
package/dist-types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.0.0
|
|
1
|
+
export declare const version = "2.0.0";
|
|
@@ -4,11 +4,11 @@ description: Build policy-aware custom consent components in Next.js using the h
|
|
|
4
4
|
---
|
|
5
5
|
Building custom consent UI is easier now because c15t exposes multiple layers of policy-aware primitives instead of forcing you to reconstruct banner rules by hand.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Think of customization as a ladder:
|
|
8
8
|
|
|
9
9
|
* stock component props for the shortest path
|
|
10
|
-
* `ConsentBanner.PolicyActions` and `ConsentWidget.PolicyActions`
|
|
11
|
-
* `useHeadlessConsentUI()`
|
|
10
|
+
* `ConsentBanner.PolicyActions` and `ConsentWidget.PolicyActions` when you want custom structure but still want c15t to resolve policy-aware actions
|
|
11
|
+
* `useHeadlessConsentUI()` when you need fully manual action rendering, custom controls, or non-standard flow
|
|
12
12
|
|
|
13
13
|
> ⚠️ **Warning:**
|
|
14
14
|
> 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.
|
|
@@ -26,6 +26,29 @@ The split is intentional: `@c15t/ui` owns pure policy-action resolution, while t
|
|
|
26
26
|
> ℹ️ **Info:**
|
|
27
27
|
> This guide is about building your own components while still respecting resolved policy-pack behavior. For the general headless overview, see Headless Mode.
|
|
28
28
|
|
|
29
|
+
## Choose the Smallest Layer That Solves the Job
|
|
30
|
+
|
|
31
|
+
Start with the smallest API surface that still gives you the behavior you need:
|
|
32
|
+
|
|
33
|
+
* Stay with stock components when you only need theming, spacing, copy, or legal-link changes
|
|
34
|
+
* Use `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions` when you want a custom compound-component layout but still want grouped actions, ordering, and primary emphasis to come from policy
|
|
35
|
+
* Add `renderAction` when the grouping is still correct but you want to remap actions to stock c15t button compounds
|
|
36
|
+
* Reach for `useHeadlessConsentUI()` only when you need custom button elements, need to map `actionGroups` yourself, wire non-button controls, or coordinate the consent UI with a more custom state machine
|
|
37
|
+
|
|
38
|
+
This order matters because every step down the ladder gives you more control, but also makes it easier for your UI to drift away from the resolved policy if you stop using the provided state.
|
|
39
|
+
|
|
40
|
+
## Before You Build Headless UI
|
|
41
|
+
|
|
42
|
+
Do not use headless mode for problems that are still inside the stock component model:
|
|
43
|
+
|
|
44
|
+
* Use `layout`, `direction`, `primaryButton`, and `legalLinks` before you rebuild banner markup
|
|
45
|
+
* Use `theme.consentActions` before you swap out stock actions
|
|
46
|
+
* Use tokens such as `colors.surface` and `colors.surfaceHover` before raw CSS overrides
|
|
47
|
+
* Use slots such as `consentBannerCard`, `consentBannerFooter`, and `consentDialogCard` before compound components
|
|
48
|
+
* Use `ConsentManagerProvider.options.i18n` before rebuilding UI just to change text
|
|
49
|
+
|
|
50
|
+
A good rule: if the stock banner or dialog structure is still correct, you probably do not need headless mode.
|
|
51
|
+
|
|
29
52
|
## What the Headless Tooling Gives You
|
|
30
53
|
|
|
31
54
|
The main win is that your custom UI can stay aligned with policy packs without duplicating policy logic in your components.
|
|
@@ -40,9 +63,67 @@ The main win is that your custom UI can stay aligned with policy packs without d
|
|
|
40
63
|
* UI profile and scroll-lock hints
|
|
41
64
|
* whether the banner or dialog should currently be visible
|
|
42
65
|
|
|
66
|
+
The hook also gives you the policy-aware action helpers you are expected to call:
|
|
67
|
+
|
|
68
|
+
* `performBannerAction('accept' | 'reject')`
|
|
69
|
+
* `performDialogAction('accept' | 'reject')`
|
|
70
|
+
* `saveCustomPreferences()` for the dialog `customize` action
|
|
71
|
+
* `openDialog()`, `openBanner()`, and `closeUI()` for surface visibility
|
|
72
|
+
|
|
43
73
|
That means your component mostly focuses on markup and design-system concerns instead of re-implementing policy interpretation.
|
|
44
74
|
|
|
45
|
-
For most compound-component layouts, start with `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions`. They render stock c15t buttons and translations by default, and `renderAction` is only needed when you want to override
|
|
75
|
+
For most compound-component layouts, start with `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions`. They render stock c15t buttons and translations by default, and `renderAction` is only needed when you want to override which stock compound renders for each action. Reach for manual `actionGroups` mapping when you need action rendering that no longer fits the stock button compounds.
|
|
76
|
+
|
|
77
|
+
## Policy-Aware Compound Components First
|
|
78
|
+
|
|
79
|
+
If your goal is "custom layout, same policy behavior", start here before dropping to manual `actionGroups` rendering:
|
|
80
|
+
|
|
81
|
+
```tsx title="components/consent-manager/banner-shell.tsx"
|
|
82
|
+
'use client';
|
|
83
|
+
|
|
84
|
+
import { ConsentBanner } from '@c15t/nextjs';
|
|
85
|
+
|
|
86
|
+
export function BannerShell() {
|
|
87
|
+
return (
|
|
88
|
+
<ConsentBanner.Root>
|
|
89
|
+
<ConsentBanner.Card>
|
|
90
|
+
<ConsentBanner.Header>
|
|
91
|
+
<ConsentBanner.Title />
|
|
92
|
+
<ConsentBanner.Description />
|
|
93
|
+
</ConsentBanner.Header>
|
|
94
|
+
<ConsentBanner.PolicyActions />
|
|
95
|
+
</ConsentBanner.Card>
|
|
96
|
+
</ConsentBanner.Root>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use `renderAction` only when you want to remap actions to stock button compounds while keeping the same policy-driven grouping and ordering:
|
|
102
|
+
|
|
103
|
+
```tsx title="components/consent-manager/banner-actions.tsx"
|
|
104
|
+
'use client';
|
|
105
|
+
|
|
106
|
+
import { ConsentBanner } from '@c15t/nextjs';
|
|
107
|
+
|
|
108
|
+
export function BannerActionsWithCustomMapping() {
|
|
109
|
+
return (
|
|
110
|
+
<ConsentBanner.PolicyActions
|
|
111
|
+
renderAction={(action, props) => {
|
|
112
|
+
const { key, ...buttonProps } = props;
|
|
113
|
+
|
|
114
|
+
switch (action) {
|
|
115
|
+
case 'accept':
|
|
116
|
+
return <ConsentBanner.AcceptButton key={key} {...buttonProps} />;
|
|
117
|
+
case 'reject':
|
|
118
|
+
return <ConsentBanner.RejectButton key={key} {...buttonProps} />;
|
|
119
|
+
case 'customize':
|
|
120
|
+
return <ConsentBanner.CustomizeButton key={key} {...buttonProps} />;
|
|
121
|
+
}
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
46
127
|
|
|
47
128
|
> ℹ️ **Info:**
|
|
48
129
|
> For custom layouts built from c15t compound components, prefer ConsentBanner.PolicyActions and ConsentWidget.PolicyActions. The examples below intentionally use manual actionGroups mapping to show the fully headless escape hatch.
|
|
@@ -89,9 +170,20 @@ export function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
89
170
|
import { useHeadlessConsentUI, useTranslations } from '@c15t/nextjs/headless';
|
|
90
171
|
|
|
91
172
|
export function CustomConsentBanner() {
|
|
92
|
-
const { banner, openDialog,
|
|
173
|
+
const { banner, openDialog, performBannerAction } = useHeadlessConsentUI();
|
|
93
174
|
const translations = useTranslations();
|
|
94
175
|
|
|
176
|
+
function getActionLabel(action: (typeof banner.allowedActions)[number]) {
|
|
177
|
+
switch (action) {
|
|
178
|
+
case 'accept':
|
|
179
|
+
return translations.common.acceptAll;
|
|
180
|
+
case 'reject':
|
|
181
|
+
return translations.common.rejectAll;
|
|
182
|
+
case 'customize':
|
|
183
|
+
return translations.common.customize;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
95
187
|
if (!banner.isVisible) return null;
|
|
96
188
|
|
|
97
189
|
return (
|
|
@@ -114,14 +206,10 @@ export function CustomConsentBanner() {
|
|
|
114
206
|
openDialog();
|
|
115
207
|
return;
|
|
116
208
|
}
|
|
117
|
-
void
|
|
209
|
+
void performBannerAction(action);
|
|
118
210
|
}}
|
|
119
211
|
>
|
|
120
|
-
{action
|
|
121
|
-
? translations.common.acceptAll
|
|
122
|
-
: action === 'reject'
|
|
123
|
-
? translations.common.rejectAll
|
|
124
|
-
: translations.common.customize}
|
|
212
|
+
{getActionLabel(action)}
|
|
125
213
|
</button>
|
|
126
214
|
))}
|
|
127
215
|
</div>
|
|
@@ -154,6 +242,17 @@ export function CustomConsentDialog() {
|
|
|
154
242
|
} = useConsentManager();
|
|
155
243
|
const translations = useTranslations();
|
|
156
244
|
|
|
245
|
+
function getActionLabel(action: (typeof dialog.allowedActions)[number]) {
|
|
246
|
+
switch (action) {
|
|
247
|
+
case 'accept':
|
|
248
|
+
return translations.common.acceptAll;
|
|
249
|
+
case 'reject':
|
|
250
|
+
return translations.common.rejectAll;
|
|
251
|
+
case 'customize':
|
|
252
|
+
return translations.common.save;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
157
256
|
if (!dialog.isVisible) return null;
|
|
158
257
|
|
|
159
258
|
const displayedTypes = consentTypes.filter(
|
|
@@ -202,11 +301,7 @@ export function CustomConsentDialog() {
|
|
|
202
301
|
void performDialogAction(action);
|
|
203
302
|
}}
|
|
204
303
|
>
|
|
205
|
-
{action
|
|
206
|
-
? translations.common.acceptAll
|
|
207
|
-
: action === 'reject'
|
|
208
|
-
? translations.common.rejectAll
|
|
209
|
-
: translations.common.save}
|
|
304
|
+
{getActionLabel(action)}
|
|
210
305
|
</button>
|
|
211
306
|
))}
|
|
212
307
|
</div>
|
|
@@ -217,6 +312,17 @@ export function CustomConsentDialog() {
|
|
|
217
312
|
}
|
|
218
313
|
```
|
|
219
314
|
|
|
315
|
+
## What Headless Is Not For
|
|
316
|
+
|
|
317
|
+
Headless mode is not the recommended path for:
|
|
318
|
+
|
|
319
|
+
* changing the banner footer background
|
|
320
|
+
* rounding the stock banner card
|
|
321
|
+
* restyling stock banner or dialog buttons
|
|
322
|
+
* changing consent copy
|
|
323
|
+
|
|
324
|
+
Those should stay in the pre-built stack with tokens, slots, `theme.consentActions`, and provider `i18n`.
|
|
325
|
+
|
|
220
326
|
## What a Policy-Aware Headless Component Should Respect
|
|
221
327
|
|
|
222
328
|
When you build custom banner or dialog components, make sure they use:
|
|
@@ -241,36 +241,7 @@ renderAction={(action, props) => {
|
|
|
241
241
|
/>
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
import { ConsentBanner, useTranslations } from '@c15t/react';
|
|
248
|
-
|
|
249
|
-
export function CustomBannerActions() {
|
|
250
|
-
const { common } = useTranslations();
|
|
251
|
-
|
|
252
|
-
return (
|
|
253
|
-
<ConsentBanner.PolicyActions
|
|
254
|
-
renderAction={(action, props) => (
|
|
255
|
-
<button
|
|
256
|
-
key={props.key}
|
|
257
|
-
type="button"
|
|
258
|
-
className={props.isPrimary ? 'btn-primary' : 'btn-secondary'}
|
|
259
|
-
style={props.style}
|
|
260
|
-
>
|
|
261
|
-
{action === 'accept'
|
|
262
|
-
? common.acceptAll
|
|
263
|
-
: action === 'reject'
|
|
264
|
-
? common.rejectAll
|
|
265
|
-
: common.customize}
|
|
266
|
-
</button>
|
|
267
|
-
)}
|
|
268
|
-
/>
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
For maximum control, use `useHeadlessConsentUI()` and render `banner.actionGroups` manually.
|
|
244
|
+
`renderAction` is still meant for stock button compounds. If you want completely custom button elements and click handling, use `useHeadlessConsentUI()` and render `banner.actionGroups` manually instead of `ConsentBanner.PolicyActions`.
|
|
274
245
|
|
|
275
246
|
If you only need styling changes, stay with tokens and slots instead of rebuilding the banner layout.
|
|
276
247
|
|
|
@@ -75,7 +75,7 @@ Add a floating button that lets users re-open the dialog after dismissing the ba
|
|
|
75
75
|
|
|
76
76
|
## Branding
|
|
77
77
|
|
|
78
|
-
Hide the c15t branding
|
|
78
|
+
Hide the c15t branding tag:
|
|
79
79
|
|
|
80
80
|
```tsx
|
|
81
81
|
<ConsentDialog hideBranding />
|
|
@@ -84,7 +84,7 @@ Hide the c15t branding in the dialog footer:
|
|
|
84
84
|
## Styling First
|
|
85
85
|
|
|
86
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,
|
|
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, consentWidgetFooter, and consentDialogTag. See Styling Overview.
|
|
88
88
|
|
|
89
89
|
```tsx
|
|
90
90
|
<ConsentManagerProvider
|
|
@@ -97,7 +97,8 @@ Hide the c15t branding in the dialog footer:
|
|
|
97
97
|
slots: {
|
|
98
98
|
consentDialogCard: 'rounded-[32px] shadow-xl',
|
|
99
99
|
consentDialogHeader: 'gap-3',
|
|
100
|
-
|
|
100
|
+
consentWidgetFooter: 'gap-3 pt-6',
|
|
101
|
+
consentDialogTag: 'shadow-none',
|
|
101
102
|
},
|
|
102
103
|
},
|
|
103
104
|
}}
|
|
@@ -36,15 +36,15 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
36
36
|
|Property|Type|Description|Default|Required|
|
|
37
37
|
|:--|:--|:--|:--|:--:|
|
|
38
38
|
|enabled|boolean \|undefined|Whether c15t should be active.|true|Optional|
|
|
39
|
-
|callbacks|[Callbacks \|undefined](https://
|
|
40
|
-
|scripts|[Script \|undefined](https://
|
|
39
|
+
|callbacks|[Callbacks \|undefined](https://c15t.com/docs/frameworks/react/callbacks)|Event callbacks for consent actions.|-|Optional|
|
|
40
|
+
|scripts|[Script \|undefined](https://c15t.com/docs/frameworks/react/script-loader)|Dynamically load scripts based on consent state.|-|Optional|
|
|
41
41
|
|legalLinks|Object \|undefined|Configuration for the legal links.|-|Optional|
|
|
42
42
|
|storageConfig|StorageConfig \|undefined|Storage configuration for consent persistence.|-|Optional|
|
|
43
43
|
|user|User \|undefined|The user's information. Usually your own internal ID for the user from your auth provider.|-|Optional|
|
|
44
44
|
|overrides|Overrides \|undefined|Forcefully set values like country, region, language for the consent manager. These values will override the values detected from the browser.|-|Optional|
|
|
45
|
-
|networkBlocker|[NetworkBlockerConfig \|undefined](https://
|
|
46
|
-
|iab|[IABConfig \|undefined](https://
|
|
47
|
-
|ssrData|[Object \|undefined](https://
|
|
45
|
+
|networkBlocker|[NetworkBlockerConfig \|undefined](https://c15t.com/docs/frameworks/react/network-blocker)|Configuration for the network request blocker.|-|Optional|
|
|
46
|
+
|iab|[IABConfig \|undefined](https://c15t.com/docs/frameworks/react/iab/overview)|IAB TCF 2.3 configuration.|-|Optional|
|
|
47
|
+
|ssrData|[Object \|undefined](https://c15t.com/docs/frameworks/react/server-side)|SSR-prefetched data for hydration.|-|Optional|
|
|
48
48
|
|
|
49
49
|
#### `callbacks` Callbacks
|
|
50
50
|
|
|
@@ -171,8 +171,8 @@ SSR-prefetched data for hydration.
|
|
|
171
171
|
|Property|Type|Description|Default|Required|
|
|
172
172
|
|:--|:--|:--|:--|:--:|
|
|
173
173
|
|i18n|I18nConfig \|undefined|Preferred i18n configuration in c15t v2.|-|Optional|
|
|
174
|
-
|translations|[TranslationConfig \|undefined](https://
|
|
175
|
-
|consentCategories|[AllConsentNames \|undefined](https://
|
|
174
|
+
|translations|[TranslationConfig \|undefined](https://c15t.com/docs/frameworks/react/internationalization)|Translation configuration to seed the store with.|-|Optional|
|
|
175
|
+
|consentCategories|[AllConsentNames \|undefined](https://c15t.com/docs/frameworks/react/concepts/consent-categories)|Consent categories to show in the consent banner.|-|Optional|
|
|
176
176
|
|
|
177
177
|
#### `i18n` I18nConfig
|
|
178
178
|
|
|
@@ -198,12 +198,12 @@ Translation configuration to seed the store with.
|
|
|
198
198
|
|
|
199
199
|
|Property|Type|Description|Default|Required|
|
|
200
200
|
|:--|:--|:--|:--|:--:|
|
|
201
|
-
|theme|[Theme \|undefined](https://
|
|
201
|
+
|theme|[Theme \|undefined](https://c15t.com/docs/frameworks/react/styling/tokens)|Visual theme to apply.|-|Optional|
|
|
202
202
|
|disableAnimation|boolean \|undefined|Whether to disable animations.|false|Optional|
|
|
203
203
|
|scrollLock|boolean \|undefined|Whether to lock scroll when dialogs are open.|false|Optional|
|
|
204
204
|
|trapFocus|boolean \|undefined|Whether to trap focus within dialogs.|true|Optional|
|
|
205
|
-
|colorScheme|["light" \|"dark" \|"system" \|undefined](https://
|
|
206
|
-
|noStyle|[boolean \|undefined](https://
|
|
205
|
+
|colorScheme|["light" \|"dark" \|"system" \|undefined](https://c15t.com/docs/frameworks/react/styling/color-scheme)|Color scheme preference. With this option, you can force the theme to be light, dark or system. Otherwise, the theme will be detected if you have '.dark' classname in your document.|-|Optional|
|
|
206
|
+
|noStyle|[boolean \|undefined](https://c15t.com/docs/frameworks/react/headless)|Whether to disable default styles.|false|Optional|
|
|
207
207
|
|
|
208
208
|
#### `theme` Theme
|
|
209
209
|
|
|
@@ -392,7 +392,7 @@ Read the full guide at [Policy Packs](/docs/frameworks/react/policy-packs) and t
|
|
|
392
392
|
|Property|Type|Description|Default|Required|
|
|
393
393
|
|:--|:--|:--|:--|:--:|
|
|
394
394
|
|children|ReactNode|React children to render within the provider.|-|✅ Required|
|
|
395
|
-
|options|[ConsentManagerOptions](https://
|
|
395
|
+
|options|[ConsentManagerOptions](https://c15t.com/docs/frameworks/react/components/consent-manager-provider)|Configuration options for the consent manager. This includes core, React, store, and translation settings.|-|✅ Required|
|
|
396
396
|
|
|
397
397
|
#### `options` ConsentManagerOptions
|
|
398
398
|
|
|
@@ -117,34 +117,7 @@ renderAction={(action, props) => {
|
|
|
117
117
|
/>
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
import { ConsentWidget, useTranslations } from '@c15t/react';
|
|
124
|
-
|
|
125
|
-
export function CustomWidgetActions() {
|
|
126
|
-
const { common } = useTranslations();
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<ConsentWidget.PolicyActions
|
|
130
|
-
renderAction={(action, props) => (
|
|
131
|
-
<button
|
|
132
|
-
key={props.key}
|
|
133
|
-
type="button"
|
|
134
|
-
className={props.isPrimary ? 'btn-primary' : 'btn-secondary'}
|
|
135
|
-
style={props.style}
|
|
136
|
-
>
|
|
137
|
-
{action === 'accept'
|
|
138
|
-
? common.acceptAll
|
|
139
|
-
: action === 'reject'
|
|
140
|
-
? common.rejectAll
|
|
141
|
-
: common.save}
|
|
142
|
-
</button>
|
|
143
|
-
)}
|
|
144
|
-
/>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
120
|
+
`renderAction` is still meant for stock button compounds. If you want completely custom button elements and handlers, use `useHeadlessConsentUI()` and render `dialog.actionGroups` manually instead of `ConsentWidget.PolicyActions`.
|
|
148
121
|
|
|
149
122
|
For a fixed footer layout, render `ConsentWidget.Footer` and `ConsentWidget.FooterSubGroup` manually instead of using `ConsentWidget.PolicyActions`.
|
|
150
123
|
|
|
@@ -15,7 +15,7 @@ c15t supports three client modes that determine how consent data is stored and s
|
|
|
15
15
|
|
|
16
16
|
## Hosted Mode (Recommended)
|
|
17
17
|
|
|
18
|
-
The default mode. Connects to a c15t backend for full consent lifecycle management. We recommend using [
|
|
18
|
+
The default mode. Connects to a c15t backend for full consent lifecycle management. We recommend using [inth.com](https://inth.com) for a fully managed experience, but you can [self-host](/docs/self-host) as well.
|
|
19
19
|
|
|
20
20
|
> ℹ️ **Info:**
|
|
21
21
|
> mode: 'hosted' is the preferred value. The legacy alias mode: 'c15t' is still supported for backward compatibility.
|
|
@@ -8,7 +8,7 @@ A policy pack is an ordered array of policies. Each policy targets a region or c
|
|
|
8
8
|
|
|
9
9
|
There are three ways to configure policy packs:
|
|
10
10
|
|
|
11
|
-
1. **
|
|
11
|
+
1. **inth.com (recommended)** — use [inth.com](https://inth.com) as your hosted backend. Configure packs visually in the dashboard or via API — no code changes required. Works with any frontend, including static sites.
|
|
12
12
|
2. **Self-hosted backend** — define packs in code via `policyPacks` and resolve them from real request geo data. Full control over policy logic and storage.
|
|
13
13
|
3. **Offline fallback** — pass the same policy shapes to the frontend via `offlinePolicy.policyPacks`. Use this mainly for local development, demos, deterministic testing, or resilience when the backend is temporarily unreachable. If you omit `offlinePolicy.policyPacks`, c15t falls back to a synthetic worldwide opt-in banner instead of no-banner mode.
|
|
14
14
|
|
|
@@ -87,7 +87,7 @@ Information about when and how consent was given
|
|
|
87
87
|
|:--|:--|:--|:--|:--:|
|
|
88
88
|
|time|number|The epoch timestamp of when the consent was recorded|-|✅ Required|
|
|
89
89
|
|subjectId|string \|undefined|The client-generated subject ID in sub\_xxx format|-|Optional|
|
|
90
|
-
|id|string \|undefined|
|
|
90
|
+
|id|string \|undefined|Effective GPC signal used for the request.|-|Optional|
|
|
91
91
|
|externalId|string \|undefined|The external user ID linked to this subject|-|Optional|
|
|
92
92
|
|materialPolicyFingerprint|string \|undefined|Material fingerprint of the active policy when this consent was accepted.|-|Optional|
|
|
93
93
|
|identityProvider|string \|undefined|The identity provider that provided the external ID|-|Optional|
|
|
@@ -189,6 +189,7 @@ IAB TCF 2.3 state and actions (null when not configured or not in IAB mode).
|
|
|
189
189
|
|setOverrides|Object \|undefined \|null|Sets the overrides for the consent manager. Automatically attempts to fetch the consent manager again with the new overrides.|-|✅ Required|
|
|
190
190
|
|setLanguage|Object \|undefined \|null|Set the language override for the consent manager. This will override the language detected from the browser and re-fetch the consent banner information.|-|✅ Required|
|
|
191
191
|
|identifyUser|Object|Identifies the user by setting the external ID.|-|✅ Required|
|
|
192
|
+
|unstable\_acceptPolicyConsent|Object \|undefined|Writes a policy-based consent such as terms and conditions.|-|✅ Required|
|
|
192
193
|
|setSelectedConsent|(name: AllConsentNames, value: boolean) => void|Updates the selected consent state for a specific consent type.|-|✅ Required|
|
|
193
194
|
|saveConsents|Object \|undefined|Saves the user's consent preferences.|-|✅ Required|
|
|
194
195
|
|setConsent|(name: AllConsentNames, value: boolean) => void|Updates the consent state for a specific consent type & automatically save the consent.|-|✅ Required|
|
|
@@ -246,6 +247,21 @@ Identifies the user by setting the external ID.
|
|
|
246
247
|
|id|string|Usually your own internal ID for the user from your auth provider|-|✅ Required|
|
|
247
248
|
|identityProvider|string \|undefined|The identity provider of the user. Usually the name of the identity provider e.g. 'clerk', 'auth0', 'custom', etc.|-|Optional|
|
|
248
249
|
|
|
250
|
+
#### `unstable_acceptPolicyConsent`
|
|
251
|
+
|
|
252
|
+
Writes a policy-based consent such as terms and conditions.
|
|
253
|
+
|
|
254
|
+
|Property|Type|Description|Default|Required|
|
|
255
|
+
|:--|:--|:--|:--|:--:|
|
|
256
|
+
|type|Object|-|-|✅ Required|
|
|
257
|
+
|domain|string \|undefined|-|-|Optional|
|
|
258
|
+
|givenAt|number \|undefined|-|-|Optional|
|
|
259
|
+
|metadata|Record\<string, unknown> \|undefined|-|-|Optional|
|
|
260
|
+
|preferences|Record\<string, boolean> \|undefined|-|-|Optional|
|
|
261
|
+
|uiSource|string \|undefined|-|-|Optional|
|
|
262
|
+
|externalId|string \|undefined|-|-|Optional|
|
|
263
|
+
|identityProvider|string \|undefined|-|-|Optional|
|
|
264
|
+
|
|
249
265
|
#### `subscribeToConsentChanges`
|
|
250
266
|
|
|
251
267
|
Subscribes to change-only consent saves.
|
|
@@ -344,7 +360,7 @@ function LoginForm() {
|
|
|
344
360
|
```
|
|
345
361
|
|
|
346
362
|
> ℹ️ **Info:**
|
|
347
|
-
> identifyUser sends the user data to the c15t backend. It
|
|
363
|
+
> identifyUser sends the user data to the c15t backend. It works in hosted mode, including the legacy alias mode: 'c15t'. In mode: 'offline', the call is a no-op.
|
|
348
364
|
|
|
349
365
|
## Key Types
|
|
350
366
|
|
|
@@ -19,6 +19,7 @@ Use this component instead of `ConsentBanner` when you need IAB TCF compliance f
|
|
|
19
19
|
|
|
20
20
|
```tsx
|
|
21
21
|
import { type ReactNode } from 'react';
|
|
22
|
+
import { iab } from '@c15t/iab';
|
|
22
23
|
import { ConsentManagerProvider } from '@c15t/nextjs';
|
|
23
24
|
import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
|
|
24
25
|
|
|
@@ -28,11 +29,12 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
28
29
|
options={{
|
|
29
30
|
mode: 'hosted',
|
|
30
31
|
backendURL: '/api/c15t',
|
|
31
|
-
iab: {
|
|
32
|
-
enabled: true,
|
|
33
|
-
cmpId: 123,
|
|
32
|
+
iab: iab({
|
|
34
33
|
vendors: [1, 2, 10, 25],
|
|
35
|
-
|
|
34
|
+
// cmpId is automatically provided by the backend when using consent.io.
|
|
35
|
+
// Only set this if you have your own CMP registration.
|
|
36
|
+
// cmpId: 123,
|
|
37
|
+
}),
|
|
36
38
|
}}
|
|
37
39
|
>
|
|
38
40
|
<IABConsentBanner />
|
|
@@ -13,6 +13,7 @@ Pair it with `IABConsentBanner` inside the provider:
|
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { type ReactNode } from 'react';
|
|
16
|
+
import { iab } from '@c15t/iab';
|
|
16
17
|
import { ConsentManagerProvider } from '@c15t/nextjs';
|
|
17
18
|
import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
|
|
18
19
|
|
|
@@ -22,11 +23,12 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
22
23
|
options={{
|
|
23
24
|
mode: 'hosted',
|
|
24
25
|
backendURL: '/api/c15t',
|
|
25
|
-
iab: {
|
|
26
|
-
enabled: true,
|
|
27
|
-
cmpId: 123,
|
|
26
|
+
iab: iab({
|
|
28
27
|
vendors: [1, 2, 10, 25],
|
|
29
|
-
|
|
28
|
+
// cmpId is automatically provided by the backend when using consent.io.
|
|
29
|
+
// Only set this if you have your own CMP registration.
|
|
30
|
+
// cmpId: 123,
|
|
31
|
+
}),
|
|
30
32
|
}}
|
|
31
33
|
>
|
|
32
34
|
<IABConsentBanner />
|
package/docs/iab/overview.md
CHANGED
|
@@ -15,12 +15,12 @@ When your site participates in the IAB ecosystem (ad exchanges, SSPs, DSPs, DMPs
|
|
|
15
15
|
|
|
16
16
|
## CMP Registration
|
|
17
17
|
|
|
18
|
-
[
|
|
18
|
+
[inth.com](https://inth.com) is pending validation as an IAB Europe-registered CMP for c15t. Once approved, when you use inth.com as your backend, the correct CMP ID will be automatically provided to your client via the `/init` endpoint — no client-side configuration needed.
|
|
19
19
|
|
|
20
20
|
If you self-host the c15t backend and have your own CMP registration with IAB Europe, you can configure your CMP ID on the backend via `advanced.iab.cmpId` or on the client via the `iab.cmpId` option. A valid (non-zero) CMP ID is required for IAB TCF compliance.
|
|
21
21
|
|
|
22
22
|
> ℹ️ **Info:**
|
|
23
|
-
> If you heavily customize or build your own IAB banner or dialog (rather than using the default IABConsentBanner and IABConsentDialog components), you cannot use
|
|
23
|
+
> If you heavily customize or build your own IAB banner or dialog (rather than using the default IABConsentBanner and IABConsentDialog components), you cannot use inth.com's CMP ID. You must register your own CMP with IAB Europe and use your own CMP ID.
|
|
24
24
|
|
|
25
25
|
## How c15t Implements TCF
|
|
26
26
|
|
|
@@ -45,6 +45,7 @@ If you use the prebuilt styled IAB UI, import the IAB stylesheet alongside the b
|
|
|
45
45
|
|
|
46
46
|
```tsx
|
|
47
47
|
import { type ReactNode } from 'react';
|
|
48
|
+
import { iab } from '@c15t/iab';
|
|
48
49
|
import { ConsentManagerProvider } from '@c15t/nextjs';
|
|
49
50
|
import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
|
|
50
51
|
|
|
@@ -54,13 +55,12 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
54
55
|
options={{
|
|
55
56
|
mode: 'hosted',
|
|
56
57
|
backendURL: '/api/c15t',
|
|
57
|
-
iab: {
|
|
58
|
-
enabled: true,
|
|
58
|
+
iab: iab({
|
|
59
59
|
vendors: [1, 2, 10, 25], // IAB vendor IDs you work with
|
|
60
|
-
// cmpId is automatically provided by the backend (
|
|
60
|
+
// cmpId is automatically provided by the backend (inth.com).
|
|
61
61
|
// Only set this if you have your own CMP registration with IAB Europe.
|
|
62
62
|
// cmpId: 123,
|
|
63
|
-
},
|
|
63
|
+
}),
|
|
64
64
|
}}
|
|
65
65
|
>
|
|
66
66
|
<IABConsentBanner />
|
|
@@ -73,14 +73,13 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
73
73
|
|
|
74
74
|
## IAB Configuration Options
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Configure IAB mode with `iab({ ... })` from `@c15t/iab`. The factory enables the addon and injects the runtime module automatically. The user-facing options are:
|
|
77
77
|
|
|
78
78
|
|Option|Type|Description|
|
|
79
79
|
|--|--|--|
|
|
80
|
-
|`
|
|
81
|
-
|`cmpId`|`number`|CMP ID registered with IAB Europe. Automatically provided by the backend when using consent.io. Only set this if you have your own CMP registration.|
|
|
80
|
+
|`cmpId`|`number`|CMP ID registered with IAB Europe. Automatically provided by the backend when using inth.com. Only set this if you have your own CMP registration.|
|
|
82
81
|
|`vendors`|`number[]`|IAB vendor IDs that your site works with|
|
|
83
|
-
|`
|
|
82
|
+
|`customVendors`|`NonIABVendor[]`|Custom vendors not in the IAB registry|
|
|
84
83
|
|
|
85
84
|
## Key Concepts
|
|
86
85
|
|
|
@@ -122,4 +121,6 @@ Each vendor in the GVL declares which purposes it uses, whether via consent or l
|
|
|
122
121
|
|--|--|
|
|
123
122
|
|[IABConsentBanner](/docs/frameworks/next/iab/consent-banner)|TCF-compliant banner with partner disclosure|
|
|
124
123
|
|[IABConsentDialog](/docs/frameworks/next/iab/consent-dialog)|Tabbed preference center for purposes and vendors|
|
|
125
|
-
|
|
124
|
+
|
|
125
|
+
> ℹ️ **Info:**
|
|
126
|
+
> For lower-level custom IAB flows, use useHeadlessIABConsentUI() from @c15t/react/iab. useGVLData() is currently internal and is not part of the public package surface.
|
package/docs/iab/use-gvl-data.md
CHANGED
|
@@ -1,208 +1,20 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: useGVLData
|
|
3
|
-
description:
|
|
2
|
+
title: useGVLData (Internal)
|
|
3
|
+
description: Status note for the internal GVL hook used by the built-in IAB dialog.
|
|
4
4
|
---
|
|
5
5
|
> ❌ **Error:**
|
|
6
6
|
> c15t is not yet IAB certified. The IAB TCF components are under active development and should not be used in production. APIs and behavior may change before certification is achieved.
|
|
7
7
|
|
|
8
|
-
`useGVLData()`
|
|
8
|
+
`useGVLData()` currently powers the built-in `IABConsentDialog`, but it is **not part of the public package surface**.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Older docs showed it as a public hook. That is no longer accurate.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
import { useGVLData } from '@c15t/react/hooks';
|
|
12
|
+
If you need supported customization points today:
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (isLoading) return <p>Loading vendor data...</p>;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div>
|
|
22
|
-
<p>{totalVendors} partners</p>
|
|
23
|
-
{standalonePurposes.map((purpose) => (
|
|
24
|
-
<div key={purpose.id}>
|
|
25
|
-
<h3>{purpose.name}</h3>
|
|
26
|
-
<p>{purpose.description}</p>
|
|
27
|
-
<p>{purpose.vendors.length} vendors</p>
|
|
28
|
-
</div>
|
|
29
|
-
))}
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
```
|
|
14
|
+
* Use `IABConsentBanner` and `IABConsentDialog` from `@c15t/react/iab` for the supported prebuilt UI
|
|
15
|
+
* Use `useHeadlessIABConsentUI()` from `@c15t/react/iab` when you need lower-level control over banner/dialog state and actions
|
|
34
16
|
|
|
35
17
|
> ℹ️ **Info:**
|
|
36
|
-
>
|
|
37
|
-
|
|
38
|
-
## Return Value
|
|
39
|
-
|
|
40
|
-
The hook returns a `GVLData` object:
|
|
41
|
-
|
|
42
|
-
### GVLData
|
|
43
|
-
|
|
44
|
-
|Property|Type|Description|Default|Required|
|
|
45
|
-
|:--|:--|:--|:--|:--:|
|
|
46
|
-
|purposes|ProcessedPurpose|-|-|✅ Required|
|
|
47
|
-
|specialPurposes|ProcessedPurpose|-|-|✅ Required|
|
|
48
|
-
|specialFeatures|ProcessedSpecialFeature|-|-|✅ Required|
|
|
49
|
-
|features|ProcessedFeature|-|-|✅ Required|
|
|
50
|
-
|stacks|ProcessedStack|-|-|✅ Required|
|
|
51
|
-
|standalonePurposes|ProcessedPurpose|-|-|✅ Required|
|
|
52
|
-
|totalVendors|number|-|-|✅ Required|
|
|
53
|
-
|isLoading|boolean|-|-|✅ Required|
|
|
54
|
-
|
|
55
|
-
#### `purposes` ProcessedPurpose
|
|
56
|
-
|
|
57
|
-
|Property|Type|Description|Default|Required|
|
|
58
|
-
|:--|:--|:--|:--|:--:|
|
|
59
|
-
|id|number|-|-|✅ Required|
|
|
60
|
-
|name|string|-|-|✅ Required|
|
|
61
|
-
|description|string|-|-|✅ Required|
|
|
62
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
63
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
64
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
65
|
-
|isSpecialPurpose|boolean \|undefined|-|-|Optional|
|
|
66
|
-
|
|
67
|
-
#### `specialPurposes` ProcessedPurpose
|
|
68
|
-
|
|
69
|
-
|Property|Type|Description|Default|Required|
|
|
70
|
-
|:--|:--|:--|:--|:--:|
|
|
71
|
-
|id|number|-|-|✅ Required|
|
|
72
|
-
|name|string|-|-|✅ Required|
|
|
73
|
-
|description|string|-|-|✅ Required|
|
|
74
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
75
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
76
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
77
|
-
|isSpecialPurpose|boolean \|undefined|-|-|Optional|
|
|
78
|
-
|
|
79
|
-
#### `specialFeatures` ProcessedSpecialFeature
|
|
80
|
-
|
|
81
|
-
|Property|Type|Description|Default|Required|
|
|
82
|
-
|:--|:--|:--|:--|:--:|
|
|
83
|
-
|id|number|-|-|✅ Required|
|
|
84
|
-
|name|string|-|-|✅ Required|
|
|
85
|
-
|description|string|-|-|✅ Required|
|
|
86
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
87
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
88
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
89
|
-
|
|
90
|
-
#### `features` ProcessedFeature
|
|
91
|
-
|
|
92
|
-
|Property|Type|Description|Default|Required|
|
|
93
|
-
|:--|:--|:--|:--|:--:|
|
|
94
|
-
|id|number|-|-|✅ Required|
|
|
95
|
-
|name|string|-|-|✅ Required|
|
|
96
|
-
|description|string|-|-|✅ Required|
|
|
97
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
98
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
99
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
100
|
-
|
|
101
|
-
#### `stacks` ProcessedStack
|
|
102
|
-
|
|
103
|
-
|Property|Type|Description|Default|Required|
|
|
104
|
-
|:--|:--|:--|:--|:--:|
|
|
105
|
-
|id|number|-|-|✅ Required|
|
|
106
|
-
|name|string|-|-|✅ Required|
|
|
107
|
-
|description|string|-|-|✅ Required|
|
|
108
|
-
|purposes|ProcessedPurpose|-|-|✅ Required|
|
|
109
|
-
|
|
110
|
-
#### `standalonePurposes` ProcessedPurpose
|
|
111
|
-
|
|
112
|
-
|Property|Type|Description|Default|Required|
|
|
113
|
-
|:--|:--|:--|:--|:--:|
|
|
114
|
-
|id|number|-|-|✅ Required|
|
|
115
|
-
|name|string|-|-|✅ Required|
|
|
116
|
-
|description|string|-|-|✅ Required|
|
|
117
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
118
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
119
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
120
|
-
|isSpecialPurpose|boolean \|undefined|-|-|Optional|
|
|
121
|
-
|
|
122
|
-
## Types
|
|
123
|
-
|
|
124
|
-
### ProcessedPurpose
|
|
125
|
-
|
|
126
|
-
|Property|Type|Description|Default|Required|
|
|
127
|
-
|:--|:--|:--|:--|:--:|
|
|
128
|
-
|id|number|-|-|✅ Required|
|
|
129
|
-
|name|string|-|-|✅ Required|
|
|
130
|
-
|description|string|-|-|✅ Required|
|
|
131
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
132
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
133
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
134
|
-
|isSpecialPurpose|boolean \|undefined|-|-|Optional|
|
|
135
|
-
|
|
136
|
-
#### `vendors` ProcessedVendor
|
|
137
|
-
|
|
138
|
-
|Property|Type|Description|Default|Required|
|
|
139
|
-
|:--|:--|:--|:--|:--:|
|
|
140
|
-
|id|VendorId|-|-|✅ Required|
|
|
141
|
-
|name|string|-|-|✅ Required|
|
|
142
|
-
|policyUrl|string|-|-|✅ Required|
|
|
143
|
-
|usesNonCookieAccess|boolean|-|-|✅ Required|
|
|
144
|
-
|deviceStorageDisclosureUrl|string \|null|-|-|✅ Required|
|
|
145
|
-
|usesCookies|boolean|-|-|✅ Required|
|
|
146
|
-
|cookieMaxAgeSeconds|number \|null|-|-|✅ Required|
|
|
147
|
-
|cookieRefresh|boolean \|undefined|-|-|Optional|
|
|
148
|
-
|specialPurposes|number\[]|-|-|✅ Required|
|
|
149
|
-
|specialFeatures|number\[]|-|-|✅ Required|
|
|
150
|
-
|features|number\[]|-|-|✅ Required|
|
|
151
|
-
|purposes|number\[]|-|-|✅ Required|
|
|
152
|
-
|legIntPurposes|number\[]|-|-|✅ Required|
|
|
153
|
-
|legitimateInterestUrl|string \|null \|undefined|-|-|Optional|
|
|
154
|
-
|isCustom|boolean \|undefined|-|-|Optional|
|
|
155
|
-
|usesLegitimateInterest|boolean \|undefined|-|-|Optional|
|
|
156
|
-
|dataRetention|Object \|undefined|-|-|Optional|
|
|
157
|
-
|dataDeclaration|number\[] \|undefined|-|-|Optional|
|
|
158
|
-
|
|
159
|
-
### ProcessedVendor
|
|
160
|
-
|
|
161
|
-
|Property|Type|Description|Default|Required|
|
|
162
|
-
|:--|:--|:--|:--|:--:|
|
|
163
|
-
|id|VendorId|-|-|✅ Required|
|
|
164
|
-
|name|string|-|-|✅ Required|
|
|
165
|
-
|policyUrl|string|-|-|✅ Required|
|
|
166
|
-
|usesNonCookieAccess|boolean|-|-|✅ Required|
|
|
167
|
-
|deviceStorageDisclosureUrl|string \|null|-|-|✅ Required|
|
|
168
|
-
|usesCookies|boolean|-|-|✅ Required|
|
|
169
|
-
|cookieMaxAgeSeconds|number \|null|-|-|✅ Required|
|
|
170
|
-
|cookieRefresh|boolean \|undefined|-|-|Optional|
|
|
171
|
-
|specialPurposes|number\[]|-|-|✅ Required|
|
|
172
|
-
|specialFeatures|number\[]|-|-|✅ Required|
|
|
173
|
-
|features|number\[]|-|-|✅ Required|
|
|
174
|
-
|purposes|number\[]|-|-|✅ Required|
|
|
175
|
-
|legIntPurposes|number\[]|-|-|✅ Required|
|
|
176
|
-
|legitimateInterestUrl|string \|null \|undefined|-|-|Optional|
|
|
177
|
-
|isCustom|boolean \|undefined|-|-|Optional|
|
|
178
|
-
|usesLegitimateInterest|boolean \|undefined|-|-|Optional|
|
|
179
|
-
|dataRetention|Object \|undefined|-|-|Optional|
|
|
180
|
-
|dataDeclaration|number\[] \|undefined|-|-|Optional|
|
|
181
|
-
|
|
182
|
-
#### `dataRetention`
|
|
183
|
-
|
|
184
|
-
|Property|Type|Description|Default|Required|
|
|
185
|
-
|:--|:--|:--|:--|:--:|
|
|
186
|
-
|purposes|Record\<number, number> \|undefined|-|-|Optional|
|
|
187
|
-
|specialPurposes|Record\<number, number> \|undefined|-|-|Optional|
|
|
188
|
-
|stdRetention|number \|undefined|-|-|Optional|
|
|
189
|
-
|
|
190
|
-
### ProcessedStack
|
|
191
|
-
|
|
192
|
-
|Property|Type|Description|Default|Required|
|
|
193
|
-
|:--|:--|:--|:--|:--:|
|
|
194
|
-
|id|number|-|-|✅ Required|
|
|
195
|
-
|name|string|-|-|✅ Required|
|
|
196
|
-
|description|string|-|-|✅ Required|
|
|
197
|
-
|purposes|ProcessedPurpose|-|-|✅ Required|
|
|
198
|
-
|
|
199
|
-
### ProcessedSpecialFeature
|
|
18
|
+
> Until useGVLData() is exported as public API, avoid importing it from deep internal paths. Those paths are not covered by semver guarantees and can change without notice.
|
|
200
19
|
|
|
201
|
-
|
|
202
|
-
|:--|:--|:--|:--|:--:|
|
|
203
|
-
|id|number|-|-|✅ Required|
|
|
204
|
-
|name|string|-|-|✅ Required|
|
|
205
|
-
|description|string|-|-|✅ Required|
|
|
206
|
-
|descriptionLegal|string \|undefined|-|-|Optional|
|
|
207
|
-
|illustrations|string\[]|-|-|✅ Required|
|
|
208
|
-
|vendors|ProcessedVendor|-|-|✅ Required|
|
|
20
|
+
When a public GVL-focused hook becomes part of the supported API, this page should document that public surface instead of the internal dialog hook.
|
|
@@ -10,7 +10,7 @@ There are two ways c15t can load translations: client-side or server-side.
|
|
|
10
10
|
|
|
11
11
|
|Server-side|Client-side|
|
|
12
12
|
|--|--|
|
|
13
|
-
|The best way to reduce bundle size and improve performance. We can detect the user's language based on the browser's language settings, allowing for the most accurate translations. By default, when using a [
|
|
13
|
+
|The best way to reduce bundle size and improve performance. We can detect the user's language based on the browser's language settings, allowing for the most accurate translations. By default, when using a [inth.com](https://inth.com) hosted instance, [these languages](https://github.com/c15t/c15t/tree/main/packages/translations/src/translations) are supported.|Bundled with the application allowing for multiple languages to be supported without the need for a backend. The more translations you have, the larger the bundle size will be, which may impact the performance of your application.|
|
|
14
14
|
|
|
15
15
|
## Basic Configuration
|
|
16
16
|
|
package/docs/optimization.md
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Optimization
|
|
3
|
-
description: Improve c15t startup performance in Next.js with rewrites, static prefetching, and
|
|
4
|
-
lastModified: 2026-04-
|
|
3
|
+
description: Improve c15t startup performance in Next.js with same-origin rewrites, static prefetching, and dynamic-route SSR.
|
|
4
|
+
lastModified: 2026-04-14
|
|
5
5
|
---
|
|
6
6
|
Use this guide when you care about banner visibility speed, route static-ness, and reducing backend round-trip cost.
|
|
7
7
|
|
|
8
|
+
## Start Here
|
|
9
|
+
|
|
10
|
+
Apply the optimizations in this order:
|
|
11
|
+
|
|
12
|
+
|Situation|Use|Why|
|
|
13
|
+
|--|--|--|
|
|
14
|
+
|Any production Next.js app|Same-origin `/api/c15t` rewrite|Lowers browser startup overhead and keeps the backend origin out of client config|
|
|
15
|
+
|Static route, but banner speed matters|`C15tPrefetch`|Starts `/init` before hydration without making the route dynamic|
|
|
16
|
+
|Dynamic route, or you want the fastest first banner|`fetchInitialData()`|Starts `/init` on the server and streams the result into the provider|
|
|
17
|
+
|You want the simplest setup|Client-only init|No extra moving parts, but the banner appears later on cold loads|
|
|
18
|
+
|
|
19
|
+
> ℹ️ **Info:**
|
|
20
|
+
> C15tPrefetch is the only static-route prefetch step you need in @c15t/nextjs. Matching prefetched data is consumed automatically during first initialization.
|
|
21
|
+
>
|
|
22
|
+
> ℹ️ **Info:**
|
|
23
|
+
> Prefetched or SSR data is reused only when the request context still matches at runtime. That includes the backend URL, credentials, overrides, and the browser's ambient GPC signal.
|
|
24
|
+
|
|
25
|
+
In production benchmarks with a same-origin rewrite, prefetching strategies show measurable improvement over client-only init:
|
|
26
|
+
|
|
27
|
+
|Strategy|Scripts loaded|Data request starts|Banner visible|
|
|
28
|
+
|--|--|--|--|
|
|
29
|
+
|Client-only (no prefetch)|baseline|baseline|baseline|
|
|
30
|
+
|Browser prefetch|\~1.3x faster|\~2.6x earlier|\~1.25x faster|
|
|
31
|
+
|Server prefetch|\~2x faster|before page loads|\~1.9x faster|
|
|
32
|
+
|
|
8
33
|
## 1) Prefer Same-Origin Rewrites
|
|
9
34
|
|
|
10
35
|
Proxy c15t requests through your Next.js app so the browser calls your own origin instead of a third-party domain.
|
|
@@ -39,30 +64,24 @@ Why this helps:
|
|
|
39
64
|
* You can change backend infrastructure without touching client code
|
|
40
65
|
|
|
41
66
|
> ℹ️ **Info:**
|
|
42
|
-
> Set NEXT\_PUBLIC\_C15T\_URL in .env to your backend URL, for example https\://your-
|
|
67
|
+
> Set NEXT\_PUBLIC\_C15T\_URL in .env to your backend URL, for example https\://your-project.inth.app.
|
|
43
68
|
>
|
|
44
69
|
> ℹ️ **Info:**
|
|
45
|
-
> Use rewrites for browser-side calls (ConsentManagerProvider, C15tPrefetch). For server-side fetchInitialData(), prefer a direct backend URL (for example https\://your-
|
|
70
|
+
> Use rewrites for browser-side calls (ConsentManagerProvider, C15tPrefetch). For server-side fetchInitialData(), prefer a direct backend URL (for example https\://your-project.inth.app) to avoid an extra server proxy hop.
|
|
46
71
|
|
|
47
|
-
## 2)
|
|
72
|
+
## 2) Choose A Startup Strategy
|
|
48
73
|
|
|
49
|
-
|
|
50
|
-
|--|--|--|
|
|
51
|
-
|Keep routes fully static|`C15tPrefetch`|Still client-side fetch, but starts earlier|
|
|
52
|
-
|Fastest first banner on dynamic routes|`fetchInitialData()` in a Server Component (do not await)|Route becomes dynamic because of `next/headers`|
|
|
53
|
-
|Simplest setup|Client-only init (no prefetch)|Banner appears later on slow networks|
|
|
74
|
+
Start with a same-origin rewrite and the default client-side provider. Add one of the preloading strategies below only when the route behavior or performance target calls for it.
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
### Client-Only Init
|
|
56
77
|
|
|
57
|
-
|
|
58
|
-
|--|--|--|--|
|
|
59
|
-
|Client-only (no prefetch)|baseline|baseline|baseline|
|
|
60
|
-
|Browser prefetch|\~1.3x faster|\~2.6x earlier|\~1.25x faster|
|
|
61
|
-
|Server prefetch|\~2x faster|before page loads|\~1.9x faster|
|
|
78
|
+
Keep the default provider setup when you want the least complexity. This works on both static and dynamic routes, but the banner only appears after the client runtime starts and the initial `/init` request completes.
|
|
62
79
|
|
|
63
80
|
### Dynamic Routes: Fetch On The Server And Stream
|
|
64
81
|
|
|
65
|
-
Use `fetchInitialData()`
|
|
82
|
+
Use `fetchInitialData()` when the route is already dynamic, or when you are willing to make it dynamic in exchange for the fastest first banner.
|
|
83
|
+
|
|
84
|
+
Because it depends on `next/headers`, this opts the route into dynamic rendering.
|
|
66
85
|
|
|
67
86
|
```tsx title="app/layout.tsx"
|
|
68
87
|
import { fetchInitialData } from '@c15t/nextjs';
|
|
@@ -106,7 +125,7 @@ export default function ConsentManager({
|
|
|
106
125
|
options={{
|
|
107
126
|
mode: 'hosted',
|
|
108
127
|
backendURL: '/api/c15t',
|
|
109
|
-
|
|
128
|
+
ssrData,
|
|
110
129
|
}}
|
|
111
130
|
>
|
|
112
131
|
<ConsentBanner />
|
|
@@ -121,11 +140,11 @@ export default function ConsentManager({
|
|
|
121
140
|
> Do not await fetchInitialData(). Pass the unresolved Promise to the provider so Next.js can stream the route while /init runs in parallel.
|
|
122
141
|
>
|
|
123
142
|
> ℹ️ **Info:**
|
|
124
|
-
> For fetchInitialData(), prefer a direct backend URL such as https\://your-
|
|
143
|
+
> For fetchInitialData(), prefer a direct backend URL such as https\://your-project.inth.app instead of a rewrite to avoid an extra server-side proxy hop. See Server-Side Data Fetching for the full flow.
|
|
125
144
|
|
|
126
145
|
### Static Routes: Start Fetch Early In The Browser
|
|
127
146
|
|
|
128
|
-
Use `C15tPrefetch`
|
|
147
|
+
Use `C15tPrefetch` when the route needs to stay static but you still want the `/init` request to start before hydration. Matching prefetched data is consumed automatically by the runtime during first store initialization.
|
|
129
148
|
|
|
130
149
|
```tsx title="app/layout.tsx"
|
|
131
150
|
import { C15tPrefetch } from '@c15t/nextjs';
|
|
@@ -175,10 +194,10 @@ export default function ConsentManagerClient({ children }: { children: React.Rea
|
|
|
175
194
|
```
|
|
176
195
|
|
|
177
196
|
> ℹ️ **Info:**
|
|
178
|
-
> C15tPrefetch uses Next.js beforeInteractive script loading, so the /init request can start before hydration.
|
|
197
|
+
> C15tPrefetch uses Next.js beforeInteractive script loading, so the /init request can start before hydration.
|
|
179
198
|
>
|
|
180
199
|
> ℹ️ **Info:**
|
|
181
|
-
> If
|
|
200
|
+
> If the request context changes between prefetch time and runtime, c15t falls back to a normal client /init. A common example is overrides.gpc conflicting with the browser's ambient GPC signal.
|
|
182
201
|
|
|
183
202
|
## Keep The Provider Mounted Across Navigation
|
|
184
203
|
|
|
@@ -210,6 +229,6 @@ If you must use a cross-origin backend URL, add preconnect so the browser starts
|
|
|
210
229
|
|
|
211
230
|
```tsx title="app/layout.tsx"
|
|
212
231
|
<head>
|
|
213
|
-
<link rel="preconnect" href="https://your-
|
|
232
|
+
<link rel="preconnect" href="https://your-project.inth.app" crossOrigin="" />
|
|
214
233
|
</head>
|
|
215
234
|
```
|
package/docs/policy-packs.md
CHANGED
|
@@ -13,7 +13,7 @@ When a backend isn't available — local development, static previews, Storybook
|
|
|
13
13
|
|
|
14
14
|
## Hosted Mode (Recommended)
|
|
15
15
|
|
|
16
|
-
When using
|
|
16
|
+
When using inth.com or a self-hosted backend, the provider connects automatically. No policy configuration is needed on the frontend:
|
|
17
17
|
|
|
18
18
|
```tsx
|
|
19
19
|
<ConsentManagerProvider
|
package/docs/quickstart.md
CHANGED
|
@@ -17,10 +17,10 @@ availableIn:
|
|
|
17
17
|
|
|
18
18
|
|Package manager|Command|
|
|
19
19
|
|:--|:--|
|
|
20
|
-
|npm|`npx @c15t/cli
|
|
21
|
-
|pnpm|`pnpm dlx @c15t/cli
|
|
22
|
-
|yarn|`yarn dlx @c15t/cli
|
|
23
|
-
|bun|`bunx @c15t/cli
|
|
20
|
+
|npm|`npx @c15t/cli`|
|
|
21
|
+
|pnpm|`pnpm dlx @c15t/cli`|
|
|
22
|
+
|yarn|`yarn dlx @c15t/cli`|
|
|
23
|
+
|bun|`bunx @c15t/cli`|
|
|
24
24
|
|
|
25
25
|
## Manual Installation
|
|
26
26
|
|
|
@@ -151,10 +151,10 @@ Install c15t agent skills to let AI agents help with styling, i18n, scripts & ot
|
|
|
151
151
|
|
|
152
152
|
|Package manager|Command|
|
|
153
153
|
|:--|:--|
|
|
154
|
-
|npm|`npx @c15t/cli
|
|
155
|
-
|pnpm|`pnpm dlx @c15t/cli
|
|
156
|
-
|yarn|`yarn dlx @c15t/cli
|
|
157
|
-
|bun|`bunx @c15t/cli
|
|
154
|
+
|npm|`npx @c15t/cli skills`|
|
|
155
|
+
|pnpm|`pnpm dlx @c15t/cli skills`|
|
|
156
|
+
|yarn|`yarn dlx @c15t/cli skills`|
|
|
157
|
+
|bun|`bunx @c15t/cli skills`|
|
|
158
158
|
|
|
159
159
|
See [AI Agents](/docs/ai-agents) for bundled package docs and agent skills.
|
|
160
160
|
|
package/docs/server-side.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Server-Side Data Fetching
|
|
3
|
-
description: Pre-fetch consent data in Server Components with fetchInitialData
|
|
3
|
+
description: Pre-fetch consent data in Server Components with fetchInitialData for dynamic Next.js routes.
|
|
4
4
|
---
|
|
5
|
-
The `@c15t/nextjs` package provides `fetchInitialData`, a server-side function that fetches consent data before rendering.
|
|
5
|
+
The `@c15t/nextjs` package provides `fetchInitialData`, a server-side function that fetches consent data before rendering. Use it when the route is already dynamic, or when you want the fastest first banner and are okay opting the route into dynamic rendering. If you need to keep a route fully static, use `C15tPrefetch` instead.
|
|
6
|
+
|
|
7
|
+
Because `fetchInitialData()` resolves request headers from `next/headers`, using it opts the route into dynamic rendering.
|
|
6
8
|
|
|
7
9
|
> ℹ️ **Info:**
|
|
8
10
|
> SSR hydration is part of the initialization flow. When SSR data is available, the client skips the API fetch entirely.
|
|
@@ -20,7 +22,7 @@ import { fetchInitialData } from '@c15t/nextjs';
|
|
|
20
22
|
|
|
21
23
|
// In a Server Component — do NOT await
|
|
22
24
|
const ssrData = fetchInitialData({
|
|
23
|
-
backendURL: '
|
|
25
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
24
26
|
debug: process.env.NODE_ENV === 'development',
|
|
25
27
|
});
|
|
26
28
|
```
|
|
@@ -29,6 +31,9 @@ const ssrData = fetchInitialData({
|
|
|
29
31
|
> Do not await fetchInitialData() in Server Components. Pass the Promise directly to your client component so Next.js can stream the page while the consent data loads in parallel.
|
|
30
32
|
>
|
|
31
33
|
> ℹ️ **Info:**
|
|
34
|
+
> For fetchInitialData(), prefer a direct backend URL such as https\://your-instance.c15t.dev. For browser-side calls from the provider, C15tPrefetch, and other client runtime work, prefer a same-origin /api/c15t rewrite. See Optimization for the full decision guide.
|
|
35
|
+
>
|
|
36
|
+
> ℹ️ **Info:**
|
|
32
37
|
> Need fully static routes? Use C15tPrefetch in your layout. Matching prefetched data is consumed automatically by the runtime instead of using fetchInitialData(). See Optimization.
|
|
33
38
|
|
|
34
39
|
### How Streaming Works
|
|
@@ -94,7 +99,7 @@ export default function ConsentManager({
|
|
|
94
99
|
options={{
|
|
95
100
|
mode: 'hosted',
|
|
96
101
|
backendURL: '/api/c15t',
|
|
97
|
-
|
|
102
|
+
ssrData,
|
|
98
103
|
}}
|
|
99
104
|
>
|
|
100
105
|
<ConsentBanner />
|
|
@@ -111,7 +116,7 @@ import ConsentManager from '@/components/consent-manager';
|
|
|
111
116
|
|
|
112
117
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
113
118
|
const ssrData = fetchInitialData({
|
|
114
|
-
backendURL: '
|
|
119
|
+
backendURL: 'https://your-instance.c15t.dev',
|
|
115
120
|
});
|
|
116
121
|
|
|
117
122
|
return (
|
|
@@ -65,7 +65,7 @@ function ThemeToggle() {
|
|
|
65
65
|
|
|
66
66
|
## How Dark Mode Works
|
|
67
67
|
|
|
68
|
-
When dark mode is active, c15t applies the `dark` token values as CSS variable overrides. Only tokens specified in `dark` are overridden - unset tokens fall back to the `colors` values.
|
|
68
|
+
When dark mode is active, c15t applies the `dark` token values as CSS variable overrides. Only tokens specified in `dark` are overridden - unset tokens fall back to the `colors` values. This also applies to `textOnPrimary`: if you omit it, c15t derives a readable foreground from the active `primary` color in that scheme.
|
|
69
69
|
|
|
70
70
|
```tsx
|
|
71
71
|
const theme = {
|
|
@@ -18,7 +18,7 @@ Every theme token is converted to a `--c15t-*` CSS custom property at runtime. Y
|
|
|
18
18
|
|--c15t-border-hover|string \|undefined|\`colors.borderHover\` (default: \`hsl(0, 0%, 85%)\`)|-|Optional|
|
|
19
19
|
|--c15t-text|string \|undefined|\`colors.text\` (default: \`hsl(0, 0%, 10%)\`)|-|Optional|
|
|
20
20
|
|--c15t-text-muted|string \|undefined|\`colors.textMuted\` (default: \`hsl(0, 0%, 40%)\`)|-|Optional|
|
|
21
|
-
|--c15t-text-on-primary|string \|undefined|\`colors.textOnPrimary\` (
|
|
21
|
+
|--c15t-text-on-primary|string \|undefined|\`colors.textOnPrimary\` (auto-derived from \`colors.primary\` when omitted)|-|Optional|
|
|
22
22
|
|--c15t-overlay|string \|undefined|\`colors.overlay\` (default: \`hsla(0, 0%, 0%, 0.5)\`)|-|Optional|
|
|
23
23
|
|--c15t-switch-track|string \|undefined|\`colors.switchTrack\` (default: \`hsl(0, 0%, 85%)\`)|-|Optional|
|
|
24
24
|
|--c15t-switch-track-active|string \|undefined|\`colors.switchTrackActive\` (default: \`hsl(228, 100%, 60%)\`)|-|Optional|
|
package/docs/styling/overview.md
CHANGED
|
@@ -99,6 +99,7 @@ Use tokens first when the change is semantic:
|
|
|
99
99
|
* Banner card background -> `theme.colors.surface`
|
|
100
100
|
* Banner footer background -> `theme.colors.surfaceHover`
|
|
101
101
|
* Shared copy color -> `theme.colors.text` and `theme.colors.textMuted`
|
|
102
|
+
* Primary-filled surfaces such as stock branding tags and filled actions -> `theme.colors.primary` with `theme.colors.textOnPrimary` as the matching foreground override
|
|
102
103
|
|
|
103
104
|
```tsx
|
|
104
105
|
options={{
|
|
@@ -111,6 +112,8 @@ options={{
|
|
|
111
112
|
}}
|
|
112
113
|
```
|
|
113
114
|
|
|
115
|
+
If you set `theme.colors.primary` but omit `theme.colors.textOnPrimary`, c15t derives a readable foreground automatically. Add `textOnPrimary` only when you need to force a specific branded foreground color.
|
|
116
|
+
|
|
114
117
|
### 3. Component slots
|
|
115
118
|
|
|
116
119
|
Target specific component parts via the `slots` object:
|
|
@@ -314,7 +317,7 @@ Color palette for light mode.
|
|
|
314
317
|
|borderHover|string \|undefined|Hover state for bordered elements.|-|Optional|
|
|
315
318
|
|text|string \|undefined|Primary text color for headings and body.|-|Optional|
|
|
316
319
|
|textMuted|string \|undefined|Muted text color for secondary content.|-|Optional|
|
|
317
|
-
|textOnPrimary|string \|undefined|Text color for content on primary background.|-|Optional|
|
|
320
|
+
|textOnPrimary|string \|undefined|Text color for content on primary background. Auto-derived from \`primary\` when omitted.|-|Optional|
|
|
318
321
|
|overlay|string \|undefined|Overlay color for modal backdrops.|-|Optional|
|
|
319
322
|
|switchTrack|string \|undefined|Toggle track color (off state).|-|Optional|
|
|
320
323
|
|switchTrackActive|string \|undefined|Toggle track color (on state).|-|Optional|
|
|
@@ -334,7 +337,7 @@ Dark mode color overrides.
|
|
|
334
337
|
|borderHover|string \|undefined|Hover state for bordered elements.|-|Optional|
|
|
335
338
|
|text|string \|undefined|Primary text color for headings and body.|-|Optional|
|
|
336
339
|
|textMuted|string \|undefined|Muted text color for secondary content.|-|Optional|
|
|
337
|
-
|textOnPrimary|string \|undefined|Text color for content on primary background.|-|Optional|
|
|
340
|
+
|textOnPrimary|string \|undefined|Text color for content on primary background. Auto-derived from \`primary\` when omitted.|-|Optional|
|
|
338
341
|
|overlay|string \|undefined|Overlay color for modal backdrops.|-|Optional|
|
|
339
342
|
|switchTrack|string \|undefined|Toggle track color (off state).|-|Optional|
|
|
340
343
|
|switchTrackActive|string \|undefined|Toggle track color (on state).|-|Optional|
|
|
@@ -420,6 +423,7 @@ Component-specific style overrides.
|
|
|
420
423
|
|consentBannerDescription|SlotStyle \|undefined|Banner description text element.|-|Optional|
|
|
421
424
|
|consentBannerFooter|SlotStyle \|undefined|Footer container for banner action buttons.|-|Optional|
|
|
422
425
|
|consentBannerFooterSubGroup|SlotStyle \|undefined|Nested button group inside the banner footer.|-|Optional|
|
|
426
|
+
|consentBannerTag|SlotStyle \|undefined|Branding tag rendered above the consent banner card.|-|Optional|
|
|
423
427
|
|consentBannerOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the banner when enabled.|-|Optional|
|
|
424
428
|
|consentDialog|SlotStyle \|undefined|Root wrapper for the consent dialog modal.|-|Optional|
|
|
425
429
|
|consentDialogCard|SlotStyle \|undefined|Main dialog card container.|-|Optional|
|
|
@@ -427,22 +431,25 @@ Component-specific style overrides.
|
|
|
427
431
|
|consentDialogTitle|SlotStyle \|undefined|Dialog title text element.|-|Optional|
|
|
428
432
|
|consentDialogDescription|SlotStyle \|undefined|Dialog description text element.|-|Optional|
|
|
429
433
|
|consentDialogContent|SlotStyle \|undefined|Dialog content region (typically holds ConsentWidget).|-|Optional|
|
|
430
|
-
|consentDialogFooter|SlotStyle \|undefined|
|
|
434
|
+
|consentDialogFooter|SlotStyle \|undefined|Footer container used by compound dialog layouts.|-|Optional|
|
|
435
|
+
|consentDialogTag|SlotStyle \|undefined|Branding tag rendered below the stock consent dialog card.|-|Optional|
|
|
431
436
|
|consentDialogOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the dialog.|-|Optional|
|
|
432
437
|
|consentWidget|SlotStyle \|undefined|Root wrapper for the consent widget/preferences panel.|-|Optional|
|
|
433
438
|
|consentWidgetAccordion|SlotStyle \|undefined|Accordion region listing consent categories.|-|Optional|
|
|
434
439
|
|consentWidgetFooter|SlotStyle \|undefined|Footer area for widget actions and links.|-|Optional|
|
|
435
|
-
|
|
|
440
|
+
|consentWidgetTag|SlotStyle \|undefined|Branding tag rendered below the standalone consent widget.|-|Optional|
|
|
436
441
|
|frame|SlotStyle \|undefined|Frame wrapper used by blocking placeholders (e.g., iframe blocking).|-|Optional|
|
|
437
442
|
|iabConsentBanner|SlotStyle \|undefined|Root wrapper for the IAB consent banner.|-|Optional|
|
|
438
443
|
|iabConsentBannerCard|SlotStyle \|undefined|Main card container for IAB banner content.|-|Optional|
|
|
439
444
|
|iabConsentBannerHeader|SlotStyle \|undefined|Header region for IAB banner title/description.|-|Optional|
|
|
440
445
|
|iabConsentBannerFooter|SlotStyle \|undefined|Footer container for IAB banner actions.|-|Optional|
|
|
446
|
+
|iabConsentBannerTag|SlotStyle \|undefined|Branding tag rendered above the IAB banner card.|-|Optional|
|
|
441
447
|
|iabConsentBannerOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the IAB banner.|-|Optional|
|
|
442
448
|
|iabConsentDialog|SlotStyle \|undefined|Root wrapper for the IAB consent dialog.|-|Optional|
|
|
443
449
|
|iabConsentDialogCard|SlotStyle \|undefined|Main card container for IAB dialog content.|-|Optional|
|
|
444
450
|
|iabConsentDialogHeader|SlotStyle \|undefined|Header region for IAB dialog title/description.|-|Optional|
|
|
445
451
|
|iabConsentDialogFooter|SlotStyle \|undefined|Footer container for IAB dialog actions.|-|Optional|
|
|
452
|
+
|iabConsentDialogTag|SlotStyle \|undefined|Branding tag rendered below the IAB dialog card.|-|Optional|
|
|
446
453
|
|iabConsentDialogOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the IAB dialog.|-|Optional|
|
|
447
454
|
|buttonPrimary|SlotStyle \|undefined|Shared primary button style used across consent components.|-|Optional|
|
|
448
455
|
|buttonSecondary|SlotStyle \|undefined|Shared secondary button style used across consent components.|-|Optional|
|
package/docs/styling/slots.md
CHANGED
|
@@ -4,7 +4,7 @@ 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 such as `consentBannerTitle` and `
|
|
7
|
+
Slots let you target specific parts of consent components with styles. Each component is built from named slots such as `consentBannerTitle` and `consentDialogTag`.
|
|
8
8
|
|
|
9
9
|
Use slots after the stock component APIs and design tokens:
|
|
10
10
|
|
|
@@ -88,6 +88,7 @@ Use the typed API reference below for the full slot list and descriptions. It st
|
|
|
88
88
|
|consentBannerDescription|SlotStyle \|undefined|Banner description text element.|-|Optional|
|
|
89
89
|
|consentBannerFooter|SlotStyle \|undefined|Footer container for banner action buttons.|-|Optional|
|
|
90
90
|
|consentBannerFooterSubGroup|SlotStyle \|undefined|Nested button group inside the banner footer.|-|Optional|
|
|
91
|
+
|consentBannerTag|SlotStyle \|undefined|Branding tag rendered above the consent banner card.|-|Optional|
|
|
91
92
|
|consentBannerOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the banner when enabled.|-|Optional|
|
|
92
93
|
|consentDialog|SlotStyle \|undefined|Root wrapper for the consent dialog modal.|-|Optional|
|
|
93
94
|
|consentDialogCard|SlotStyle \|undefined|Main dialog card container.|-|Optional|
|
|
@@ -95,22 +96,25 @@ Use the typed API reference below for the full slot list and descriptions. It st
|
|
|
95
96
|
|consentDialogTitle|SlotStyle \|undefined|Dialog title text element.|-|Optional|
|
|
96
97
|
|consentDialogDescription|SlotStyle \|undefined|Dialog description text element.|-|Optional|
|
|
97
98
|
|consentDialogContent|SlotStyle \|undefined|Dialog content region (typically holds ConsentWidget).|-|Optional|
|
|
98
|
-
|consentDialogFooter|SlotStyle \|undefined|
|
|
99
|
+
|consentDialogFooter|SlotStyle \|undefined|Footer container used by compound dialog layouts.|-|Optional|
|
|
100
|
+
|consentDialogTag|SlotStyle \|undefined|Branding tag rendered below the stock consent dialog card.|-|Optional|
|
|
99
101
|
|consentDialogOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the dialog.|-|Optional|
|
|
100
102
|
|consentWidget|SlotStyle \|undefined|Root wrapper for the consent widget/preferences panel.|-|Optional|
|
|
101
103
|
|consentWidgetAccordion|SlotStyle \|undefined|Accordion region listing consent categories.|-|Optional|
|
|
102
104
|
|consentWidgetFooter|SlotStyle \|undefined|Footer area for widget actions and links.|-|Optional|
|
|
103
|
-
|
|
|
105
|
+
|consentWidgetTag|SlotStyle \|undefined|Branding tag rendered below the standalone consent widget.|-|Optional|
|
|
104
106
|
|frame|SlotStyle \|undefined|Frame wrapper used by blocking placeholders (e.g., iframe blocking).|-|Optional|
|
|
105
107
|
|iabConsentBanner|SlotStyle \|undefined|Root wrapper for the IAB consent banner.|-|Optional|
|
|
106
108
|
|iabConsentBannerCard|SlotStyle \|undefined|Main card container for IAB banner content.|-|Optional|
|
|
107
109
|
|iabConsentBannerHeader|SlotStyle \|undefined|Header region for IAB banner title/description.|-|Optional|
|
|
108
110
|
|iabConsentBannerFooter|SlotStyle \|undefined|Footer container for IAB banner actions.|-|Optional|
|
|
111
|
+
|iabConsentBannerTag|SlotStyle \|undefined|Branding tag rendered above the IAB banner card.|-|Optional|
|
|
109
112
|
|iabConsentBannerOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the IAB banner.|-|Optional|
|
|
110
113
|
|iabConsentDialog|SlotStyle \|undefined|Root wrapper for the IAB consent dialog.|-|Optional|
|
|
111
114
|
|iabConsentDialogCard|SlotStyle \|undefined|Main card container for IAB dialog content.|-|Optional|
|
|
112
115
|
|iabConsentDialogHeader|SlotStyle \|undefined|Header region for IAB dialog title/description.|-|Optional|
|
|
113
116
|
|iabConsentDialogFooter|SlotStyle \|undefined|Footer container for IAB dialog actions.|-|Optional|
|
|
117
|
+
|iabConsentDialogTag|SlotStyle \|undefined|Branding tag rendered below the IAB dialog card.|-|Optional|
|
|
114
118
|
|iabConsentDialogOverlay|SlotStyle \|undefined|Backdrop overlay rendered behind the IAB dialog.|-|Optional|
|
|
115
119
|
|buttonPrimary|SlotStyle \|undefined|Shared primary button style used across consent components.|-|Optional|
|
|
116
120
|
|buttonSecondary|SlotStyle \|undefined|Shared secondary button style used across consent components.|-|Optional|
|
package/docs/styling/tokens.md
CHANGED
|
@@ -6,6 +6,8 @@ description: The six base token categories that control colors, typography, spac
|
|
|
6
6
|
|
|
7
7
|
Color tokens define the palette for all consent components. Set `colors` for light mode and `dark` for dark mode overrides.
|
|
8
8
|
|
|
9
|
+
When `textOnPrimary` is omitted, c15t derives it automatically from `primary` to keep text readable on primary-filled surfaces such as stock branding tags and buttons. Set `textOnPrimary` explicitly when you need a specific foreground color.
|
|
10
|
+
|
|
9
11
|
```tsx
|
|
10
12
|
const theme = {
|
|
11
13
|
colors: {
|
|
@@ -45,7 +47,7 @@ const theme = {
|
|
|
45
47
|
|borderHover|string \|undefined|Hover state for bordered elements.|-|Optional|
|
|
46
48
|
|text|string \|undefined|Primary text color for headings and body.|-|Optional|
|
|
47
49
|
|textMuted|string \|undefined|Muted text color for secondary content.|-|Optional|
|
|
48
|
-
|textOnPrimary|string \|undefined|Text color for content on primary background.|-|Optional|
|
|
50
|
+
|textOnPrimary|string \|undefined|Text color for content on primary background. Auto-derived from \`primary\` when omitted.|-|Optional|
|
|
49
51
|
|overlay|string \|undefined|Overlay color for modal backdrops.|-|Optional|
|
|
50
52
|
|switchTrack|string \|undefined|Toggle track color (off state).|-|Optional|
|
|
51
53
|
|switchTrackActive|string \|undefined|Toggle track color (on state).|-|Optional|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c15t/nextjs",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"url": "https://github.com/c15t/c15t.git",
|
|
27
27
|
"directory": "packages/nextjs"
|
|
28
28
|
},
|
|
29
|
-
"license": "
|
|
29
|
+
"license": "Apache-2.0",
|
|
30
30
|
"sideEffects": [
|
|
31
31
|
"**/*.css"
|
|
32
32
|
],
|
|
@@ -75,17 +75,17 @@
|
|
|
75
75
|
"dev": "bun prebuild && rslib build && bun ../../scripts/normalize-dist-types.mjs && bun scripts/generate-distribution-css.ts",
|
|
76
76
|
"fmt": "bun biome format --write . && bun biome check --formatter-enabled=false --linter-enabled=false --write",
|
|
77
77
|
"lint": "bun biome lint ./src",
|
|
78
|
-
"prepack": "
|
|
78
|
+
"prepack": "bun run build",
|
|
79
79
|
"test": "bun prebuild && vitest run --passWithNoTests",
|
|
80
80
|
"test:watch": "bun prebuild && vitest --passWithNoTests"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@c15t/react": "2.0.0
|
|
84
|
-
"@c15t/translations": "2.0.0
|
|
85
|
-
"c15t": "2.0.0
|
|
83
|
+
"@c15t/react": "2.0.0",
|
|
84
|
+
"@c15t/translations": "2.0.0",
|
|
85
|
+
"c15t": "2.0.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
|
-
"@c15t/typescript-config": "0.0.1
|
|
88
|
+
"@c15t/typescript-config": "0.0.1",
|
|
89
89
|
"@c15t/vitest-config": "1.0.0",
|
|
90
90
|
"genversion": "3.2.0",
|
|
91
91
|
"typescript": "6.0.2"
|