@c15t/react 3.0.0-alpha.7 → 3.0.0-alpha.9

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.
Files changed (63) hide show
  1. package/AGENTS.md +3 -3
  2. package/dist/components/consent-gate/atoms.js +1 -1
  3. package/dist/components/iab-panel/atoms/root.js +1 -1
  4. package/dist/components/iab-panel/iab-panel.js +1 -1
  5. package/dist/components/iab-prompt/atoms/root.js +1 -1
  6. package/dist/components/panel/atoms/root.js +1 -1
  7. package/dist/components/panel-trigger/atoms/root.js +1 -1
  8. package/dist/components/preferences/atoms/root.js +1 -1
  9. package/dist/components/prompt/atoms/root.js +1 -1
  10. package/dist/components/prompt/prompt.js +1 -1
  11. package/dist/components/shared/dialog-sheets.js +1 -0
  12. package/dist/components/shared/first-paint-sheets.js +1 -0
  13. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  14. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  15. package/dist/components/shared/primitives/legal-links/index.js +1 -1
  16. package/dist/components/shared/streamed-surface.js +2 -0
  17. package/dist/components/shared/surface-styles.js +2 -0
  18. package/dist/context.js +1 -1
  19. package/dist/hooks/use-iab-policy-guard.js +1 -0
  20. package/dist/hooks.js +1 -1
  21. package/dist/iab/styles.tw3.css +1 -1
  22. package/dist/iab-context.js +1 -1
  23. package/dist/kernel-selector.js +1 -1
  24. package/dist/provider.js +1 -1
  25. package/dist/version.js +1 -1
  26. package/dist-types/components/prompt/atoms/root.d.ts +4 -0
  27. package/dist-types/components/prompt/prompt.d.ts +4 -0
  28. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  29. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  30. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  31. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  32. package/dist-types/components/shared/streamed-surface.d.ts +23 -0
  33. package/dist-types/components/shared/surface-styles.d.ts +36 -0
  34. package/dist-types/context.d.ts +17 -1
  35. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  36. package/dist-types/kernel-selector.d.ts +10 -0
  37. package/dist-types/provider.d.ts +36 -4
  38. package/dist-types/ui-config-context.d.ts +4 -0
  39. package/dist-types/version.d.ts +1 -1
  40. package/docs/README.md +3 -3
  41. package/docs/concepts/choose-your-setup.md +4 -2
  42. package/docs/concepts/data-fetching.md +3 -1
  43. package/docs/customization/overview.md +3 -2
  44. package/docs/customization/stylesheets.md +163 -50
  45. package/docs/customization/tailwind.md +59 -44
  46. package/docs/customization/tokens.md +6 -6
  47. package/docs/frameworks/react/components/consent-banner.md +7 -4
  48. package/docs/frameworks/react/components/consent-dialog.md +8 -5
  49. package/docs/frameworks/react/components/consent-provider.md +41 -36
  50. package/docs/frameworks/react/components.md +9 -5
  51. package/docs/frameworks/react/compose.md +3 -3
  52. package/docs/frameworks/react/content-security-policy.md +22 -8
  53. package/docs/frameworks/react/customize.md +21 -13
  54. package/docs/frameworks/react/iab.md +36 -4
  55. package/docs/frameworks/react/quickstart.md +84 -85
  56. package/docs/frameworks/react/rendering.md +24 -12
  57. package/docs/frameworks/react/scripts.md +7 -4
  58. package/docs/frameworks/react/troubleshooting.md +70 -19
  59. package/docs/frameworks/react/upgrade-v3.md +23 -17
  60. package/docs/frameworks/react/vendor-consent.md +19 -7
  61. package/docs/guides/banner-experiments.md +2 -4
  62. package/docs/guides/troubleshooting.md +9 -9
  63. package/package.json +7 -7
@@ -1,8 +1,7 @@
1
1
  ---
2
2
  title: Tailwind CSS
3
- description: Load c15t's styles next to Tailwind CSS 4 or 3 in every framework,
4
- put utilities on c15t component parts, and use Tailwind's dark variant with
5
- c15t.
3
+ description: Use c15t's styles with Tailwind CSS 4 or 3 in every framework, put
4
+ utilities on c15t component parts, and use Tailwind's dark variant with c15t.
6
5
  group: customization
7
6
  ---
8
7
 
@@ -21,7 +20,10 @@ directive and fails the build on a stylesheet that uses it without a matching
21
20
  `@tailwind components`. c15t's Tailwind 3 PostCSS plugin removes the layer
22
21
  wrappers from c15t's stylesheets before Tailwind 3 runs. c15t's rules then
23
22
  win by specificity, as they did in c15t v2, so a utility on a c15t part needs
24
- Tailwind's important modifier, such as `!p-2`.
23
+ Tailwind's important modifier, such as `!p-2`. The plugin cannot reach the
24
+ `<style>` elements c15t renders itself, so on Tailwind 3 you turn those off
25
+ with `styles: false` and import the stylesheet instead. The Astro integration
26
+ does this for you when it finds Tailwind 3.
25
27
 
26
28
  The setups below come from fixtures that CI builds for each framework and
27
29
  checks in Chromium. The Next.js snippets come from the Next.js 16 Turbopack
@@ -34,9 +36,11 @@ without a backend, so they use `offline()` mode or a local `backendURL`. Keep th
34
36
 
35
37
  ## Set up Tailwind CSS 4
36
38
 
37
- Install Tailwind's plugin for your build tool, add it to the build, and load
38
- c15t's stylesheet in the same CSS entry as Tailwind. Vue, Nuxt and Astro load
39
- c15t's styles for you, so their CSS entry holds only Tailwind.
39
+ Install Tailwind's plugin for your build tool and add it to the build. c15t
40
+ loads its own styles in every framework, so your CSS entry holds only
41
+ Tailwind. Do not import c15t's stylesheet next to Tailwind 4: the stock
42
+ components already render the same rules, and the import would hold back the
43
+ first paint.
40
44
 
41
45
  **Next.js**
42
46
 
@@ -46,20 +50,18 @@ Next.js runs Tailwind 4 through `@tailwindcss/postcss`:
46
50
  export default { plugins: { '@tailwindcss/postcss': {} } };
47
51
  ```
48
52
 
49
- Import c15t after Tailwind in the global stylesheet that `app/layout.tsx`
50
- or `pages/_app.tsx` loads:
53
+ Import Tailwind in the global stylesheet that `app/layout.tsx` or
54
+ `pages/_app.tsx` loads:
51
55
 
52
56
  ```css title="app/globals.css"
53
57
  @import 'tailwindcss';
54
- @import 'c15t/next/styles.css';
55
58
 
56
59
  @custom-variant dark (&:where(.dark, .dark *));
57
60
  ```
58
61
 
59
62
  **TanStack Start**
60
63
 
61
- Add `@tailwindcss/vite` before the TanStack Start plugin, then import
62
- Tailwind and c15t from the stylesheet your root route links:
64
+ Add `@tailwindcss/vite` before the TanStack Start plugin:
63
65
 
64
66
  ```ts title="vite.config.ts"
65
67
  import tailwindcss from '@tailwindcss/vite';
@@ -76,11 +78,10 @@ export default defineConfig({
76
78
  });
77
79
  ```
78
80
 
79
- Import Tailwind and c15t from the stylesheet your root route links:
81
+ Import Tailwind from the stylesheet your root route links:
80
82
 
81
83
  ```css title="src/styles.css"
82
84
  @import 'tailwindcss';
83
- @import 'c15t/tanstack-start/styles.css';
84
85
 
85
86
  @custom-variant dark (&:where(.dark, .dark *));
86
87
  ```
@@ -95,12 +96,10 @@ import { defineConfig } from 'vite';
95
96
  export default defineConfig({ plugins: [react(), tailwindcss()] });
96
97
  ```
97
98
 
98
- Import this stylesheet from your entry module in place of a separate
99
- `c15t/react/styles.css` import:
99
+ Import this stylesheet from your entry module:
100
100
 
101
101
  ```css title="src/app.css"
102
102
  @import 'tailwindcss';
103
- @import 'c15t/react/styles.css';
104
103
 
105
104
  @custom-variant dark (&:where(.dark, .dark *));
106
105
  ```
@@ -155,8 +154,9 @@ Import this stylesheet from `src/main.ts`:
155
154
 
156
155
  **Astro**
157
156
 
158
- The integration adds `c15t/astro/styles.css` to every page, so the CSS
159
- entry holds only Tailwind. Add `@tailwindcss/vite` under `vite.plugins`:
157
+ c15t inlines the banner's rules into each page and links the dialog's
158
+ when it opens, so the CSS entry holds only Tailwind. Add
159
+ `@tailwindcss/vite` under `vite.plugins`:
160
160
 
161
161
  ```js title="astro.config.mjs"
162
162
  import svelte from '@astrojs/svelte';
@@ -202,12 +202,10 @@ import { defineConfig } from 'vite';
202
202
  export default defineConfig({ plugins: [svelte(), tailwindcss()] });
203
203
  ```
204
204
 
205
- Import this stylesheet from `src/main.ts` in place of a separate
206
- `@c15t/svelte/styles.css` import:
205
+ Import this stylesheet from `src/main.ts`:
207
206
 
208
207
  ```css title="src/app.css"
209
208
  @import 'tailwindcss';
210
- @import '@c15t/svelte/styles.css';
211
209
 
212
210
  @custom-variant dark (&:where(.dark, .dark *));
213
211
  ```
@@ -226,7 +224,6 @@ Import this stylesheet from your root `+layout.svelte`:
226
224
 
227
225
  ```css title="src/app.css"
228
226
  @import 'tailwindcss';
229
- @import '@c15t/svelte/styles.css';
230
227
 
231
228
  @custom-variant dark (&:where(.dark, .dark *));
232
229
  ```
@@ -256,8 +253,12 @@ c15t's parts, pass the URL of your built stylesheet in
256
253
 
257
254
  ## Set up Tailwind CSS 3
258
255
 
259
- Tailwind 3 loads the same `styles.css` as Tailwind 4 and needs one PostCSS
260
- plugin. Every c15t package that ships a stylesheet exports it as
256
+ Tailwind 3's preflight is unlayered, so it beats the layered rules c15t
257
+ renders itself and strips the banner's padding and borders. On Tailwind 3,
258
+ turn those rules off with `styles: false` and import c15t's `styles.css`
259
+ through a PostCSS plugin. The Astro integration needs only the plugin: it
260
+ detects Tailwind 3 and links `c15t/astro/styles.css` itself. Every c15t
261
+ package that ships a stylesheet exports the plugin as
261
262
  `<package>/postcss-tailwind3`, so use the one from the package you installed:
262
263
 
263
264
  | You installed | Plugin name |
@@ -269,21 +270,26 @@ plugin. Every c15t package that ships a stylesheet exports it as
269
270
  They are the same plugin. A config that already lists
270
271
  `@c15t/ui/postcss-tailwind3` keeps working.
271
272
 
272
- Make three changes:
273
+ Make four changes:
273
274
 
274
- 1. Add the plugin to your PostCSS plugins, before `tailwindcss`. Write the plugins as an object, as the examples below do.
275
+ 1. Set `styles: false` where you configure c15t: `options` on `ConsentRoot`
276
+ in Next.js and TanStack Start, `options` on `ConsentProvider` in React,
277
+ and `styles={false}` on `ConsentManagerProvider` in Svelte and SvelteKit.
278
+ Vue, Nuxt and Astro need no change.
279
+ 2. Add the plugin to your PostCSS plugins, before `tailwindcss`. Write the plugins as an object, as the examples below do.
275
280
  PostCSS runs them in the order of the keys. Next.js also accepts an array
276
281
  of plugin names, but Vite's PostCSS loader rejects that form.
277
- 2. Where you import c15t's stylesheet yourself, put the import above the
278
- `@tailwind` directives, whatever your build tool. Vite drops an `@import`
282
+ 3. Import c15t's stylesheet above the `@tailwind` directives, whatever your
283
+ build tool. Vue, Nuxt and Astro skip this step. Vite drops an `@import`
279
284
  that follows another rule, with the warning
280
285
  `@import must precede all other statements`, and the banner renders
281
286
  unstyled. Next.js 16 with Turbopack fails the build.
282
- 3. Set `darkMode: 'class'` so Tailwind's `dark:` variant follows the same
287
+ 4. Set `darkMode: 'class'` so Tailwind's `dark:` variant follows the same
283
288
  `dark` class c15t reads.
284
289
 
285
- The plugin also processes the stylesheets c15t loads for you, such as Astro's
286
- injected one. Without it, Tailwind 3 stops
290
+ The plugin also processes the stylesheets the Vue and Nuxt components import,
291
+ and the one the Astro integration adds on a Tailwind 3 site. Without it,
292
+ Tailwind 3 stops
287
293
  the build with "`@layer components` is used but no matching
288
294
  `@tailwind components` directive is present", or purges c15t's rules, because
289
295
  c15t's class names never appear in your source.
@@ -293,8 +299,9 @@ class. Add the important modifier to utilities you pass to c15t parts:
293
299
  `!bg-red-500`, `hover:!bg-red-500`. A bare `bg-red-500` has no effect. Theme
294
300
  tokens work the same on Tailwind 3 and 4.
295
301
 
296
- In React and Next.js apps, `c15t setup` makes the first two changes when it
297
- finds Tailwind 3. It replaces an existing
302
+ In React and Next.js apps, `c15t setup` adds the plugin and the stylesheet
303
+ import when it finds Tailwind 3, but does not set `styles: false`. Add that
304
+ yourself. It replaces an existing
298
305
  `styles.tw3.css` import from an earlier setup with `styles.css`. When it
299
306
  cannot edit your PostCSS config, it prints the step to do by hand. See
300
307
  [setup](https://c15t.com/docs/cli/commands/setup#set-up-styles-and-tailwind-css).
@@ -327,7 +334,7 @@ export default {
327
334
 
328
335
  Import `c15t/next/styles.css` above the directives. Turbopack fails the
329
336
  build when the import sits below them. The same order works with
330
- webpack:
337
+ webpack. Set `styles: false` in `ConsentRoot`'s `options` as well:
331
338
 
332
339
  ```css title="app/globals.css"
333
340
  /* Turbopack rejects an @import below other rules, so it goes first. */
@@ -358,7 +365,8 @@ export default {
358
365
  ```
359
366
 
360
367
  Import `c15t/tanstack-start/styles.css` above the directives, in the
361
- stylesheet your root route links:
368
+ stylesheet your root route links, and set `styles: false` in
369
+ `ConsentRoot`'s `options`:
362
370
 
363
371
  ```css title="src/styles.css"
364
372
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -388,8 +396,9 @@ export default {
388
396
  } satisfies Config;
389
397
  ```
390
398
 
391
- Import `c15t/react/styles.css` above the directives, and import this
392
- stylesheet from your entry module:
399
+ Import `c15t/react/styles.css` above the directives, import this
400
+ stylesheet from your entry module, and set `styles: false` in the
401
+ `ConsentProvider` options:
393
402
 
394
403
  ```css title="src/app.css"
395
404
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -489,8 +498,12 @@ export default {
489
498
  } satisfies Config;
490
499
  ```
491
500
 
492
- The plugin processes the stylesheet the integration injects, so the CSS
493
- entry holds only the directives:
501
+ When the integration finds Tailwind 3 in the project, it adds
502
+ `c15t/astro/styles.css` to every page through Astro's CSS pipeline
503
+ instead of inlining the banner's rules, so the plugin processes it. You
504
+ do not set `styles: false` or import c15t's stylesheet, and the CSS
505
+ entry holds only the directives. The first paint still waits for that
506
+ stylesheet:
494
507
 
495
508
  ```css title="src/styles/global.css"
496
509
  @tailwind base;
@@ -521,8 +534,9 @@ export default {
521
534
  } satisfies Config;
522
535
  ```
523
536
 
524
- Import `@c15t/svelte/styles.css` above the directives, and import this
525
- stylesheet from `src/main.ts`:
537
+ Import `@c15t/svelte/styles.css` above the directives, import this
538
+ stylesheet from `src/main.ts`, and pass `styles={false}` to
539
+ `ConsentManagerProvider`:
526
540
 
527
541
  ```css title="src/app.css"
528
542
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -556,8 +570,9 @@ export default {
556
570
  } satisfies Config;
557
571
  ```
558
572
 
559
- Import `@c15t/svelte/styles.css` above the directives, and import this
560
- stylesheet from your root `+layout.svelte`:
573
+ Import `@c15t/svelte/styles.css` above the directives, import this
574
+ stylesheet from your root `+layout.svelte`, and pass `styles={false}` to
575
+ `ConsentManagerProvider`:
561
576
 
562
577
  ```css title="src/app.css"
563
578
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -8,14 +8,14 @@ group: customization
8
8
  ## Where tokens come from
9
9
 
10
10
  c15t's components take their colors from `--c15t-*` CSS variables, and most
11
- of their fonts, radii, spacing, shadows and motion too. c15t's stylesheet sets
12
- the defaults. You change them with a theme object or with CSS, and every part
11
+ of their fonts, radii, spacing, shadows and motion too. c15t's rules set the
12
+ defaults. You change them with a theme object or with CSS, and every part
13
13
  that reads a token changes with it. Some values are still fixed for one
14
14
  component, such as button padding, the radius of the "Secured by" tag and
15
15
  several font sizes and weights, so a token change does not move them. Restyle
16
16
  those parts through [slots](./slots.md) or your own CSS.
17
17
  [Stylesheets and CSS layers](./stylesheets.md)
18
- covers which stylesheet to load, and [dark mode](./dark-mode.md)
18
+ covers how c15t loads its rules, and [dark mode](./dark-mode.md)
19
19
  covers the dark set of tokens.
20
20
 
21
21
  ## Set semantic values together
@@ -59,9 +59,9 @@ setup.
59
59
  ## Combine a generated theme with your own CSS
60
60
 
61
61
  `generateThemeCSS` writes its variables on `:root:root` and
62
- `.c15t-theme-root.c15t-theme-root`, one step more specific than the defaults
63
- in `styles.css`. The theme therefore overrides the defaults whether its
64
- `<style>` element comes before or after the stylesheet. The same output backs
62
+ `.c15t-theme-root.c15t-theme-root`, more specific than c15t's defaults. The
63
+ theme therefore overrides the defaults whether its
64
+ `<style>` element comes before or after c15t's rules. The same output backs
65
65
  `ConsentTheme` in React, Next.js and TanStack Start, Astro's `theme` option and
66
66
  the script tag's `ui.theme`.
67
67
 
@@ -12,20 +12,23 @@ 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
- import 'c15t/react/styles.css';
27
-
28
- const mode = hosted({ url: 'https://your-project.inth.app' });
27
+ const mode = manifest({
28
+ backendURL:
29
+ import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
30
+ manifest: consentManifest,
31
+ });
29
32
 
30
33
  export const Consent = ({ children }: { children: ReactNode }) => (
31
34
  <ConsentProvider options={{ mode, scripts }}>
@@ -14,20 +14,23 @@ 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
- import 'c15t/react/styles.css';
29
-
30
- const mode = hosted({ url: 'https://your-project.inth.app' });
29
+ const mode = manifest({
30
+ backendURL:
31
+ import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
32
+ manifest: consentManifest,
33
+ });
31
34
 
32
35
  export const Consent = ({ children }: { children: ReactNode }) => (
33
36
  <ConsentProvider options={{ mode, scripts }}>
@@ -130,7 +133,7 @@ If a preload fails, for example because the visitor is offline, nothing is
130
133
  cached: the next hover, focus or open tries the download again.
131
134
 
132
135
  The dialog's CSS travels in the same chunk and is applied before the dialog
133
- renders, so your app's `styles.css` import stays small.
136
+ renders, so the first page load carries only the banner's rules.
134
137
 
135
138
  Without an `open` prop, Escape closes the dialog without saving. If the
136
139
  policy still owes a choice, the banner comes back. Save Settings, Accept All and
@@ -14,20 +14,23 @@ 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
- import 'c15t/react/styles.css';
29
-
30
- const mode = hosted({ url: 'https://your-project.inth.app' });
29
+ const mode = manifest({
30
+ backendURL:
31
+ import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
32
+ manifest: consentManifest,
33
+ });
31
34
 
32
35
  export const Consent = ({ children }: { children: ReactNode }) => (
33
36
  <ConsentProvider options={{ mode, scripts }}>
@@ -41,9 +44,11 @@ export const Consent = ({ children }: { children: ReactNode }) => (
41
44
  );
42
45
  ```
43
46
 
44
- `hosted({ url })` points the provider at your Inth backend. Replace
45
- `https://your-project.inth.app` with the URL from your Inth project. The
46
- Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
47
+ `manifest()` from `@c15t/browser/headless` resolves the policy from the
48
+ snapshot the build bundled, and sends consent choices to `backendURL`. Replace
49
+ the demo project URL with the URL from your Inth project, or set
50
+ `VITE_C15T_BACKEND_URL`. To fetch the policy from the backend on every page
51
+ load instead, pass `hosted({ url })` from `c15t/react`. The Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
47
52
  this provider and passes it server-resolved state.
48
53
 
49
54
  ## Provider options
@@ -56,35 +61,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
56
61
  `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
57
62
  `enabled` apply when they change.
58
63
 
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
- | `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) |
83
- | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
84
- | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
85
- | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
86
- | `nonce` | Content Security Policy nonce for script elements c15t inserts |
87
- | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
64
+ | Option | Purpose |
65
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `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 |
67
+ | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
68
+ | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
69
+ | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
70
+ | `scriptLoader` | Options for the script loader module |
71
+ | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
72
+ | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
73
+ | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
74
+ | `storageConfig` | Storage key and cookie settings for the stored choice |
75
+ | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
76
+ | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
77
+ | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
78
+ | `overrides` | Force the country, region or language the policy is resolved for, for testing |
79
+ | `user` | Link consent records to a signed-in user |
80
+ | `callbacks` | Choice, permission and error events |
81
+ | `i18n` | Languages and message overrides |
82
+ | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
83
+ | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
84
+ | `components` | Slot attributes per component part |
85
+ | `presentation` | Banner and dialog shape, position and blocking |
86
+ | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
87
+ | `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) |
88
+ | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
89
+ | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
90
+ | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
91
+ | `nonce` | Content Security Policy nonce for script elements c15t inserts |
92
+ | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
88
93
 
89
94
  `enabled: false` grants categories and allows gated loading while suppressing
90
95
  consent UI. It is not a way to fix failed initialization or hide a banner in
@@ -36,20 +36,23 @@ 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
- import 'c15t/react/styles.css';
51
-
52
- const mode = hosted({ url: 'https://your-project.inth.app' });
51
+ const mode = manifest({
52
+ backendURL:
53
+ import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
54
+ manifest: consentManifest,
55
+ });
53
56
 
54
57
  export const Consent = ({ children }: { children: ReactNode }) => (
55
58
  <ConsentProvider options={{ mode, scripts }}>
@@ -79,7 +82,8 @@ explains the trade-off.
79
82
 
80
83
  Import `IABProvider`, `IABConsentBanner` and `IABConsentDialog` from
81
84
  `c15t/react/iab`, render them inside `ConsentProvider` next to
82
- `ConsentBanner` and `ConsentDialog`, and load `c15t/react/iab/styles.css`. The stock banner and dialog stay closed
85
+ `ConsentBanner` and `ConsentDialog`, and load `c15t/react/styles.css` and
86
+ `c15t/react/iab/styles.css` with `styles: false`. The stock banner and dialog stay closed
83
87
  under an IAB policy. See
84
88
  [IAB TCF](./iab.md).
85
89
 
@@ -315,9 +315,9 @@ banner keeps them as long as you keep the parts that own them:
315
315
 
316
316
  ## Keep theme tokens and slots working
317
317
 
318
- The parts render the stock class names, so the c15t stylesheet and its
319
- `--c15t-*` tokens style a composed banner the way they style the stock one.
320
- Keep the stylesheet import.
318
+ The parts render the stock class names, and the banner's `Root` renders
319
+ c15t's rules, so c15t's styles and `--c15t-*` tokens style a composed
320
+ banner the way they style the stock one.
321
321
 
322
322
  Slots in the provider's `options.components` reach each part by key:
323
323
  `banner.root`, `banner.card`, `banner.header`, `banner.title`,
@@ -10,13 +10,14 @@ group: frameworks
10
10
 
11
11
  A React app with c15t adds these to the page, and your policy must allow them:
12
12
 
13
- | What | Directive |
14
- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------- |
15
- | `c15t/react/styles.css`, which your bundler emits with your own CSS | `style-src 'self'` |
16
- | The `<style id="c15t-theme">` element, if you render `ConsentTheme` | `style-src` with a nonce or its hash |
17
- | `<script>` elements the script loader creates for `scripts` entries, both `src` and inline | `script-src` with each vendor's host, or a nonce |
18
- | Browser requests to your backend's `/init` and `/subjects` | `connect-src` with the backend origin |
19
- | Iframes inside `ConsentGate`, and the frames, images and requests your vendors load | `frame-src`, `img-src`, `connect-src` for each vendor |
13
+ | What | Directive |
14
+ | --------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
15
+ | The `<style>` elements the banner, dialog and other stock components render with c15t's rules | `style-src` with a nonce |
16
+ | `c15t/react/styles.css`, only if you import it with `styles: false` | `style-src 'self'` |
17
+ | The `<style id="c15t-theme">` element, if you render `ConsentTheme` | `style-src` with a nonce or its hash |
18
+ | `<script>` elements the script loader creates for `scripts` entries, both `src` and inline | `script-src` with each vendor's host, or a nonce |
19
+ | Browser requests to your backend's `/init` and `/subjects` | `connect-src` with the backend origin |
20
+ | Iframes inside `ConsentGate`, and the frames, images and requests your vendors load | `frame-src`, `img-src`, `connect-src` for each vendor |
20
21
 
21
22
  In a client-rendered app, c15t components set their inline styles through
22
23
  React. A policy does not restrict styles set that way, so they need no
@@ -60,7 +61,8 @@ when a server renders or rewrites your `index.html` per request.
60
61
 
61
62
  Pass the nonce as `options.nonce` on `ConsentProvider`. The script loader
62
63
  stamps it on every `<script>` it creates. A `nonce` on a single `scripts`
63
- entry takes precedence for that element.
64
+ entry takes precedence for that element. The stock components put it on the
65
+ `<style>` elements that carry c15t's rules.
64
66
 
65
67
  With Vite, set `html.cspNonce` to a placeholder in `vite.config.ts`, and have
66
68
  your server replace it with a new nonce on every response. Vite then adds
@@ -83,6 +85,18 @@ The provider reads `options.nonce` once, when it mounts. A server-rendered
83
85
  React app, such as one built with React Router framework mode, reads the nonce
84
86
  from its own request and passes it the same way.
85
87
 
88
+ ## Allow c15t's style elements
89
+
90
+ The banner, dialog and other stock components render c15t's rules as
91
+ `<style>` elements. A `style-src` without `'unsafe-inline'` must admit them.
92
+ Either:
93
+
94
+ * Set `options.nonce` on `ConsentProvider`, as in [use a nonce](#use-a-nonce).
95
+ The elements then render next to each component with the nonce. React 19
96
+ would otherwise move them into `<head>` and drop the nonce.
97
+ * Or set `styles: false` in the provider options and import
98
+ `c15t/react/styles.css` from your bundle. `style-src 'self'` admits it.
99
+
86
100
  ## Allow the theme style element
87
101
 
88
102
  `ConsentTheme` renders your theme tokens into a `<style id="c15t-theme">`
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  title: Customize
3
3
  description: Change the colors, fonts, layout, button styles and copy of the
4
- c15t banner and dialog in a React app with the stylesheet, theme tokens,
5
- provider options and slots.
4
+ c15t banner and dialog in a React app with theme tokens, provider options and
5
+ slots, and load the stylesheet yourself when you need to.
6
6
  group: frameworks
7
7
  ---
8
8
 
@@ -24,26 +24,35 @@ across frameworks.
24
24
  See the [design gallery](../../customization/recipes.md) for five banner designs,
25
25
  from a bottom bar to a fully custom one, with tested code for this framework.
26
26
 
27
- ## Import the stylesheet
27
+ ## Import the stylesheet yourself
28
28
 
29
- Import the stylesheet once, in the module that renders `ConsentProvider`. The
30
- [quickstart](./quickstart.md) does this in `src/consent.tsx`:
29
+ A React app imports no c15t stylesheet. `ConsentBanner`, `ConsentDialog` and
30
+ the other stock components render the rules they use as `<style>` elements.
31
31
 
32
- ```tsx
32
+ Import the stylesheet yourself with Tailwind CSS 3, to put c15t's rules in a
33
+ named cascade layer. Set `styles: false` in the provider
34
+ options, so the components add no second copy, and import the stylesheet once
35
+ in the module that renders `ConsentProvider`:
36
+
37
+ ```tsx title="src/consent.tsx (partial)"
33
38
  import 'c15t/react/styles.css';
39
+
40
+ // In the ConsentProvider options:
41
+ styles: false,
34
42
  ```
35
43
 
36
- The import is the same with or without Tailwind. To import the stylesheet from
37
- CSS instead, put it at the top of your global stylesheet, above any
38
- `@tailwind` directives. `postcss-import`, which Vite uses, ignores an
39
- `@import` that follows other rules. Tailwind 3 also needs a PostCSS plugin;
44
+ To import the stylesheet from CSS instead, put it at the top of your global
45
+ stylesheet, above any `@tailwind` directives. `postcss-import`, which Vite
46
+ uses, ignores an `@import` that follows other rules. Tailwind 3 also needs a
47
+ PostCSS plugin;
40
48
  [Tailwind CSS](../../customization/tailwind.md#set-up-tailwind-css-3) shows the
41
49
  setup.
42
50
 
43
51
  ## Change colors, fonts and radius
44
52
 
45
53
  Colors, radii, shadows, typography, and motion come from `--c15t-*` custom
46
- properties. The package stylesheet sets the default values. To change them,
54
+ properties. The rules the components render set the default values, as
55
+ does `styles.css` when you import it with `styles: false`. To change them,
47
56
  render `ConsentTheme` with your theme where your app renders on the server,
48
57
  or override the variables directly in your CSS.
49
58
 
@@ -80,8 +89,7 @@ inside `<style>`.
80
89
 
81
90
  A single-page app has no server render, so pick one of two ways:
82
91
 
83
- * **Set the variables in your CSS.** Nothing extra ships to the browser. Load
84
- this after `c15t/react/styles.css`:
92
+ * **Set the variables in your CSS.** Nothing extra ships to the browser:
85
93
 
86
94
  ```css title="src/index.css"
87
95
  :root {