@c15t/react 2.0.0-rc.6 → 2.0.0-rc.8
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 +9 -0
- package/dist/components/consent-banner/atoms/root.cjs +1 -1
- package/dist/components/consent-banner/atoms/root.js +1 -1
- package/dist/components/consent-banner/consent-banner.cjs +1 -1
- package/dist/components/consent-banner/consent-banner.js +1 -1
- package/dist/components/consent-banner/index.cjs +1 -1
- package/dist/components/consent-banner/index.js +1 -1
- package/dist/components/consent-banner/policy-actions.cjs +1 -0
- package/dist/components/consent-banner/policy-actions.js +1 -0
- package/dist/components/consent-dialog/atoms/card.cjs +1 -1
- package/dist/components/consent-dialog/atoms/card.js +1 -1
- package/dist/components/consent-dialog/atoms/root.cjs +1 -1
- package/dist/components/consent-dialog/atoms/root.js +1 -1
- package/dist/components/consent-dialog-trigger/atoms/icon.cjs +1 -1
- package/dist/components/consent-dialog-trigger/atoms/icon.js +1 -1
- package/dist/components/consent-widget/atoms/accordion.cjs +1 -1
- package/dist/components/consent-widget/atoms/accordion.js +1 -1
- package/dist/components/consent-widget/atoms/footer.cjs +1 -1
- package/dist/components/consent-widget/atoms/footer.js +1 -1
- package/dist/components/consent-widget/consent-widget.cjs +1 -1
- package/dist/components/consent-widget/consent-widget.js +1 -1
- package/dist/components/consent-widget/index.cjs +1 -1
- package/dist/components/consent-widget/index.js +1 -1
- package/dist/components/consent-widget/policy-actions.cjs +1 -0
- package/dist/components/consent-widget/policy-actions.js +1 -0
- package/dist/components/iab-consent-banner/atoms/card.cjs +1 -1
- package/dist/components/iab-consent-banner/atoms/card.js +1 -1
- package/dist/components/iab-consent-banner/atoms/footer.cjs +1 -1
- package/dist/components/iab-consent-banner/atoms/footer.js +1 -1
- package/dist/components/iab-consent-banner/atoms/header.cjs +1 -1
- package/dist/components/iab-consent-banner/atoms/header.js +1 -1
- package/dist/components/iab-consent-banner/atoms/root.cjs +1 -1
- package/dist/components/iab-consent-banner/atoms/root.js +1 -1
- package/dist/components/iab-consent-banner/iab-consent-banner.cjs +1 -1
- package/dist/components/iab-consent-banner/iab-consent-banner.js +1 -1
- package/dist/components/iab-consent-dialog/atoms/card.cjs +1 -1
- package/dist/components/iab-consent-dialog/atoms/card.js +1 -1
- package/dist/components/iab-consent-dialog/atoms/footer.cjs +1 -1
- package/dist/components/iab-consent-dialog/atoms/footer.js +1 -1
- package/dist/components/iab-consent-dialog/atoms/header.cjs +1 -1
- package/dist/components/iab-consent-dialog/atoms/header.js +1 -1
- package/dist/components/iab-consent-dialog/atoms/root.cjs +1 -1
- package/dist/components/iab-consent-dialog/atoms/root.js +1 -1
- package/dist/components/iab-consent-dialog/iab-consent-dialog.cjs +1 -1
- package/dist/components/iab-consent-dialog/iab-consent-dialog.js +1 -1
- package/dist/components/shared/policy-actions.cjs +1 -0
- package/dist/components/shared/policy-actions.js +1 -0
- package/dist/components/shared/primitives/box.cjs +1 -1
- package/dist/components/shared/primitives/box.js +1 -1
- package/dist/components/shared/ui/branding.cjs +1 -0
- package/dist/components/shared/ui/branding.js +1 -0
- package/dist/components/shared/ui/logo.cjs +1 -1
- package/dist/components/shared/ui/logo.js +1 -1
- package/dist/components/shared/ui/switch/index.cjs +1 -1
- package/dist/components/shared/ui/switch/index.js +1 -1
- package/dist/hooks/index.cjs +1 -1
- package/dist/hooks/use-consent-manager.cjs +1 -1
- package/dist/hooks/use-consent-manager.js +1 -1
- package/dist/hooks/use-headless-consent-ui.cjs +1 -1
- package/dist/hooks/use-headless-consent-ui.js +1 -1
- package/dist/iab/styles.css +11 -1
- package/dist/iab/styles.tw3.css +14 -0
- package/dist/providers/consent-manager-provider.cjs +1 -1
- package/dist/providers/consent-manager-provider.js +1 -1
- package/dist/server/fetch-ssr-data.cjs +1 -1
- package/dist/server/fetch-ssr-data.js +1 -1
- package/dist/styles.css +10 -1
- package/dist/styles.tw3.css +13 -0
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist-types/components/consent-banner/atoms/root.d.ts +44 -13
- package/dist-types/components/consent-banner/consent-banner.d.ts +7 -2
- package/dist-types/components/consent-banner/index.d.ts +47 -24
- package/dist-types/components/consent-banner/policy-actions.d.ts +11 -0
- package/dist-types/components/consent-dialog/atoms/card.d.ts +7 -2
- package/dist-types/components/consent-dialog/atoms/root.d.ts +1 -1
- package/dist-types/components/consent-dialog/consent-dialog.d.ts +1 -1
- package/dist-types/components/consent-dialog/index.d.ts +29 -8
- package/dist-types/components/consent-dialog-trigger/atoms/icon.d.ts +1 -1
- package/dist-types/components/consent-dialog-trigger/types.d.ts +1 -1
- package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +0 -1
- package/dist-types/components/consent-widget/atoms/accordion.d.ts +4 -3
- package/dist-types/components/consent-widget/index.d.ts +26 -6
- package/dist-types/components/consent-widget/policy-actions.d.ts +11 -0
- package/dist-types/components/frame/atoms.d.ts +1 -1
- package/dist-types/components/frame/types.d.ts +1 -1
- package/dist-types/components/iab-consent-banner/atoms/root.d.ts +1 -1
- package/dist-types/components/iab-consent-banner/iab-consent-banner.d.ts +1 -1
- package/dist-types/components/iab-consent-dialog/atoms/root.d.ts +1 -1
- package/dist-types/components/iab-consent-dialog/atoms/vendor-list.d.ts +1 -1
- package/dist-types/components/iab-consent-dialog/iab-consent-dialog.d.ts +1 -1
- package/dist-types/components/shared/policy-actions.d.ts +29 -0
- package/dist-types/components/shared/primitives/button.d.ts +1 -1
- package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
- package/dist-types/components/shared/primitives/legal-links/index.d.ts +1 -1
- package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
- package/dist-types/components/shared/ui/accordion/accordion.types.d.ts +1 -1
- package/dist-types/components/shared/ui/branding.d.ts +23 -0
- package/dist-types/components/shared/ui/button/button.d.ts +1 -1
- package/dist-types/components/shared/ui/logo.d.ts +8 -0
- package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +1 -1
- package/dist-types/components/shared/ui/switch/index.d.ts +1 -1
- package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
- package/dist-types/components/shared/ui/switch/switch.types.d.ts +1 -1
- package/dist-types/components/shared/ui/tabs/tabs.d.ts +1 -1
- package/dist-types/context/consent-manager-context.d.ts +1 -1
- package/dist-types/headless.d.ts +1 -1
- package/dist-types/hooks/use-component-config.d.ts +1 -1
- package/dist-types/hooks/use-consent-manager.d.ts +1 -1
- package/dist-types/hooks/use-headless-consent-ui.d.ts +1 -1
- package/dist-types/hooks/use-ssr-status.d.ts +2 -1
- package/dist-types/hooks/use-translations.d.ts +1 -1
- package/dist-types/index.d.ts +2 -2
- package/dist-types/server/fetch-ssr-data.d.ts +1 -1
- package/dist-types/server/init-cache-key.d.ts +1 -1
- package/dist-types/server/types.d.ts +1 -1
- package/dist-types/types/consent-manager.d.ts +1 -1
- package/dist-types/types/theme/style-keys.d.ts +1 -1
- package/dist-types/types/theme/style-types.d.ts +2 -2
- package/dist-types/utils/cn.d.ts +1 -1
- package/dist-types/utils/theme-utils.d.ts +1 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/building-headless-components.md +43 -22
- package/docs/callbacks.md +76 -9
- package/docs/components/consent-banner.md +148 -24
- package/docs/components/consent-dialog.md +42 -3
- package/docs/components/consent-manager-provider.md +3 -1
- package/docs/components/consent-widget.md +91 -9
- package/docs/concepts/client-modes.md +16 -4
- package/docs/concepts/initialization-flow.md +9 -2
- package/docs/concepts/policy-packs.md +2 -2
- package/docs/headless.md +13 -7
- package/docs/hooks/use-consent-manager/overview.md +17 -3
- package/docs/hooks/use-ssr-status.md +1 -1
- package/docs/hooks/use-translations.md +1 -0
- package/docs/iab/consent-banner.md +2 -5
- package/docs/iab/consent-dialog.md +3 -6
- package/docs/iab/overview.md +11 -5
- package/docs/integrations/building-integrations.md +405 -0
- package/docs/integrations/databuddy.md +22 -5
- package/docs/integrations/google-tag-manager.md +2 -2
- package/docs/integrations/google-tag.md +2 -29
- package/docs/integrations/linkedin-insights.md +1 -1
- package/docs/integrations/meta-pixel.md +1 -1
- package/docs/integrations/microsoft-uet.md +1 -1
- package/docs/integrations/overview.md +18 -2
- package/docs/integrations/posthog.md +39 -17
- package/docs/integrations/tiktok-pixel.md +1 -1
- package/docs/integrations/x-pixel.md +1 -1
- package/docs/optimization.md +2 -6
- package/docs/policy-packs.md +7 -7
- package/docs/quickstart.md +11 -5
- package/docs/script-loader.md +22 -1
- package/docs/styling/classnames.md +17 -9
- package/docs/styling/overview.md +166 -29
- package/docs/styling/slots.md +37 -7
- package/docs/styling/tailwind.md +25 -27
- package/iab/styles.css +1 -0
- package/package.json +15 -9
- package/readme.json +6 -0
- package/src/iab/styles.css +11 -0
- package/src/iab/styles.tw3.css +14 -0
- package/src/styles.css +10 -0
- package/src/styles.tw3.css +13 -0
- package/styles.css +1 -0
- package/dist/components/shared/libs/policy-actions.cjs +0 -1
- package/dist/components/shared/libs/policy-actions.js +0 -1
- package/dist-types/components/shared/libs/policy-actions.d.ts +0 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: PostHog
|
|
3
3
|
description: PostHog is an open-source product analytics platform for tracking user behavior, session replays, feature flags, and A/B testing. It supports cookieless tracking, allowing analytics to continue even without cookie consent.
|
|
4
|
-
lastModified:
|
|
4
|
+
lastModified: 2026-04-08
|
|
5
5
|
|
|
6
6
|
icon: posthog
|
|
7
7
|
---
|
|
@@ -25,7 +25,7 @@ This is the recommended approach if you're using the Posthog JS SDK, this is com
|
|
|
25
25
|
posthog.opt_out_capturing() // Avoids accidental tracking without consent till c15t has loaded
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
2. **
|
|
28
|
+
2. **Sync settled consent once, then subscribe to real changes** The recommended PostHog SDK approach uses two phases: run one initial sync after c15t has finished resolving consent, then subscribe to future real preference changes with subscribeToConsentChanges().
|
|
29
29
|
|
|
30
30
|
> ℹ️ Info:
|
|
31
31
|
>
|
|
@@ -35,20 +35,38 @@ This is the recommended approach if you're using the Posthog JS SDK, this is com
|
|
|
35
35
|
import { getOrCreateConsentRuntime } from 'c15t';
|
|
36
36
|
import { posthog } from 'posthog-js';
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
function syncPostHogMeasurementConsent(hasMeasurementConsent: boolean) {
|
|
39
|
+
if (hasMeasurementConsent) {
|
|
40
|
+
posthog.opt_in_capturing();
|
|
41
|
+
} else {
|
|
42
|
+
posthog.opt_out_capturing();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const runtime = getOrCreateConsentRuntime({
|
|
39
47
|
mode: 'hosted',
|
|
40
48
|
callbacks: {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
+
onBannerFetched() {
|
|
50
|
+
syncPostHogMeasurementConsent(
|
|
51
|
+
runtime.consentStore.getState().has('measurement')
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
49
55
|
});
|
|
56
|
+
|
|
57
|
+
runtime.consentStore
|
|
58
|
+
.getState()
|
|
59
|
+
.subscribeToConsentChanges(({ allowedCategories }) => {
|
|
60
|
+
syncPostHogMeasurementConsent(
|
|
61
|
+
allowedCategories.includes('measurement')
|
|
62
|
+
);
|
|
63
|
+
});
|
|
50
64
|
```
|
|
51
65
|
|
|
66
|
+
> ℹ️ Info:
|
|
67
|
+
>
|
|
68
|
+
> Avoid using onConsentSet plus manual deduplication for PostHog. subscribeToConsentChanges() already gives you the exact change-only semantics most analytics SDKs need.
|
|
69
|
+
|
|
52
70
|
## PostHog Script Implementation
|
|
53
71
|
|
|
54
72
|
If you want to load posthog via a script tag it's recommended to use this approach.
|
|
@@ -80,8 +98,13 @@ By default c15t will always load the script regardless of consent. This is becau
|
|
|
80
98
|
posthog({
|
|
81
99
|
id: 'phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
|
82
100
|
apiHost: 'https://eu.i.posthog.com',
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
scriptUrl: 'https://eu-assets.i.posthog.com/static/array.js',
|
|
102
|
+
initOptions: {
|
|
103
|
+
api_host: 'https://eu.i.posthog.com',
|
|
104
|
+
ui_host: 'https://eu.i.posthog.com',
|
|
105
|
+
autocapture: false,
|
|
106
|
+
person_profiles: 'identified_only',
|
|
107
|
+
}
|
|
85
108
|
})
|
|
86
109
|
```
|
|
87
110
|
|
|
@@ -92,10 +115,9 @@ By default c15t will always load the script regardless of consent. This is becau
|
|
|
92
115
|
|Property|Type|Description|Default|Required|
|
|
93
116
|
|:--|:--|:--|:--|:--:|
|
|
94
117
|
|id|string|Your posthog id, begins with 'phc\_'.|-|✅ Required|
|
|
95
|
-
|apiHost|string|Your posthog api host.|'https\://eu.i.posthog.com'
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|script|Script \|undefined|Override or extend the default script values. Options: \`id\`: 'posthog-consent'; \`category\`: 'measurement'|-|Optional|
|
|
118
|
+
|apiHost|string \|undefined|Your posthog api host.|'https\://eu.i.posthog.com'|Optional|
|
|
119
|
+
|scriptUrl|string \|undefined|The PostHog array loader URL.|-|Optional|
|
|
120
|
+
|initOptions|Record\<string, unknown> \|undefined|PostHog init options passed to \`posthog.init(...)\`.|-|Optional|
|
|
99
121
|
|
|
100
122
|
### Script
|
|
101
123
|
|
|
@@ -31,7 +31,7 @@ tiktokPixel({
|
|
|
31
31
|
|Property|Type|Description|Default|Required|
|
|
32
32
|
|:--|:--|:--|:--|:--:|
|
|
33
33
|
|pixelId|string|Your TikTok Pixel ID|-|✅ Required|
|
|
34
|
-
|
|
|
34
|
+
|scriptSrc|string \|undefined|TikTok Pixel loader base URL.|-|Optional|
|
|
35
35
|
|
|
36
36
|
### Script
|
|
37
37
|
|
|
@@ -37,7 +37,7 @@ xPixelEvent('tw-xxxx-xxxx', { value: 10.00, currency: 'USD' });
|
|
|
37
37
|
|Property|Type|Description|Default|Required|
|
|
38
38
|
|:--|:--|:--|:--|:--:|
|
|
39
39
|
|pixelId|string|Your X Pixel ID|-|✅ Required|
|
|
40
|
-
|
|
|
40
|
+
|scriptSrc|string \|undefined|X Pixel loader URL.|-|Optional|
|
|
41
41
|
|
|
42
42
|
### Script
|
|
43
43
|
|
package/docs/optimization.md
CHANGED
|
@@ -50,7 +50,7 @@ const prefetchScript = buildPrefetchScript({
|
|
|
50
50
|
</head>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Then
|
|
53
|
+
Then initialize your provider normally. Matching prefetched data is consumed automatically by the runtime during first store initialization:
|
|
54
54
|
|
|
55
55
|
```tsx
|
|
56
56
|
import {
|
|
@@ -58,7 +58,6 @@ import {
|
|
|
58
58
|
ConsentBanner,
|
|
59
59
|
ConsentDialog,
|
|
60
60
|
} from '@c15t/react';
|
|
61
|
-
import { getPrefetchedInitialData } from 'c15t';
|
|
62
61
|
|
|
63
62
|
export default function App({ children }: { children: React.ReactNode }) {
|
|
64
63
|
return (
|
|
@@ -66,9 +65,6 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|
|
66
65
|
options={{
|
|
67
66
|
mode: 'hosted',
|
|
68
67
|
backendURL: '/api/c15t',
|
|
69
|
-
ssrData: getPrefetchedInitialData({
|
|
70
|
-
backendURL: '/api/c15t',
|
|
71
|
-
}),
|
|
72
68
|
}}
|
|
73
69
|
>
|
|
74
70
|
<ConsentBanner />
|
|
@@ -80,7 +76,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|
|
80
76
|
```
|
|
81
77
|
|
|
82
78
|
> ℹ️ **Info:**
|
|
83
|
-
> If
|
|
79
|
+
> If overrides.gpc conflicts with the browser's ambient GPC signal, the prefetched entry is not reused and c15t falls back to a normal client /init.
|
|
84
80
|
|
|
85
81
|
## Keep The Provider Mounted Across Navigation
|
|
86
82
|
|
package/docs/policy-packs.md
CHANGED
|
@@ -6,7 +6,7 @@ Policy packs configure how c15t handles regional consent — which model (opt-in
|
|
|
6
6
|
|
|
7
7
|
**For most apps, you just need a `ConsentManagerProvider` pointing at your backend with presets configured there.** The frontend receives the resolved policy via the `/init` response — no client-side policy config required.
|
|
8
8
|
|
|
9
|
-
When a backend isn't available — local development, static previews, Storybook, or as a resilience fallback — you can pass policies directly to the provider via `offlinePolicy.policyPacks` and c15t resolves them locally.
|
|
9
|
+
When a backend isn't available — local development, static previews, Storybook, automated tests, or as a resilience fallback during a temporary outage — you can pass policies directly to the provider via `offlinePolicy.policyPacks` and c15t resolves them locally.
|
|
10
10
|
|
|
11
11
|
> ℹ️ **Info:**
|
|
12
12
|
> For QA and testing, use the c15t DevTools to simulate different regions and policy responses against your real backend, rather than switching to offline mode.
|
|
@@ -29,9 +29,9 @@ When using consent.io or a self-hosted backend, the provider connects automatica
|
|
|
29
29
|
|
|
30
30
|
The backend resolves the correct policy based on the visitor's geo data and returns it in the `/init` response. Configure your presets on the backend side.
|
|
31
31
|
|
|
32
|
-
## Offline Presets (Fallback)
|
|
32
|
+
## Offline Presets (Development and Fallback)
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Use offline presets mainly for local development, Storybook, deterministic tests, or temporary backend outages:
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
37
37
|
import { policyPackPresets } from '@c15t/react';
|
|
@@ -105,7 +105,7 @@ export function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
105
105
|
|
|
106
106
|
## Offline / Fallback
|
|
107
107
|
|
|
108
|
-
For local development,
|
|
108
|
+
For local development, previews, automated tests, or when the backend is temporarily unreachable, pass policies directly:
|
|
109
109
|
|
|
110
110
|
```tsx
|
|
111
111
|
import type { ReactNode } from 'react';
|
|
@@ -144,7 +144,7 @@ export function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
144
144
|
## Provider Shape
|
|
145
145
|
|
|
146
146
|
Configure packs through `offlinePolicy.policyPacks`. Add `offlinePolicy.i18n`
|
|
147
|
-
when you want
|
|
147
|
+
when you want local previews or fallback behavior to mirror hosted policy-profile language behavior:
|
|
148
148
|
|
|
149
149
|
```tsx
|
|
150
150
|
<ConsentManagerProvider
|
|
@@ -203,7 +203,7 @@ With that setup, offline mode resolves language the same way as hosted mode:
|
|
|
203
203
|
|`offlinePolicy: { policyPacks: [] }`|Explicit no-banner mode|
|
|
204
204
|
|Non-empty pack, no match, no default|Explicit no-banner mode|
|
|
205
205
|
|
|
206
|
-
Omitting the option gives you a safe opt-in default for local development and outage scenarios. Providing it tells c15t you want
|
|
206
|
+
Omitting the option gives you a safe opt-in default for local development and outage scenarios. Providing it tells c15t you want deterministic preview or fallback behavior exactly as configured.
|
|
207
207
|
|
|
208
208
|
## QA and Debugging
|
|
209
209
|
|
|
@@ -215,7 +215,7 @@ For deeper inspection:
|
|
|
215
215
|
* Open the DevTools Policy panel to inspect matcher resolution and fingerprints
|
|
216
216
|
* Compare your frontend preview with the backend `/init` response before shipping
|
|
217
217
|
|
|
218
|
-
If you need fully deterministic resolution without a backend (
|
|
218
|
+
If you need fully deterministic resolution without a backend during testing or preview work (for example, in automated tests or Storybook), pair `offlinePolicy.policyPacks` with `overrides`:
|
|
219
219
|
|
|
220
220
|
```tsx
|
|
221
221
|
options={{
|
package/docs/quickstart.md
CHANGED
|
@@ -33,15 +33,17 @@ availableIn:
|
|
|
33
33
|
|yarn|`yarn add @c15t/react`|
|
|
34
34
|
|bun|`bun add @c15t/react`|
|
|
35
35
|
|
|
36
|
-
2. **Import styles** Import the prebuilt component stylesheet
|
|
36
|
+
2. **Import styles** Import the prebuilt component stylesheet in your app-level CSS entrypoint. This is required for styled components to render correctly.
|
|
37
37
|
|
|
38
|
-
```
|
|
39
|
-
import
|
|
38
|
+
```css
|
|
39
|
+
@import "@c15t/react/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.
|
|
43
|
+
|
|
42
44
|
> ℹ️ Info:
|
|
43
45
|
>
|
|
44
|
-
> If you are using the headless API or fully custom styling, you can skip this import.
|
|
46
|
+
> If you are using the headless API or fully custom styling, you can skip this import. src/main.tsx should keep importing ./index.css as usual.
|
|
45
47
|
|
|
46
48
|
3. **Create ConsentManager components** Create a provider component with the consent UI and a wrapper that re-exports it. This initializes the consent store and makes consent state available to all child components.
|
|
47
49
|
|
|
@@ -81,7 +83,11 @@ availableIn:
|
|
|
81
83
|
|
|
82
84
|
> ℹ️ Info:
|
|
83
85
|
>
|
|
84
|
-
>
|
|
86
|
+
> Hosted mode is the recommended production setup because the backend resolves jurisdiction and policy, keeps durable consent records, and lets c15t recover from temporary network failures by re-syncing later.
|
|
87
|
+
>
|
|
88
|
+
> ℹ️ Info:
|
|
89
|
+
>
|
|
90
|
+
> Don't have a backend yet? You can use mode: 'offline' for local-only consent storage, but it gives up backend audit history, server-side consent awareness, and automatic jurisdiction detection. Review the browser-only storage consequences before choosing it for production.
|
|
85
91
|
|
|
86
92
|
4. **Mount ConsentManager at the app root** Wrap your existing app tree with ConsentManager so all routes/components can access consent state.
|
|
87
93
|
|
package/docs/script-loader.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Gate third-party scripts behind consent - load Google Analytics, Me
|
|
|
4
4
|
---
|
|
5
5
|
The script loader manages third-party scripts based on consent state. Scripts are defined in the provider's `scripts` option and are automatically loaded when their required consent category is granted, and unloaded when consent is revoked.
|
|
6
6
|
|
|
7
|
-
c15t has a collection of premade scripts available
|
|
7
|
+
c15t has a collection of premade scripts available in `@c15t/scripts`. Check the [integrations overview](/docs/integrations/overview) first before manually building a script.
|
|
8
8
|
|
|
9
9
|
|Package manager|Command|
|
|
10
10
|
|:--|:--|
|
|
@@ -15,6 +15,12 @@ c15t has a collection of premade scripts available on the @c15t/scripts package.
|
|
|
15
15
|
|
|
16
16
|
> ℹ️ **Info:**
|
|
17
17
|
> We recommend using the pre-built integrations when possible.
|
|
18
|
+
>
|
|
19
|
+
> ℹ️ **Info:**
|
|
20
|
+
> If you need a vendor we do not ship yet, see the custom integration guide. It covers both one-off Script objects and reusable manifest-backed integrations.
|
|
21
|
+
>
|
|
22
|
+
> ℹ️ **Info:**
|
|
23
|
+
> For app-specific scripts, use a plain Script object. For reusable integrations, prefer a manifest-backed helper so startup phases, consent signaling, and future server-side loading support stay structured.
|
|
18
24
|
|
|
19
25
|
## Basic Usage
|
|
20
26
|
|
|
@@ -47,6 +53,21 @@ export function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
47
53
|
}
|
|
48
54
|
```
|
|
49
55
|
|
|
56
|
+
## Choose the Right Approach
|
|
57
|
+
|
|
58
|
+
* Use a plain `Script` for one-off app code.
|
|
59
|
+
* Use a manifest-backed helper in `@c15t/scripts` for reusable integrations, contributions, or anything that needs structured startup behavior.
|
|
60
|
+
|
|
61
|
+
If you are building something reusable, start with the [custom integration guide](/docs/integrations/building-integrations) before using raw callbacks.
|
|
62
|
+
|
|
63
|
+
## Reusable Integrations
|
|
64
|
+
|
|
65
|
+
For app-specific use, raw `Script` objects are usually enough.
|
|
66
|
+
|
|
67
|
+
For reusable integrations, c15t uses a manifest-backed model in `@c15t/scripts`. That keeps startup phases, consent signaling, and vendor-specific boot logic structured instead of hidden inside large callback bodies.
|
|
68
|
+
|
|
69
|
+
If you are building an integration for multiple apps or contributing upstream, use the [custom integration guide](/docs/integrations/building-integrations).
|
|
70
|
+
|
|
50
71
|
## Script Types
|
|
51
72
|
|
|
52
73
|
### Standard Scripts
|
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
title: Class Names
|
|
3
3
|
description: Style consent components using className props and per-slot className targeting via the theme.
|
|
4
4
|
---
|
|
5
|
-
##
|
|
5
|
+
## Prefer Slots for Stock Components
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
There is no single top-level `className` contract across every pre-built consent component.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
<ConsentBanner className="my-banner" />
|
|
11
|
-
<ConsentDialog className="my-dialog" />
|
|
12
|
-
<ConsentWidget className="my-widget" />
|
|
13
|
-
```
|
|
9
|
+
For the stock `ConsentBanner`, `ConsentDialog`, and `ConsentWidget`, prefer `theme.slots` first. That keeps the markup intact and lets you target the exact part you need.
|
|
14
10
|
|
|
15
11
|
## Per-Slot className
|
|
16
12
|
|
|
@@ -59,9 +55,19 @@ const theme = {
|
|
|
59
55
|
}
|
|
60
56
|
```
|
|
61
57
|
|
|
62
|
-
##
|
|
58
|
+
## When to Use Raw className
|
|
59
|
+
|
|
60
|
+
Use raw className-level styling when:
|
|
61
|
+
|
|
62
|
+
* your styling system is already class-driven
|
|
63
|
+
* tokens are too broad for the change
|
|
64
|
+
* slots already identify the correct element
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
If the request is "make the banner footer darker", prefer `theme.colors.surfaceHover` first. If the request is "add a border and spacing only to the footer", prefer `theme.slots.consentBannerFooter`.
|
|
67
|
+
|
|
68
|
+
## Advanced: `noStyle`
|
|
69
|
+
|
|
70
|
+
Use `noStyle` only when you want to remove defaults and style from scratch while still keeping c15t's component structure:
|
|
65
71
|
|
|
66
72
|
```tsx
|
|
67
73
|
{/* Remove all styles from a specific component */}
|
|
@@ -81,4 +87,6 @@ const theme = {
|
|
|
81
87
|
} satisfies Theme;
|
|
82
88
|
```
|
|
83
89
|
|
|
90
|
+
Treat `noStyle` as an advanced escape hatch. Do not jump to it just because a token or slot needs debugging.
|
|
91
|
+
|
|
84
92
|
For full custom markup and behavior, continue to [Headless Mode](../headless).
|
package/docs/styling/overview.md
CHANGED
|
@@ -2,24 +2,34 @@
|
|
|
2
2
|
title: Styling Overview
|
|
3
3
|
description: Customize every aspect of c15t's consent components using design tokens, component slots, and CSS variables.
|
|
4
4
|
---
|
|
5
|
-
c15t's theming system gives you multiple levels of control,
|
|
5
|
+
c15t's theming system gives you multiple levels of control, but most customization should stay inside the pre-built components.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Start with the lowest-power tool that solves the problem:
|
|
8
8
|
|
|
9
|
-
1. **
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
9
|
+
1. **Pre-built component APIs** — provider options and component props such as `layout`, `direction`, `primaryButton`, `legalLinks`, and `theme.consentActions`
|
|
10
|
+
2. **Design tokens** — global colors, typography, spacing, radius, shadows, and motion
|
|
11
|
+
3. **Slots** — targeted styling for specific parts such as the banner card, footer, or title
|
|
12
|
+
4. **CSS variables or className-level overrides** — when you need to integrate with external CSS systems
|
|
13
|
+
5. **Compound components** — when you must rearrange markup while still using c15t primitives
|
|
14
|
+
6. **`noStyle`** — when you want c15t structure but you need to own all visual styling
|
|
15
|
+
7. **Headless** — when you want fully custom markup and behavior
|
|
16
|
+
|
|
17
|
+
Keep styling and escalation as separate decisions:
|
|
18
|
+
|
|
19
|
+
* If you are still using the stock banner, dialog, or widget, stay with props, tokens, and slots.
|
|
20
|
+
* Escalate to compound components, `noStyle`, or headless only when the structure or behavior itself must change.
|
|
13
21
|
|
|
14
22
|
## Styling Approaches
|
|
15
23
|
|
|
16
24
|
|Approach|Control|Use When|
|
|
17
25
|
|--|--|--|
|
|
26
|
+
|**Component and provider APIs**|High|Reordering actions, changing button emphasis, configuring links, hiding branding, changing copy via `i18n`|
|
|
18
27
|
|**Tokens**|High|Changing global colors, typography, spacing, radius, shadows, or motion|
|
|
19
|
-
|**Slots**|Medium|Targeting specific component parts (
|
|
20
|
-
|**CSS
|
|
21
|
-
|**
|
|
22
|
-
|**noStyle**|Full|
|
|
28
|
+
|**Slots**|Medium|Targeting specific component parts (for example `consentBannerFooter` or `consentDialogCard`)|
|
|
29
|
+
|**CSS variables / className**|Medium|Integrating with an existing stylesheet or utility classes after tokens and slots|
|
|
30
|
+
|**Compound components**|Structure|Rearranging existing c15t primitives without going fully custom|
|
|
31
|
+
|**noStyle**|Full visuals|Keeping c15t structure but replacing all visual defaults|
|
|
32
|
+
|**Headless**|Full|Replacing both markup and behavior|
|
|
23
33
|
|
|
24
34
|
## Quick Start
|
|
25
35
|
|
|
@@ -61,9 +71,25 @@ export function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
61
71
|
}
|
|
62
72
|
```
|
|
63
73
|
|
|
64
|
-
## Styling
|
|
74
|
+
## Styling Inside Pre-Built Components
|
|
75
|
+
|
|
76
|
+
Start here before you consider compound components or headless mode.
|
|
77
|
+
|
|
78
|
+
### 1. Provider and component configuration
|
|
79
|
+
|
|
80
|
+
Use the stock APIs first:
|
|
81
|
+
|
|
82
|
+
* `layout`, `direction`, and `primaryButton` for banner action arrangement
|
|
83
|
+
* `legalLinks` for link visibility
|
|
84
|
+
* `hideBranding` and `showTrigger` for dialog and widget behavior
|
|
85
|
+
* `theme.consentActions` for stock banner and dialog button treatment
|
|
86
|
+
* `i18n` on `ConsentManagerProvider` for copy changes
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<ConsentBanner layout={['customize', ['reject', 'accept']]} primaryButton="accept" />
|
|
90
|
+
```
|
|
65
91
|
|
|
66
|
-
###
|
|
92
|
+
### 2. Design tokens
|
|
67
93
|
|
|
68
94
|
Set global values for colors, typography, spacing, radius, shadows, and motion:
|
|
69
95
|
|
|
@@ -71,55 +97,140 @@ Set global values for colors, typography, spacing, radius, shadows, and motion:
|
|
|
71
97
|
options={{ theme: { colors: { primary: '#6366f1' } } }}
|
|
72
98
|
```
|
|
73
99
|
|
|
74
|
-
|
|
100
|
+
Use tokens first when the change is semantic:
|
|
101
|
+
|
|
102
|
+
* Banner card background -> `theme.colors.surface`
|
|
103
|
+
* Banner footer background -> `theme.colors.surfaceHover`
|
|
104
|
+
* Shared copy color -> `theme.colors.text` and `theme.colors.textMuted`
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
options={{
|
|
108
|
+
theme: {
|
|
109
|
+
colors: {
|
|
110
|
+
surface: '#ffffff',
|
|
111
|
+
surfaceHover: '#f6f3ee',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Component slots
|
|
75
118
|
|
|
76
119
|
Target specific component parts via the `slots` object:
|
|
77
120
|
|
|
78
121
|
```tsx
|
|
79
|
-
options={{
|
|
122
|
+
options={{
|
|
123
|
+
theme: {
|
|
124
|
+
slots: {
|
|
125
|
+
consentBannerCard: 'rounded-[28px] shadow-xl',
|
|
126
|
+
consentBannerFooter: 'border-t border-black/10',
|
|
127
|
+
consentBannerTitle: 'tracking-tight',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}}
|
|
80
131
|
```
|
|
81
132
|
|
|
82
|
-
|
|
133
|
+
Use slots when the component part is right but the local styling needs adjustment.
|
|
83
134
|
|
|
84
|
-
|
|
135
|
+
### 4. CSS variables and className-level overrides
|
|
85
136
|
|
|
86
|
-
|
|
137
|
+
Override `--c15t-*` custom properties in your stylesheet or attach classes through slots when your app styling is driven externally.
|
|
87
138
|
|
|
88
|
-
|
|
139
|
+
Reach for this after tokens and slots, not before.
|
|
89
140
|
|
|
90
141
|
```tsx
|
|
91
|
-
|
|
142
|
+
options={{
|
|
143
|
+
theme: {
|
|
144
|
+
slots: {
|
|
145
|
+
consentBannerFooter: 'bg-[var(--banner-footer)]',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}}
|
|
92
149
|
```
|
|
93
150
|
|
|
94
|
-
|
|
151
|
+
## Escalating Beyond Pre-Built Components
|
|
152
|
+
|
|
153
|
+
Only move up this ladder when the lower rung cannot satisfy the request.
|
|
95
154
|
|
|
96
|
-
|
|
155
|
+
### 5. Compound components
|
|
156
|
+
|
|
157
|
+
Use compound components when you need to rearrange existing c15t primitives:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<ConsentBanner.Root>
|
|
161
|
+
<ConsentBanner.Card>
|
|
162
|
+
<ConsentBanner.Header>
|
|
163
|
+
<ConsentBanner.Title />
|
|
164
|
+
<ConsentBanner.Description />
|
|
165
|
+
</ConsentBanner.Header>
|
|
166
|
+
<ConsentBanner.Footer>
|
|
167
|
+
<ConsentBanner.CustomizeButton />
|
|
168
|
+
<ConsentBanner.FooterSubGroup>
|
|
169
|
+
<ConsentBanner.RejectButton />
|
|
170
|
+
<ConsentBanner.AcceptButton />
|
|
171
|
+
</ConsentBanner.FooterSubGroup>
|
|
172
|
+
</ConsentBanner.Footer>
|
|
173
|
+
</ConsentBanner.Card>
|
|
174
|
+
</ConsentBanner.Root>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 6. `noStyle`
|
|
178
|
+
|
|
179
|
+
Use `noStyle` only when the c15t structure is still correct but you want to replace all visual defaults:
|
|
97
180
|
|
|
98
181
|
```tsx
|
|
99
182
|
<ConsentBanner noStyle />
|
|
100
183
|
```
|
|
101
184
|
|
|
185
|
+
### 7. Headless
|
|
186
|
+
|
|
187
|
+
Go headless only when you are replacing both markup and behavior. For that path, continue to [Headless Mode](../headless).
|
|
188
|
+
|
|
102
189
|
## Common Styling Tasks
|
|
103
190
|
|
|
104
|
-
### Change
|
|
191
|
+
### Change the banner footer background
|
|
105
192
|
|
|
106
193
|
```tsx
|
|
107
|
-
options={{
|
|
194
|
+
options={{
|
|
195
|
+
theme: {
|
|
196
|
+
colors: {
|
|
197
|
+
surfaceHover: '#f6f3ee',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
}}
|
|
108
201
|
```
|
|
109
202
|
|
|
110
|
-
|
|
203
|
+
Use `theme.colors.surfaceHover` before trying raw CSS.
|
|
204
|
+
|
|
205
|
+
### Change the banner card background
|
|
111
206
|
|
|
112
207
|
```tsx
|
|
113
|
-
options={{
|
|
208
|
+
options={{
|
|
209
|
+
theme: {
|
|
210
|
+
colors: {
|
|
211
|
+
surface: '#fffdf8',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
}}
|
|
114
215
|
```
|
|
115
216
|
|
|
116
|
-
|
|
217
|
+
Use `theme.colors.surface` before overriding banner CSS variables directly.
|
|
218
|
+
|
|
219
|
+
### Tweak the banner card, footer, or title styling without changing markup
|
|
117
220
|
|
|
118
221
|
```tsx
|
|
119
|
-
options={{
|
|
222
|
+
options={{
|
|
223
|
+
theme: {
|
|
224
|
+
slots: {
|
|
225
|
+
consentBannerCard: 'rounded-[28px] shadow-xl',
|
|
226
|
+
consentBannerFooter: 'border-t border-black/10 px-6',
|
|
227
|
+
consentBannerTitle: 'text-xl tracking-tight',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
}}
|
|
120
231
|
```
|
|
121
232
|
|
|
122
|
-
### Change consent action button styles semantically
|
|
233
|
+
### Change stock consent action button styles semantically
|
|
123
234
|
|
|
124
235
|
```tsx
|
|
125
236
|
options={{
|
|
@@ -135,6 +246,29 @@ options={{
|
|
|
135
246
|
|
|
136
247
|
Use `theme.consentActions` when you want to change the stock banner/dialog button treatment without rewriting the component layout. Policy packs still control action arrangement and primary-action hints. The theme controls whether those actions render as `stroke`, `filled`, `ghost`, or `lighter`.
|
|
137
248
|
|
|
249
|
+
### Change banner copy without replacing the component
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
options={{
|
|
253
|
+
i18n: {
|
|
254
|
+
locale: 'en',
|
|
255
|
+
messages: {
|
|
256
|
+
en: {
|
|
257
|
+
cookieBanner: {
|
|
258
|
+
title: 'We value your privacy',
|
|
259
|
+
description: 'We use cookies to improve the site and measure performance.',
|
|
260
|
+
},
|
|
261
|
+
common: {
|
|
262
|
+
acceptAll: 'Accept all',
|
|
263
|
+
rejectAll: 'Reject all',
|
|
264
|
+
customize: 'Manage preferences',
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
}}
|
|
270
|
+
```
|
|
271
|
+
|
|
138
272
|
### Enable dark mode safely
|
|
139
273
|
|
|
140
274
|
```tsx
|
|
@@ -147,8 +281,11 @@ options={{
|
|
|
147
281
|
}}
|
|
148
282
|
```
|
|
149
283
|
|
|
284
|
+
> ℹ️ **Info:**
|
|
285
|
+
> If a token change does not show up where you expect, check how that component maps tokens to CSS variables before escalating. For example, the stock banner footer background comes from colors.surfaceHover, not a separate footer token.
|
|
286
|
+
>
|
|
150
287
|
> ⚠️ **Warning:**
|
|
151
|
-
> noStyle: true removes layout and visual defaults.
|
|
288
|
+
> Do not jump to CSS overrides or !important because a token did not appear to work at first glance.noStyle: true removes layout and visual defaults. Treat it as an advanced opt-out, not a normal theming step.Headless mode is for replacing markup and behavior, not for styling-only requests.Use either tokens/slots or raw CSS variable overrides intentionally to avoid conflicting style sources.For dark mode, c15t supports .dark and .c15t-dark.
|
|
152
289
|
|
|
153
290
|
## API Reference
|
|
154
291
|
|