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

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 (77) hide show
  1. package/AGENTS.md +6 -4
  2. package/SKILL.md +1 -1
  3. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  4. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  5. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  6. package/dist/components/iab-panel/index.js +1 -1
  7. package/dist/components/iab-prompt/atoms/overlay.js +1 -1
  8. package/dist/components/iab-prompt/atoms/root.js +1 -1
  9. package/dist/components/panel/index.js +1 -1
  10. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  11. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
  12. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  13. package/dist/components/preferences/index.js +1 -1
  14. package/dist/components/prompt/atoms/overlay.js +1 -1
  15. package/dist/components/prompt/atoms/root.js +1 -1
  16. package/dist/components/shared/ui/accordion/accordion.js +1 -1
  17. package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
  18. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  19. package/dist/components/shared/ui/switch/switch.js +1 -1
  20. package/dist/components/shared/ui/tabs/tabs.js +1 -1
  21. package/dist/devtools-launcher.js +2 -0
  22. package/dist/devtools.js +1 -1
  23. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  24. package/dist/hooks/use-late-entry.js +2 -0
  25. package/dist/iab/styles.tw3.css +2 -2
  26. package/dist/index-parts/core.js +1 -1
  27. package/dist/provider.js +1 -1
  28. package/dist/styles.tw3.css +29 -5
  29. package/dist/version.js +1 -1
  30. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
  31. package/dist-types/components/iab-panel/index.d.ts +0 -1
  32. package/dist-types/components/panel/index.d.ts +0 -1
  33. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
  34. package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
  35. package/dist-types/components/preferences/index.d.ts +0 -1
  36. package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
  37. package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
  38. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
  39. package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
  40. package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
  41. package/dist-types/devtools-launcher.d.ts +29 -0
  42. package/dist-types/hooks/use-iab-manager.d.ts +4 -4
  43. package/dist-types/hooks/use-late-entry.d.ts +14 -0
  44. package/dist-types/hooks.d.ts +27 -5
  45. package/dist-types/index-parts/core.d.ts +1 -1
  46. package/dist-types/index.d.ts +1 -1
  47. package/dist-types/provider.d.ts +23 -4
  48. package/dist-types/version.d.ts +1 -1
  49. package/docs/README.md +6 -4
  50. package/docs/concepts/choose-your-setup.md +23 -6
  51. package/docs/concepts/consent-state.md +47 -0
  52. package/docs/concepts/data-fetching.md +68 -11
  53. package/docs/customization/motion.md +22 -13
  54. package/docs/customization/recipes.md +4 -5
  55. package/docs/customization/slots.md +3 -2
  56. package/docs/customization/stylesheets.md +29 -39
  57. package/docs/customization/tailwind.md +5 -5
  58. package/docs/customization/tokens.md +2 -1
  59. package/docs/customization/translations.md +32 -5
  60. package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
  61. package/docs/frameworks/react/components/consent-dialog.md +2 -1
  62. package/docs/frameworks/react/components/consent-provider.md +29 -28
  63. package/docs/frameworks/react/components/dev-tools.md +23 -0
  64. package/docs/frameworks/react/headless.md +4 -4
  65. package/docs/frameworks/react/hooks.md +10 -5
  66. package/docs/frameworks/react/iab.md +24 -18
  67. package/docs/frameworks/react/quickstart.md +77 -16
  68. package/docs/frameworks/react/rendering.md +82 -11
  69. package/docs/frameworks/react/scripts.md +8 -3
  70. package/docs/frameworks/react/upgrade-v3.md +596 -0
  71. package/docs/frameworks/react/vendor-consent.md +6 -0
  72. package/docs/guides/banner-experiments.md +26 -5
  73. package/docs/guides/troubleshooting.md +3 -2
  74. package/docs/integrations/overview.md +28 -27
  75. package/docs/integrations/sentry.md +416 -0
  76. package/docs/upgrade-v3.md +205 -314
  77. package/package.json +8 -8
@@ -11,8 +11,8 @@ export declare const useIABConsentManager: (overrides?: ConsentPresentation) =>
11
11
  actionGroups: import("@c15t/core").PresentationAction[][];
12
12
  primaryActions: import("@c15t/core").PresentationAction[];
13
13
  diagnostics: import("@c15t/core").PresentationDiagnostic[];
14
- rights: readonly import("@c15t/schema/types").PolicyRight[];
15
- preferenceControls: Exclude<import("@c15t/schema/types").PolicyRight, 'disclosure'>[];
14
+ rights: readonly import("@c15t/schema").PolicyRight[];
15
+ preferenceControls: Exclude<import("@c15t/schema").PolicyRight, 'disclosure'>[];
16
16
  direction: 'row' | 'column';
17
17
  uiProfile: 'balanced' | 'compact' | 'strict';
18
18
  scrollLock: boolean;
@@ -32,8 +32,8 @@ export declare const useIABConsentManager: (overrides?: ConsentPresentation) =>
32
32
  actionGroups: import("@c15t/core").PresentationAction[][];
33
33
  primaryActions: import("@c15t/core").PresentationAction[];
34
34
  diagnostics: import("@c15t/core").PresentationDiagnostic[];
35
- rights: readonly import("@c15t/schema/types").PolicyRight[];
36
- preferenceControls: Exclude<import("@c15t/schema/types").PolicyRight, 'disclosure'>[];
35
+ rights: readonly import("@c15t/schema").PolicyRight[];
36
+ preferenceControls: Exclude<import("@c15t/schema").PolicyRight, 'disclosure'>[];
37
37
  direction: 'row' | 'column';
38
38
  uiProfile: 'balanced' | 'compact' | 'strict';
39
39
  scrollLock: boolean;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Whether a banner that shows now arrives after the page has painted.
3
+ *
4
+ * Decided once each time `shown` turns on, and held while it stays on, so a
5
+ * re-render never restarts the entry. A banner hydrated from server HTML is
6
+ * part of the first paint: the hook answers `false` while hydrating, which
7
+ * also keeps the markup matching the server's.
8
+ *
9
+ * @param shown - Whether the banner is rendered.
10
+ * @returns Whether to mark the mount `data-entry="late"`.
11
+ *
12
+ * @internal
13
+ */
14
+ export declare const useLateEntry: (shown: boolean) => boolean;
@@ -84,13 +84,35 @@ export declare const usePolicyScopeMode: () => PolicyScopeMode;
84
84
  export declare const useIABSnapshot: () => Readonly<KernelIABState> | null;
85
85
  /** Is IAB active? */
86
86
  export declare const useIABEnabled: () => boolean;
87
- /** Consent for a specific IAB vendor. Accepts numeric or string IDs; IAB
88
- * vendors are numeric but the kernel stores them as strings for
89
- * uniformity with custom vendors. */
87
+ /**
88
+ * The preference centre's consent selection for one IAB vendor, including
89
+ * changes the visitor has not saved yet. Use it to render a control, not to
90
+ * gate a vendor: the saved choice is the TC string that `__tcfapi` serves,
91
+ * and `useIABSnapshot()?.authority` holds its decoded signals.
92
+ *
93
+ * @param vendorId - Numeric or string ID. IAB vendors are numeric; the
94
+ * kernel stores IDs as strings so custom vendors fit the same map.
95
+ * @returns Whether the vendor is selected.
96
+ */
90
97
  export declare const useVendorConsent: (vendorId: string | number) => boolean;
91
- /** Consent for a specific IAB purpose (1–11). */
98
+ /**
99
+ * The preference centre's consent selection for one IAB purpose (1-11),
100
+ * including changes the visitor has not saved yet. Use it to render a
101
+ * control, not to gate a vendor: see {@link useVendorConsent}.
102
+ *
103
+ * @param purposeId - IAB purpose ID.
104
+ * @returns Whether the purpose is selected.
105
+ */
92
106
  export declare const usePurposeConsent: (purposeId: number) => boolean;
93
- /** Opt-in for a special feature (1 = geolocation, 2 = device ID). */
107
+ /**
108
+ * The preference centre's opt-in selection for one special feature
109
+ * (1 = geolocation, 2 = device ID), including changes the visitor has not
110
+ * saved yet. Use it to render a control, not to gate a vendor: see
111
+ * {@link useVendorConsent}.
112
+ *
113
+ * @param featureId - IAB special feature ID.
114
+ * @returns Whether the special feature is selected.
115
+ */
94
116
  export declare const useSpecialFeatureOptIn: (featureId: number) => boolean;
95
117
  /** Latest TCF string. `null` until the IAB module encodes one. */
96
118
  export declare const useTCString: () => string | null;
@@ -4,5 +4,5 @@
4
4
  * (esbuild) does not treat every module the index names as part of an app
5
5
  * that imports one hook: only the groups it uses are live.
6
6
  */
7
- export { createConsentKernel, custom, hosted } from '@c15t/core';
7
+ export { createConsentKernel, custom, hosted, policyRulePresets, } from '@c15t/core';
8
8
  export { offline } from '../transports/offline.js';
@@ -31,7 +31,7 @@
31
31
  * cache patching. No method rewriting. Provider boot work is explicit
32
32
  * module wiring around a single per-mount kernel.
33
33
  */
34
- export type { ConsentKernel, ConsentSnapshot, ConsentState, HostedTransportOptions, InitContext, InitResponse, InitResult, KernelConfig, KernelEvent, KernelOverrides, KernelTransport, KernelUser, Listener, HostedModeOptions, ProviderTransportContext, ProviderTransportFactory, ProviderTransportKind, SavePayload, SaveResult, Unsubscribe, } from '@c15t/core';
34
+ export type { AllConsentNames, ClearOnRevocationConfig, ConsentKernel, ConsentSnapshot, ConsentState, HostedTransportOptions, InitContext, InitResponse, InitResult, KernelConfig, KernelEvent, KernelOverrides, KernelTransport, KernelUser, Listener, HostedModeOptions, PolicyRule, ProviderTransportContext, ProviderTransportFactory, ProviderTransportKind, SavePayload, SaveResult, Unsubscribe, } from '@c15t/core';
35
35
  export type { OfflineModeOptions } from './transports/offline.js';
36
36
  export type { ConsentBannerButton, ConsentBannerLayout, ConsentBannerProps, ConsentBannerRight, ConsentBannerRightLinkProps, ConsentBannerRightsProps, ConsentBannerSurface, } from './components/prompt/index.js';
37
37
  export type { ConsentDialogCompoundComponent, ConsentDialogProps, } from './components/panel/index.js';
@@ -1,7 +1,7 @@
1
- import type { AllConsentNames, Callbacks, ClearOnRevocationConfig, ConsentExperiment, ConsentPresentation, ExperimentState, I18nConfig, KernelConfig, KernelOverrides, KernelUser, LegalLinks, ProviderTransportFactory, StorageConfig, User, Vendor } from '@c15t/core';
1
+ import type { AllConsentNames, Callbacks, ClearOnRevocationConfig, ConsentExperiment, ConsentJourneyOption, ConsentPresentation, ExperimentState, JourneyState, I18nConfig, KernelConfig, KernelOverrides, KernelUser, LegalLinks, ProviderTransportFactory, StorageConfig, User, Vendor } from '@c15t/core';
2
2
  import type { Script } from '@c15t/core/modules/script-loader';
3
3
  import type { ConsentControlOptions } from '@c15t/core/runtime';
4
- import type { ConsentRuntime, ResolveStreamedInit } from '@c15t/core/runtime/provider';
4
+ import type { ConsentRuntime, ConsentRuntimeModules, ResolveStreamedInit } from '@c15t/core/runtime/provider';
5
5
  import type { ReactNode } from 'react';
6
6
  import type { DialogPreload } from './chunk-warming.js';
7
7
  import type { UseNetworkBlockerOptions, UsePersistenceOptions, UseScriptLoaderOptions } from './module-hooks.js';
@@ -13,9 +13,10 @@ export type ConsentProviderCallbacks = Pick<Callbacks, 'onChoiceRecorded' | 'onP
13
13
  /**
14
14
  * Prepared policy and records; legacy consent projections are not provider
15
15
  * inputs. An `experiment` a server helper resolved runs instead of
16
- * `options.experiment`.
16
+ * `options.experiment`, and a `journey` it started is the one the provider
17
+ * continues.
17
18
  */
18
- export type ConsentProviderPrefetch = Omit<KernelConfig, 'initialDraft' | 'transport'> & ExperimentState;
19
+ export type ConsentProviderPrefetch = Omit<KernelConfig, 'initialDraft' | 'transport'> & ExperimentState & JourneyState;
19
20
  export interface ConsentProviderOptions extends ConsentControlOptions, Pick<ReactUIOptions, 'colorScheme' | 'disableAnimation' | 'noStyle' | 'scrollLock' | 'theme' | 'trapFocus'> {
20
21
  enabled?: boolean;
21
22
  presentation?: ConsentPresentation;
@@ -26,6 +27,13 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
26
27
  * provider to change the experiment.
27
28
  */
28
29
  experiment?: ConsentExperiment;
30
+ /**
31
+ * Random journey id that links each `/init` to the save that follows:
32
+ * `'page'`, `'tab'` or `false`. Initial-only.
33
+ *
34
+ * @default 'page'
35
+ */
36
+ journey?: ConsentJourneyOption;
29
37
  /**
30
38
  * Content Security Policy nonce applied to DOM nodes c15t injects.
31
39
  *
@@ -171,6 +179,17 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
171
179
  * @internal
172
180
  */
173
181
  __resolveStreamedInit?: ResolveStreamedInit;
182
+ /**
183
+ * `preloadScriptLoaderWith` from
184
+ * `@c15t/core/runtime/script-loader-preload`. With it the runtime starts
185
+ * the script loader's download during the first render when consent
186
+ * already lets a script run, instead of from the mount effect. A
187
+ * framework root whose state usually holds a returning visitor's choice
188
+ * (`ConsentRoot`) passes it; without it the provider ships none of that
189
+ * decision. Read once, at mount.
190
+ * @internal
191
+ */
192
+ __preloadScriptLoader?: (load: () => Promise<unknown>) => ConsentRuntimeModules['preloadScriptLoader'];
174
193
  }
175
194
  /**
176
195
  * Options accepted when an external runtime supplies the kernel.
@@ -1 +1 @@
1
- export declare const version = "3.0.0-alpha.5";
1
+ export declare const version = "3.0.0-alpha.7";
package/docs/README.md CHANGED
@@ -15,7 +15,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
15
15
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
16
16
  - [Customize the interface](./customization/overview.md): Change c15t's consent banner and dialog one step at a time, from a prop to your own markup, and find where each step lives in your framework.
17
17
  - [Verify consent](./guides/verify-consent.md): Check in a production build that vendor requests wait for consent, rejection survives a reload, preferences reopen and privacy signals apply.
18
- - [Migrate to v3](./upgrade-v3.md): Upgrade a c15t v2 app to v3. Covers packages, the Next.js and React providers, the JavaScript runtime, custom UI built on useConsentManager, callbacks, policies, stored consent and a self-hosted backend.
18
+ - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
19
19
 
20
20
  ## More documentation
21
21
 
@@ -48,6 +48,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
48
48
  - [Scripts](./frameworks/react/scripts.md): Load vendor scripts by consent category in a React app with ConsentProvider, and clear stored data or reload the page when a visitor withdraws consent.
49
49
  - [Translations](./frameworks/react/translations.md): Where c15t banner and dialog copy comes from in a React app, how the browser picks the visitor's language, and how to override copy and switch languages with the provider's i18n option and hooks.
50
50
  - [Troubleshooting](./frameworks/react/troubleshooting.md): Fix a missing banner, early vendor requests, unstyled components and theme warnings in a React app that uses c15t/react.
51
+ - [Upgrade from v2](./frameworks/react/upgrade-v3.md): Upgrade a React app from c15t v2 (@c15t/react) to v3. Covers packages, ConsentProvider and transports, the useConsentManager codemod, callbacks, policies, styles, IAB and stored consent.
51
52
  - [Vendor consent](./frameworks/react/vendor-consent.md): Let visitors allow a category such as marketing in a React app and still turn off one vendor in it, with the vendors option on ConsentProvider and useVendorAllowed.
52
53
 
53
54
  ## Concepts
@@ -55,7 +56,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
55
56
  - [Choose your setup](./concepts/choose-your-setup.md): Pick the c15t setup for your framework, rendering mode and hosting, and decide who runs the consent backend.
56
57
  - [Consent categories](./concepts/consent-categories.md): Assign scripts, embeds and features to c15t consent categories, and understand which categories the preference dialog shows.
57
58
  - [Consent state reference](./concepts/consent-state.md): How c15t saves choices, gates IAB vendors, hydrates server records and keeps browser tabs and storage in step.
58
- - [Data fetching](./concepts/data-fetching.md): How c15t gets policy data through a cached manifest, backend /init, the browser or offline rules, and where consent choices are saved.
59
+ - [Data fetching](./concepts/data-fetching.md): Choose the recommended build-time policy snapshot or runtime manifest fetching, backend /init, browser resolution or offline rules, and understand where consent choices are saved.
59
60
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
60
61
  - [Policies](./concepts/policies.md): How policy models, prompts and scope decide what c15t asks visitors, where to change the rules, and why a banner may not appear.
61
62
 
@@ -73,7 +74,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
73
74
  - [Customize the interface](./customization/overview.md): Change c15t's consent banner and dialog one step at a time, from a prop to your own markup, and find where each step lives in your framework.
74
75
  - [Banner designs](./customization/recipes.md): Five consent banner designs built with c15t, from one prop to your own markup, with tested code for React, Vue, Svelte, Astro and plain HTML.
75
76
  - [Component parts](./customization/slots.md): Find every part of c15t's banner, dialog, widget, trigger and ConsentGate placeholder, its key in your framework's part API, and the data attributes to select on.
76
- - [Stylesheets and CSS layers](./customization/stylesheets.md): Load the right c15t stylesheet for your framework, see when the dialog's CSS loads, order c15t's cascade layer against your own, and run c15t without its styles.
77
+ - [Stylesheets and CSS layers](./customization/stylesheets.md): Load the right c15t stylesheet for your framework, see which file holds the dialog's rules, order c15t's cascade layer against your own, and run c15t without its styles.
77
78
  - [Tailwind CSS](./customization/tailwind.md): Load c15t's styles next to Tailwind CSS 4 or 3 in every framework, put utilities on c15t component parts, and use Tailwind's dark variant with c15t.
78
79
  - [Theme tokens](./customization/tokens.md): Change c15t's colors, type, radius, spacing, shadows and motion with theme tokens, and see every --c15t-* variable with its default.
79
80
  - [Copy and translations](./customization/translations.md): Change consent wording through i18n and test the complete prompt and preferences flow.
@@ -118,6 +119,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
118
119
  - [RudderStack](./integrations/rudderstack.md): Load the RudderStack JavaScript SDK after measurement consent with the c15t rudderstack helper, or map c15t categories to destination consent IDs, and check each mode.
119
120
  - [Rybbit Analytics](./integrations/rybbit-analytics.md): Load Rybbit Analytics only after measurement consent with the c15t rybbitAnalytics helper, map its tracking options to data attributes, and check it in DevTools.
120
121
  - [Segment](./integrations/segment.md): Load Segment Analytics.js only after measurement consent with the c15t segment helper, guard your own track and identify calls, and check it in DevTools.
122
+ - [Sentry](./integrations/sentry.md): Configure Sentry's CDN or your own SDK with consent for error monitoring, Session Replay and user data.
121
123
  - [Snapchat Pixel](./integrations/snapchat-pixel.md): Load the Snapchat Pixel only after marketing consent with the c15t snapchatPixel helper, guard snaptr event calls, and check it in DevTools.
122
124
  - [TikTok Pixel](./integrations/tiktok-pixel.md): Load the TikTok Pixel only after marketing consent with the c15t tiktokPixel helper, guard ttq event calls, and check it in DevTools.
123
125
  - [Umami Analytics](./integrations/umami-analytics.md): Load the Umami Analytics tracker only after measurement consent with the c15t umamiAnalytics helper, point it at a self-hosted instance, and check it in DevTools.
@@ -127,4 +129,4 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
127
129
 
128
130
  ## Reference
129
131
 
130
- - [Migrate to v3](./upgrade-v3.md): Upgrade a c15t v2 app to v3. Covers packages, the Next.js and React providers, the JavaScript runtime, custom UI built on useConsentManager, callbacks, policies, stored consent and a self-hosted backend.
132
+ - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
@@ -12,9 +12,9 @@ to the alternatives it rules out.
12
12
 
13
13
  | Your app | Setup | Guide |
14
14
  | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
15
- | Next.js App Router with a Next.js server | Server resolves consent from a cached manifest and streams it to the page | [App Router](https://c15t.com/docs/frameworks/next/rendering#pick-your-rendering-path) |
15
+ | Next.js App Router with a Next.js server | Server resolves consent from a build-time manifest and streams it to the page | [App Router](https://c15t.com/docs/frameworks/next/quickstart) |
16
16
  | Next.js App Router, banner must be in the first HTML | Await consent inside `Suspense` | [Stream or await](https://c15t.com/docs/frameworks/next/rendering#stream-or-await-consent-in-the-app-router) |
17
- | Next.js with static, ISR or `'use cache'` pages | Browser resolves consent on those pages | [Static, ISR and cached pages](https://c15t.com/docs/frameworks/next/rendering#render-static-isr-and-cached-pages) |
17
+ | Next.js with static, ISR, `'use cache'` or `ensureStatic = 'navigation'` pages | Browser resolves consent on those pages | [Static, ISR and cached pages](https://c15t.com/docs/frameworks/next/rendering#render-static-isr-and-cached-pages) |
18
18
  | Next.js Pages Router | Resolve consent in `getServerSideProps` | [Pages Router](https://c15t.com/docs/frameworks/next/rendering#pick-your-rendering-path) |
19
19
  | Next.js with `output: 'export'` | Browser resolves consent | [Static export](https://c15t.com/docs/frameworks/next/static-export) |
20
20
  | TanStack Start with a server | Root loader resolves consent on the server | [TanStack Start](https://c15t.com/docs/frameworks/tanstack-start/quickstart) |
@@ -22,14 +22,14 @@ to the alternatives it rules out.
22
22
  | TanStack Start in SPA mode or prerendered | Browser resolves consent | [TanStack Start rendering](https://c15t.com/docs/frameworks/tanstack-start/rendering#spa-mode-prerendered-pages-and-static-hosts) |
23
23
  | React with Vite, or another single-page app | Browser resolves consent | [React](../frameworks/react/quickstart.md) |
24
24
  | React Router framework mode, Remix, or another server-rendered React app | Browser resolves consent after hydration | [React rendering](../frameworks/react/rendering.md#react-router-remix-and-other-server-rendered-apps) |
25
- | Nuxt with a Nuxt server | Nuxt module resolves consent on the server from a cached manifest | [Nuxt](https://c15t.com/docs/frameworks/nuxt/quickstart) |
25
+ | Nuxt with a Nuxt server | Nuxt module resolves consent on the server from a build-time manifest | [Nuxt](https://c15t.com/docs/frameworks/nuxt/quickstart) |
26
26
  | Nuxt with prerendered or cached routes | Nuxt server for most pages, the browser on prerendered and cached routes | [Nuxt prerendered and cached routes](https://c15t.com/docs/frameworks/nuxt/rendering#prerendered-and-cached-routes) |
27
27
  | Nuxt with `nuxt generate` on static hosting | Nuxt module, browser resolves consent from the manifest or `/init` | [Nuxt static hosting](https://c15t.com/docs/frameworks/nuxt/rendering#deploy-to-static-hosting) |
28
28
  | Nuxt with `ssr: false` | Nuxt module, browser resolves consent from the manifest | [Nuxt single-page app](https://c15t.com/docs/frameworks/nuxt/rendering#build-a-single-page-app-with-ssr-false) |
29
29
  | Vue with Vite | Vue plugin, browser resolves consent | [Vue](https://c15t.com/docs/frameworks/vue/quickstart) |
30
30
  | Vue rendered on the server without Nuxt | Vue plugin; the banner mounts after hydration. Use Nuxt for the banner in server HTML. | [Vue rendering](https://c15t.com/docs/frameworks/vue/rendering#server-rendering-without-nuxt) |
31
31
  | Astro with static output | Browser resolves consent through the hosted backend | [Astro static output](https://c15t.com/docs/frameworks/astro/rendering#static-output-with-hosted) |
32
- | Astro with server output and an adapter | Middleware resolves consent from a cached manifest | [Astro server output](https://c15t.com/docs/frameworks/astro/rendering#server-output-with-manifest) |
32
+ | Astro with server output and an adapter | Middleware resolves consent from a build-time manifest | [Astro server output](https://c15t.com/docs/frameworks/astro/rendering#server-output-with-manifest) |
33
33
  | Svelte with Vite | Svelte provider, browser resolves consent | [Svelte](https://c15t.com/docs/frameworks/svelte/quickstart) |
34
34
  | SvelteKit with a server | Root layout load resolves consent on the server | [SvelteKit](https://c15t.com/docs/frameworks/sveltekit/quickstart) |
35
35
  | SvelteKit with `adapter-static`, prerendered pages or SPA mode | Browser resolves consent | [SvelteKit rendering](https://c15t.com/docs/frameworks/sveltekit/rendering#build-a-static-site) |
@@ -75,9 +75,26 @@ public policy manifest, never a response that contains a visitor's consent.
75
75
 
76
76
  ## How the server gets the policy
77
77
 
78
- Server setups fetch policy one of two ways:
78
+ Use a build-time manifest as the default for supported production builds.
79
+ Next.js, TanStack Start, Nuxt server output, Astro server output and SvelteKit
80
+ can resolve requests from the deployment's snapshot. Vite apps can bundle it
81
+ for browser resolution too; location-based policies may still need a location
82
+ request. The framework quickstarts show the build hook and snapshot wiring.
79
83
 
80
- * **Manifest**, used by the Next.js, Nuxt and Astro server guides.
84
+ The build fetches your public policy once and bundles it, so the server never
85
+ fetches it at runtime.
86
+
87
+ The snapshot is fixed at build time:
88
+
89
+ * Rebuild after changing policies, translations or vendors. If your CI caches
90
+ build output, force a fresh build.
91
+ * The build must reach the backend. A failed fetch fails the build.
92
+ * Consent choices still go to the backend.
93
+
94
+ For deployments that need policy updates without a rebuild, choose a runtime
95
+ path:
96
+
97
+ * **Cached manifest**.
81
98
  Your server downloads the project's public policy file, caches it, and
82
99
  resolves each visitor locally. Consent saves still go to the backend.
83
100
  * **`/init`**. Each visit asks the backend to resolve the visitor. Simpler, but
@@ -27,6 +27,42 @@ itself stays refused: its `effectivePermissions` entry is `false`, and targets
27
27
  that name only the category, or that also declare a consent purpose or special
28
28
  feature, stay blocked.
29
29
 
30
+ ## Categories under an IAB policy
31
+
32
+ Under an IAB policy the visitor chooses TCF purposes, and c15t derives each
33
+ category from them when the choice is saved. A category is granted when the
34
+ visitor consented to every purpose in it that a listed vendor processes on
35
+ consent:
36
+
37
+ | Category | TCF purposes |
38
+ | --------------- | ------------ |
39
+ | `marketing` | 2, 3, 4 |
40
+ | `measurement` | 7, 8, 9 |
41
+ | `experience` | 5, 6 |
42
+ | `functionality` | 10, 11 |
43
+
44
+ `necessary` is always granted. Purpose 1, device storage, decides no category.
45
+
46
+ Accept All consents only to the purposes and special features that the vendors
47
+ in your list declare, because the TCF Policies allow no consent signal for a
48
+ purpose the visitor was not shown. A category whose purposes no listed vendor
49
+ processes on consent therefore stays denied after Accept All, and so does every
50
+ script, iframe or network rule gated on it. With a vendor list whose vendors
51
+ declare only purposes 1 to 4 and 7, for example, `experience` and
52
+ `functionality` stay denied.
53
+
54
+ If your own scripts depend on such a category, do one of these:
55
+
56
+ * Declare the processing. Add a custom vendor for it, with the purposes it
57
+ uses, through the `customVendors` option of your IAB setup. The preference
58
+ centre then lists those purposes, and Accept All and the purpose switches
59
+ can grant the category.
60
+ * Gate the script on a category that your listed vendors' purposes cover, or on
61
+ `necessary` if it needs no consent.
62
+
63
+ Check `effectivePermissions` after Accept All on a fresh visit to see which
64
+ categories your vendor list leaves denied.
65
+
30
66
  ## When a choice is saved
31
67
 
32
68
  A save records the choice in the browser first and sends it to the backend
@@ -63,6 +99,12 @@ backend that signs policy snapshot tokens rejects a replay made after the
63
99
  token expires, which is 30 minutes by default for the self-hosted backend. A
64
100
  save still queued by then is recorded only in the browser.
65
101
 
102
+ A replay that fails and stays queued emits `save:replayed` with `ok: false` but
103
+ not `command:error`, since the save already reported its first failure. A save
104
+ that leaves the queue without the backend recording it, because it ran out of
105
+ attempts, waited more than 7 days or was refused, emits `command:error` once.
106
+ Development builds also log a `[c15t]` console warning for each of these.
107
+
66
108
  IAB surfaces close in the click task too, but an IAB choice is recorded only
67
109
  after its TC string is encoded, which can wait for the TCF library to load.
68
110
  If that local step records nothing, for example because the vendor list
@@ -76,6 +118,11 @@ unchanged selection, closes the surface when it resolves successfully. Opening
76
118
  or closing a surface, or starting another save, before then leaves the surface
77
119
  as the visitor set it.
78
120
 
121
+ Closing the dialog without saving, with Escape or the adapter's close call,
122
+ leaves the same surface a save would. A visitor who opens preferences from the
123
+ banner and backs out still owes a choice, so the banner comes back. Once a
124
+ choice is recorded, closing the dialog leaves nothing open.
125
+
79
126
  ## Preserve records during hydration
80
127
 
81
128
  Server helpers return records with policy information and evaluation time.
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  title: Data fetching
3
- description: How c15t gets policy data through a cached manifest, backend /init,
4
- the browser or offline rules, and where consent choices are saved.
3
+ description: Choose the recommended build-time policy snapshot or runtime
4
+ manifest fetching, backend /init, browser resolution or offline rules, and
5
+ understand where consent choices are saved.
5
6
  group: concepts
6
7
  ---
7
8
 
@@ -13,13 +14,14 @@ the same protocol, so every path below works with either. Only `offline()`
13
14
  removes backend requests. For a recommendation by framework, start with
14
15
  [choose your setup](./choose-your-setup.md).
15
16
 
16
- | Fetching path | Where policy resolves | Where choices are submitted | Choose it when |
17
- | ------------------------------ | ------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
18
- | Cached manifest on your server | Your application resolves public policy data with each request's location and signals | Inth or your c15t backend | Your host has a runtime server and you want to reuse policy data across requests |
19
- | Regular backend `/init` | The consent backend | The same backend | You want the fewest moving parts, need backend-owned request resolution, or have no application server |
20
- | Manifest in the browser | The browser, using supplied or unknown location | Inth or your c15t backend | You deliberately want client resolution and have planned geography, bundle size and policy refresh |
21
- | Offline | The browser or local runtime, using bundled rules | No backend submission | Local development and tests. Not recommended for production environments. |
22
- | Custom transport | Your implementation | Your implementation | An existing service cannot use the c15t backend protocol |
17
+ | Fetching path | Where policy resolves | Where choices are submitted | Choose it when |
18
+ | -------------------------------- | ------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
19
+ | Build-time manifest, recommended | Your server or browser resolves a bundled policy snapshot with each visitor's inputs | Inth or your c15t backend | Your framework supports a build integration and policy changes can ship with a rebuild |
20
+ | Cached manifest on your server | Your application resolves public policy data with each request's location and signals | Inth or your c15t backend | Policy changes must reach the app without rebuilding, or builds cannot access the backend |
21
+ | Regular backend `/init` | The consent backend | The same backend | You want the fewest moving parts, need backend-owned request resolution, or have no application server |
22
+ | Manifest in the browser | The browser, using supplied or unknown location | Inth or your c15t backend | You deliberately want client resolution and have planned geography, bundle size and policy refresh |
23
+ | Offline | The browser or local runtime, using bundled rules | No backend submission | Local development and tests. Not recommended for production environments. |
24
+ | Custom transport | Your implementation | Your implementation | An existing service cannot use the c15t backend protocol |
23
25
 
24
26
  ## What is a consent manifest?
25
27
 
@@ -38,6 +40,24 @@ Do not put secrets, visitor identifiers or consent records into a manifest.
38
40
  Keep personalized init responses out of shared caches. Changing a policy also
39
41
  requires a refresh strategy for cached or build-time manifests.
40
42
 
43
+ ## Use a build-time manifest by default
44
+
45
+ The build fetches your public policy once and bundles it, so the server never
46
+ fetches it at runtime.
47
+
48
+ The framework quickstarts include the build integration for supported
49
+ deployments. Server resolution uses the snapshot without an upstream manifest
50
+ request. Browser resolution includes the policy and resolver in the client
51
+ bundle, and may still request location for regional policies. Static Nuxt and
52
+ Astro sites and script-tag sites keep the runtime setup from their guides.
53
+
54
+ The snapshot is fixed at build time:
55
+
56
+ * Rebuild after changing policies, translations or vendors. If your CI caches
57
+ build output, force a fresh build.
58
+ * The build must reach the backend. A failed fetch fails the build.
59
+ * Consent choices still go to the backend.
60
+
41
61
  ## How manifest mode counts visitors
42
62
 
43
63
  The backend counts visitors from `/init` requests, and manifest resolution
@@ -48,6 +68,40 @@ no identity; the visitor's IP address and user agent are forwarded under the
48
68
  backend's usual IP handling. Static output resolves in the browser and sends
49
69
  no report. Set `reportSessions: false` on an adapter to turn it off.
50
70
 
71
+ ## Link an init to the save that follows
72
+
73
+ Each page load gets a journey id, a random UUID sent on requests c15t already
74
+ makes:
75
+
76
+ | Request | Query parameters |
77
+ | ---------------- | ------------------------------------------------------------------------------------------------ |
78
+ | `GET /init` | `c15tJourney`, `c15tJourneyScope`, `c15tStored` (`1` if a choice or notice dismissal was stored) |
79
+ | `POST /subjects` | `c15tJourney`, `c15tJourneyScope` |
80
+
81
+ Session reports carry it as `journey: { id, scope, storedChoice, prompt, domain }`.
82
+ A report and a save with the same `id` belong to one page load. `prompt` is
83
+ `due`, `stored` or `not-required`; a page that falls back to the default opt-in
84
+ banner counts as `due` or `stored`. A stored answer counts as `stored` even if a
85
+ policy change makes c15t ask again.
86
+
87
+ The id is random, is never the subject id and is never written to a cookie.
88
+ Every save that carries one follows an `/init` or report with the same id.
89
+ Saves replayed after going offline carry none.
90
+
91
+ | `journey` | Behavior |
92
+ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
93
+ | `'page'` (default) | One id per page load, in memory. |
94
+ | `'tab'` | Kept in `sessionStorage` while a prompt is due, and removed once the visitor chooses or no prompt is due. Only when the browser resolves init; server-rendered pages use `'page'`. |
95
+ | `false` | No journey. |
96
+
97
+ Set it on the runtime or provider. In Next.js use `defineConsentConfig({ journey })`;
98
+ in TanStack Start pass the same value to `resolveConsent` and `ConsentRoot`; with
99
+ the inline prefetch script, pass it to `buildPrefetchScript`. Vue, Svelte, Astro
100
+ and the script tag use `'page'`. A manifest that resolves locally in the
101
+ browser reports no init, so it sends no journey.
102
+
103
+ If you pass `hosted()` a `fetch`, match on the path: URLs now carry a query string.
104
+
51
105
  ## What does regular `/init` do?
52
106
 
53
107
  `hosted({ url })` uses `${url}/init` for initialization and `${url}/subjects` for
@@ -156,10 +210,13 @@ the kernel contract. Prefer a built-in transport when your backend supports it.
156
210
  ## Verify the selected path
157
211
 
158
212
  Inspect browser and server requests separately. A server manifest fetch will
159
- not appear in the browser's Network panel. On a warm manifest path, check that
213
+ not appear in the browser's Network panel. With a build-time snapshot, start a
214
+ fresh production server and confirm it makes no upstream `/manifest` request.
215
+ After a policy edit, rebuild and confirm the new policy appears. On a runtime
216
+ manifest path, check that
160
217
  page requests do not call the backend `/init`, a visitor's choice still reaches
161
218
  the backend's `/subjects`, and policy changes become visible after the configured
162
- refresh. In the recommended Next.js setup, the browser should call only
219
+ refresh. With a same-origin consent proxy, the browser should call only
163
220
  `/api/c15t` paths for consent HTTP traffic; inspect server logs to verify their
164
221
  upstream destinations.
165
222
 
@@ -8,20 +8,21 @@ group: customization
8
8
 
9
9
  ## Change the speed with motion tokens
10
10
 
11
- c15t animates its surfaces with three durations and four easing curves. Set
11
+ c15t animates its surfaces with three durations and five easing curves. Set
12
12
  them in the theme's `motion` object, or as CSS variables:
13
13
 
14
- | CSS variable | Theme key | Default | Used by |
15
- | ------------------------ | ------------------------ | -------------------------------------- | ----------------------------------------------- |
16
- | `--c15t-duration-fast` | `motion.duration.fast` | `80ms` | Banner, buttons, tabs |
17
- | `--c15t-duration-normal` | `motion.duration.normal` | `150ms` | Preference dialog, switches, accordions |
18
- | `--c15t-duration-slow` | `motion.duration.slow` | `200ms` | Legal links, floating trigger |
19
- | `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | Banner fade, switches, accordions |
20
- | `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` | Preference dialog, floating trigger hover |
21
- | `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` | Floating trigger snapping to a corner |
22
- | `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | The banner's slide in and the dialog's scale in |
14
+ | CSS variable | Theme key | Default | Used by |
15
+ | ------------------------ | ------------------------ | ---------------------------------------- | -------------------------------------------------------------- |
16
+ | `--c15t-duration-fast` | `motion.duration.fast` | `80ms` | Banner fade out, buttons, tabs |
17
+ | `--c15t-duration-normal` | `motion.duration.normal` | `150ms` | Preference dialog, late banner fade in, switches, accordions |
18
+ | `--c15t-duration-slow` | `motion.duration.slow` | `200ms` | Legal links, floating trigger |
19
+ | `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | Switches, accordions |
20
+ | `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` | Preference dialog, late banner fade in, floating trigger hover |
21
+ | `--c15t-easing-in` | `motion.easingIn` | `cubic-bezier(0.55, 0.055, 0.675, 0.19)` | Banner fade out |
22
+ | `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` | Floating trigger snapping to a corner |
23
+ | `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | The dialog's scale in |
23
24
 
24
- This theme slows the dialog down and removes the banner's overshoot:
25
+ This theme slows the dialog down and removes its overshoot:
25
26
 
26
27
  ```ts title="consent-theme.ts"
27
28
  export const theme = {
@@ -32,6 +33,14 @@ export const theme = {
32
33
  };
33
34
  ```
34
35
 
36
+ The banner never slides in. When it shows with the page, it is part of the
37
+ first paint and appears on its first frame. When it arrives more than 100ms
38
+ after the page first painted, such as after a slow script or a client-side
39
+ init, it fades in over `--c15t-duration-normal` instead, and its root and
40
+ backdrop carry `data-entry="late"`. A banner rendered on the server always
41
+ shows at once. When it hides, Vue, Svelte and the script tag fade it out over
42
+ `--c15t-duration-fast`.
43
+
35
44
  The theme goes where your framework's other tokens go. See
36
45
  [theme tokens](./tokens.md) and your framework's customize page.
37
46
  In Vue and Nuxt, set `tokens` with the variable name without the leading
@@ -39,8 +48,8 @@ In Vue and Nuxt, set `tokens` with the variable name without the leading
39
48
 
40
49
  ## Turn animations off
41
50
 
42
- `disableAnimation` removes the enter and exit transitions of the banner and
43
- dialogs, and the hover and snap transitions of the floating
51
+ `disableAnimation` removes the banner's fades, the enter and exit
52
+ transitions of the dialogs, and the hover and snap transitions of the floating
44
53
  `ConsentDialogTrigger`. Set it once for every surface, then override it for one
45
54
  surface where your framework allows:
46
55
 
@@ -517,8 +517,7 @@ from your Inth project, and put the block before `</body>`.
517
517
 
518
518
  ```html title="index.html"
519
519
  <script
520
- src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.headless.js"
521
- data-backend-url="https://your-project.inth.app"
520
+ src="https://your-project.inth.app/c15t.headless.js"
522
521
  defer
523
522
  ></script>
524
523
 
@@ -622,8 +621,8 @@ from your Inth project, and put the block before `</body>`.
622
621
  hidden
623
622
  >
624
623
  <p class="cookie-bar__text">
625
- We use cookies to measure traffic and improve this site. Choose which ones
626
- can run.
624
+ We use cookies to measure traffic and improve this site. Choose which
625
+ ones can run.
627
626
  </p>
628
627
  <fieldset
629
628
  class="cookie-bar__prefs"
@@ -867,5 +866,5 @@ The source repository renders each design in its Storybook under
867
866
  `Docs / Banner designs`: `apps/storybook-react`, `apps/storybook-vue`,
868
867
  `apps/storybook-svelte` and `apps/storybook-astro`. Each story uses an
869
868
  in-memory policy and loads no analytics. The HTML headless bar runs at
870
- `/consent-example/headless` in `examples/script-tag`, which the example
869
+ `/consent-example/headless` in `internals/fixtures/script-tag`, which the example
871
870
  acceptance tests cover.
@@ -129,8 +129,9 @@ The widget is the list of categories inside the dialog. React and Vue call it
129
129
  | Label | `trigger.text` | None |
130
130
  | Toolbar, its items and icons | `trigger.toolbar`, `trigger.toolbarItem`, `trigger.toolbarIcon` | `consentDialogTriggerToolbar`, `consentDialogTriggerToolbarItem`, `consentDialogTriggerToolbarIcon` |
131
131
 
132
- Only React renders the toolbar. Vue has no toolbar, and the toolbar slots only
133
- reach Astro's React dialog.
132
+ React renders the toolbar from `ConsentDialogTriggerToolbar`, and while
133
+ `ConsentDevTools` is mounted. Vue renders it only while `ConsentDevTools` is
134
+ mounted. In Astro, the toolbar slots reach the React dialog.
134
135
 
135
136
  ## Legal links and IAB TCF parts
136
137