@c15t/react 2.2.1 → 2.3.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 (128) hide show
  1. package/AGENTS.md +6 -3
  2. package/README.md +1 -1
  3. package/dist/components/iab-consent-dialog/atoms/purpose-item.cjs +1 -1
  4. package/dist/components/iab-consent-dialog/atoms/purpose-item.js +1 -1
  5. package/dist/components/iab-consent-dialog/hooks/use-gvl-data.cjs +1 -1
  6. package/dist/components/iab-consent-dialog/hooks/use-gvl-data.js +1 -1
  7. package/dist/components/iab-consent-dialog/iab-consent-dialog.cjs +1 -1
  8. package/dist/components/iab-consent-dialog/iab-consent-dialog.js +1 -1
  9. package/dist/iab/styles.tw3.css +1 -1
  10. package/dist/version.cjs +1 -1
  11. package/dist/version.js +1 -1
  12. package/dist-types/components/consent-banner/components.d.ts +3 -3
  13. package/dist-types/components/consent-banner/consent-banner.d.ts +1 -1
  14. package/dist-types/components/consent-banner/index.d.ts +9 -9
  15. package/dist-types/components/consent-banner/policy-actions.d.ts +2 -2
  16. package/dist-types/components/consent-dialog/atoms/card.d.ts +5 -5
  17. package/dist-types/components/consent-dialog/atoms/overlay.d.ts +1 -1
  18. package/dist-types/components/consent-dialog/consent-dialog.d.ts +2 -2
  19. package/dist-types/components/consent-dialog/index.d.ts +7 -7
  20. package/dist-types/components/consent-dialog-link/index.d.ts +1 -1
  21. package/dist-types/components/consent-dialog-trigger/atoms/button.d.ts +1 -1
  22. package/dist-types/components/consent-dialog-trigger/atoms/icon.d.ts +1 -1
  23. package/dist-types/components/consent-dialog-trigger/atoms/root.d.ts +2 -2
  24. package/dist-types/components/consent-dialog-trigger/atoms/toolbar.d.ts +2 -2
  25. package/dist-types/components/consent-dialog-trigger/consent-dialog-trigger-toolbar.d.ts +1 -1
  26. package/dist-types/components/consent-dialog-trigger/consent-dialog-trigger.d.ts +5 -5
  27. package/dist-types/components/consent-dialog-trigger/index.d.ts +4 -4
  28. package/dist-types/components/consent-dialog-trigger/types.d.ts +1 -1
  29. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  30. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  31. package/dist-types/components/consent-widget/atoms/accordion.d.ts +3 -3
  32. package/dist-types/components/consent-widget/atoms/button.d.ts +1 -1
  33. package/dist-types/components/consent-widget/atoms/footer.d.ts +1 -1
  34. package/dist-types/components/consent-widget/atoms/root.d.ts +1 -1
  35. package/dist-types/components/consent-widget/consent-widget.d.ts +1 -1
  36. package/dist-types/components/consent-widget/index.d.ts +11 -11
  37. package/dist-types/components/consent-widget/policy-actions.d.ts +2 -2
  38. package/dist-types/components/consent-widget/types.d.ts +1 -1
  39. package/dist-types/components/frame/atoms.d.ts +2 -2
  40. package/dist-types/components/frame/frame.d.ts +1 -1
  41. package/dist-types/components/frame/index.d.ts +3 -3
  42. package/dist-types/components/frame/types.d.ts +1 -1
  43. package/dist-types/components/iab-consent-banner/iab-consent-banner.d.ts +2 -2
  44. package/dist-types/components/iab-consent-banner/index.d.ts +19 -19
  45. package/dist-types/components/iab-consent-dialog/atoms/purpose-item.d.ts +21 -2
  46. package/dist-types/components/iab-consent-dialog/atoms/stack-item.d.ts +1 -1
  47. package/dist-types/components/iab-consent-dialog/atoms/vendor-list.d.ts +1 -1
  48. package/dist-types/components/iab-consent-dialog/hooks/use-gvl-data.d.ts +7 -1
  49. package/dist-types/components/iab-consent-dialog/iab-consent-dialog.d.ts +5 -4
  50. package/dist-types/components/iab-consent-dialog/index.d.ts +25 -25
  51. package/dist-types/components/iab-consent-dialog/use-iab-translations.d.ts +4 -0
  52. package/dist-types/components/integrations/index.d.ts +2 -2
  53. package/dist-types/components/integrations/shared.d.ts +1 -1
  54. package/dist-types/components/integrations/youtube-embed.d.ts +1 -1
  55. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  56. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  57. package/dist-types/components/shared/primitives/button.d.ts +4 -4
  58. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  59. package/dist-types/components/shared/primitives/legal-links/index.d.ts +1 -1
  60. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  61. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  62. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  63. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  64. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  65. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  66. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  67. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  68. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  69. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  70. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  71. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  72. package/dist-types/context/theme-context.d.ts +1 -1
  73. package/dist-types/headless.d.ts +9 -9
  74. package/dist-types/hooks/index.d.ts +12 -12
  75. package/dist-types/hooks/use-headless-consent-ui.d.ts +1 -1
  76. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  77. package/dist-types/hooks/use-styles.d.ts +1 -1
  78. package/dist-types/hooks/use-theme.d.ts +1 -1
  79. package/dist-types/iab.d.ts +4 -4
  80. package/dist-types/index.d.ts +20 -20
  81. package/dist-types/primitives/accordion.d.ts +1 -1
  82. package/dist-types/primitives/button.d.ts +1 -1
  83. package/dist-types/primitives/collapsible.d.ts +1 -1
  84. package/dist-types/primitives/dialog.d.ts +1 -1
  85. package/dist-types/primitives/preference-item.d.ts +1 -1
  86. package/dist-types/primitives/switch.d.ts +1 -1
  87. package/dist-types/primitives/tabs.d.ts +1 -1
  88. package/dist-types/primitives.d.ts +7 -7
  89. package/dist-types/providers/consent-manager-provider.d.ts +1 -1
  90. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  91. package/dist-types/server/index.d.ts +5 -5
  92. package/dist-types/types/theme/index.d.ts +2 -2
  93. package/dist-types/types/theme/style-types.d.ts +1 -1
  94. package/dist-types/utils/merge-styles.d.ts +1 -1
  95. package/dist-types/utils/test-helpers.d.ts +1 -1
  96. package/dist-types/utils/theme-utils.d.ts +1 -1
  97. package/dist-types/version.d.ts +1 -1
  98. package/docs/README.md +6 -3
  99. package/docs/frameworks/react/concepts/client-modes.md +5 -3
  100. package/docs/frameworks/react/concepts/consent-models.md +2 -2
  101. package/docs/frameworks/react/concepts/cookie-management.md +4 -0
  102. package/docs/frameworks/react/concepts/glossary.md +1 -1
  103. package/docs/frameworks/react/concepts/initialization-flow.md +3 -3
  104. package/docs/frameworks/react/concepts/policy-packs.md +4 -2
  105. package/docs/frameworks/react/hooks/use-consent-manager/overview.md +1 -1
  106. package/docs/frameworks/react/iab/consent-banner.md +2 -2
  107. package/docs/frameworks/react/iab/consent-dialog.md +12 -3
  108. package/docs/frameworks/react/iab/overview.md +18 -6
  109. package/docs/frameworks/react/iframe-blocking.md +2 -0
  110. package/docs/frameworks/react/policy-packs.md +1 -1
  111. package/docs/integrations/front-chat.md +178 -0
  112. package/docs/integrations/one-dollar-stats.md +118 -0
  113. package/docs/integrations/overview.md +3 -0
  114. package/docs/integrations/pinterest-tag.md +236 -0
  115. package/docs/shared/concepts/client-modes.md +5 -3
  116. package/docs/shared/concepts/consent-models.md +2 -2
  117. package/docs/shared/concepts/cookie-management.md +4 -0
  118. package/docs/shared/concepts/glossary.md +1 -1
  119. package/docs/shared/concepts/initialization-flow.md +3 -3
  120. package/docs/shared/concepts/policy-packs.md +4 -2
  121. package/docs/shared/react/guides/iframe-blocking.md +2 -0
  122. package/docs/shared/react/guides/policy-packs.md +1 -1
  123. package/docs/shared/react/hooks/use-consent-manager/overview.md +1 -1
  124. package/docs/shared/react/iab/consent-banner.md +1 -1
  125. package/docs/shared/react/iab/consent-dialog.md +11 -2
  126. package/docs/shared/react/iab/overview.md +16 -4
  127. package/package.json +6 -6
  128. package/readme.json +1 -1
@@ -9,7 +9,7 @@ c15t supports four consent models that control how consent defaults, banner visi
9
9
  |--|--|--|--|
10
10
  |`opt-in`|Explicit consent required|Blocking banner|`false` (except `necessary`)|
11
11
  |`opt-out`|Processing allowed by default|Non-blocking notice|`true` (all granted)|
12
- |`iab`|IAB TCF 2.3 for programmatic ads|TCF banner|Managed by TCF framework|
12
+ |`iab`|IAB TCF 2.4 for programmatic ads|TCF banner|Managed by TCF framework|
13
13
  |`null`|No regulation detected|No banner|`true` (all auto-granted)|
14
14
 
15
15
  There are two ways c15t determines which model applies:
@@ -56,7 +56,7 @@ When the policy has `consent.gpc: true`, the browser's Global Privacy Control si
56
56
 
57
57
  ### IAB
58
58
 
59
- IAB Transparency and Consent Framework (TCF) 2.3 mode for programmatic advertising compliance. Only activates when two conditions are met: the jurisdiction is `GDPR` or `UK_GDPR`, and `iab.enabled` is `true` in your configuration.
59
+ IAB Transparency and Consent Framework (TCF) 2.4 mode for programmatic advertising compliance. Only activates when two conditions are met: the jurisdiction is `GDPR` or `UK_GDPR`, and `iab.enabled` is `true` in your configuration.
60
60
 
61
61
  When active, IAB mode generates TC strings, registers the `__tcfapi` CMP API, and works with the Global Vendor List (GVL) for machine-readable consent signals. If `iab.enabled` is not set, GDPR jurisdictions fall back to standard opt-in.
62
62
 
@@ -14,6 +14,10 @@ There are four sources of cookies on a typical website:
14
14
  **1. c15t itself**
15
15
  c15t stores the user's consent state in a `c15t` cookie (and mirrors it to localStorage). This cookie records which categories the user consented to, when they consented, and their subject ID. This is a strictly necessary cookie - it exists so the site remembers the user's consent choice.
16
16
 
17
+ If reading `window.localStorage` or calling a storage method throws, c15t logs a warning and skips unavailable pending-submission queues. Runtime construction still completes, and cookie-backed choices can still be read and saved. In v2, a failed localStorage access check skips policy initialization and hides the banner. Optional categories without a saved choice remain denied.
18
+
19
+ If the browser also blocks cookies, it cannot retain your choice across page loads. Do not use `enabled: false` to handle storage restrictions: that option grants every category.
20
+
17
21
  **2. Third-party scripts**
18
22
  When you load a tracking script (Google Analytics, Meta Pixel, etc.), that script sets its own cookies. Google Analytics creates `_ga` and `_gid` cookies. Meta creates `_fbp` and `_fbc`. These cookies are set by the script's code running in the browser - c15t prevents them by not loading the script until the user consents.
19
23
 
@@ -20,5 +20,5 @@ group: frameworks
20
20
  |--|--|
21
21
  |`jurisdiction`|A privacy regulation code (e.g., `GDPR`, `CCPA`, `PIPEDA`, `QC_LAW25`) detected from the user's geolocation. Maps to a consent model. See [Consent Models](/docs/frameworks/react/concepts/consent-models) for the full mapping table.|
22
22
  |GPC|Global Privacy Control — a browser signal (`Sec-GPC: 1`) indicating the user opts out of data sale/sharing. Honored when a policy sets `consent.gpc: true` (enabled by default in California presets).|
23
- |TCF|IAB Transparency and Consent Framework (v2.3) — a standard for programmatic advertising consent in GDPR jurisdictions. See [IAB TCF](/docs/frameworks/react/iab/overview).|
23
+ |TCF|IAB Transparency and Consent Framework (v2.4) — a standard for programmatic advertising consent in GDPR jurisdictions. See [IAB TCF](/docs/frameworks/react/iab/overview).|
24
24
  |GVL|Global Vendor List — an IAB-maintained registry of ad-tech vendors and their declared purposes, used by the TCF consent flow.|
@@ -69,9 +69,9 @@ sequenceDiagram
69
69
 
70
70
  **Init** — The store fetches the resolved policy, location, and translation data. In hosted mode this calls `GET /init` on your backend; in offline mode it resolves from `offlinePolicy.policyPacks` locally. If SSR data was passed to the provider, the network fetch is skipped entirely. See [Server-Side Utilities](/docs/frameworks/react/server-side) for SSR setup.
71
71
 
72
- **Policy resolution** — When [policy packs](/docs/frameworks/react/concepts/policy-packs) are configured, the backend resolves the right policy for the visitor based on their geo-location (region → country → fallback → default). The resolved policy determines the consent model (`opt-in`, `opt-out`, `iab`, or `none`), which categories are in scope, and what UI to show. For `opt-out` and `none` models, all categories are auto-granted — unless the resolved policy has `consent.gpc: true` and the browser sends a Global Privacy Control signal, in which case `marketing` and `measurement` are denied. If no policy pack is configured, the legacy jurisdiction-to-model mapping is used instead. See [Consent Models](/docs/frameworks/react/concepts/consent-models) for details.
72
+ **Policy resolution** — When [policy packs](/docs/frameworks/react/concepts/policy-packs) are configured, the backend resolves the right policy for the visitor based on their geo-location (region → country → fallback → default). The resolved policy determines the consent model (`opt-in`, `opt-out`, `iab`, or `none`), which categories are in scope, and what UI to show. For `opt-out` and `none` models, categories are auto-granted only when there is no saved choice — unless the resolved policy has `consent.gpc: true` and the browser sends a Global Privacy Control signal, in which case `marketing` and `measurement` are denied. If no policy pack is configured, the legacy jurisdiction-to-model mapping is used instead. See [Consent Models](/docs/frameworks/react/concepts/consent-models) for details.
73
73
 
74
- **Re-prompting** — If the resolved policy's material fingerprint differs from the fingerprint stored with the user's last consent, the banner is shown again. This happens automatically when you change consent-affecting fields (model, categories, scope mode, allowed actions). Presentation-only changes do not trigger re-prompts. See [Policy Packs — Re-Prompting](/docs/frameworks/react/concepts/policy-packs#re-prompting) for details.
74
+ **Re-prompting** — If the resolved policy's material fingerprint differs from the fingerprint stored with the user's last consent, previous grants become invalid, while the saved choice and its denials remain. The consent UI opens again when the resolved policy has one; under a no-banner policy, invalidated grants stay denied until the user makes a new choice. This happens automatically when you change consent-affecting fields (model, categories, scope mode, allowed actions). Presentation-only changes do not trigger re-prompts. See [Policy Packs — Re-Prompting](/docs/frameworks/react/concepts/policy-packs#re-prompting) for details.
75
75
 
76
76
  **Save** — When the user interacts with the banner or dialog, their choices are persisted to localStorage/cookies and synced to the backend (along with the `policySnapshotToken` if snapshot signing is configured). Script, iframe, and network gating rules update immediately based on the new consent state. See the [Script Loader](/docs/frameworks/react/script-loader), [Iframe Blocking](/docs/frameworks/react/iframe-blocking), and [Network Blocker](/docs/frameworks/react/network-blocker) guides for gating details.
77
77
 
@@ -85,7 +85,7 @@ The banner appears when any of these conditions are true:
85
85
  2. **Policy changed** — the material policy fingerprint differs from the fingerprint stored with the user's last consent (re-prompting)
86
86
  3. **Storage is accessible** — the browser allows localStorage (not blocked in private mode)
87
87
 
88
- If the resolved model is `none` or `opt-out` (and `ui.mode` is `none`), consents are auto-granted and the banner never appears. See [Consent Models](/docs/frameworks/react/concepts/consent-models) and [Policy Packs](/docs/frameworks/react/concepts/policy-packs) for details.
88
+ If the resolved model is `none` or `opt-out` (and `ui.mode` is `none`) and no saved choice exists, consents are auto-granted and the banner never appears. An existing saved choice keeps its denials, but grants invalidated by a policy change stay denied until the user makes a new choice. See [Consent Models](/docs/frameworks/react/concepts/consent-models) and [Policy Packs](/docs/frameworks/react/concepts/policy-packs) for details.
89
89
 
90
90
  ## Debugging the Lifecycle
91
91
 
@@ -32,7 +32,7 @@ const policies = [
32
32
  |Preset|Model|UI|Matches|
33
33
  |--|--|--|--|
34
34
  |`europeOptIn()`|`opt-in`|banner|EEA + UK countries + geo fallback|
35
- |`europeIab()`|`iab`|banner|EEA + UK countries + geo fallback (TCF 2.3)|
35
+ |`europeIab()`|`iab`|banner|EEA + UK countries + geo fallback (TCF 2.4)|
36
36
  |`californiaOptOut()`|`opt-out`|none|US-CA region|
37
37
  |`quebecOptIn()`|`opt-in`|banner|CA-QC region|
38
38
  |`worldNoBanner()`|`none`|none|default fallback|
@@ -65,7 +65,7 @@ Each policy combination produces a different consent experience:
65
65
  |`model: 'opt-out'`, `ui.mode: 'none'`|No banner, everything loads immediately — user opts out via a "Do Not Sell" link|
66
66
  |`model: 'none'`, `ui.mode: 'none'`|No banner, all categories auto-granted silently|
67
67
  |`model: 'opt-in'`, `ui.mode: 'dialog'`|Full-screen dialog, nothing loads until the user consents|
68
- |`model: 'iab'`, `ui.mode: 'banner'`|IAB TCF 2.3 banner with vendor-level controls|
68
+ |`model: 'iab'`, `ui.mode: 'banner'`|IAB TCF 2.4 banner with vendor-level controls|
69
69
 
70
70
  ## How Policy Resolution Works
71
71
 
@@ -203,6 +203,8 @@ back to the `eu` profile's `fallbackLanguage`.
203
203
 
204
204
  When you change a policy in a way that affects consent semantics — like adding a category, changing the consent model, or modifying allowed actions — c15t automatically re-prompts returning users.
205
205
 
206
+ A material policy change invalidates previous grants but keeps the user's choice record and denials. Optional categories stay denied until the user makes a new choice. The resolved policy controls whether a banner or dialog appears; a no-banner policy does not silently grant previously rejected categories. A temporary hosted initialization failure does not count as an authoritative policy change.
207
+
206
208
  This works through the **material policy fingerprint**: a hash of only the consent-affecting fields (model, categories, scope, allowed actions, grouped action layout, direction, proof settings). Presentation-only changes like copy, button styling, or scroll lock do not trigger re-prompts.
207
209
 
208
210
  |Change|Re-prompts?|
@@ -168,5 +168,5 @@ type Model = 'opt-in' | 'opt-out' | 'iab' | null;
168
168
 
169
169
  * `'opt-in'` — Explicit consent required before tracking (GDPR)
170
170
  * `'opt-out'` — Tracking allowed by default, user can opt out (CCPA)
171
- * `'iab'` — IAB TCF 2.3 compliance mode
171
+ * `'iab'` — IAB TCF 2.4 compliance mode
172
172
  * `null` — No jurisdiction detected yet
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  title: IABConsentBanner
3
- description: An IAB TCF 2.3 compliant consent banner that displays partner
3
+ description: An IAB TCF 2.4 compliant consent banner that displays partner
4
4
  count, purpose summaries, and legitimate interest notices.
5
5
  group: frameworks
6
6
  ---
7
7
  > ℹ️ **Info:**
8
8
  > c15t's IAB TCF support can be used in production. Use Inth for a hosted, IAB TCF-certified CMP setup with a managed CMP ID, or register your own CMP with IAB Europe and configure your own CMP ID.
9
9
 
10
- `IABConsentBanner` is a pre-built consent banner that follows the [IAB Transparency & Consent Framework (TCF) 2.3](https://iabeurope.eu/tcf-2-0/) specification. It renders when the consent model is set to `'iab'` and includes required disclosures like partner count, purpose summaries, and legitimate interest notices.
10
+ `IABConsentBanner` is a pre-built consent banner that follows the [IAB Transparency & Consent Framework (TCF) 2.4](https://iabeurope.eu/tcf-2-0/) specification. It renders when the consent model is set to `'iab'` and includes required disclosures like partner count, purpose summaries, and legitimate interest notices.
11
11
 
12
12
  Use this component instead of `ConsentBanner` when you need IAB TCF compliance for programmatic advertising in EU jurisdictions.
13
13
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  title: IABConsentDialog
3
- description: An IAB TCF 2.3 compliant preference center with tabbed purpose and
3
+ description: An IAB TCF 2.4 compliant preference center with tabbed purpose and
4
4
  vendor management.
5
5
  group: frameworks
6
6
  ---
7
7
  > ℹ️ **Info:**
8
8
  > c15t's IAB TCF support can be used in production. Use Inth for a hosted, IAB TCF-certified CMP setup with a managed CMP ID, or register your own CMP with IAB Europe and configure your own CMP ID.
9
9
 
10
- `IABConsentDialog` is an IAB TCF 2.3 compliant consent dialog that provides a tabbed interface for managing purpose consent and vendor preferences. It includes purpose grouping via stacks, individual purpose/vendor toggles, special purpose and feature disclosures, and legitimate interest handling.
10
+ `IABConsentDialog` is an IAB TCF 2.4 compliant consent dialog that provides a tabbed interface for managing purpose consent and vendor preferences. It includes purpose grouping via stacks, individual purpose/vendor toggles, special purpose and feature disclosures, and legitimate interest handling.
11
11
 
12
12
  ## Basic Usage
13
13
 
@@ -52,7 +52,8 @@ Displays all IAB purposes grouped into:
52
52
  * **Standalone purposes** — Purpose 1 (Store and/or access information on a device) is always shown standalone per IAB TCF spec
53
53
  * **Stacks** — Groups of related purposes determined by the GVL. Each stack is expandable to show individual purpose toggles
54
54
  * **Special features** — Opt-in features like precise geolocation
55
- * **Essential functions** — Special purposes and features that are locked (no user toggle) because they're required for basic operation
55
+ * **Essential functions** — Special purposes, which are locked (no user toggle) because vendors need them for basic operation
56
+ * **Features** — An informational section, shown when at least one vendor declares a feature
56
57
 
57
58
  Each purpose shows:
58
59
 
@@ -62,6 +63,14 @@ Each purpose shows:
62
63
  * Legitimate interest toggle where applicable
63
64
  * Expandable vendor list
64
65
 
66
+ ### Features section
67
+
68
+ TCF Policies v5.0.b treat features as information, not choices. The Features section follows those rules:
69
+
70
+ * Under the heading, the dialog shows the IAB standard text from the vendor list (`standardTexts.features`). If the vendor list does not include it, the dialog shows the `iab.preferenceCenter.features.description` translation.
71
+ * Each feature shows its name, description, IAB illustrations under **Examples**, and the names of the vendors that use it.
72
+ * The section has no switches, lock icons or per-vendor toggles. Features only serve the purposes the user chooses elsewhere in the dialog.
73
+
65
74
  ### Vendors Tab
66
75
 
67
76
  Displays all vendors from the GVL plus any custom vendors:
@@ -1,12 +1,12 @@
1
1
  ---
2
- title: IAB TCF 2.3
3
- description: Implement IAB Transparency & Consent Framework 2.3 compliance for
2
+ title: IAB TCF 2.4
3
+ description: Implement IAB Transparency & Consent Framework 2.4 compliance for
4
4
  programmatic advertising in EU/EEA jurisdictions.
5
5
  group: frameworks
6
6
  ---
7
7
  ## What is IAB TCF?
8
8
 
9
- The [IAB Transparency & Consent Framework (TCF)](https://iabeurope.eu/tcf-2-0/) is a standardized protocol for communicating user consent choices to ad tech vendors in the programmatic advertising ecosystem. TCF 2.3 is the current version and is widely adopted across the EU/EEA.
9
+ The [IAB Transparency & Consent Framework (TCF)](https://iabeurope.eu/tcf-2-0/) is a standardized protocol for communicating user consent choices to ad tech vendors in the programmatic advertising ecosystem. TCF 2.4 is the current version and is widely adopted across the EU/EEA.
10
10
 
11
11
  When your site participates in the IAB ecosystem (ad exchanges, SSPs, DSPs, DMPs), you need to:
12
12
 
@@ -26,7 +26,7 @@ If you self-host the c15t backend or want to operate as your own CMP, register y
26
26
 
27
27
  ## How c15t Implements TCF
28
28
 
29
- c15t provides a complete IAB TCF 2.3 CMP (Consent Management Platform) implementation:
29
+ c15t provides a complete IAB TCF 2.4 CMP (Consent Management Platform) implementation:
30
30
 
31
31
  1. **Global Vendor List (GVL)** — Automatically fetched from the c15t backend. Contains the official IAB vendor registry with purposes, features, and stacks.
32
32
  2. **IABConsentBanner** — A pre-built banner showing partner count, purpose summaries, and legitimate interest notices.
@@ -95,7 +95,11 @@ Purposes are grouped into **stacks** by the GVL for a simplified UI presentation
95
95
 
96
96
  ### Special Features
97
97
 
98
- Features like precise geolocation or device scanning that require explicit opt-in beyond standard consent.
98
+ Features like precise geolocation, or identifying devices based on information actively requested, that require explicit opt-in beyond standard consent.
99
+
100
+ ### Features
101
+
102
+ Features, such as linking different devices, are means of processing that vendors use only for the purposes the user chooses. They have no consent toggle. The preference center lists them in an informational section with the IAB standard text.
99
103
 
100
104
  ### Legitimate Interest
101
105
 
@@ -105,11 +109,19 @@ Some purposes can be processed under legitimate interest rather than consent. Us
105
109
 
106
110
  Each vendor in the GVL declares which purposes it uses, whether via consent or legitimate interest. The preference center lets users toggle consent per-vendor.
107
111
 
112
+ ## Consent Scope
113
+
114
+ c15t stores TCF choices per site and browser, so every TC string it creates is service-specific. c15t does not sync TCF choices across devices.
115
+
116
+ Do not use `/consent/check` or your own account data to skip the TCF UI on another device. Reusing choices across devices is a multi-device scope, and the TCF requires you to disclose that scope in the first layer of the UI, which the prebuilt IAB components do not do.
117
+
118
+ The `isServiceSpecific` option is deprecated. TCF requires IsServiceSpecific=1 in every TC string, so c15t always encodes `true` and logs a warning if you pass `false`.
119
+
108
120
  ## Standard vs IAB Components
109
121
 
110
122
  |Feature|ConsentBanner / ConsentDialog|IABConsentBanner / IABConsentDialog|
111
123
  |--|--|--|
112
- |Consent model|opt-in / opt-out|IAB TCF 2.3|
124
+ |Consent model|opt-in / opt-out|IAB TCF 2.4|
113
125
  |Granularity|Category-level (measurement, marketing, etc.)|Purpose-level + vendor-level|
114
126
  |Vendor management|No|Yes (full GVL integration)|
115
127
  |TC String|No|Yes|
@@ -73,6 +73,8 @@ For iframes outside of React (e.g., CMS content, server-rendered HTML), add `dat
73
73
 
74
74
  When consent for the specified category is granted, c15t automatically swaps `data-src` to `src`, loading the iframe. When consent is revoked, `src` is moved back to `data-src`.
75
75
 
76
+ The `data-category` value must be one of `necessary`, `functionality`, `measurement`, `experience`, or `marketing`. An iframe with any other value, including an empty one, stays blocked, and c15t logs a warning to the console.
77
+
76
78
  ### Dynamic Iframes
77
79
 
78
80
  c15t uses a `MutationObserver` to watch for dynamically added iframes. Any iframe with `data-category` added to the DOM after initialization is automatically processed.
@@ -73,7 +73,7 @@ Available presets:
73
73
  |Preset|Model|Matches|
74
74
  |--|--|--|
75
75
  |`europeOptIn()`|`opt-in`|EEA + UK countries + geo fallback|
76
- |`europeIab()`|`iab`|EEA + UK countries + geo fallback (TCF 2.3)|
76
+ |`europeIab()`|`iab`|EEA + UK countries + geo fallback (TCF 2.4)|
77
77
  |`californiaOptOut()`|`opt-out`|US-CA region|
78
78
  |`quebecOptIn()`|`opt-in`|CA-QC region|
79
79
  |`worldNoBanner()`|`none`|default fallback|
@@ -0,0 +1,178 @@
1
+ ---
2
+ title: Front Chat
3
+ description: Load Front Chat after functionality consent and initialize the
4
+ widget when its SDK is ready.
5
+ group: integrations
6
+ ---
7
+ Front Chat adds a live chat widget connected to your Front inbox. c15t waits for
8
+ functionality consent before loading the widget's SDK and calling `FrontChat('init')`.
9
+
10
+ ## Official Front documentation
11
+
12
+ * [Set up a Front Chat inbox and install the widget](https://help.front.com/en/articles/2049)
13
+ * [Chat Widget SDK reference](https://dev.frontapp.com/docs/chat-sdk-reference)
14
+
15
+ ## Integrate with c15t
16
+
17
+ Copy the `chatId` from the widget code snippet in your Front Chat channel's
18
+ **Installation** tab. Pass that value to `frontChat()` instead of adding Front's
19
+ script snippet separately.
20
+
21
+ **React**
22
+
23
+ ```tsx
24
+ import { type ReactNode } from 'react';
25
+ import { ConsentManagerProvider } from '@c15t/react';
26
+ import { frontChat } from '@c15t/scripts/front-chat';
27
+
28
+ const scripts = [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID' })];
29
+
30
+ export function ConsentProvider({ children }: { children: ReactNode }) {
31
+ return (
32
+ <ConsentManagerProvider
33
+ options={{
34
+ mode: 'hosted',
35
+ backendURL: 'https://your-instance.c15t.dev',
36
+ scripts,
37
+ }}
38
+ >
39
+ {children}
40
+ </ConsentManagerProvider>
41
+ );
42
+ }
43
+ ```
44
+
45
+ **Next.js**
46
+
47
+ ```tsx
48
+ 'use client';
49
+
50
+ import { type ReactNode } from 'react';
51
+ import { ConsentManagerProvider } from '@c15t/nextjs';
52
+ import { frontChat } from '@c15t/scripts/front-chat';
53
+
54
+ const scripts = [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID' })];
55
+
56
+ export function ConsentProvider({ children }: { children: ReactNode }) {
57
+ return (
58
+ <ConsentManagerProvider
59
+ options={{
60
+ mode: 'hosted',
61
+ backendURL: '/api/c15t',
62
+ scripts,
63
+ }}
64
+ >
65
+ {children}
66
+ </ConsentManagerProvider>
67
+ );
68
+ }
69
+ ```
70
+
71
+ **JavaScript**
72
+
73
+ ```ts
74
+ import { getOrCreateConsentRuntime } from 'c15t';
75
+ import { frontChat } from '@c15t/scripts/front-chat';
76
+
77
+ getOrCreateConsentRuntime({
78
+ mode: 'hosted',
79
+ backendURL: 'https://your-instance.c15t.dev',
80
+ scripts: [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID' })],
81
+ });
82
+ ```
83
+
84
+ ## How c15t loads it
85
+
86
+ * **Category:** `functionality` (Functional)
87
+ * **Loads when:** functionality consent is granted
88
+ * **Script:** `https://chat-assets.frontapp.com/v1/chat.bundle.js`
89
+ * **After load:** c15t calls `window.FrontChat('init', ...)` with your chat ID and launcher options
90
+ * **On revocation:** c15t reloads the page by default and leaves Front Chat unloaded while functionality consent is denied
91
+
92
+ Keep the default `reloadOnConsentRevoked: true` behavior. Removing a script tag
93
+ does not stop a widget that has already initialized, and this integration does
94
+ not provide automatic widget shutdown when you disable the reload.
95
+
96
+ ## Configure the integration
97
+
98
+ `chatId` must be a non-empty string. The default launcher is enabled unless you
99
+ set `useDefaultLauncher` to `false`:
100
+
101
+ ```ts
102
+ frontChat({
103
+ chatId: 'YOUR_FRONT_CHAT_ID',
104
+ useDefaultLauncher: false,
105
+ })
106
+ ```
107
+
108
+ With the default launcher hidden, use Front's SDK to open the widget from your
109
+ own launcher after consent is granted and the widget has initialized. See the
110
+ [SDK reference](https://dev.frontapp.com/docs/chat-sdk-reference) for commands
111
+ and initialization requirements.
112
+
113
+ ### Content Security Policy
114
+
115
+ If your Content Security Policy (CSP) uses nonces, pass the server-generated nonce
116
+ for the current response. c15t applies it to the loader script and forwards it
117
+ to Front's initialization call:
118
+
119
+ ```ts
120
+ function createChatScripts(nonce: string) {
121
+ return [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID', nonce })];
122
+ }
123
+ ```
124
+
125
+ Front creates additional resources for the widget. Follow Front's
126
+ [CSP setup instructions](https://help.front.com/en/articles/2049) for the
127
+ remaining policy requirements. You can override the loader URL with `scriptSrc`
128
+ if you serve the SDK through your own proxy; this does not proxy the widget's
129
+ other requests.
130
+
131
+ ### Optional session cleanup
132
+
133
+ `frontChat()` does not automatically delete an existing Front session. You can
134
+ also request session cleanup before c15t's revocation reload using
135
+ `shutdownFrontChat()`:
136
+
137
+ ```ts
138
+ import { getOrCreateConsentRuntime } from 'c15t';
139
+ import { frontChat, shutdownFrontChat } from '@c15t/scripts/front-chat';
140
+
141
+ getOrCreateConsentRuntime({
142
+ mode: 'hosted',
143
+ backendURL: 'https://your-instance.c15t.dev',
144
+ scripts: [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID' })],
145
+ callbacks: {
146
+ onBeforeConsentRevocationReload: ({ preferences }) => {
147
+ if (!preferences.functionality) {
148
+ shutdownFrontChat();
149
+ }
150
+ },
151
+ },
152
+ });
153
+ ```
154
+
155
+ The function calls Front's `shutdown` command with `clearSession: true` when
156
+ the SDK is available. This is a best-effort request: the reload does not wait
157
+ for Front to finish clearing its session. Keep reload on revocation enabled;
158
+ the cleanup call does not replace it.
159
+
160
+ ## Types
161
+
162
+ ### FrontChatOptions
163
+
164
+ |Property|Value|
165
+ |:--|:--|
166
+ |Type Name|\`FrontChatOptions\`|
167
+ |Source Path|\`./packages/scripts/src/vendors/functional/front-chat.ts\`|
168
+
169
+ \*ExtractedTypeTable: Could not extract "FrontChatOptions" from "./packages/scripts/src/vendors/functional/front-chat.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
170
+
171
+ ### Script
172
+
173
+ |Property|Value|
174
+ |:--|:--|
175
+ |Type Name|\`Script\`|
176
+ |Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|
177
+
178
+ \*ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: OneDollarStats
3
+ description: Load OneDollarStats after measurement consent with optional tracker
4
+ data attributes.
5
+ group: integrations
6
+ ---
7
+ [OneDollarStats](https://onedollarstats.com/) matches events to the domain registered in your dashboard, so no API key is needed. This helper loads its classic deferred script after measurement consent and forwards settings as `data-*` attributes.
8
+
9
+ ## Integrate with c15t
10
+
11
+ **React**
12
+
13
+ ```tsx
14
+ import { type ReactNode } from 'react';
15
+ import { ConsentManagerProvider } from '@c15t/react';
16
+ import { oneDollarStats } from '@c15t/scripts/one-dollar-stats';
17
+
18
+ const scripts = [oneDollarStats()];
19
+
20
+ export function ConsentProvider({ children }: { children: ReactNode }) {
21
+ return (
22
+ <ConsentManagerProvider
23
+ options={{
24
+ mode: 'hosted',
25
+ backendURL: 'https://your-instance.c15t.dev',
26
+ scripts,
27
+ }}
28
+ >
29
+ {children}
30
+ </ConsentManagerProvider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ **Next.js**
36
+
37
+ ```tsx
38
+ 'use client';
39
+
40
+ import { type ReactNode } from 'react';
41
+ import { ConsentManagerProvider } from '@c15t/nextjs';
42
+ import { oneDollarStats } from '@c15t/scripts/one-dollar-stats';
43
+
44
+ const scripts = [oneDollarStats()];
45
+
46
+ export function ConsentProvider({ children }: { children: ReactNode }) {
47
+ return (
48
+ <ConsentManagerProvider
49
+ options={{
50
+ mode: 'hosted',
51
+ backendURL: '/api/c15t',
52
+ scripts,
53
+ }}
54
+ >
55
+ {children}
56
+ </ConsentManagerProvider>
57
+ );
58
+ }
59
+ ```
60
+
61
+ **JavaScript**
62
+
63
+ ```ts
64
+ import { getOrCreateConsentRuntime } from 'c15t';
65
+ import { oneDollarStats } from '@c15t/scripts/one-dollar-stats';
66
+
67
+ getOrCreateConsentRuntime({
68
+ mode: 'hosted',
69
+ backendURL: 'https://your-instance.c15t.dev',
70
+ scripts: [oneDollarStats()],
71
+ });
72
+ ```
73
+
74
+ ## Tracker settings
75
+
76
+ All settings use strings and omit the `data-` prefix. Unknown settings are forwarded for compatibility with future tracker options.
77
+
78
+ ```ts
79
+ oneDollarStats({
80
+ hostname: 'docs.example.com',
81
+ devmode: 'true',
82
+ autocollect: 'false',
83
+ 'hash-routing': 'true',
84
+ url: 'https://analytics.example.com/events',
85
+ })
86
+ ```
87
+
88
+ * `hostname` must be a bare hostname, optionally including a port. It overrides the event hostname on every host, including production.
89
+ * Local testing needs both `hostname` and `devmode: 'true'`.
90
+ * `autocollect: 'false'` disables automatic pageview and click collection.
91
+ * `'hash-routing': 'false'` omits the attribute entirely: the CDN tracker enables hash routing whenever the attribute is present, regardless of its value.
92
+ * `url` overrides the collection endpoint. Leave it unset to use the vendor default.
93
+
94
+ The tracker handles browser history navigation itself; no extra router hook is required. See the [vendor installation reference](https://docs.onedollarstats.com/installation) for its settings.
95
+
96
+ ## Consent behaviour
97
+
98
+ The script uses the `measurement` category and loads only when that consent is granted. On revocation, c15t removes its script element. Removing a script does not undo code that already executed, including the tracker's history hooks and event listeners. Use c15t's reload-on-revocation behaviour to clear the running tracker, and check measurement consent before making manual tracking calls.
99
+
100
+ ## Types
101
+
102
+ ### OneDollarStatsOptions
103
+
104
+ |Property|Value|
105
+ |:--|:--|
106
+ |Type Name|\`OneDollarStatsOptions\`|
107
+ |Source Path|\`./packages/scripts/src/vendors/analytics/one-dollar-stats.ts\`|
108
+
109
+ \*ExtractedTypeTable: Could not extract "OneDollarStatsOptions" from "./packages/scripts/src/vendors/analytics/one-dollar-stats.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
110
+
111
+ ### Script
112
+
113
+ |Property|Value|
114
+ |:--|:--|
115
+ |Type Name|\`Script\`|
116
+ |Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|
117
+
118
+ \*ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
@@ -94,6 +94,7 @@ Analytics integrations usually require `measurement` consent. Some vendors can r
94
94
  * [LogRocket](/docs/integrations/logrocket)
95
95
  * [Matomo Analytics](/docs/integrations/matomo-analytics)
96
96
  * [PostHog](/docs/integrations/posthog)
97
+ * [OneDollarStats](/docs/integrations/one-dollar-stats)
97
98
  * [Promptwatch](/docs/integrations/promptwatch)
98
99
  * [Pirsch](/docs/integrations/pirsch)
99
100
  * [RudderStack](/docs/integrations/rudderstack)
@@ -107,6 +108,7 @@ Analytics integrations usually require `measurement` consent. Some vendors can r
107
108
  Functional integrations usually require `functionality` consent. They add optional user-facing capabilities, such as chat widgets, that should only run after the user allows them.
108
109
 
109
110
  * [Crisp](/docs/integrations/crisp)
111
+ * [Front Chat](/docs/integrations/front-chat)
110
112
  * [Intercom](/docs/integrations/intercom)
111
113
 
112
114
  ## Ads & Pixels
@@ -120,6 +122,7 @@ Advertising pixels usually require `marketing` consent. Many of them need startu
120
122
  * [LinkedIn Insight Tag](/docs/integrations/linkedin-insights)
121
123
  * [Microsoft UET](/docs/integrations/microsoft-uet)
122
124
  * [X (Twitter) Pixel](/docs/integrations/x-pixel)
125
+ * [Pinterest Tag](/docs/integrations/pinterest-tag)
123
126
 
124
127
  ## Tag Managers
125
128