@c15t/react 3.0.0-alpha.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/AGENTS.md +10 -12
  2. package/README.md +41 -70
  3. package/SKILL.md +1 -1
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/components/consent-gate/atoms.js +1 -1
  6. package/dist/components/consent-gate/frame.js +2 -0
  7. package/dist/components/consent-gate/index.js +1 -1
  8. package/dist/components/iab-panel/atoms/root.js +1 -1
  9. package/dist/components/iab-panel/iab-panel.js +1 -1
  10. package/dist/components/iab-prompt/atoms/root.js +1 -1
  11. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  12. package/dist/components/panel/atoms/root.js +1 -1
  13. package/dist/components/panel-trigger/atoms/root.js +1 -1
  14. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  15. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  16. package/dist/components/preferences/atoms/root.js +1 -1
  17. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  18. package/dist/components/prompt/atoms/root.js +1 -1
  19. package/dist/components/prompt/prompt.js +1 -1
  20. package/dist/components/shared/dialog-sheets.js +1 -0
  21. package/dist/components/shared/first-paint-sheets.js +1 -0
  22. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  23. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  24. package/dist/components/shared/primitives/button.js +1 -1
  25. package/dist/components/shared/surface-styles.js +2 -0
  26. package/dist/consent-gate.js +1 -1
  27. package/dist/context/index.js +1 -1
  28. package/dist/devtools.js +1 -1
  29. package/dist/hooks/use-iab-policy-guard.js +1 -0
  30. package/dist/iab/styles.tw3.css +2 -2
  31. package/dist/iab-context.js +1 -1
  32. package/dist/iab-dialog-deferred.js +2 -0
  33. package/dist/iab.js +1 -1
  34. package/dist/index-parts/consent-gate.js +1 -1
  35. package/dist/index-parts/core.js +1 -1
  36. package/dist/index-parts/frame.js +1 -0
  37. package/dist/index-parts/hosted.js +1 -0
  38. package/dist/index-parts/offline.js +1 -0
  39. package/dist/index-parts/presets.js +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/modes.js +1 -0
  42. package/dist/panel-link.js +1 -1
  43. package/dist/panel-trigger.js +1 -1
  44. package/dist/provider.js +1 -1
  45. package/dist/styles.tw3.css +2 -2
  46. package/dist/transports/hosted.js +1 -0
  47. package/dist/transports/manifest.js +1 -0
  48. package/dist/utils/deferred-module.js +1 -1
  49. package/dist/version.js +1 -1
  50. package/dist-types/chunk-warming.d.ts +7 -6
  51. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  52. package/dist-types/components/consent-gate/index.d.ts +3 -19
  53. package/dist-types/components/iab-prompt/iab-prompt.d.ts +1 -1
  54. package/dist-types/components/panel/index.d.ts +1 -1
  55. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  56. package/dist-types/components/preferences/index.d.ts +1 -1
  57. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  58. package/dist-types/components/prompt/index.d.ts +1 -1
  59. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  60. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  61. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  62. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  63. package/dist-types/components/shared/surface-styles.d.ts +60 -0
  64. package/dist-types/consent-gate.d.ts +1 -1
  65. package/dist-types/context/index.d.ts +3 -3
  66. package/dist-types/context.d.ts +2 -0
  67. package/dist-types/gpp.d.ts +1 -1
  68. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  69. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  70. package/dist-types/iab.d.ts +3 -2
  71. package/dist-types/index-parts/consent-gate.d.ts +1 -1
  72. package/dist-types/index-parts/core.d.ts +1 -2
  73. package/dist-types/index-parts/frame.d.ts +7 -0
  74. package/dist-types/index-parts/hosted.d.ts +7 -0
  75. package/dist-types/index-parts/offline.d.ts +7 -0
  76. package/dist-types/index-parts/presets.d.ts +7 -0
  77. package/dist-types/index.d.ts +5 -1
  78. package/dist-types/modes.d.ts +26 -0
  79. package/dist-types/panel-link.d.ts +1 -1
  80. package/dist-types/panel-trigger.d.ts +1 -1
  81. package/dist-types/provider.d.ts +22 -4
  82. package/dist-types/transports/hosted.d.ts +36 -0
  83. package/dist-types/transports/manifest.d.ts +48 -0
  84. package/dist-types/ui-config-context.d.ts +4 -0
  85. package/dist-types/version.d.ts +1 -1
  86. package/docs/README.md +10 -12
  87. package/docs/concepts/choose-your-setup.md +45 -8
  88. package/docs/concepts/consent-categories.md +1 -1
  89. package/docs/concepts/consent-state.md +5 -2
  90. package/docs/concepts/data-fetching.md +100 -40
  91. package/docs/concepts/modes.md +281 -0
  92. package/docs/customization/dark-mode.md +1 -1
  93. package/docs/customization/motion.md +2 -2
  94. package/docs/customization/overview.md +3 -2
  95. package/docs/customization/recipes.md +4 -4
  96. package/docs/customization/slots.md +25 -25
  97. package/docs/customization/stylesheets.md +161 -50
  98. package/docs/customization/tailwind.md +71 -50
  99. package/docs/customization/tokens.md +42 -13
  100. package/docs/frameworks/react/components/consent-banner.md +14 -12
  101. package/docs/frameworks/react/components/consent-dialog.md +15 -13
  102. package/docs/frameworks/react/components/consent-provider.md +51 -46
  103. package/docs/frameworks/react/components.md +16 -13
  104. package/docs/frameworks/react/compose.md +3 -3
  105. package/docs/frameworks/react/content-security-policy.md +35 -12
  106. package/docs/frameworks/react/customize.md +21 -13
  107. package/docs/frameworks/react/hooks.md +2 -2
  108. package/docs/frameworks/react/iab.md +56 -6
  109. package/docs/frameworks/react/quickstart.md +65 -99
  110. package/docs/frameworks/react/rendering.md +170 -94
  111. package/docs/frameworks/react/scripts.md +19 -30
  112. package/docs/frameworks/react/troubleshooting.md +100 -32
  113. package/docs/frameworks/react/upgrade-v3.md +133 -79
  114. package/docs/frameworks/react/vendor-consent.md +19 -7
  115. package/docs/guides/banner-experiments.md +21 -8
  116. package/docs/guides/troubleshooting.md +90 -26
  117. package/docs/guides/verify-consent.md +23 -8
  118. package/docs/integrations/adobe-analytics.md +60 -64
  119. package/docs/integrations/ahrefs-analytics.md +58 -63
  120. package/docs/integrations/amplitude.md +60 -64
  121. package/docs/integrations/clearbit.md +59 -63
  122. package/docs/integrations/cloudflare-web-analytics.md +56 -60
  123. package/docs/integrations/cloudflare-zaraz.md +55 -57
  124. package/docs/integrations/crisp.md +64 -69
  125. package/docs/integrations/databuddy.md +62 -64
  126. package/docs/integrations/fathom-analytics.md +56 -60
  127. package/docs/integrations/front-chat.md +56 -60
  128. package/docs/integrations/google-maps.md +25 -13
  129. package/docs/integrations/google-tag-manager.md +153 -77
  130. package/docs/integrations/google-tag.md +134 -78
  131. package/docs/integrations/heap.md +56 -60
  132. package/docs/integrations/hightouch.md +55 -60
  133. package/docs/integrations/hotjar.md +60 -64
  134. package/docs/integrations/intercom.md +55 -60
  135. package/docs/integrations/klaviyo.md +65 -66
  136. package/docs/integrations/linkedin-insights.md +58 -63
  137. package/docs/integrations/logrocket.md +55 -60
  138. package/docs/integrations/matomo-analytics.md +58 -60
  139. package/docs/integrations/meta-pixel.md +56 -60
  140. package/docs/integrations/microsoft-clarity.md +57 -60
  141. package/docs/integrations/microsoft-uet.md +58 -61
  142. package/docs/integrations/mixpanel-analytics.md +73 -75
  143. package/docs/integrations/one-dollar-stats.md +53 -59
  144. package/docs/integrations/openai-pixel.md +58 -60
  145. package/docs/integrations/overview.md +59 -42
  146. package/docs/integrations/pinterest-tag.md +56 -60
  147. package/docs/integrations/pirsch.md +54 -60
  148. package/docs/integrations/plausible-analytics.md +55 -60
  149. package/docs/integrations/posthog.md +56 -60
  150. package/docs/integrations/promptwatch.md +57 -63
  151. package/docs/integrations/reddit-pixel.md +57 -60
  152. package/docs/integrations/rudderstack.md +65 -67
  153. package/docs/integrations/rybbit-analytics.md +70 -75
  154. package/docs/integrations/segment.md +56 -60
  155. package/docs/integrations/sentry.md +65 -60
  156. package/docs/integrations/snapchat-pixel.md +54 -60
  157. package/docs/integrations/tiktok-pixel.md +59 -63
  158. package/docs/integrations/umami-analytics.md +54 -60
  159. package/docs/integrations/vercel-analytics.md +53 -59
  160. package/docs/integrations/vercel-speed-insights.md +317 -0
  161. package/docs/integrations/x-pixel.md +57 -63
  162. package/docs/integrations/youtube.md +25 -13
  163. package/package.json +18 -8
  164. package/docs/upgrade-v3.md +0 -429
@@ -0,0 +1,281 @@
1
+ ---
2
+ title: Consent modes
3
+ description: Reference for manifest(), hosted() and offline() in every c15t
4
+ framework package, where each one resolves the visitor's policy, what it adds
5
+ to first-load JavaScript, the build-time manifest fetch and its failure
6
+ policy, and the backend URL variable each framework reads.
7
+ group: concepts
8
+ ---
9
+
10
+ ## Choose a mode
11
+
12
+ A mode decides how c15t finds the policy that applies to a visitor. Every
13
+ framework package exports the same three factories, `manifest()`, `hosted()`
14
+ and `offline()`. Consent choices go to the backend in `manifest()` and
15
+ `hosted()`, whether that backend is [Inth](https://inth.com) or a
16
+ [self-hosted c15t backend](https://c15t.com/docs/self-host/quickstart).
17
+
18
+ | Mode | Where the policy resolves | Backend requests on a first visit | Use it when |
19
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `manifest()`, the default | Your server, from the policy the build downloaded. With `resolve: 'browser'`, or in a single-page app, the browser. | None to resolve the policy, unless the browser needs a location it doesn't have. Choices post to `/subjects`. | Server-rendered apps. Rebuild to ship policy changes, or set `source: 'runtime'`. |
21
+ | `hosted()` | The backend's `GET /init` | One `/init` per page that resolves consent. | Single-page apps and static hosting without a server, where the backend reads each visitor's location from the request. Policy edits apply without a rebuild. |
22
+ | `offline()` | The browser, from rules in your bundle | None. Choices stay in the browser. | Local development, tests and demos. Not recommended for production environments. |
23
+
24
+ `manifest()` is the default in Next.js, TanStack Start, Nuxt, Astro and
25
+ SvelteKit. React, Vue, Svelte and `@c15t/browser` have no default: pass a
26
+ mode yourself. The React, Vue and Svelte quickstarts use `hosted()`, because
27
+ a browser has no request location and a bundled regional policy still needs
28
+ `/init` on a first visit. The JavaScript quickstart uses `manifest()`. The
29
+ script tag picks its
30
+ mode from the bundle file; see
31
+ [choose a mode](https://c15t.com/docs/frameworks/html/attributes-and-api#choose-a-mode).
32
+
33
+ [Data fetching](./data-fetching.md) compares these paths with the
34
+ custom transport and same-origin proxies.
35
+
36
+ ## Import the factories from your framework package
37
+
38
+ There are two kinds of factory, and each framework package exports the kind it
39
+ needs under the same three names.
40
+
41
+ | Framework | Import from | Pass the mode to | Kind |
42
+ | -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------- |
43
+ | Next.js | `c15t/next` | `mode` in `c15t.config.ts` | Data |
44
+ | TanStack Start | `c15t/tanstack-start` | `createConsentStateHandler({ mode })` | Data |
45
+ | Nuxt | `c15t/vue` | `c15t.mode` in `nuxt.config.ts` | Data |
46
+ | Astro | `c15t/astro` | `c15t({ mode })` in `astro.config.mjs` | Data |
47
+ | SvelteKit | `@c15t/svelte/kit` | `c15tHandle({ mode })` in `src/hooks.server.ts` | Data |
48
+ | React | `c15t/react` | `ConsentProvider`'s `options.mode` | Transport |
49
+ | Vue | `c15t/vue/vue-plugin` | `app.use(c15tVue, { mode })` | Transport |
50
+ | Svelte | `@c15t/svelte` | `<ConsentProvider mode={...}>` | Transport |
51
+ | JavaScript | `@c15t/browser` | `init({ mode })` | Transport |
52
+ | Script tag | none | The bundle file: `c15t.js` is hosted, `c15t.offline.js` offline. The headless and IAB scripts also take `data-mode="manifest"`. | Name |
53
+
54
+ **Data factories** return a plain object such as `{ type: 'manifest' }`.
55
+ Server-rendered frameworks read their config on the server and in the
56
+ browser, and Nuxt and Astro serialize it, so the mode has to be data. The
57
+ framework turns it into a transport in the browser and loads the code of
58
+ any mode other than the default with `import()`. The same factories are
59
+ available from `c15t/modes` (or `@c15t/core/modes`) for shared code.
60
+
61
+ **Transport factories** return the transport itself, with the same `type` and
62
+ options attached as properties. A single-page app has one environment, so
63
+ the bundler keeps only the mode you import and drops the others.
64
+
65
+ A server-rendered root that receives a transport factory still works, for
66
+ example `hosted()` from `c15t/react` passed to Next.js `ConsentRoot` as
67
+ `options.mode`. Outside production it warns that the transport's code is now
68
+ in the first-load bundle. `custom(transport)` exists only as a transport, so
69
+ it cannot go in a serializable config. Pass it through the root's
70
+ `options.mode` instead.
71
+
72
+ ## manifest()
73
+
74
+ `manifest()` resolves the visitor's policy from the backend's public consent
75
+ manifest instead of asking the backend's `/init`. A
76
+ [consent manifest](./data-fetching.md#what-is-a-consent-manifest)
77
+ holds your policy rules and copy, and no visitor data.
78
+
79
+ | Option | Default | Behavior |
80
+ | ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
81
+ | `source` | `'build'` | `'build'` uses the snapshot the build integration downloaded. Without one, such as in dev after a failed fetch, it fetches the manifest at runtime. `'runtime'` always fetches at runtime, so policy edits apply without a rebuild. |
82
+ | `snapshot` | none | A manifest you supply. You can pass `snapshot` or `source`, not both. |
83
+ | `resolve` | `'server'` | `'server'` resolves each visitor on the server and ships no resolver to the browser. `'browser'` resolves in the browser, for pages the server does not render per visitor. Server-rendered frameworks only. |
84
+ | `manifestURL` | see below | Where to fetch the manifest at runtime. On the server, `${backendURL}/manifest`. In the browser, `${routePrefix}/manifest`, else `${backendURL}/manifest`. |
85
+ | `geoURL` | none | A same-origin route that answers with the visitor's `{ country, region }`. Browser resolution only. |
86
+ | `inputs` | none | `{ country, region }` when the page already knows the visitor's location, for example from an edge worker. Browser resolution only. |
87
+
88
+ The transport `manifest()` from `c15t/react`, `c15t/vue/vue-plugin`,
89
+ `@c15t/svelte` and `@c15t/browser` always resolves in the browser, so it has
90
+ no `resolve` option. It also takes `backendURL`, `headers`, `credentials`,
91
+ `domain`, `fetch` and `initFallback`. With no options it reads the snapshot
92
+ and the backend URL your build integration downloaded. With `manifestURL`,
93
+ the browser fetches that URL when the page loads and the build's snapshot is
94
+ not used. Saves still go to `backendURL`, or the build's backend URL.
95
+
96
+ When the policy depends on where the visitor is, and the browser does not
97
+ know the country or region, browser resolution asks `geoURL`, then the
98
+ backend's `/init`. Set `initFallback: false` to resolve for an unknown location
99
+ instead. A policy with location rules therefore saves no request in a
100
+ single-page app unless you pass `inputs` or `geoURL`. The `consentManifest()`
101
+ plugins from `c15t/build`, `c15t/vue/vite` and `@c15t/svelte/vite` warn
102
+ about this when a single-page app build bundles such a policy.
103
+
104
+ ## hosted()
105
+
106
+ `hosted()` asks the backend's `GET /init` for each visitor's policy and posts
107
+ choices to `${backendURL}/subjects`.
108
+
109
+ | Option | Default | Behavior |
110
+ | ------------ | --------------------------- | ------------------------------------------------------ |
111
+ | `backendURL` | the framework's backend URL | Backend URL, absolute or relative such as `/api/c15t`. |
112
+ | `headers` | none | Headers sent with `GET /init`. |
113
+
114
+ The transport `hosted()` also takes `initURL`, `assertDecisionInputs`,
115
+ `domain` and `fetch`. `initURL` sends `GET /init` to another URL, usually a
116
+ same-origin route that resolves the manifest, while saves keep going to
117
+ `${backendURL}/subjects`. `assertDecisionInputs` defaults to `true` when
118
+ `initURL` is set, so each save carries the policy decision it was made
119
+ against and the backend can reject a save made against a stale policy.
120
+
121
+ `hosted()` from `c15t` (`@c15t/core`) is the plain transport and requires
122
+ `backendURL`. The framework exports fill it in from your config or your
123
+ build.
124
+
125
+ ## offline()
126
+
127
+ Not recommended for production environments.
128
+
129
+ `offline()` resolves policy rules in the browser and stores choices in browser
130
+ storage. Nothing reaches a backend, so there is no consent record, no
131
+ cross-device history and no IP geolocation.
132
+
133
+ | Option | Default | Behavior |
134
+ | ------------- | ------------------------- | ----------------------------------------------------------- |
135
+ | `policyRules` | the recommended rule pack | Rules to resolve. Passing rules replaces the pack entirely. |
136
+
137
+ Unknown country and region are real inputs: test the missing-location case
138
+ against your rules. See [policy rules](./policies.md).
139
+
140
+ ## What each mode adds to first-load JavaScript
141
+
142
+ In a server-rendered framework, the browser receives the resolved state with
143
+ the page. It only needs the code to save a choice, and loads the rest when it
144
+ runs:
145
+
146
+ | Mode | First-load JavaScript | Loaded with `import()` when it runs |
147
+ | ---------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
148
+ | `manifest()` | The code that saves choices and builds an init request | The hosted init path, if the browser has to resolve consent again |
149
+ | `manifest({ resolve: 'browser' })` | The code that saves choices | The browser resolver, English copy and the fetch of `${routePrefix}/manifest` or `${backendURL}/manifest`. Other languages load per language. |
150
+ | `hosted()` | The code that saves choices | The hosted init path |
151
+ | `offline()` | Nothing mode-specific | The offline transport, the rule pack and its copy |
152
+
153
+ With `manifest()` resolved on the server, the manifest snapshot, the resolver,
154
+ the offline rule pack and other languages stay out of the browser bundle.
155
+
156
+ In a single-page app the bundle contains the mode you imported:
157
+
158
+ | Mode | In the bundle |
159
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
160
+ | `manifest()` | The snapshot, when the build downloaded one, and the code that saves choices. The resolver starts loading when `manifest()` runs, unless the policy needs a location the page does not know. English copy is bundled; other languages load the first time a visitor needs one. |
161
+ | `hosted()` | The hosted transport |
162
+ | `offline()` | The offline transport, the recommended rule pack and its copy |
163
+
164
+ ## Set the backend URL
165
+
166
+ Each framework reads its backend URL from a public environment variable, so
167
+ the quickstarts never write it in code. Put it in `.env` or your host's build
168
+ settings. The value is public configuration, not a secret.
169
+
170
+ | Framework | Variable | Inth alternative |
171
+ | --------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
172
+ | Next.js | `NEXT_PUBLIC_C15T_BACKEND_URL` | `NEXT_PUBLIC_INTH_PROJECT_URL` |
173
+ | Nuxt | `NUXT_PUBLIC_C15T_BACKEND_URL` | `NUXT_PUBLIC_INTH_PROJECT_URL` |
174
+ | Astro | `PUBLIC_C15T_BACKEND_URL` | `PUBLIC_INTH_PROJECT_URL` |
175
+ | SvelteKit and Svelte | `PUBLIC_C15T_BACKEND_URL`, then `VITE_C15T_BACKEND_URL` | `PUBLIC_INTH_PROJECT_URL`, then `VITE_INTH_PROJECT_URL` |
176
+ | TanStack Start, React, Vue and JavaScript with Vite | `VITE_C15T_BACKEND_URL` | `VITE_INTH_PROJECT_URL` |
177
+ | Script tag | `data-backend-url` on the script element | – |
178
+
179
+ Set either variable. The Inth one holds your Inth project URL, so other Inth
180
+ SDKs in the same app can read it too. When both are set, the c15t variable
181
+ wins.
182
+
183
+ A backend URL resolves in this order: the mode's own `backendURL`, then the
184
+ framework's top-level `backendURL` option, then the c15t variable, then the
185
+ Inth variable. The build reads each variable from the process environment,
186
+ then from `.env.[mode].local`, `.env.[mode]`, `.env.local` and `.env` in the
187
+ project root, so a c15t variable in `.env` still beats an Inth variable in
188
+ the environment. The Vite plugins set an unset `VITE_C15T_BACKEND_URL` to the
189
+ URL they used, so `import.meta.env.VITE_C15T_BACKEND_URL` in app code reads
190
+ the same value. `withConsentManifest()` does the same for
191
+ `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js.
192
+
193
+ Next.js inlines `NEXT_PUBLIC_` variables at build time. Changing the variable
194
+ on a built app has no effect until you rebuild.
195
+
196
+ ## Download the manifest at build time
197
+
198
+ `manifest()` with the default `source: 'build'` resolves from a snapshot that a
199
+ build integration downloads from `${backendURL}/manifest`:
200
+
201
+ | Framework | Build integration | Name in messages |
202
+ | -------------------- | ------------------------------------------------------------------- | ---------------------------- |
203
+ | Next.js | `withConsentManifest()` from `c15t/next/build`, in `next.config.ts` | `@c15t/nextjs/build` |
204
+ | TanStack Start | `consentManifest()` from `c15t/tanstack-start/build` | `@c15t/tanstack-start/build` |
205
+ | React and JavaScript | `consentManifest()` from `c15t/build` | `@c15t/core/build` |
206
+ | Vue | `consentManifest()` from `c15t/vue/vite` | `@c15t/vue/vite` |
207
+ | Svelte and SvelteKit | `consentManifest()` from `@c15t/svelte/vite` | `@c15t/svelte/vite` |
208
+ | Nuxt | the `c15t/vue` module | `@c15t/vue` |
209
+ | Astro | the `c15t()` integration | `@c15t/astro` |
210
+
211
+ The integration downloads the manifest when the production build or the dev
212
+ server starts, and waits at most 10 seconds. Nothing is written into your
213
+ source tree. App code reads the result from `c15t/generated`:
214
+
215
+ ```ts
216
+ import { backendURL, snapshot } from 'c15t/generated';
217
+ ```
218
+
219
+ Both exports are `undefined` when the build has no snapshot, so imports
220
+ compile on a fresh checkout. In the browser bundle of a server-rendered
221
+ framework, `snapshot` is always `undefined`. Next.js goes further: a client
222
+ component that imports `c15t/generated` fails the build.
223
+
224
+ Rebuild after you change policies, translations or vendors in your project. If
225
+ your CI caches build output, force a fresh build.
226
+
227
+ ## What happens when the download fails
228
+
229
+ The same policy applies in every framework:
230
+
231
+ | Command | Default when the download fails or no backend URL is set |
232
+ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
233
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
234
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning. The server, or a single-page app's browser, fetches the manifest at runtime. |
235
+
236
+ `onBuildError` picks one behavior for both commands:
237
+
238
+ * `'fail'` stops dev too.
239
+ * `'runtime'` lets a production build finish without a snapshot. The manifest
240
+ is then fetched at runtime.
241
+
242
+ Pass it where you configure the integration: the second argument of
243
+ `withConsentManifest()`, `consentManifest({ onBuildError })`, the `c15t` key
244
+ in `nuxt.config.ts`, or `c15t({ onBuildError })` in Astro.
245
+
246
+ The `C15T_ON_BUILD_ERROR` environment variable overrides the option, so you
247
+ can deploy during a backend outage without a code change:
248
+
249
+ ```sh
250
+ C15T_ON_BUILD_ERROR=runtime npm run build
251
+ ```
252
+
253
+ Any value other than `fail` or `runtime` stops the command. Turborepo's strict
254
+ environment mode hides undeclared variables from tasks, so list
255
+ `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
256
+
257
+ A missing backend URL follows the same policy as a failed download: a
258
+ production build stops, dev warns, and an explicit `'runtime'` logs a notice
259
+ and continues. Astro and Nuxt are stricter in `manifest()` mode, including
260
+ `manifest({ snapshot })`: the browser saves consent to the backend, so a
261
+ missing backend URL stops `astro dev` and `nuxt dev` as well.
262
+
263
+ The download is skipped without an error when the build cannot use a
264
+ snapshot:
265
+
266
+ * The backend URL is relative, such as `/api/c15t`. With an explicit
267
+ `onBuildError: 'fail'`, a relative URL stops the build instead.
268
+ * Next.js builds with `output: 'export'`, which has no server.
269
+ * The mode is `hosted()`, `offline()`, `manifest({ snapshot })` or
270
+ `manifest({ source: 'runtime' })` in Next.js, Nuxt and Astro. Next.js reads
271
+ the mode from `c15t.config.ts`.
272
+ * A single-page app built with Vite (React, Vue, Svelte, JavaScript) does
273
+ not use `manifest()`. The mode is set in app code, so the plugin downloads
274
+ the manifest only once the bundle turns out to read it, after unused code
275
+ is dropped. A `hosted()` or `offline()` build never contacts the backend.
276
+ SvelteKit and TanStack Start choose the mode on the server at runtime, so
277
+ their builds always download it.
278
+
279
+ A failed download never reuses an older snapshot. The
280
+ [troubleshooting guide](../guides/troubleshooting.md#why-does-the-build-fail-to-download-the-manifest)
281
+ lists each message and its fix.
@@ -35,7 +35,7 @@ your site and let c15t follow it.
35
35
  | Nuxt | `colorScheme` under the `c15t` key in `nuxt.config.ts`, or in `app/app.config.ts` | Unset: copies `dark` |
36
36
  | Vue | `colorScheme` in the `c15tVue` options | Unset: copies `dark` |
37
37
  | Astro | `colorScheme` in the `c15t()` integration options | `'system'` |
38
- | Svelte, SvelteKit | `colorScheme` on `ConsentManagerProvider` | Unset: copies `dark` |
38
+ | Svelte, SvelteKit | `colorScheme` on `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) | Unset: copies `dark` |
39
39
  | HTML | `data-color-scheme` on the script tag: `light`, `dark`, `system` or `none` | `system` |
40
40
  | JavaScript | `ui.colorScheme` in `init()` | `'system'` |
41
41
 
@@ -55,12 +55,12 @@ surface where your framework allows:
55
55
 
56
56
  | Framework | For every surface | For one surface |
57
57
  | ----------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
58
- | Next.js, TanStack Start | `options.disableAnimation` on `ConsentRoot` | `disableAnimation` prop on `ConsentBanner`, `ConsentDialog`, `IABConsentBanner`, `IABConsentDialog` |
58
+ | Next.js, TanStack Start | `options.disableAnimation` in Next.js `c15t.config.ts`, or on `ConsentRoot` | `disableAnimation` prop on `ConsentBanner`, `ConsentDialog`, `IABConsentBanner`, `IABConsentDialog` |
59
59
  | React | `disableAnimation` in the `ConsentProvider` options | The same component props |
60
60
  | Nuxt | `disableAnimation` in the `c15t` module options | `disableAnimation` prop on `consent-banner.vue`, `consent-manager.vue` and the IAB banner and dialog, when you render them yourself |
61
61
  | Vue | `disableAnimation` in the `c15tVue` options | The same component props |
62
62
  | Astro | `disableAnimation` in the `c15t()` integration options | `disableAnimation` prop on `ConsentBanner`, `ConsentDialog`, `IABConsentBanner`, `IABConsentDialog` |
63
- | Svelte, SvelteKit | `disableAnimation` on `ConsentManagerProvider` | `disableAnimation` prop on `ConsentBanner`, `ConsentDialog`, `IABConsentBanner`, `IABConsentDialog` |
63
+ | Svelte, SvelteKit | `disableAnimation` on `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) | `disableAnimation` prop on `ConsentBanner`, `ConsentDialog`, `IABConsentBanner`, `IABConsentDialog` |
64
64
  | HTML | `data-disable-animation` on the script tag, or `ui.disableAnimation` in `config` | `ui.banner.disableAnimation`, `ui.dialog.disableAnimation` |
65
65
  | JavaScript | `ui.disableAnimation` in `init()` | `ui.banner.disableAnimation`, `ui.dialog.disableAnimation` |
66
66
 
@@ -67,8 +67,9 @@ policy requires, c15t puts it back.
67
67
  dark first paint.
68
68
  * [Motion and animation](./motion.md): duration and easing
69
69
  tokens, `disableAnimation` and reduced motion.
70
- * [Stylesheets and CSS layers](./stylesheets.md): which file to
71
- load, when the dialog's CSS loads, and how to run without c15t's styles.
70
+ * [Stylesheets and CSS layers](./stylesheets.md): how c15t
71
+ loads its rules, when to import `styles.css` yourself, when the dialog's CSS
72
+ loads, and how to run without c15t's styles.
72
73
  * [Component parts](./slots.md): every part, its keys in each
73
74
  framework, and the `data-*` attributes to select on.
74
75
  * [Class names and CSS-in-JS](./class-names.md): CSS Modules,
@@ -82,7 +82,7 @@ presentation: {
82
82
  **Svelte**
83
83
 
84
84
  Render `CookieBanner` in place of `ConsentBanner`, inside
85
- `ConsentManagerProvider` and next to `ConsentDialog`.
85
+ `ConsentProvider` and next to `ConsentDialog`.
86
86
 
87
87
  ```svelte title="src/lib/cookie-banner.svelte"
88
88
  <script lang="ts">
@@ -245,7 +245,7 @@ export const slimBar = {
245
245
 
246
246
  **Svelte**
247
247
 
248
- Spread `slimBar` into the options you pass to `ConsentManagerProvider`.
248
+ Spread `slimBar` into the props you pass to `ConsentProvider`.
249
249
  Svelte scopes component styles, so the classes live in a global
250
250
  stylesheet.
251
251
 
@@ -255,7 +255,7 @@ import type { ConsentManagerOptions } from '@c15t/svelte';
255
255
  import './slim-bar.css';
256
256
 
257
257
  /**
258
- * Spread into the options you pass to `ConsentManagerProvider`. The bar
258
+ * Spread into the options you pass to `ConsentProvider`. The bar
259
259
  * keeps the stock markup and actions; the slot classes put it on one row.
260
260
  */
261
261
  export const slimBar = {
@@ -451,7 +451,7 @@ const run = (action: string) => {
451
451
 
452
452
  `getHeadlessConsent()` returns the banner state and `performAction`.
453
453
  Render `CookieBar` in place of `ConsentBanner`, inside
454
- `ConsentManagerProvider`, and keep `ConsentDialog`, which Preferences
454
+ `ConsentProvider`, and keep `ConsentDialog`, which Preferences
455
455
  opens.
456
456
 
457
457
  ```svelte title="src/lib/cookie-bar.svelte"
@@ -12,15 +12,15 @@ Each framework has one way to add classes, inline styles or attributes to a
12
12
  single part of a stock component. The keys differ between the two
13
13
  vocabularies, so a configuration from one does not work in the other.
14
14
 
15
- | Framework | Part API | Keys look like | Class key |
16
- | ----------------------- | ------------------------------------------------------------- | ------------------------- | ------------------------ |
17
- | Next.js, TanStack Start | `options.components` on `ConsentRoot` | `components.banner.card` | `className` |
18
- | React | `components` in the `ConsentProvider` options | `components.banner.card` | `className` |
19
- | Nuxt | `components` in the `c15t` options, or in `app/app.config.ts` | `components.banner.card` | `class` |
20
- | Vue | `components` in the `c15tVue` options | `components.banner.card` | `class` |
21
- | Astro | `theme.slots` in the `c15t()` integration options | `slots.consentBannerCard` | A string, or `className` |
22
- | Svelte, SvelteKit | `theme.slots` on `ConsentManagerProvider` | `slots.consentBannerCard` | A string, or `className` |
23
- | HTML, JavaScript | `ui.theme.slots` | `slots.consentBannerCard` | A string, or `className` |
15
+ | Framework | Part API | Keys look like | Class key |
16
+ | ----------------------- | ------------------------------------------------------------------------ | ------------------------- | ------------------------ |
17
+ | Next.js, TanStack Start | `options.components` in Next.js `c15t.config.ts`, or on `ConsentRoot` | `components.banner.card` | `className` |
18
+ | React | `components` in the `ConsentProvider` options | `components.banner.card` | `className` |
19
+ | Nuxt | `components` in the `c15t` options, or in `app/app.config.ts` | `components.banner.card` | `class` |
20
+ | Vue | `components` in the `c15tVue` options | `components.banner.card` | `class` |
21
+ | Astro | `theme.slots` in the `c15t()` integration options | `slots.consentBannerCard` | A string, or `className` |
22
+ | Svelte, SvelteKit | `theme.slots` on `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) | `slots.consentBannerCard` | A string, or `className` |
23
+ | HTML, JavaScript | `ui.theme.slots` | `slots.consentBannerCard` | A string, or `className` |
24
24
 
25
25
  React and Vue also read `theme.slots` from the theme in their options, and
26
26
  apply each slot to the matching `components` part, such as
@@ -65,22 +65,22 @@ Modules, vanilla-extract, StyleX and Emotion, and
65
65
 
66
66
  ## Banner parts
67
67
 
68
- | Part | `components` key | `theme.slots` key | `data-testid` |
69
- | ------------------------------------------- | ------------------------------------ | ---------------------------------- | --------------------- |
70
- | Positioning root, with the state attributes | `banner.root` | `consentBanner` | `consent-banner-root` |
71
- | Card sizing and branding container | `banner.cardShell` | None | |
72
- | Visible card | `banner.card` | `consentBannerCard` | `consent-banner-card` |
73
- | Title and description container | `banner.header` | `consentBannerHeader` | |
74
- | Title | `banner.title` | `consentBannerTitle` | |
75
- | Description | `description.banner` | `consentBannerDescription` | |
76
- | Action area | `banner.footer` | `consentBannerFooter` | |
77
- | Group of action groups | `banner.actions` | None | |
78
- | One group of equally prominent actions | `banner.actionGroup` | `consentBannerFooterSubGroup` | |
79
- | Group of right links | `banner.rights` | `consentBannerRights` | |
80
- | One right link | `banner.rightLink` | `consentBannerRightLink` | |
81
- | Backdrop of a blocking banner | `banner.overlay` | `consentBannerOverlay` | |
82
- | "Secured by" tag | `tag.banner` | `consentBannerTag` | |
83
- | Filled and outlined buttons | `button.primary`, `button.secondary` | `buttonPrimary`, `buttonSecondary` | |
68
+ | Part | `components` key | `theme.slots` key | `data-testid` |
69
+ | ------------------------------------------- | ------------------------------------ | ---------------------------------- | ------------------------- |
70
+ | Positioning root, with the state attributes | `banner.root` | `consentBanner` | `consent-banner-root` |
71
+ | Card sizing and branding container | `banner.cardShell` | None | |
72
+ | Visible card | `banner.card` | `consentBannerCard` | `consent-banner-card` |
73
+ | Title and description container | `banner.header` | `consentBannerHeader` | |
74
+ | Title | `banner.title` | `consentBannerTitle` | |
75
+ | Description | `description.banner` | `consentBannerDescription` | |
76
+ | Action area | `banner.footer` | `consentBannerFooter` | |
77
+ | Group of action groups | `banner.actions` | None | |
78
+ | One group of equally prominent actions | `banner.actionGroup` | `consentBannerFooterSubGroup` | |
79
+ | Group of right links | `banner.rights` | `consentBannerRights` | |
80
+ | One right link | `banner.rightLink` | `consentBannerRightLink` | |
81
+ | Backdrop of a blocking banner | `banner.overlay` | `consentBannerOverlay` | |
82
+ | "Secured by" tag | `tag.banner` | `consentBannerTag` | `consent-banner-branding` |
83
+ | Filled and outlined buttons | `button.primary`, `button.secondary` | `buttonPrimary`, `buttonSecondary` | |
84
84
 
85
85
  The button parts apply to every c15t button of that style, in the banner and
86
86
  the dialog. The script tag's banner has no rights group, so