@c15t/nextjs 2.0.0-rc.9 → 2.0.2
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 +26 -21
- package/dist/iab/styles.tw3.css +54 -11
- package/dist/styles.tw3.css +70 -10
- 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/ai-agents.md +111 -0
- 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 +13 -13
- package/docs/components/consent-widget.md +1 -28
- package/docs/components/dev-tools.md +33 -0
- 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 +8 -6
- package/docs/iab/consent-dialog.md +8 -6
- package/docs/iab/overview.md +13 -12
- package/docs/iab/use-gvl-data.md +11 -199
- package/docs/internationalization.md +1 -1
- package/docs/optimization.md +42 -23
- package/docs/policy-packs.md +1 -1
- package/docs/quickstart.md +9 -9
- 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/tailwind.md +23 -1
- package/docs/styling/tokens.md +3 -1
- package/iab/styles.tw3.css +1 -0
- package/package.json +31 -14
- package/readme.json +5 -5
- package/styles.tw3.css +1 -0
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
title: IABConsentDialog
|
|
3
3
|
description: An IAB TCF 2.3 compliant preference center with tabbed purpose and vendor management.
|
|
4
4
|
---
|
|
5
|
-
>
|
|
6
|
-
> c15t
|
|
5
|
+
> ℹ️ **Info:**
|
|
6
|
+
> c15t's IAB TCF support can be used in production. Use Inth for a hosted, IAB TCF-certified CMP setup with a managed CMP ID, or register your own CMP with IAB Europe and configure your own CMP ID.
|
|
7
7
|
|
|
8
8
|
`IABConsentDialog` is an IAB TCF 2.3 compliant consent dialog that provides a tabbed interface for managing purpose consent and vendor preferences. It includes purpose grouping via stacks, individual purpose/vendor toggles, special purpose and feature disclosures, and legitimate interest handling.
|
|
9
9
|
|
|
@@ -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 Inth.
|
|
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](https://inth.com), c15t's hosted platform, is IAB TCF certified. When you use Inth as your backend with c15t's prebuilt IAB UI, the correct CMP ID is automatically provided to your client via the `/init` endpoint — no client-side configuration needed.
|
|
19
19
|
|
|
20
|
-
If you self-host the c15t backend
|
|
20
|
+
If you self-host the c15t backend or want to operate as your own CMP, register your own CMP with IAB Europe and configure your CMP ID on the backend via `advanced.iab.cmpId` or on the client via the `iab.cmpId` option. Registering your own CMP may also involve IAB Europe fees, so check IAB Europe's current CMP registration terms and pricing before choosing this route. 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
|
|
23
|
+
> If you heavily customize or build your own IAB banner or dialog instead of using the default IABConsentBanner and IABConsentDialog components, you cannot use Inth'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 with the prebuilt IAB UI. 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
|
-
> ❌ **Error:**
|
|
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
|
-
|
|
8
|
-
`useGVLData()` processes the raw IAB Global Vendor List (GVL) into a UI-friendly format. It handles purpose grouping into stacks, vendor mapping, special purpose/feature extraction, and loading state.
|
|
9
|
-
|
|
10
|
-
Use this hook when building a custom IAB TCF UI instead of the pre-built `IABConsentDialog`.
|
|
11
|
-
|
|
12
|
-
```tsx
|
|
13
|
-
import { useGVLData } from '@c15t/react/hooks';
|
|
14
|
-
|
|
15
|
-
function CustomIABPreferences() {
|
|
16
|
-
const { purposes, stacks, standalonePurposes, totalVendors, isLoading } = useGVLData();
|
|
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
|
-
```
|
|
34
|
-
|
|
35
5
|
> ℹ️ **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
|
|
6
|
+
> c15t's IAB TCF support can be used in production through Inth or with your own registered CMP ID. useGVLData() is different: it remains an internal hook and is not part of the supported public API.
|
|
80
7
|
|
|
81
|
-
|
|
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|
|
|
8
|
+
`useGVLData()` currently powers the built-in `IABConsentDialog`, but it is **not part of the public package surface**.
|
|
89
9
|
|
|
90
|
-
|
|
10
|
+
Older docs showed it as a public hook. That is no longer accurate.
|
|
91
11
|
|
|
92
|
-
|
|
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|
|
|
12
|
+
If you need supported customization points today:
|
|
100
13
|
|
|
101
|
-
|
|
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
|
|
102
16
|
|
|
103
|
-
|
|
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
|
|
17
|
+
> ℹ️ **Info:**
|
|
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
|
|
|
@@ -39,7 +39,7 @@ availableIn:
|
|
|
39
39
|
@import "@c15t/nextjs/styles.css";
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Keeping the c15t stylesheet in your global CSS entrypoint makes layer and cascade order explicit. JS/TSX side-effect imports can load in a different order across framework and Tailwind tooling, which makes style regressions harder to debug.
|
|
42
|
+
Keeping the c15t stylesheet in your global CSS entrypoint makes layer and cascade order explicit. JS/TSX side-effect imports can load in a different order across framework and Tailwind tooling, which makes style regressions harder to debug.With Tailwind v4, keep c15t at the end of the top-level @import block so Fumadocs, tw-animate-css, and other preset imports do not override c15t theme tokens.
|
|
43
43
|
|
|
44
44
|
> ℹ️ Info:
|
|
45
45
|
>
|
|
@@ -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|
|