@c15t/react 3.0.0-alpha.6 → 3.0.0-alpha.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/SKILL.md +1 -1
- package/dist/components/iab-prompt/atoms/overlay.js +1 -1
- package/dist/components/iab-prompt/atoms/root.js +1 -1
- package/dist/components/prompt/atoms/overlay.js +1 -1
- package/dist/components/prompt/atoms/root.js +1 -1
- package/dist/components/prompt/prompt.js +1 -1
- package/dist/components/shared/primitives/legal-links/index.js +1 -1
- package/dist/components/shared/streamed-surface.js +2 -0
- package/dist/context.js +1 -1
- package/dist/hooks/use-late-entry.js +2 -0
- package/dist/hooks.js +1 -1
- package/dist/iab/styles.tw3.css +2 -2
- package/dist/kernel-selector.js +1 -1
- package/dist/provider.js +1 -1
- package/dist/styles.tw3.css +4 -4
- package/dist/version.js +1 -1
- package/dist-types/components/prompt/atoms/root.d.ts +4 -0
- package/dist-types/components/prompt/prompt.d.ts +4 -0
- package/dist-types/components/shared/streamed-surface.d.ts +23 -0
- package/dist-types/context.d.ts +17 -1
- package/dist-types/hooks/use-late-entry.d.ts +14 -0
- package/dist-types/kernel-selector.d.ts +10 -0
- package/dist-types/provider.d.ts +28 -6
- package/dist-types/version.d.ts +1 -1
- package/docs/concepts/choose-your-setup.md +2 -2
- package/docs/concepts/consent-state.md +6 -0
- package/docs/concepts/data-fetching.md +34 -0
- package/docs/customization/motion.md +22 -13
- package/docs/customization/tokens.md +1 -0
- package/docs/frameworks/react/components/consent-banner.md +7 -2
- package/docs/frameworks/react/components/consent-dialog.md +7 -2
- package/docs/frameworks/react/components/consent-provider.md +41 -33
- package/docs/frameworks/react/components.md +7 -2
- package/docs/frameworks/react/quickstart.md +80 -81
- package/docs/frameworks/react/rendering.md +19 -9
- package/docs/frameworks/react/scripts.md +7 -2
- package/docs/frameworks/react/troubleshooting.md +28 -13
- package/docs/guides/banner-experiments.md +2 -2
- package/docs/upgrade-v3.md +5 -1
- package/package.json +7 -7
package/dist-types/provider.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AllConsentNames, Callbacks, ClearOnRevocationConfig, ConsentExperiment, ConsentPresentation, ExperimentState, I18nConfig, KernelConfig, KernelOverrides, KernelUser, LegalLinks, ProviderTransportFactory, StorageConfig, User, Vendor } from '@c15t/core';
|
|
1
|
+
import type { AllConsentNames, Callbacks, ClearOnRevocationConfig, ConsentExperiment, ConsentJourneyOption, ConsentPresentation, ExperimentState, JourneyState, I18nConfig, KernelConfig, KernelOverrides, KernelUser, LegalLinks, ProviderTransportFactory, StorageConfig, User, Vendor } from '@c15t/core';
|
|
2
2
|
import type { Script } from '@c15t/core/modules/script-loader';
|
|
3
3
|
import type { ConsentControlOptions } from '@c15t/core/runtime';
|
|
4
4
|
import type { ConsentRuntime, ConsentRuntimeModules, ResolveStreamedInit } from '@c15t/core/runtime/provider';
|
|
@@ -13,9 +13,10 @@ export type ConsentProviderCallbacks = Pick<Callbacks, 'onChoiceRecorded' | 'onP
|
|
|
13
13
|
/**
|
|
14
14
|
* Prepared policy and records; legacy consent projections are not provider
|
|
15
15
|
* inputs. An `experiment` a server helper resolved runs instead of
|
|
16
|
-
* `options.experiment
|
|
16
|
+
* `options.experiment`, and a `journey` it started is the one the provider
|
|
17
|
+
* continues.
|
|
17
18
|
*/
|
|
18
|
-
export type ConsentProviderPrefetch = Omit<KernelConfig, 'initialDraft' | 'transport'> & ExperimentState;
|
|
19
|
+
export type ConsentProviderPrefetch = Omit<KernelConfig, 'initialDraft' | 'transport'> & ExperimentState & JourneyState;
|
|
19
20
|
export interface ConsentProviderOptions extends ConsentControlOptions, Pick<ReactUIOptions, 'colorScheme' | 'disableAnimation' | 'noStyle' | 'scrollLock' | 'theme' | 'trapFocus'> {
|
|
20
21
|
enabled?: boolean;
|
|
21
22
|
presentation?: ConsentPresentation;
|
|
@@ -26,6 +27,13 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
|
|
|
26
27
|
* provider to change the experiment.
|
|
27
28
|
*/
|
|
28
29
|
experiment?: ConsentExperiment;
|
|
30
|
+
/**
|
|
31
|
+
* Random journey id that links each `/init` to the save that follows:
|
|
32
|
+
* `'page'`, `'tab'` or `false`. Initial-only.
|
|
33
|
+
*
|
|
34
|
+
* @default 'page'
|
|
35
|
+
*/
|
|
36
|
+
journey?: ConsentJourneyOption;
|
|
29
37
|
/**
|
|
30
38
|
* Content Security Policy nonce applied to DOM nodes c15t injects.
|
|
31
39
|
*
|
|
@@ -77,9 +85,12 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
|
|
|
77
85
|
* Pass the pending `Promise<KernelConfig>` instead and the provider
|
|
78
86
|
* mounts at once with a provisional policy, so `children` render (and
|
|
79
87
|
* the static shell can prerender) while the consent data streams in.
|
|
80
|
-
* Consent surfaces stay hidden until the promise resolves
|
|
81
|
-
* `
|
|
82
|
-
*
|
|
88
|
+
* Consent surfaces stay hidden until the promise resolves. On the
|
|
89
|
+
* server, `ConsentBanner` waits for it in its own Suspense boundary and
|
|
90
|
+
* follows the page in a later chunk of the same response, before
|
|
91
|
+
* hydration, when the resolved policy shows a banner; see
|
|
92
|
+
* `streamBanner`. The resolved config then answers the first `init()`
|
|
93
|
+
* in place of the transport's network init. A config that resolves without a policy (persisted
|
|
83
94
|
* consents, geo, language only) is applied to the kernel and the
|
|
84
95
|
* transport init runs as usual; a rejected promise is logged outside
|
|
85
96
|
* production and falls through to the transport init. Initial-only:
|
|
@@ -97,6 +108,17 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
|
|
|
97
108
|
* ```
|
|
98
109
|
*/
|
|
99
110
|
prefetch?: ConsentProviderPrefetch | Promise<ConsentProviderPrefetch>;
|
|
111
|
+
/**
|
|
112
|
+
* Whether `ConsentBanner` renders on the server once a `prefetch`
|
|
113
|
+
* promise resolves, so it arrives in the streamed response before the
|
|
114
|
+
* page hydrates. It shows only when the resolved policy shows a banner
|
|
115
|
+
* for this visitor. Set `false` to mount the banner after hydration
|
|
116
|
+
* instead, as with a client-only provider. No effect when `prefetch` is
|
|
117
|
+
* not a promise. Use the same value on the server and in the browser.
|
|
118
|
+
*
|
|
119
|
+
* @default true
|
|
120
|
+
*/
|
|
121
|
+
streamBanner?: boolean;
|
|
100
122
|
callbacks?: ConsentProviderCallbacks;
|
|
101
123
|
/**
|
|
102
124
|
* Remove configured browser data when its consent permission is revoked.
|
package/dist-types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "3.0.0-alpha.
|
|
1
|
+
export declare const version = "3.0.0-alpha.8";
|
|
@@ -13,8 +13,8 @@ to the alternatives it rules out.
|
|
|
13
13
|
| Your app | Setup | Guide |
|
|
14
14
|
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
15
|
| Next.js App Router with a Next.js server | Server resolves consent from a build-time manifest and streams it to the page | [App Router](https://c15t.com/docs/frameworks/next/quickstart) |
|
|
16
|
-
| Next.js App Router,
|
|
17
|
-
| Next.js with static, ISR
|
|
16
|
+
| Next.js App Router, page must arrive with its banner | Await consent inside `Suspense` | [Stream or await](https://c15t.com/docs/frameworks/next/rendering#stream-or-await-consent-in-the-app-router) |
|
|
17
|
+
| Next.js with static, ISR, `'use cache'` or `ensureStatic = 'navigation'` pages | Browser resolves consent on those pages | [Static, ISR and cached pages](https://c15t.com/docs/frameworks/next/rendering#render-static-isr-and-cached-pages) |
|
|
18
18
|
| Next.js Pages Router | Resolve consent in `getServerSideProps` | [Pages Router](https://c15t.com/docs/frameworks/next/rendering#pick-your-rendering-path) |
|
|
19
19
|
| Next.js with `output: 'export'` | Browser resolves consent | [Static export](https://c15t.com/docs/frameworks/next/static-export) |
|
|
20
20
|
| TanStack Start with a server | Root loader resolves consent on the server | [TanStack Start](https://c15t.com/docs/frameworks/tanstack-start/quickstart) |
|
|
@@ -99,6 +99,12 @@ backend that signs policy snapshot tokens rejects a replay made after the
|
|
|
99
99
|
token expires, which is 30 minutes by default for the self-hosted backend. A
|
|
100
100
|
save still queued by then is recorded only in the browser.
|
|
101
101
|
|
|
102
|
+
A replay that fails and stays queued emits `save:replayed` with `ok: false` but
|
|
103
|
+
not `command:error`, since the save already reported its first failure. A save
|
|
104
|
+
that leaves the queue without the backend recording it, because it ran out of
|
|
105
|
+
attempts, waited more than 7 days or was refused, emits `command:error` once.
|
|
106
|
+
Development builds also log a `[c15t]` console warning for each of these.
|
|
107
|
+
|
|
102
108
|
IAB surfaces close in the click task too, but an IAB choice is recorded only
|
|
103
109
|
after its TC string is encoded, which can wait for the TCF library to load.
|
|
104
110
|
If that local step records nothing, for example because the vendor list
|
|
@@ -68,6 +68,40 @@ no identity; the visitor's IP address and user agent are forwarded under the
|
|
|
68
68
|
backend's usual IP handling. Static output resolves in the browser and sends
|
|
69
69
|
no report. Set `reportSessions: false` on an adapter to turn it off.
|
|
70
70
|
|
|
71
|
+
## Link an init to the save that follows
|
|
72
|
+
|
|
73
|
+
Each page load gets a journey id, a random UUID sent on requests c15t already
|
|
74
|
+
makes:
|
|
75
|
+
|
|
76
|
+
| Request | Query parameters |
|
|
77
|
+
| ---------------- | ------------------------------------------------------------------------------------------------ |
|
|
78
|
+
| `GET /init` | `c15tJourney`, `c15tJourneyScope`, `c15tStored` (`1` if a choice or notice dismissal was stored) |
|
|
79
|
+
| `POST /subjects` | `c15tJourney`, `c15tJourneyScope` |
|
|
80
|
+
|
|
81
|
+
Session reports carry it as `journey: { id, scope, storedChoice, prompt, domain }`.
|
|
82
|
+
A report and a save with the same `id` belong to one page load. `prompt` is
|
|
83
|
+
`due`, `stored` or `not-required`; a page that falls back to the default opt-in
|
|
84
|
+
banner counts as `due` or `stored`. A stored answer counts as `stored` even if a
|
|
85
|
+
policy change makes c15t ask again.
|
|
86
|
+
|
|
87
|
+
The id is random, is never the subject id and is never written to a cookie.
|
|
88
|
+
Every save that carries one follows an `/init` or report with the same id.
|
|
89
|
+
Saves replayed after going offline carry none.
|
|
90
|
+
|
|
91
|
+
| `journey` | Behavior |
|
|
92
|
+
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `'page'` (default) | One id per page load, in memory. |
|
|
94
|
+
| `'tab'` | Kept in `sessionStorage` while a prompt is due, and removed once the visitor chooses or no prompt is due. Only when the browser resolves init; server-rendered pages use `'page'`. |
|
|
95
|
+
| `false` | No journey. |
|
|
96
|
+
|
|
97
|
+
Set it on the runtime or provider. In Next.js use `defineConsentConfig({ journey })`;
|
|
98
|
+
in TanStack Start pass the same value to `resolveConsent` and `ConsentRoot`; with
|
|
99
|
+
the inline prefetch script, pass it to `buildPrefetchScript`. Vue, Svelte, Astro
|
|
100
|
+
and the script tag use `'page'`. A manifest that resolves locally in the
|
|
101
|
+
browser reports no init, so it sends no journey.
|
|
102
|
+
|
|
103
|
+
If you pass `hosted()` a `fetch`, match on the path: URLs now carry a query string.
|
|
104
|
+
|
|
71
105
|
## What does regular `/init` do?
|
|
72
106
|
|
|
73
107
|
`hosted({ url })` uses `${url}/init` for initialization and `${url}/subjects` for
|
|
@@ -8,20 +8,21 @@ group: customization
|
|
|
8
8
|
|
|
9
9
|
## Change the speed with motion tokens
|
|
10
10
|
|
|
11
|
-
c15t animates its surfaces with three durations and
|
|
11
|
+
c15t animates its surfaces with three durations and five easing curves. Set
|
|
12
12
|
them in the theme's `motion` object, or as CSS variables:
|
|
13
13
|
|
|
14
|
-
| CSS variable | Theme key | Default
|
|
15
|
-
| ------------------------ | ------------------------ |
|
|
16
|
-
| `--c15t-duration-fast` | `motion.duration.fast` | `80ms`
|
|
17
|
-
| `--c15t-duration-normal` | `motion.duration.normal` | `150ms`
|
|
18
|
-
| `--c15t-duration-slow` | `motion.duration.slow` | `200ms`
|
|
19
|
-
| `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)`
|
|
20
|
-
| `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)`
|
|
21
|
-
| `--c15t-easing-in
|
|
22
|
-
| `--c15t-easing-
|
|
14
|
+
| CSS variable | Theme key | Default | Used by |
|
|
15
|
+
| ------------------------ | ------------------------ | ---------------------------------------- | -------------------------------------------------------------- |
|
|
16
|
+
| `--c15t-duration-fast` | `motion.duration.fast` | `80ms` | Banner fade out, buttons, tabs |
|
|
17
|
+
| `--c15t-duration-normal` | `motion.duration.normal` | `150ms` | Preference dialog, late banner fade in, switches, accordions |
|
|
18
|
+
| `--c15t-duration-slow` | `motion.duration.slow` | `200ms` | Legal links, floating trigger |
|
|
19
|
+
| `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | Switches, accordions |
|
|
20
|
+
| `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` | Preference dialog, late banner fade in, floating trigger hover |
|
|
21
|
+
| `--c15t-easing-in` | `motion.easingIn` | `cubic-bezier(0.55, 0.055, 0.675, 0.19)` | Banner fade out |
|
|
22
|
+
| `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` | Floating trigger snapping to a corner |
|
|
23
|
+
| `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | The dialog's scale in |
|
|
23
24
|
|
|
24
|
-
This theme slows the dialog down and removes
|
|
25
|
+
This theme slows the dialog down and removes its overshoot:
|
|
25
26
|
|
|
26
27
|
```ts title="consent-theme.ts"
|
|
27
28
|
export const theme = {
|
|
@@ -32,6 +33,14 @@ export const theme = {
|
|
|
32
33
|
};
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
The banner never slides in. When it shows with the page, it is part of the
|
|
37
|
+
first paint and appears on its first frame. When it arrives more than 100ms
|
|
38
|
+
after the page first painted, such as after a slow script or a client-side
|
|
39
|
+
init, it fades in over `--c15t-duration-normal` instead, and its root and
|
|
40
|
+
backdrop carry `data-entry="late"`. A banner rendered on the server always
|
|
41
|
+
shows at once. When it hides, Vue, Svelte and the script tag fade it out over
|
|
42
|
+
`--c15t-duration-fast`.
|
|
43
|
+
|
|
35
44
|
The theme goes where your framework's other tokens go. See
|
|
36
45
|
[theme tokens](./tokens.md) and your framework's customize page.
|
|
37
46
|
In Vue and Nuxt, set `tokens` with the variable name without the leading
|
|
@@ -39,8 +48,8 @@ In Vue and Nuxt, set `tokens` with the variable name without the leading
|
|
|
39
48
|
|
|
40
49
|
## Turn animations off
|
|
41
50
|
|
|
42
|
-
`disableAnimation` removes the
|
|
43
|
-
dialogs, and the hover and snap transitions of the floating
|
|
51
|
+
`disableAnimation` removes the banner's fades, the enter and exit
|
|
52
|
+
transitions of the dialogs, and the hover and snap transitions of the floating
|
|
44
53
|
`ConsentDialogTrigger`. Set it once for every surface, then override it for one
|
|
45
54
|
surface where your framework allows:
|
|
46
55
|
|
|
@@ -130,6 +130,7 @@ easing tokens.
|
|
|
130
130
|
| `--c15t-duration-slow` | `motion.duration.slow` | `200ms` |
|
|
131
131
|
| `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` |
|
|
132
132
|
| `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` |
|
|
133
|
+
| `--c15t-easing-in` | `motion.easingIn` | `cubic-bezier(0.55, 0.055, 0.675, 0.19)` |
|
|
133
134
|
| `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` |
|
|
134
135
|
| `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
|
|
135
136
|
|
|
@@ -12,20 +12,25 @@ The [quickstart](../quickstart.md) mounts both in
|
|
|
12
12
|
`src/consent.tsx`:
|
|
13
13
|
|
|
14
14
|
```tsx title="src/consent.tsx"
|
|
15
|
+
import { manifest } from '@c15t/browser/headless';
|
|
15
16
|
import {
|
|
16
17
|
ConsentBanner,
|
|
17
18
|
ConsentDialog,
|
|
18
19
|
ConsentDialogLink,
|
|
19
20
|
ConsentProvider,
|
|
20
|
-
hosted,
|
|
21
21
|
} from 'c15t/react';
|
|
22
22
|
import type { ReactNode } from 'react';
|
|
23
23
|
|
|
24
|
+
import { consentManifest } from './c15t-manifest';
|
|
24
25
|
import { scripts } from './scripts';
|
|
25
26
|
|
|
26
27
|
import 'c15t/react/styles.css';
|
|
27
28
|
|
|
28
|
-
const mode =
|
|
29
|
+
const mode = manifest({
|
|
30
|
+
backendURL:
|
|
31
|
+
import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
32
|
+
manifest: consentManifest,
|
|
33
|
+
});
|
|
29
34
|
|
|
30
35
|
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
31
36
|
<ConsentProvider options={{ mode, scripts }}>
|
|
@@ -14,20 +14,25 @@ group: frameworks
|
|
|
14
14
|
code.
|
|
15
15
|
|
|
16
16
|
```tsx title="src/consent.tsx"
|
|
17
|
+
import { manifest } from '@c15t/browser/headless';
|
|
17
18
|
import {
|
|
18
19
|
ConsentBanner,
|
|
19
20
|
ConsentDialog,
|
|
20
21
|
ConsentDialogLink,
|
|
21
22
|
ConsentProvider,
|
|
22
|
-
hosted,
|
|
23
23
|
} from 'c15t/react';
|
|
24
24
|
import type { ReactNode } from 'react';
|
|
25
25
|
|
|
26
|
+
import { consentManifest } from './c15t-manifest';
|
|
26
27
|
import { scripts } from './scripts';
|
|
27
28
|
|
|
28
29
|
import 'c15t/react/styles.css';
|
|
29
30
|
|
|
30
|
-
const mode =
|
|
31
|
+
const mode = manifest({
|
|
32
|
+
backendURL:
|
|
33
|
+
import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
34
|
+
manifest: consentManifest,
|
|
35
|
+
});
|
|
31
36
|
|
|
32
37
|
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
33
38
|
<ConsentProvider options={{ mode, scripts }}>
|
|
@@ -14,20 +14,25 @@ The [quickstart](../quickstart.md) does this in
|
|
|
14
14
|
`src/consent.tsx`:
|
|
15
15
|
|
|
16
16
|
```tsx title="src/consent.tsx"
|
|
17
|
+
import { manifest } from '@c15t/browser/headless';
|
|
17
18
|
import {
|
|
18
19
|
ConsentBanner,
|
|
19
20
|
ConsentDialog,
|
|
20
21
|
ConsentDialogLink,
|
|
21
22
|
ConsentProvider,
|
|
22
|
-
hosted,
|
|
23
23
|
} from 'c15t/react';
|
|
24
24
|
import type { ReactNode } from 'react';
|
|
25
25
|
|
|
26
|
+
import { consentManifest } from './c15t-manifest';
|
|
26
27
|
import { scripts } from './scripts';
|
|
27
28
|
|
|
28
29
|
import 'c15t/react/styles.css';
|
|
29
30
|
|
|
30
|
-
const mode =
|
|
31
|
+
const mode = manifest({
|
|
32
|
+
backendURL:
|
|
33
|
+
import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
34
|
+
manifest: consentManifest,
|
|
35
|
+
});
|
|
31
36
|
|
|
32
37
|
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
33
38
|
<ConsentProvider options={{ mode, scripts }}>
|
|
@@ -41,9 +46,11 @@ export const Consent = ({ children }: { children: ReactNode }) => (
|
|
|
41
46
|
);
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
`manifest()` from `@c15t/browser/headless` resolves the policy from the
|
|
50
|
+
snapshot the build bundled, and sends consent choices to `backendURL`. Replace
|
|
51
|
+
the demo project URL with the URL from your Inth project, or set
|
|
52
|
+
`VITE_C15T_BACKEND_URL`. To fetch the policy from the backend on every page
|
|
53
|
+
load instead, pass `hosted({ url })` from `c15t/react`. The Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
|
|
47
54
|
this provider and passes it server-resolved state.
|
|
48
55
|
|
|
49
56
|
## Provider options
|
|
@@ -56,34 +63,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
|
|
|
56
63
|
`iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
|
|
57
64
|
`enabled` apply when they change.
|
|
58
65
|
|
|
59
|
-
| Option | Purpose
|
|
60
|
-
| ----------------------------- |
|
|
61
|
-
| `mode` | Required. `hosted({ url })` for a backend, `offline()` for local policies, or `custom()` for your own transport
|
|
62
|
-
| `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper
|
|
63
|
-
| `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md)
|
|
64
|
-
| `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md)
|
|
65
|
-
| `scriptLoader` | Options for the script loader module
|
|
66
|
-
| `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false`
|
|
67
|
-
| `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off
|
|
68
|
-
| `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default
|
|
69
|
-
| `storageConfig` | Storage key and cookie settings for the stored choice
|
|
70
|
-
| `clearOnRevocation` | Cookies and storage keys to delete when a category is denied
|
|
71
|
-
| `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true`
|
|
72
|
-
| `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope
|
|
73
|
-
| `overrides` | Force the country, region or language the policy is resolved for, for testing
|
|
74
|
-
| `user` | Link consent records to a signed-in user
|
|
75
|
-
| `callbacks` | Choice, permission and error events
|
|
76
|
-
| `i18n` | Languages and message overrides
|
|
77
|
-
| `legalLinks` | Privacy policy and other links shown in the banner and dialog
|
|
78
|
-
| `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md)
|
|
79
|
-
| `components` | Slot attributes per component part
|
|
80
|
-
| `presentation` | Banner and dialog shape, position and blocking
|
|
81
|
-
| `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md)
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
66
|
+
| Option | Purpose |
|
|
67
|
+
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `mode` | Required. `hosted({ url })` for a backend, `manifest()` from `@c15t/browser/headless` for a bundled policy, `offline()` for local policies, or `custom()` for your own transport |
|
|
69
|
+
| `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
|
|
70
|
+
| `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
|
|
71
|
+
| `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
|
|
72
|
+
| `scriptLoader` | Options for the script loader module |
|
|
73
|
+
| `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
|
|
74
|
+
| `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
|
|
75
|
+
| `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
|
|
76
|
+
| `storageConfig` | Storage key and cookie settings for the stored choice |
|
|
77
|
+
| `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
|
|
78
|
+
| `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
|
|
79
|
+
| `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
|
|
80
|
+
| `overrides` | Force the country, region or language the policy is resolved for, for testing |
|
|
81
|
+
| `user` | Link consent records to a signed-in user |
|
|
82
|
+
| `callbacks` | Choice, permission and error events |
|
|
83
|
+
| `i18n` | Languages and message overrides |
|
|
84
|
+
| `legalLinks` | Privacy policy and other links shown in the banner and dialog |
|
|
85
|
+
| `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
|
|
86
|
+
| `components` | Slot attributes per component part |
|
|
87
|
+
| `presentation` | Banner and dialog shape, position and blocking |
|
|
88
|
+
| `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
|
|
89
|
+
| `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows) |
|
|
90
|
+
| `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
|
|
91
|
+
| `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
|
|
92
|
+
| `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
|
|
93
|
+
| `nonce` | Content Security Policy nonce for script elements c15t inserts |
|
|
94
|
+
| `enabled` | Set `false` only when you deliberately bypass consent enforcement |
|
|
87
95
|
|
|
88
96
|
`enabled: false` grants categories and allows gated loading while suppressing
|
|
89
97
|
consent UI. It is not a way to fix failed initialization or hide a banner in
|
|
@@ -36,20 +36,25 @@ Most apps start with four components. `ConsentProvider` holds the runtime,
|
|
|
36
36
|
one file:
|
|
37
37
|
|
|
38
38
|
```tsx title="src/consent.tsx"
|
|
39
|
+
import { manifest } from '@c15t/browser/headless';
|
|
39
40
|
import {
|
|
40
41
|
ConsentBanner,
|
|
41
42
|
ConsentDialog,
|
|
42
43
|
ConsentDialogLink,
|
|
43
44
|
ConsentProvider,
|
|
44
|
-
hosted,
|
|
45
45
|
} from 'c15t/react';
|
|
46
46
|
import type { ReactNode } from 'react';
|
|
47
47
|
|
|
48
|
+
import { consentManifest } from './c15t-manifest';
|
|
48
49
|
import { scripts } from './scripts';
|
|
49
50
|
|
|
50
51
|
import 'c15t/react/styles.css';
|
|
51
52
|
|
|
52
|
-
const mode =
|
|
53
|
+
const mode = manifest({
|
|
54
|
+
backendURL:
|
|
55
|
+
import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
56
|
+
manifest: consentManifest,
|
|
57
|
+
});
|
|
53
58
|
|
|
54
59
|
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
55
60
|
<ConsentProvider options={{ mode, scripts }}>
|
|
@@ -7,10 +7,11 @@ group: frameworks
|
|
|
7
7
|
|
|
8
8
|
## Before you start
|
|
9
9
|
|
|
10
|
-
This guide sets up a client-rendered React app built with Vite. The
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
This guide sets up a client-rendered React app built with Vite. The build
|
|
11
|
+
bundles your project's public policy. The browser loads the page, resolves the
|
|
12
|
+
visitor's policy from that bundle, then shows the banner or runs the scripts
|
|
13
|
+
the visitor allowed. Regional policies may still need a backend location
|
|
14
|
+
request.
|
|
14
15
|
|
|
15
16
|
* **Next.js or TanStack Start?** Use that framework's guide from the
|
|
16
17
|
[framework list](https://c15t.com/docs/frameworks). Their adapters resolve consent on the
|
|
@@ -27,21 +28,65 @@ the backend URL.
|
|
|
27
28
|
[Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
|
|
28
29
|
covers self-hosting and offline mode.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
deploying, and rebuild after changing policies, translations or vendors.
|
|
31
|
+
Bundling the policy at build time is the recommended production setup.
|
|
32
|
+
Rebuild after changing policies, translations or vendors.
|
|
33
33
|
|
|
34
34
|
## Install c15t
|
|
35
35
|
|
|
36
|
-
| Package manager | Command
|
|
37
|
-
| :-------------- |
|
|
38
|
-
| npm | `npm install c15t@alpha @c15t/integrations@alpha` |
|
|
39
|
-
| pnpm | `pnpm add c15t@alpha @c15t/integrations@alpha` |
|
|
40
|
-
| yarn | `yarn add c15t@alpha @c15t/integrations@alpha` |
|
|
41
|
-
| bun | `bun add c15t@alpha @c15t/integrations@alpha` |
|
|
36
|
+
| Package manager | Command |
|
|
37
|
+
| :-------------- | :-------------------------------------------------------------------- |
|
|
38
|
+
| npm | `npm install c15t@alpha @c15t/browser@alpha @c15t/integrations@alpha` |
|
|
39
|
+
| pnpm | `pnpm add c15t@alpha @c15t/browser@alpha @c15t/integrations@alpha` |
|
|
40
|
+
| yarn | `yarn add c15t@alpha @c15t/browser@alpha @c15t/integrations@alpha` |
|
|
41
|
+
| bun | `bun add c15t@alpha @c15t/browser@alpha @c15t/integrations@alpha` |
|
|
42
42
|
|
|
43
|
-
`c15t` contains the React adapter at `c15t/react
|
|
44
|
-
|
|
43
|
+
`c15t` contains the React adapter at `c15t/react` and the Vite plugin at
|
|
44
|
+
`c15t/build`. `@c15t/browser` resolves the bundled policy in the browser.
|
|
45
|
+
`@c15t/integrations` holds the vendor loaders used below.
|
|
46
|
+
|
|
47
|
+
## Bundle your policy
|
|
48
|
+
|
|
49
|
+
Add `consentManifest` to `vite.config.ts`:
|
|
50
|
+
|
|
51
|
+
```ts title="vite.config.ts"
|
|
52
|
+
import react from '@vitejs/plugin-react';
|
|
53
|
+
import { consentManifest } from 'c15t/build';
|
|
54
|
+
import { defineConfig, loadEnv } from 'vite';
|
|
55
|
+
|
|
56
|
+
export default defineConfig(({ mode }) => {
|
|
57
|
+
const env = loadEnv(mode, process.cwd(), 'VITE_');
|
|
58
|
+
return {
|
|
59
|
+
plugins: [
|
|
60
|
+
react(),
|
|
61
|
+
// Downloads the policy and writes src/c15t-manifest.ts.
|
|
62
|
+
consentManifest({
|
|
63
|
+
backendURL:
|
|
64
|
+
env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`https://benchmarks-inth.inth.app` is a demo project, so the snippet runs as
|
|
72
|
+
is. Replace it with your project's backend URL, exactly as Inth shows it,
|
|
73
|
+
including any path prefix, or set `VITE_C15T_BACKEND_URL` when you build.
|
|
74
|
+
|
|
75
|
+
`consentManifest` downloads your project's public policy from `/manifest` when
|
|
76
|
+
`vite build` or `vite dev` starts, and writes it to `src/c15t-manifest.ts`.
|
|
77
|
+
Add that file to `.gitignore`.
|
|
78
|
+
|
|
79
|
+
The snapshot is fixed at build time:
|
|
80
|
+
|
|
81
|
+
* Rebuild after changing policies, translations or vendors. If your CI caches
|
|
82
|
+
build output, force a fresh build.
|
|
83
|
+
* The build must reach the backend. A failed fetch fails the build.
|
|
84
|
+
* Consent choices still go to the backend.
|
|
85
|
+
|
|
86
|
+
Run `vite build` or `vite dev` before a standalone `tsc`, `vue-tsc` or
|
|
87
|
+
`svelte-check`; the generated file doesn't exist on a fresh checkout. Both
|
|
88
|
+
commands fetch the manifest when they start, so both need the backend.
|
|
89
|
+
`vite preview` serves the last build without fetching.
|
|
45
90
|
|
|
46
91
|
## Register your vendor scripts
|
|
47
92
|
|
|
@@ -77,20 +122,25 @@ asks for them, and `ConsentDialogLink` keeps preferences reachable after the
|
|
|
77
122
|
banner closes.
|
|
78
123
|
|
|
79
124
|
```tsx title="src/consent.tsx"
|
|
125
|
+
import { manifest } from '@c15t/browser/headless';
|
|
80
126
|
import {
|
|
81
127
|
ConsentBanner,
|
|
82
128
|
ConsentDialog,
|
|
83
129
|
ConsentDialogLink,
|
|
84
130
|
ConsentProvider,
|
|
85
|
-
hosted,
|
|
86
131
|
} from 'c15t/react';
|
|
87
132
|
import type { ReactNode } from 'react';
|
|
88
133
|
|
|
134
|
+
import { consentManifest } from './c15t-manifest';
|
|
89
135
|
import { scripts } from './scripts';
|
|
90
136
|
|
|
91
137
|
import 'c15t/react/styles.css';
|
|
92
138
|
|
|
93
|
-
const mode =
|
|
139
|
+
const mode = manifest({
|
|
140
|
+
backendURL:
|
|
141
|
+
import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
|
|
142
|
+
manifest: consentManifest,
|
|
143
|
+
});
|
|
94
144
|
|
|
95
145
|
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
96
146
|
<ConsentProvider options={{ mode, scripts }}>
|
|
@@ -104,8 +154,17 @@ export const Consent = ({ children }: { children: ReactNode }) => (
|
|
|
104
154
|
);
|
|
105
155
|
```
|
|
106
156
|
|
|
107
|
-
|
|
108
|
-
|
|
157
|
+
`manifest()` resolves the policy from the bundled snapshot in the browser,
|
|
158
|
+
with no `/manifest` request. Use the same backend URL as in
|
|
159
|
+
`vite.config.ts`: consent choices go to it, and so does an `/init` request
|
|
160
|
+
when the policy depends on the visitor's country or region and the browser
|
|
161
|
+
does not know it. That request returns the backend's current policy, which can
|
|
162
|
+
be newer than the snapshot. Vite reads `VITE_C15T_BACKEND_URL` at build time
|
|
163
|
+
for both files, so set it before you build, not when you serve the files.
|
|
164
|
+
|
|
165
|
+
To apply policy edits without rebuilding, remove `consentManifest` and use
|
|
166
|
+
`hosted({ url })` from `c15t/react` as the mode. The browser then requests the
|
|
167
|
+
policy from `/init` on every page load.
|
|
109
168
|
|
|
110
169
|
Wrap your app with it in `src/main.tsx`:
|
|
111
170
|
|
|
@@ -131,66 +190,6 @@ router, render the router inside `Consent` so navigation does not remount the
|
|
|
131
190
|
provider. The stylesheet import in `src/consent.tsx` styles the banner and
|
|
132
191
|
dialog; import it once.
|
|
133
192
|
|
|
134
|
-
## Bundle the manifest during builds
|
|
135
|
-
|
|
136
|
-
The build fetches your public policy once and bundles it, so the server never
|
|
137
|
-
fetches it at runtime.
|
|
138
|
-
|
|
139
|
-
Add `consentManifest` to your existing Vite plugin list:
|
|
140
|
-
|
|
141
|
-
```ts title="vite.config.ts (plugins option)"
|
|
142
|
-
import { consentManifest } from 'c15t/build';
|
|
143
|
-
|
|
144
|
-
export default {
|
|
145
|
-
plugins: [
|
|
146
|
-
// Keep your existing framework plugins here.
|
|
147
|
-
consentManifest({ backendURL: 'https://your-project.inth.app' }),
|
|
148
|
-
],
|
|
149
|
-
};
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Vite writes `src/c15t-manifest.ts` before compiling, including at development
|
|
153
|
-
startup. Keep the generated file out of Git. Install `@c15t/browser@alpha` for
|
|
154
|
-
its browser manifest transport. In the quickstart's `src/consent.tsx`, replace
|
|
155
|
-
the `hosted()` mode with this factory:
|
|
156
|
-
|
|
157
|
-
```tsx title="src/consent.tsx"
|
|
158
|
-
import { manifest } from '@c15t/browser/headless';
|
|
159
|
-
|
|
160
|
-
import { consentManifest } from './c15t-manifest';
|
|
161
|
-
|
|
162
|
-
const mode = manifest({
|
|
163
|
-
backendURL: 'https://your-project.inth.app',
|
|
164
|
-
manifest: consentManifest,
|
|
165
|
-
});
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Keep passing `mode` to `ConsentProvider`. The browser resolves the snapshot
|
|
169
|
-
without `/manifest` when it has the geography the policy needs, or the policy
|
|
170
|
-
does not depend on geography. Supply known country and region through `inputs`
|
|
171
|
-
or overrides to keep regional resolution on the snapshot.
|
|
172
|
-
|
|
173
|
-
When the required country or region is missing, the transport calls backend
|
|
174
|
-
`/init` and uses its resolved policy and translations. That response can use
|
|
175
|
-
newer backend policy than the build snapshot. This is a full consent resolution,
|
|
176
|
-
so these visitors still wait for a backend round trip.
|
|
177
|
-
|
|
178
|
-
Policy and resolver code are part of the browser bundle.
|
|
179
|
-
|
|
180
|
-
The snapshot is fixed at build time:
|
|
181
|
-
|
|
182
|
-
* Rebuild after changing policies, translations or vendors. If your CI caches
|
|
183
|
-
build output, force a fresh build.
|
|
184
|
-
* The build must reach the backend. A failed fetch fails the build.
|
|
185
|
-
* Consent choices still go to the backend.
|
|
186
|
-
|
|
187
|
-
Run `vite build` or `vite dev` before a standalone `tsc`, `vue-tsc` or
|
|
188
|
-
`svelte-check`; the generated file doesn't exist on a fresh checkout.
|
|
189
|
-
|
|
190
|
-
For policy updates without a rebuild, keep the original `hosted()` factory
|
|
191
|
-
and omit the build plugin. That setup resolves policy through the backend
|
|
192
|
-
`/init` on each page load.
|
|
193
|
-
|
|
194
193
|
## Gate your own features
|
|
195
194
|
|
|
196
195
|
Read a category's permission with `useConsent` inside the provider:
|
|
@@ -215,8 +214,8 @@ Build and preview the production bundle with `vite build` and `vite preview`,
|
|
|
215
214
|
then open the site in a private window with DevTools open on the Network tab.
|
|
216
215
|
Test under a policy that asks for a choice, such as an EU opt-in policy.
|
|
217
216
|
|
|
218
|
-
1. **Before a choice.** The banner shows
|
|
219
|
-
`posthog.com`.
|
|
217
|
+
1. **Before a choice.** The banner shows, with no `/manifest` request. There
|
|
218
|
+
are no requests to `posthog.com`.
|
|
220
219
|
2. **Reject All.** The banner closes and the vendor requests stay absent.
|
|
221
220
|
3. **Reload.** The banner stays closed and the vendor requests stay absent.
|
|
222
221
|
4. **Privacy settings.** The footer link opens the dialog with **Analytics** (the
|