@c15t/react 3.1.0 → 3.2.0-canary.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 (120) hide show
  1. package/AGENTS.md +11 -4
  2. package/README.md +2 -2
  3. package/dist/component-hooks/use-consent-gate.js +2 -0
  4. package/dist/component-hooks/use-consent-labels.js +2 -0
  5. package/dist/component-hooks/use-consent-text-direction.js +2 -0
  6. package/dist/component-hooks/use-vendors-listed-under.js +2 -0
  7. package/dist/components/consent-gate/atoms.js +1 -1
  8. package/dist/components/consent-gate/consent-gate.js +1 -1
  9. package/dist/components/iab-prompt/atoms/root.js +1 -1
  10. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  11. package/dist/components/preferences/atoms/button.js +1 -1
  12. package/dist/components/preferences/atoms/vendor-list.js +1 -1
  13. package/dist/components/prompt/atoms/root.js +1 -1
  14. package/dist/components/prompt/components.js +1 -1
  15. package/dist/components/prompt/policy-actions.js +1 -1
  16. package/dist/components/prompt/prompt.js +1 -1
  17. package/dist/components/prompt/surface-context.js +1 -1
  18. package/dist/components/prompt/use-banner-copy.js +1 -1
  19. package/dist/components/shared/streamed-surface.js +1 -1
  20. package/dist/headless.js +1 -1
  21. package/dist/index-parts/offline.js +1 -1
  22. package/dist/modes.js +1 -1
  23. package/dist/transports/offline.js +1 -1
  24. package/dist/version.js +1 -1
  25. package/dist-types/component-hooks/use-consent-gate.d.ts +102 -0
  26. package/dist-types/component-hooks/use-consent-labels.d.ts +32 -0
  27. package/dist-types/component-hooks/use-consent-text-direction.d.ts +22 -0
  28. package/dist-types/component-hooks/use-vendors-listed-under.d.ts +30 -0
  29. package/dist-types/components/prompt/atoms/root.d.ts +3 -3
  30. package/dist-types/components/prompt/prompt.d.ts +4 -3
  31. package/dist-types/components/prompt/surface-context.d.ts +1 -14
  32. package/dist-types/components/prompt/use-banner-copy.d.ts +8 -3
  33. package/dist-types/components/shared/streamed-surface.d.ts +47 -10
  34. package/dist-types/headless.d.ts +10 -1
  35. package/dist-types/hooks/use-iab-policy-guard.d.ts +17 -1
  36. package/dist-types/hooks/use-scroll-lock.d.ts +16 -2
  37. package/dist-types/index-parts/offline.d.ts +1 -1
  38. package/dist-types/modes.d.ts +1 -1
  39. package/dist-types/module-hooks/script-loader.d.ts +9 -0
  40. package/dist-types/provider.d.ts +3 -3
  41. package/dist-types/transports/offline.d.ts +2 -2
  42. package/dist-types/version.d.ts +1 -1
  43. package/docs/README.md +11 -4
  44. package/docs/concepts/choose-your-setup.md +9 -9
  45. package/docs/concepts/consent-state.md +81 -9
  46. package/docs/concepts/data-fetching.md +30 -23
  47. package/docs/concepts/modes.md +85 -35
  48. package/docs/customization/recipes.md +4 -4
  49. package/docs/customization/stylesheets.md +3 -3
  50. package/docs/customization/tailwind.md +17 -34
  51. package/docs/customization/translations.md +18 -12
  52. package/docs/frameworks/react/clear-on-revocation.md +3 -2
  53. package/docs/frameworks/react/components/consent-banner.md +3 -3
  54. package/docs/frameworks/react/components/consent-provider.md +29 -29
  55. package/docs/frameworks/react/gpp.md +1 -1
  56. package/docs/frameworks/react/headless.md +146 -19
  57. package/docs/frameworks/react/iab.md +8 -1
  58. package/docs/frameworks/react/quickstart.md +1 -1
  59. package/docs/frameworks/react/rendering.md +1 -1
  60. package/docs/frameworks/react/translations.md +9 -9
  61. package/docs/frameworks/react/troubleshooting.md +1 -1
  62. package/docs/frameworks/react/upgrade-v3.md +39 -32
  63. package/docs/frameworks/react/vendor-consent.md +5 -0
  64. package/docs/guides/banner-experiments.md +3 -3
  65. package/docs/guides/troubleshooting.md +3 -2
  66. package/docs/integrations/adobe-analytics.md +2 -2
  67. package/docs/integrations/ahrefs-analytics.md +2 -2
  68. package/docs/integrations/amplitude.md +2 -2
  69. package/docs/integrations/clay.md +305 -0
  70. package/docs/integrations/clearbit.md +2 -2
  71. package/docs/integrations/cloudflare-web-analytics.md +2 -2
  72. package/docs/integrations/cloudflare-zaraz.md +2 -2
  73. package/docs/integrations/crisp.md +2 -2
  74. package/docs/integrations/databuddy.md +2 -2
  75. package/docs/integrations/fathom-analytics.md +2 -2
  76. package/docs/integrations/front-chat.md +2 -2
  77. package/docs/integrations/google-maps.md +2 -2
  78. package/docs/integrations/google-tag-manager.md +59 -2
  79. package/docs/integrations/google-tag.md +2 -2
  80. package/docs/integrations/heap.md +2 -2
  81. package/docs/integrations/hightouch.md +2 -2
  82. package/docs/integrations/hotjar.md +40 -26
  83. package/docs/integrations/intercom.md +2 -2
  84. package/docs/integrations/klaviyo.md +2 -2
  85. package/docs/integrations/linkedin-insights.md +2 -2
  86. package/docs/integrations/logrocket.md +2 -2
  87. package/docs/integrations/matomo-analytics.md +2 -2
  88. package/docs/integrations/meta-pixel.md +2 -2
  89. package/docs/integrations/microsoft-clarity.md +2 -2
  90. package/docs/integrations/microsoft-uet.md +2 -2
  91. package/docs/integrations/mixpanel-analytics.md +2 -2
  92. package/docs/integrations/one-dollar-stats.md +2 -2
  93. package/docs/integrations/openai-pixel.md +2 -2
  94. package/docs/integrations/other-ads-and-pixels.md +94 -0
  95. package/docs/integrations/other-analytics.md +108 -0
  96. package/docs/integrations/other-chat-and-support.md +112 -0
  97. package/docs/integrations/other-email-and-sms.md +156 -0
  98. package/docs/integrations/other-experience.md +222 -0
  99. package/docs/integrations/overview.md +55 -21
  100. package/docs/integrations/pinterest-tag.md +2 -2
  101. package/docs/integrations/pirsch.md +2 -2
  102. package/docs/integrations/plausible-analytics.md +2 -2
  103. package/docs/integrations/posthog.md +2 -2
  104. package/docs/integrations/promptwatch.md +2 -2
  105. package/docs/integrations/reddit-pixel.md +2 -2
  106. package/docs/integrations/rudderstack.md +2 -2
  107. package/docs/integrations/rybbit-analytics.md +2 -2
  108. package/docs/integrations/segment.md +2 -2
  109. package/docs/integrations/sentry.md +2 -2
  110. package/docs/integrations/shopify.md +323 -0
  111. package/docs/integrations/snapchat-pixel.md +2 -2
  112. package/docs/integrations/tiktok-pixel.md +2 -2
  113. package/docs/integrations/umami-analytics.md +2 -2
  114. package/docs/integrations/vercel-analytics.md +2 -2
  115. package/docs/integrations/vercel-speed-insights.md +7 -2
  116. package/docs/integrations/x-pixel.md +2 -2
  117. package/docs/integrations/youtube.md +2 -2
  118. package/package.json +10 -32
  119. package/dist/components/prompt/resolve-banner-primary-actions.js +0 -1
  120. package/dist-types/components/prompt/resolve-banner-primary-actions.d.ts +0 -14
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Consent modes
3
- description: Reference for manifest(), hosted() and offline() in every c15t
3
+ description: Reference for manifest(), hosted() and local() in every c15t
4
4
  framework package, where each one resolves the visitor's policy, what it adds
5
5
  to first-load JavaScript, the build-time manifest fetch and its failure
6
6
  policy, and the backend URL variable each framework reads.
@@ -11,7 +11,8 @@ group: concepts
11
11
 
12
12
  A mode decides how c15t finds the policy that applies to a visitor. Every
13
13
  framework package exports the same three factories, `manifest()`, `hosted()`
14
- and `offline()`. Consent choices go to the backend in `manifest()` and
14
+ and `local()`. `local()` was called `offline()` before c15t 3.2, and the old
15
+ name still works until c15t v4. Consent choices go to the backend in `manifest()` and
15
16
  `hosted()`, whether that backend is [Inth](https://inth.com) or a
16
17
  [self-hosted c15t backend](https://c15t.com/docs/self-host/quickstart).
17
18
 
@@ -19,7 +20,7 @@ and `offline()`. Consent choices go to the backend in `manifest()` and
19
20
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
21
  | `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
22
  | `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
+ | `local()` | The browser, from rules in your bundle | None. Choices stay in the browser, and c15t keeps no server-side record of consent. | Local development, tests and demos. Not recommended for production environments. |
23
24
 
24
25
  `manifest()` is the default in Next.js, TanStack Start, Nuxt, Astro and
25
26
  SvelteKit. React, Vue, Svelte and `@c15t/browser` have no default: pass a
@@ -38,18 +39,18 @@ custom transport and same-origin proxies.
38
39
  There are two kinds of factory, and each framework package exports the kind it
39
40
  needs under the same three names.
40
41
 
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 |
42
+ | Framework | Import from | Pass the mode to | Kind |
43
+ | -------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------- |
44
+ | Next.js | `c15t/next` | `mode` in `c15t.config.ts` | Data |
45
+ | TanStack Start | `c15t/tanstack-start` | `createConsentStateHandler({ mode })` | Data |
46
+ | Nuxt | `c15t/vue` | `c15t.mode` in `nuxt.config.ts` | Data |
47
+ | Astro | `c15t/astro` | `c15t({ mode })` in `astro.config.mjs` | Data |
48
+ | SvelteKit | `c15t/svelte/kit` | `c15tHandle({ mode })` in `src/hooks.server.ts` | Data |
49
+ | React | `c15t/react` | `ConsentProvider`'s `options.mode` | Transport |
50
+ | Vue | `c15t/vue/vue-plugin` | `app.use(c15tVue, { mode })` | Transport |
51
+ | Svelte | `c15t/svelte` | `<ConsentProvider mode={...}>` | Transport |
52
+ | JavaScript | `@c15t/browser` | `init({ mode })` | Transport |
53
+ | Script tag | none | The bundle file: `c15t.js` is hosted, `c15t.local.js` local. The headless and IAB scripts also take `data-mode="manifest"`. | Name |
53
54
 
54
55
  **Data factories** return a plain object such as `{ type: 'manifest' }`.
55
56
  Server-rendered frameworks read their config on the server and in the
@@ -86,7 +87,7 @@ holds your policy rules and copy, and no visitor data.
86
87
  | `inputs` | none | `{ country, region }` when the page already knows the visitor's location, for example from an edge worker. Browser resolution only. |
87
88
 
88
89
  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
+ `c15t/svelte` and `@c15t/browser` always resolve in the browser, so they have
90
91
  no `resolve` option. It also takes `backendURL`, `headers`, `credentials`,
91
92
  `domain`, `fetch` and `initFallback`. With no options it reads the snapshot
92
93
  and the backend URL your build integration downloaded. With `manifestURL`,
@@ -98,7 +99,7 @@ know the country or region, browser resolution asks `geoURL`, then the
98
99
  backend's `/init`. Set `initFallback: false` to resolve for an unknown location
99
100
  instead. A policy with location rules therefore saves no request in a
100
101
  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
+ plugins from `c15t/build`, `c15t/vue/vite` and `c15t/svelte/vite` warn
102
103
  about this when a single-page app build bundles such a policy.
103
104
 
104
105
  ## hosted()
@@ -122,36 +123,85 @@ against and the backend can reject a save made against a stale policy.
122
123
  `backendURL`. The framework exports fill it in from your config or your
123
124
  build.
124
125
 
125
- ## offline()
126
+ ## local()
126
127
 
127
128
  Not recommended for production environments.
128
129
 
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.
130
+ `local()` resolves policy rules in the browser and stores choices in browser
131
+ storage. Nothing reaches a backend, so c15t keeps no server-side record of
132
+ consent. There is also no cross-device history, and with no IP geolocation
133
+ every visitor gets the policy for an unknown location unless you pass
134
+ `overrides`.
132
135
 
133
- | Option | Default | Behavior |
134
- | ------------- | ------------------------- | ----------------------------------------------------------- |
135
- | `policyRules` | the recommended rule pack | Rules to resolve. Passing rules replaces the pack entirely. |
136
+ | Option | Default | Behavior |
137
+ | ---------------------- | ------------------------- | ------------------------------------------------------------------------------ |
138
+ | `policyRules` | the recommended rule pack | Rules to resolve. Passing rules replaces the pack entirely. |
139
+ | `acknowledgeNoRecords` | `false` | `true` hides the development warning described below. It changes nothing else. |
136
140
 
137
141
  Unknown country and region are real inputs: test the missing-location case
138
142
  against your rules. See [policy rules](./policies.md).
139
143
 
144
+ ### The development warning
145
+
146
+ In a development build, local mode logs one `console.warn` per page load:
147
+
148
+ ```txt
149
+ [c15t] Local mode keeps consent only in this browser. c15t keeps no
150
+ server-side record of consent. Local mode also has no geolocation, so
151
+ every visitor gets the same policy. Use it for development and tests. For
152
+ production, see https://c15t.com/docs/concepts/modes
153
+ Set acknowledgeNoRecords: true to hide this message.
154
+ ```
155
+
156
+ Production builds never log it, and it never blocks the banner or a save.
157
+ The script-tag files are production builds, so `c15t.local.js`, and
158
+ `c15t.headless.js` or `c15t.iab.js` without a backend URL, log it on
159
+ development hosts instead:
160
+ `localhost`, `127.0.0.1`, `[::1]`, `0.0.0.0` and names ending in
161
+ `.localhost`, `.local` or `.test`. A script tag hides it with
162
+ `data-acknowledge-no-records`.
163
+
164
+ Set `acknowledgeNoRecords: true` once you have decided a browser-only record is
165
+ enough, such as in tests, or on a site that keeps its own record through
166
+ [callbacks](https://c15t.com/docs/frameworks/javascript/callbacks) or a [`custom()` transport](./data-fetching.md#can-i-provide-my-own-transport):
167
+
168
+ ```ts
169
+ import { local } from 'c15t/react';
170
+
171
+ const mode = local({ acknowledgeNoRecords: true });
172
+ ```
173
+
174
+ c15t v4 removes the `offline()` name and makes `acknowledgeNoRecords: true`
175
+ required in `local()`'s type.
176
+
177
+ ### What a consent record proves
178
+
179
+ A consent record is the backend's copy of a choice: which categories the
180
+ visitor granted or denied, when, and against which policy and copy. Under the
181
+ GDPR, the business that relies on consent must be able to show that the
182
+ visitor gave it (Article 7(1)). Browser storage alone cannot do that: the
183
+ visitor can clear it, and it never leaves their device.
184
+
185
+ `manifest()` and `hosted()` post each choice to the backend, which keeps the
186
+ record. To keep records without Inth, run a
187
+ [self-hosted backend](https://c15t.com/docs/self-host/overview), or send each choice to your
188
+ own API with `custom()`.
189
+
140
190
  ## What each mode adds to first-load JavaScript
141
191
 
142
192
  In a server-rendered framework, the browser receives the resolved state with
143
193
  the page. It only needs the code to save a choice, and loads the rest when it
144
194
  runs:
145
195
 
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 |
196
+ | Mode | First-load JavaScript | Loaded with `import()` when it runs |
197
+ | ---------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
198
+ | `manifest()` | The code that saves choices and builds an init request | The hosted init path, if the browser has to resolve consent again |
199
+ | `manifest({ resolve: 'browser' })` | The code that saves choices | The browser resolver, English copy and the fetch of `${routePrefix}/manifest` or `${backendURL}/manifest`, unless the server state already holds the policy. Other languages load per language. |
200
+ | `hosted()` | The code that saves choices | The hosted init path |
201
+ | `local()` | Nothing mode-specific | The local transport, the rule pack and its copy |
152
202
 
153
203
  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.
204
+ the local rule pack and other languages stay out of the browser bundle.
155
205
 
156
206
  In a single-page app the bundle contains the mode you imported:
157
207
 
@@ -159,7 +209,7 @@ In a single-page app the bundle contains the mode you imported:
159
209
  | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
160
210
  | `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
211
  | `hosted()` | The hosted transport |
162
- | `offline()` | The offline transport, the recommended rule pack and its copy |
212
+ | `local()` | The local transport, the recommended rule pack and its copy |
163
213
 
164
214
  ## Set the backend URL
165
215
 
@@ -204,7 +254,7 @@ build integration downloads from `${backendURL}/manifest`:
204
254
  | TanStack Start | `consentManifest()` from `c15t/tanstack-start/build` | `@c15t/tanstack-start/build` |
205
255
  | React and JavaScript | `consentManifest()` from `c15t/build` | `@c15t/core/build` |
206
256
  | Vue | `consentManifest()` from `c15t/vue/vite` | `@c15t/vue/vite` |
207
- | Svelte and SvelteKit | `consentManifest()` from `@c15t/svelte/vite` | `@c15t/svelte/vite` |
257
+ | Svelte and SvelteKit | `consentManifest()` from `c15t/svelte/vite` | `@c15t/svelte/vite` |
208
258
  | Nuxt | the `c15t/vue` module | `@c15t/vue` |
209
259
  | Astro | the `c15t()` integration | `@c15t/astro` |
210
260
 
@@ -266,13 +316,13 @@ snapshot:
266
316
  * The backend URL is relative, such as `/api/c15t`. With an explicit
267
317
  `onBuildError: 'fail'`, a relative URL stops the build instead.
268
318
  * Next.js builds with `output: 'export'`, which has no server.
269
- * The mode is `hosted()`, `offline()`, `manifest({ snapshot })` or
319
+ * The mode is `hosted()`, `local()`, `manifest({ snapshot })` or
270
320
  `manifest({ source: 'runtime' })` in Next.js, Nuxt and Astro. Next.js reads
271
321
  the mode from `c15t.config.ts`.
272
322
  * A single-page app built with Vite (React, Vue, Svelte, JavaScript) does
273
323
  not use `manifest()`. The mode is set in app code, so the plugin downloads
274
324
  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.
325
+ is dropped. A `hosted()` or `local()` build never contacts the backend.
276
326
  SvelteKit and TanStack Start choose the mode on the server at runtime, so
277
327
  their builds always download it.
278
328
 
@@ -86,7 +86,7 @@ Render `CookieBanner` in place of `ConsentBanner`, inside
86
86
 
87
87
  ```svelte title="src/lib/cookie-banner.svelte"
88
88
  <script lang="ts">
89
- import { ConsentBanner } from '@c15t/svelte';
89
+ import { ConsentBanner } from 'c15t/svelte';
90
90
  </script>
91
91
 
92
92
  <!-- The stock banner, drawn as a full-width bar along the bottom edge. -->
@@ -250,7 +250,7 @@ Svelte scopes component styles, so the classes live in a global
250
250
  stylesheet.
251
251
 
252
252
  ```ts title="src/lib/consent-options.ts"
253
- import type { ConsentManagerOptions } from '@c15t/svelte';
253
+ import type { ConsentManagerOptions } from 'c15t/svelte';
254
254
 
255
255
  import './slim-bar.css';
256
256
 
@@ -456,8 +456,8 @@ opens.
456
456
 
457
457
  ```svelte title="src/lib/cookie-bar.svelte"
458
458
  <script lang="ts">
459
- import { getHeadlessConsent } from '@c15t/svelte';
460
- import type { PresentationAction } from '@c15t/svelte';
459
+ import { getHeadlessConsent } from 'c15t/svelte';
460
+ import type { PresentationAction } from 'c15t/svelte';
461
461
 
462
462
  import './cookie-bar.css';
463
463
 
@@ -69,7 +69,7 @@ render. The elements stay after the surface unmounts. The Svelte dialog's
69
69
  sheets, its rules and the primitives, load with the dialog's code.
70
70
 
71
71
  On a server-rendered SvelteKit page, the banner leaves a
72
- `<meta name="c15t-styles">` marker, and `c15tHandle` from `@c15t/svelte/kit`
72
+ `<meta name="c15t-styles">` marker, and `c15tHandle` from `c15t/svelte/kit`
73
73
  writes the sheets into the HTML `<head>` as `<style>` elements. The banner is
74
74
  styled before hydration with no stylesheet request. Without `c15tHandle` in
75
75
  `src/hooks.server.ts`, a server-rendered banner shows unstyled until
@@ -109,7 +109,7 @@ need control over the file:
109
109
  | Next.js | `options.styles` in `c15t.config.ts` or on `ConsentRoot` | `c15t/next/styles.css`, and `c15t/next/iab/styles.css` for IAB TCF |
110
110
  | TanStack Start | `options.styles` on `ConsentRoot` | `c15t/tanstack-start/styles.css`, and `c15t/tanstack-start/iab/styles.css` for IAB TCF |
111
111
  | React | `options.styles` on `ConsentProvider` | `c15t/react/styles.css`, and `c15t/react/iab/styles.css` for IAB TCF |
112
- | Svelte, SvelteKit | `options.styles` on `ConsentProvider` or `ConsentRoot` | `@c15t/svelte/styles.css`, and `@c15t/svelte/iab/styles.css` for IAB TCF |
112
+ | Svelte, SvelteKit | `options.styles` on `ConsentProvider` or `ConsentRoot` | `c15t/svelte/styles.css`, and `c15t/svelte/iab/styles.css` for IAB TCF |
113
113
  | Astro | `styles` in the `c15t()` integration options | See [load Astro's stylesheets yourself](#load-astros-stylesheets-yourself) |
114
114
 
115
115
  ```ts title="c15t.config.ts (Next.js)"
@@ -132,7 +132,7 @@ the first paint. Remove the import, or add `styles: false` if you need it.
132
132
  | Next.js, TanStack Start, React | In the dialog sheet that `ConsentDialog` and `ConsentWidget` render. It ships with the dialog's lazily loaded code. With `styles: false`, in `styles.css`. No `@c15t/react` or `@c15t/ui` module imports CSS, so a Next.js app that uses only the Pages Router builds without `transpilePackages` |
133
133
  | Nuxt, Vue | The dialog component imports its stylesheet, so the rules load with the dialog's code, not the banner's |
134
134
  | Astro | In `@c15t/ui/styles/sheets/dialog.css`. The Svelte dialog, the default `ui`, also needs the primitives stylesheet. c15t links both when a visitor first points at, focuses or opens a control that opens the dialog, waits for them before it shows the dialog, and keeps them across `ClientRouter` navigation. On a Tailwind CSS 3 site, the dialog's rules are in `c15t/astro/styles.css`, and c15t links only the primitives stylesheet |
135
- | Svelte, SvelteKit | In the dialog's sheets, which load with the dialog's code. With `styles: false`, in `@c15t/svelte/styles.css` |
135
+ | Svelte, SvelteKit | In the dialog's sheets, which load with the dialog's code. With `styles: false`, in `c15t/svelte/styles.css` |
136
136
  | HTML, JavaScript | Part of the stylesheet in the shadow root, which holds only the rules the stock surfaces use |
137
137
 
138
138
  `styles.css` still holds every stock surface's rules for apps that set
@@ -31,7 +31,7 @@ fixture. The Next.js 15 and webpack fixtures use the same files. The check
31
31
  confirms that c15t's banner and dialog keep their styles under Tailwind's
32
32
  preflight, that a padding utility on the banner root
33
33
  beats c15t's own padding, and that the `dark:` variant applies. The fixtures run
34
- without a backend, so they use `offline()` mode or a local `backendURL`. Keep the
34
+ without a backend, so they use `local()` mode or a local `backendURL`. Keep the
35
35
  `mode` or `backendURL` from your quickstart.
36
36
 
37
37
  ## Set up Tailwind CSS 4
@@ -165,13 +165,13 @@ when it opens, so the CSS entry holds only Tailwind. Add
165
165
  import svelte from '@astrojs/svelte';
166
166
  import tailwindcss from '@tailwindcss/vite';
167
167
  import { defineConfig } from 'astro/config';
168
- import c15t, { offline } from 'c15t/astro';
168
+ import c15t, { local } from 'c15t/astro';
169
169
 
170
170
  export default defineConfig({
171
171
  integrations: [
172
172
  svelte(),
173
173
  c15t({
174
- mode: offline({
174
+ mode: local({
175
175
  policyRules: [
176
176
  {
177
177
  id: 'tailwind-matrix',
@@ -264,11 +264,10 @@ detects Tailwind 3 and links `c15t/astro/styles.css` itself. Every c15t
264
264
  package that ships a stylesheet exports the plugin as
265
265
  `<package>/postcss-tailwind3`, so use the one from the package you installed:
266
266
 
267
- | You installed | Plugin name |
268
- | --------------------------------------------------------- | --------------------------------- |
269
- | `c15t` (React, Next.js, TanStack Start, Vue, Nuxt, Astro) | `c15t/postcss-tailwind3` |
270
- | `@c15t/svelte` (Svelte, SvelteKit) | `@c15t/svelte/postcss-tailwind3` |
271
- | `@c15t/browser` (script tag in the light DOM) | `@c15t/browser/postcss-tailwind3` |
267
+ | You installed | Plugin name |
268
+ | ---------------------------------------------------------------------------- | --------------------------------- |
269
+ | `c15t` (React, Next.js, TanStack Start, Vue, Nuxt, Astro, Svelte, SvelteKit) | `c15t/postcss-tailwind3` |
270
+ | `@c15t/browser` (script tag in the light DOM) | `@c15t/browser/postcss-tailwind3` |
272
271
 
273
272
  They are the same plugin. A config that already lists
274
273
  `@c15t/ui/postcss-tailwind3` keeps working.
@@ -303,17 +302,9 @@ class. Add the important modifier to utilities you pass to c15t parts:
303
302
  `!bg-red-500`, `hover:!bg-red-500`. A bare `bg-red-500` has no effect. Theme
304
303
  tokens work the same on Tailwind 3 and 4.
305
304
 
306
- In React and Next.js apps, `c15t setup` adds the plugin and the stylesheet
307
- import when it finds Tailwind 3, but does not set `styles: false`. Add that
308
- yourself. It replaces an existing
309
- `styles.tw3.css` import from an earlier setup with `styles.css`. When it
310
- cannot edit your PostCSS config, it prints the step to do by hand. See
311
- [setup](https://c15t.com/docs/cli/commands/setup#set-up-styles-and-tailwind-css).
312
-
313
305
  Create React App ignores PostCSS config files, so Tailwind 3 cannot run the
314
- plugin and the build fails on c15t's stylesheet. `c15t setup` warns
315
- about this. Add the plugin before `tailwindcss` through CRACO, eject, or move
316
- the app to Vite.
306
+ plugin and the build fails on c15t's stylesheet. Add the plugin before
307
+ `tailwindcss` through CRACO, eject, or move the app to Vite.
317
308
 
318
309
  **Next.js**
319
310
 
@@ -521,11 +512,7 @@ stylesheet:
521
512
 
522
513
  ```js title="postcss.config.mjs"
523
514
  export default {
524
- plugins: {
525
- '@c15t/svelte/postcss-tailwind3': {},
526
- tailwindcss: {},
527
- autoprefixer: {},
528
- },
515
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
529
516
  };
530
517
  ```
531
518
 
@@ -540,13 +527,13 @@ export default {
540
527
  } satisfies Config;
541
528
  ```
542
529
 
543
- Import `@c15t/svelte/styles.css` above the directives, import this
530
+ Import `c15t/svelte/styles.css` above the directives, import this
544
531
  stylesheet from `src/main.ts`, and pass `styles={false}` to
545
532
  `ConsentProvider`:
546
533
 
547
534
  ```css title="src/app.css"
548
535
  /* Vite ignores an @import below other rules, so it goes first. */
549
- @import '@c15t/svelte/styles.css';
536
+ @import 'c15t/svelte/styles.css';
550
537
 
551
538
  @tailwind base;
552
539
  @tailwind components;
@@ -557,11 +544,7 @@ stylesheet from `src/main.ts`, and pass `styles={false}` to
557
544
 
558
545
  ```js title="postcss.config.mjs"
559
546
  export default {
560
- plugins: {
561
- '@c15t/svelte/postcss-tailwind3': {},
562
- tailwindcss: {},
563
- autoprefixer: {},
564
- },
547
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
565
548
  };
566
549
  ```
567
550
 
@@ -576,13 +559,13 @@ export default {
576
559
  } satisfies Config;
577
560
  ```
578
561
 
579
- Import `@c15t/svelte/styles.css` above the directives, import this
562
+ Import `c15t/svelte/styles.css` above the directives, import this
580
563
  stylesheet from your root `+layout.svelte`, and pass `styles={false}` to
581
564
  `ConsentRoot`:
582
565
 
583
566
  ```css title="src/app.css"
584
567
  /* Vite ignores an @import below other rules, so it goes first. */
585
- @import '@c15t/svelte/styles.css';
568
+ @import 'c15t/svelte/styles.css';
586
569
 
587
570
  @tailwind base;
588
571
  @tailwind components;
@@ -792,7 +775,7 @@ stylesheet, and the layer order keeps it above preflight:
792
775
 
793
776
  ```html title="light-dom.html"
794
777
  <script
795
- src="/c15t.offline.js"
778
+ src="/c15t.local.js"
796
779
  data-mode="offline"
797
780
  data-country="DE"
798
781
  data-shadow="false"
@@ -814,7 +797,7 @@ instead:
814
797
  window.c15t = [['config', { ui: { shadow: false, styles: false } }]];
815
798
  </script>
816
799
  <script
817
- src="/c15t.offline.js"
800
+ src="/c15t.local.js"
818
801
  data-mode="offline"
819
802
  data-country="DE"
820
803
  defer
@@ -35,7 +35,7 @@ Nuxt module have no `i18n` option; their copy comes from the backend.
35
35
 
36
36
  c15t bundles English. Every other language it ships is a separate entry point
37
37
  in `@c15t/translations`, named by its language code. Importing one registers
38
- c15t's built-in copy for that language. Offline mode can then switch to it,
38
+ c15t's built-in copy for that language. Local mode can then switch to it,
39
39
  and your messages that repeat its wording don't hide backend edits. Import
40
40
  them in the file that configures the provider, so they load in the browser:
41
41
 
@@ -51,9 +51,11 @@ string or build your messages from it:
51
51
  import { translations as de } from '@c15t/translations/de';
52
52
  ```
53
53
 
54
- The entries are `bg`, `cs`, `cy`, `da`, `de`, `el`, `es`, `et`, `fi`, `fr`,
55
- `ga`, `gu`, `he`, `hi`, `hr`, `hu`, `id`, `is`, `it`, `lb`, `lt`, `lv`, `mt`,
56
- `nb`, `nl`, `nn`, `pl`, `pt`, `rm`, `ro`, `sk`, `sl`, `sv` and `zh`.
54
+ The entries are `ar`, `bg`, `bn`, `cs`, `cy`, `da`, `de`, `el`, `es`, `et`,
55
+ `fi`, `fr`, `ga`, `gu`, `he`, `hi`, `hr`, `ht`, `hu`, `id`, `is`, `it`, `ja`,
56
+ `kn`, `ko`, `lb`, `lt`, `lv`, `ml`, `mr`, `ms`, `mt`, `nb`, `nl`, `nn`, `pa`,
57
+ `pl`, `pt`, `rm`, `ro`, `ru`, `sk`, `sl`, `sv`, `ta`, `te`, `th`, `tr`, `ur`,
58
+ `vi`, `yi` and `zh`.
57
59
  `@c15t/translations/all` still loads every language at once. The `c15t`
58
60
  package doesn't re-export these entries, so add `@c15t/translations` to your
59
61
  dependencies.
@@ -102,26 +104,30 @@ production. Rename the section to `consentGate` to remove the warning.
102
104
  ## Show right-to-left languages
103
105
 
104
106
  c15t sets `dir="rtl"` on its surfaces when the resolved language is Arabic,
105
- Hebrew, Persian, Urdu, Pashto, Sindhi, Kurdish or Dhivehi. It matches the
106
- primary language, so `ar-EG` counts as Arabic. Hebrew is the only
107
- right-to-left translation c15t ships. For the others, supply your own
108
- messages or the copy from your Inth project.
107
+ Hebrew, Yiddish, Persian, Urdu, Pashto, Sindhi, Central Kurdish (`ckb`),
108
+ Uyghur, Dhivehi, Syriac or N'Ko. It matches the primary language, so `ar-EG`
109
+ counts as Arabic, and accepts the older codes `iw` and `ji`. A script subtag
110
+ decides over the language: `ku-Arab` and `pa-Arab` are right to left, and
111
+ `sd-Deva` is left to right. Plain `ku` is left to right because it usually
112
+ means Kurmanji, which uses Latin script. c15t ships right-to-left
113
+ translations for Arabic, Hebrew, Urdu and Yiddish. For the others, supply
114
+ your own messages or the copy from your Inth project.
109
115
 
110
116
  What follows the direction:
111
117
 
112
118
  * Text, headings and the button row in the banner, the preference dialog and
113
119
  the preference widget flow right to left.
114
- * A floating or widget banner in its default corner moves to the mirrored
115
- corner, so `bottom-left` becomes `bottom-right`. A `position` you set
116
- yourself stays where you put it.
117
120
  * The HTML script tag, `@c15t/browser` and Astro's banner also set `lang` on
118
121
  their surfaces. React, Vue and Svelte set `dir` only, so set `lang` on
119
122
  `<html>` yourself.
120
123
 
124
+ The banner and the floating `ConsentDialogTrigger` stay in their corners. A
125
+ `bottom-left` banner is still at the bottom left in Hebrew, so it doesn't
126
+ jump across the page when the visitor's language changes.
127
+
121
128
  What does not follow yet:
122
129
 
123
130
  * The IAB TCF dialog aligns several labels, indents and borders to the left.
124
- * The floating `ConsentDialogTrigger` keeps the corner you give it.
125
131
 
126
132
  Test right-to-left pages with a real translation before you ship them.
127
133
 
@@ -62,8 +62,9 @@ replaces the automatic attempts for that field. Partitioned cookies need
62
62
  `partitioned: true`. A prefix only finds cookies the current page can read,
63
63
  so use an exact name for a cookie on another path.
64
64
 
65
- c15t never deletes its own consent records, even when a pattern matches
66
- them.
65
+ c15t never deletes its own consent records or the `c15t-sid` subject ID
66
+ cookie, even when a pattern matches them. The subject ID stays so an
67
+ opt-out can still reach what was kept under it.
67
68
 
68
69
  ## When cleanup runs
69
70
 
@@ -63,7 +63,7 @@ rule with `model: 'none'` owes no rights, so nothing renders unless you add
63
63
  failed, no rule matched and you set no default, or init is still withheld, no
64
64
  consent surface renders: not the banner, the dialog, the widget, the
65
65
  preferences link, or the trigger. They appear as soon as a rule resolves,
66
- without a remount. `offline()` without `policyRules` resolves the recommended
66
+ without a remount. `local()` without `policyRules` resolves the recommended
67
67
  pack, so it shows the strict opt-in banner until you pass a country.
68
68
 
69
69
  Acknowledging a notice records its dismissal. It does not record consent or
@@ -201,8 +201,8 @@ Each variant accepts its own positions:
201
201
  | `widget` | `bottom-left`, `bottom-right`, `top-left`, `top-right` | `bottom-right` |
202
202
  | `wall` | `center` | `center` |
203
203
 
204
- A default corner mirrors left and right for right-to-left languages. A
205
- position you set is never mirrored. A position that is not valid for the
204
+ Right-to-left languages keep the same position; only the text direction
205
+ changes. A position that is not valid for the
206
206
  variant falls back to the default and logs an `invalid-position` diagnostic
207
207
  in development.
208
208
 
@@ -68,35 +68,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
68
68
  `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
69
69
  `enabled` apply when they change.
70
70
 
71
- | Option | Purpose |
72
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
- | `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `offline()` for local policies, all from `c15t/react`, or `custom()` for your own transport. See [consent modes](../../../concepts/modes.md) |
74
- | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
75
- | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
76
- | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
77
- | `scriptLoader` | Options for the script loader module |
78
- | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
79
- | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
80
- | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
81
- | `storageConfig` | Storage key and cookie settings for the stored choice |
82
- | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
83
- | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
84
- | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
85
- | `overrides` | Force the country, region or language the policy is resolved for, for testing |
86
- | `user` | Link consent records to a signed-in user |
87
- | `callbacks` | Choice, permission and error events |
88
- | `i18n` | Languages and message overrides |
89
- | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
90
- | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
91
- | `components` | Slot attributes per component part |
92
- | `presentation` | Banner and dialog shape, position and blocking |
93
- | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
94
- | `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) |
95
- | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
96
- | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
97
- | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
98
- | `nonce` | Content Security Policy nonce for script elements c15t inserts |
99
- | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
71
+ | Option | Purpose |
72
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `local()` for local policies, all from `c15t/react`, or `custom()` for your own transport. See [consent modes](../../../concepts/modes.md) |
74
+ | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
75
+ | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
76
+ | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
77
+ | `scriptLoader` | Options for the script loader module |
78
+ | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
79
+ | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
80
+ | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
81
+ | `storageConfig` | Storage key and cookie settings for the stored choice |
82
+ | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
83
+ | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
84
+ | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
85
+ | `overrides` | Force the country, region or language the policy is resolved for, for testing |
86
+ | `user` | Link consent records to a signed-in user |
87
+ | `callbacks` | Choice, permission and error events |
88
+ | `i18n` | Languages and message overrides |
89
+ | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
90
+ | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
91
+ | `components` | Slot attributes per component part |
92
+ | `presentation` | Banner and dialog shape, position and blocking |
93
+ | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
94
+ | `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) |
95
+ | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
96
+ | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
97
+ | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
98
+ | `nonce` | Content Security Policy nonce for script elements c15t inserts |
99
+ | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
100
100
 
101
101
  `enabled: false` grants categories and allows gated loading while suppressing
102
102
  consent UI. It is not a way to fix failed initialization or hide a banner in
@@ -36,7 +36,7 @@ The props are the GPP options listed below, for example
36
36
  pass to `ConsentProvider` as `runtime`.
37
37
 
38
38
  The GPP string follows the policy and location your backend resolves for the
39
- visitor, so a US visitor's state comes from the backend. With `offline()`
39
+ visitor, so a US visitor's state comes from the backend. With `local()`
40
40
  mode, pass the state yourself, for example
41
41
  `overrides: { country: 'US', region: 'CA' }`.
42
42