@c15t/react 3.0.0-alpha.0 → 3.0.0-alpha.2

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 (145) hide show
  1. package/AGENTS.md +4 -1
  2. package/dist/aggregate-components.js +1 -1
  3. package/dist/component-hooks/use-manager.js +1 -1
  4. package/dist/components/consent-gate/atoms.js +1 -0
  5. package/dist/components/consent-gate/consent-gate.js +2 -0
  6. package/dist/components/consent-gate/index.js +2 -0
  7. package/dist/components/preferences/atoms/accordion.js +1 -1
  8. package/dist/components/preferences/atoms/vendor-list.js +1 -0
  9. package/dist/components/preferences/index.js +1 -1
  10. package/dist/consent-gate.js +2 -0
  11. package/dist/draft.js +1 -1
  12. package/dist/external-iab-context.js +1 -1
  13. package/dist/hooks.js +1 -1
  14. package/dist/iab/styles.tw3.css +4 -110
  15. package/dist/index.js +1 -1
  16. package/dist/provider.js +1 -1
  17. package/dist/styles.tw3.css +10 -113
  18. package/dist/utils/deferred-module.js +2 -0
  19. package/dist/version.js +1 -1
  20. package/dist-types/aggregate-components.d.ts +2 -2
  21. package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +1 -1
  22. package/dist-types/component-hooks/use-manager.d.ts +6 -1
  23. package/dist-types/components/consent-gate/atoms.d.ts +11 -0
  24. package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
  25. package/dist-types/components/consent-gate/index.d.ts +41 -0
  26. package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
  27. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  28. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  29. package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +1 -1
  30. package/dist-types/components/iab-panel/atoms/stack-item.d.ts +1 -1
  31. package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
  32. package/dist-types/components/iab-panel/iab-panel.d.ts +1 -1
  33. package/dist-types/components/iab-panel/index.d.ts +24 -24
  34. package/dist-types/components/iab-prompt/index.d.ts +17 -17
  35. package/dist-types/components/panel/atoms/card.d.ts +4 -4
  36. package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
  37. package/dist-types/components/panel/index.d.ts +7 -7
  38. package/dist-types/components/panel/panel.d.ts +2 -2
  39. package/dist-types/components/panel-link/index.d.ts +1 -1
  40. package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
  41. package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
  42. package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
  43. package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
  44. package/dist-types/components/panel-trigger/index.d.ts +4 -4
  45. package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
  46. package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -9
  47. package/dist-types/components/panel-trigger/types.d.ts +1 -1
  48. package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
  49. package/dist-types/components/preferences/atoms/button.d.ts +1 -1
  50. package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
  51. package/dist-types/components/preferences/atoms/root.d.ts +1 -1
  52. package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
  53. package/dist-types/components/preferences/index.d.ts +13 -11
  54. package/dist-types/components/preferences/policy-actions.d.ts +2 -2
  55. package/dist-types/components/preferences/preferences.d.ts +1 -1
  56. package/dist-types/components/preferences/types.d.ts +1 -1
  57. package/dist-types/components/prompt/components.d.ts +3 -3
  58. package/dist-types/components/prompt/index.d.ts +11 -11
  59. package/dist-types/components/prompt/policy-actions.d.ts +2 -2
  60. package/dist-types/components/prompt/prompt.d.ts +1 -1
  61. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  62. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  63. package/dist-types/components/shared/primitives/button.d.ts +2 -2
  64. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  65. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  66. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  67. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  68. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  69. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  70. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  71. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  72. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  73. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  74. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  75. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  76. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  77. package/dist-types/consent-gate.d.ts +1 -0
  78. package/dist-types/context/index.d.ts +3 -3
  79. package/dist-types/context/theme-context.d.ts +1 -1
  80. package/dist-types/draft.d.ts +53 -1
  81. package/dist-types/headless.d.ts +7 -7
  82. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  83. package/dist-types/hooks/use-iab-manager.d.ts +1 -1
  84. package/dist-types/hooks/use-theme.d.ts +1 -1
  85. package/dist-types/hooks.d.ts +28 -1
  86. package/dist-types/iab.d.ts +5 -5
  87. package/dist-types/index.d.ts +25 -25
  88. package/dist-types/module-hooks.d.ts +4 -4
  89. package/dist-types/panel-link.d.ts +1 -1
  90. package/dist-types/panel-trigger.d.ts +1 -1
  91. package/dist-types/panel.d.ts +1 -1
  92. package/dist-types/preferences.d.ts +1 -1
  93. package/dist-types/primitives/accordion.d.ts +1 -1
  94. package/dist-types/primitives/button.d.ts +1 -1
  95. package/dist-types/primitives/collapsible.d.ts +1 -1
  96. package/dist-types/primitives/dialog.d.ts +1 -1
  97. package/dist-types/primitives/preference-item.d.ts +1 -1
  98. package/dist-types/primitives/switch.d.ts +1 -1
  99. package/dist-types/primitives/tabs.d.ts +1 -1
  100. package/dist-types/primitives.d.ts +7 -7
  101. package/dist-types/prompt.d.ts +1 -1
  102. package/dist-types/provider.d.ts +27 -5
  103. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  104. package/dist-types/server/index.d.ts +5 -5
  105. package/dist-types/theme-provider.d.ts +2 -2
  106. package/dist-types/types/index.d.ts +3 -3
  107. package/dist-types/types/manager.d.ts +1 -1
  108. package/dist-types/types/theme/index.d.ts +1 -1
  109. package/dist-types/ui-config-context.d.ts +1 -1
  110. package/dist-types/utils/deferred-module.d.ts +11 -0
  111. package/dist-types/utils/index.d.ts +4 -4
  112. package/dist-types/utils/merge-slot-props.d.ts +2 -2
  113. package/dist-types/utils/merge-styles.d.ts +1 -1
  114. package/dist-types/utils/theme-utils.d.ts +1 -1
  115. package/dist-types/version.d.ts +1 -1
  116. package/docs/README.md +4 -1
  117. package/docs/frameworks/react/components/consent-dialog.md +1 -1
  118. package/docs/frameworks/react/components/{frame.md → consent-gate.md} +27 -27
  119. package/docs/frameworks/react/components/consent-manager-provider.md +14 -1
  120. package/docs/frameworks/react/components/consent-widget.md +39 -6
  121. package/docs/frameworks/react/concepts/consent-categories.md +31 -2
  122. package/docs/frameworks/react/script-loader.md +8 -0
  123. package/docs/frameworks/react/styling/overview.md +10 -0
  124. package/docs/guides/deployment-modes.md +12 -0
  125. package/docs/integrations/building-integrations.md +5 -0
  126. package/docs/integrations/clear-on-revocation.md +167 -0
  127. package/docs/integrations/cloudflare-zaraz.md +399 -0
  128. package/docs/integrations/google-maps.md +20 -20
  129. package/docs/integrations/granular-consent.md +208 -0
  130. package/docs/integrations/overview.md +5 -4
  131. package/docs/integrations/youtube.md +26 -21
  132. package/docs/upgrade-v3.md +47 -0
  133. package/package.json +26 -16
  134. package/dist/components/frame/atoms.js +0 -1
  135. package/dist/components/frame/frame.js +0 -2
  136. package/dist/components/frame/index.js +0 -2
  137. package/dist/frame.js +0 -2
  138. package/dist-types/components/frame/atoms.d.ts +0 -11
  139. package/dist-types/components/frame/frame.d.ts +0 -2
  140. package/dist-types/components/frame/index.d.ts +0 -24
  141. package/dist-types/frame.d.ts +0 -1
  142. package/readme.json +0 -48
  143. package/src/iab/styles.css +0 -11
  144. package/src/styles.css +0 -10
  145. /package/dist/components/{frame → consent-gate}/types.js +0 -0
@@ -85,6 +85,28 @@ Privacy Control overrides shows a note under its row: "Your saved choice is
85
85
  restricted by the current privacy settings." The switch still reflects the
86
86
  saved value; the effective permission is off.
87
87
 
88
+ ### Vendors
89
+
90
+ When the provider declares `vendors`, each category's expanded description
91
+ ends with one card per vendor whose category condition names that category,
92
+ styled like a category row: the vendor's name and a switch on the header,
93
+ and its description and privacy policy link behind the card's own expand
94
+ control. Cards start collapsed, so a long list stays one line per vendor,
95
+ and the category list scrolls inside the dialog rather than growing it.
96
+ Vendor switches edit the same draft as the category
97
+ switches and record on Save. Saving with one vendor off denies that vendor
98
+ only; the category stays granted and its other vendors keep loading. While
99
+ the category is off in the draft the vendor switches are disabled, with the
100
+ hint "Turn on this category to choose vendors." A vendor declared
101
+ `disabled`, or one that only falls under `necessary`, is listed without a
102
+ switch. Accept all and Reject all clear every vendor denial, including one
103
+ staged but not yet saved. Under an `iab` policy no vendor rows render.
104
+ Vendors that are only referenced by a script slug, without a name and
105
+ privacy policy URL, still gate loading but are not listed, and so does a
106
+ vendor whose condition negates a category, since no category row can
107
+ host it. See
108
+ [granular consent](../../../integrations/granular-consent.md).
109
+
88
110
  ### Actions
89
111
 
90
112
  The preference center always renders Reject all, Accept all and Save, with
@@ -102,8 +124,12 @@ such as `['disclosure']`, renders the widget and Save completes without
102
124
  writing a consent record.
103
125
 
104
126
  For a fully custom preference center, `useConsentDraft()` returns the same
105
- draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
106
- `update`, `acceptAll`, `rejectAll`, `save` and `reset`.
127
+ draft: `values`, `displayedCategories`, `vendors`, `isDirty`, `isStale`,
128
+ `set`, `update`, `setVendor`, `acceptAll`, `rejectAll`, `save` and `reset`.
129
+ A custom vendor control alone takes `useVendorDraft()`, the vendor slice of
130
+ that draft: `vendors`, `setVendor`, `isDirty`, `isStale`, `save` and `reset`.
131
+ `useDeclaredVendors()`, `useVendorChoice()` and `useVendorAllowed(id)` read
132
+ the declared list, the recorded denials and one vendor's effective result.
107
133
 
108
134
  `useConsentDraft` and `ConsentDraftProvider` import from `c15t/react`.
109
135
 
@@ -112,7 +138,10 @@ draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
112
138
  Each row is a disclosure: a button that expands the category description
113
139
  and a switch beside it, so a visitor can read the description without
114
140
  changing the choice. Each switch has the category title as its accessible
115
- name. The policy-change alert uses `role="alert"`, and a restriction note is
141
+ name. A category's vendor cards sit in a `region` named "Vendors" with the
142
+ count; each card's expand button carries `aria-expanded`, each vendor
143
+ switch is named "Allow" followed by the vendor name, and is described by
144
+ the vendor's name element. The policy-change alert uses `role="alert"`, and a restriction note is
116
145
  an `output` element linked to its switch through `aria-describedby`. The
117
146
  root's `dir` attribute follows the active language.
118
147
 
@@ -122,7 +151,8 @@ Every part is available as `ConsentWidget.<Part>`. `Root` provides the
122
151
  draft and accepts `noStyle`, `disableAnimation` and `uiSource`. `Accordion`
123
152
  and `AccordionItems` render the stock rows; `AccordionItem`,
124
153
  `AccordionTrigger`, `AccordionTriggerInner`, `AccordionContent`,
125
- `AccordionArrow` and `Switch` build your own. `PolicyActions` renders the
154
+ `AccordionArrow` and `Switch` build your own; `VendorList` renders one
155
+ category's vendor rows and takes a `category` prop. `PolicyActions` renders the
126
156
  resolved actions in `Footer` and `FooterSubGroup` and accepts `renderAction`
127
157
  to replace one button; `AcceptAllButton`, `RejectButton`, `SaveButton` and
128
158
  `CustomizeButton` are the individual buttons.
@@ -155,8 +185,11 @@ export function PreferenceCenter() {
155
185
  Provider component slots for the stock structure are `manager.root`,
156
186
  `manager.footer`, `manager.actionGroup`, `accordion.root`,
157
187
  `accordion.triggerRow`, `accordion.title`, `accordion.control`,
158
- `accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`
159
- and `tag.manager`.
188
+ `accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`,
189
+ `vendor-list.root`, `vendor-list.trigger`, `vendor-list.content`,
190
+ `vendor-list.item`, `vendor-list.header`,
191
+ `vendor-list.name`, `vendor-list.description`, `vendor-list.link`,
192
+ `vendor-list.control` and `tag.manager`.
160
193
 
161
194
  The compound parts import from `c15t/react` as properties of `ConsentWidget`.
162
195
  See [Styling](../styling/overview.md) for slots and tokens.
@@ -17,8 +17,23 @@ group: frameworks
17
17
 
18
18
  `necessary` is always permitted. Assign categories based on what an integration
19
19
  does in your application; renaming analytics to necessary does not change its
20
- purpose. The provider's displayed categories and the resolved policy scope must
21
- agree.
20
+ purpose.
21
+
22
+ c15t discovers categories from registered scripts, network rules, React `ConsentGate`
23
+ components, and iframes with `data-category` handled by the iframe blocker.
24
+ The browser client also discovers inert scripts tagged with `data-c15t-category`.
25
+ Compound script and network conditions contribute every category they reference.
26
+
27
+ Discovered categories are added to `consentCategories`, when supplied. The dialog
28
+ always includes Necessary and offers the optional categories in that combined
29
+ set that are also in the resolved policy scope. Choice completion uses the same
30
+ set. If neither configuration nor integrations supply categories, the dialog
31
+ uses the full policy scope.
32
+
33
+ Categories discovered later are added immediately and retained until the provider
34
+ or runtime is recreated. Adding a category can require a new choice; removing a
35
+ script or unmounting a frame does not remove its category or erase consent.
36
+ Discovery does not grant consent or change backend permission restrictions.
22
37
 
23
38
  ## Respect policy scope
24
39
 
@@ -28,6 +43,20 @@ only some optional categories, set `scopeMode` explicitly. An omitted scope,
28
43
  `['*']`, or a list containing only `necessary` expands to the default optional
29
44
  categories; a necessary-only list is not a shortcut for disabling all tracking.
30
45
 
46
+ For example, a backend rule with `categories: ['necessary']` and scripts assigned
47
+ to `marketing` and `measurement` displays Necessary, Marketing, and Analytics,
48
+ even without `consentCategories`. Accept All records both optional choices and
49
+ keeps the banner dismissed after reload. An explicit list of
50
+ `['necessary', 'measurement']` with no other integrations displays Necessary and
51
+ Analytics.
52
+
53
+ Hidden optional categories do not need a choice to dismiss the banner. Their
54
+ permissions still follow the backend policy, so hidden opt-in categories remain
55
+ denied without a valid grant. React providers enable DOM iframe blocking and
56
+ discovery by default; set `iframeBlocker: false` to disable it, or
57
+ `iframeBlocker: { disableAutomaticBlocking: true }` to scan manually with
58
+ `useIframeBlocker({ disableAutomaticBlocking: true }).processAllIframes()`.
59
+
31
60
  Use effective permissions to gate work and explicit choices to inspect what the
32
61
  visitor confirmed. Read [consent state](../../../guides/consent-state.md) for that
33
62
  distinction and [migration](../../../upgrade-v3.md) for v3 policy configuration.
@@ -53,3 +53,11 @@ Use [custom integrations](../../integrations/building-integrations.md) for an
53
53
  unlisted vendor and [verification](../../guides/verify-consent.md) for the network
54
54
  checks. Google helpers have a separate
55
55
  [Consent Mode contract](../../integrations/google-tag-manager.md).
56
+
57
+ ## Clear stored tracking data
58
+
59
+ Script gating does not remove cookies or Web Storage entries that a script
60
+ already wrote. Add `clearOnRevocation` to `ConsentProvider.options` to remove
61
+ declared data when its category is denied. See
62
+ [clear on revocation](../../integrations/clear-on-revocation.md) for configuration
63
+ and browser limits.
@@ -37,6 +37,16 @@ Colors, radii, shadows, typography, and motion come from `--c15t-*` custom
37
37
  properties. Set them on the provider through `theme`, or override the
38
38
  variables directly in your CSS.
39
39
 
40
+ React and Next.js render the theme stylesheet in the server HTML before
41
+ the banner. Pass the same theme on the server and the first client render
42
+ so your custom tokens apply before hydration. The provider's `nonce`
43
+ option also applies to this stylesheet for Content Security Policy.
44
+
45
+ `colorScheme: 'dark'` applies dark tokens in the server HTML.
46
+ `colorScheme: 'system'` uses a CSS media query to follow the visitor's
47
+ preference before hydration. If your app manages dark mode through a
48
+ root class instead, include that class in the server HTML too.
49
+
40
50
  ```tsx
41
51
  import { ConsentProvider, defineTheme } from 'c15t/react';
42
52
 
@@ -30,6 +30,18 @@ in your application while consent writes still go to Inth. Regular backend
30
30
  browser setup. See [data fetching and transports](./data-fetching.md) for
31
31
  the comparison, including custom transports and offline mode.
32
32
 
33
+ Manifest resolution removes the per-visitor `/init` request, and with it the
34
+ backend's only count of visitors. The server adapters replace it with a session
35
+ report: after each resolution, on a server-rendered page or the same-origin
36
+ init route, the host posts a small report to the backend's `POST /sessions`,
37
+ server-to-server and detached from the response. The browser makes no request
38
+ and the report stores no identity; the visitor's IP and user agent travel as
39
+ forwarded headers under the backend's usual IP handling. Each report is one
40
+ resolution; a page view can produce a `render` and a `route` report, and the
41
+ consuming side groups them into sessions by address and user agent within a
42
+ window. Static output resolves in the browser and sends none. Set `reportSessions: false` on an adapter to turn
43
+ it off.
44
+
33
45
  ## Match initialization to your application output
34
46
 
35
47
  | Application output | Initial state | Required setup |
@@ -31,6 +31,11 @@ Replace the example URL and implement the vendor's initialization. This is a
31
31
  loader template, not a functioning analytics SDK. The script stays blocked
32
32
  while measurement permission is denied.
33
33
 
34
+ Add `vendor: 'example-analytics'` and declare the vendor in the runtime's
35
+ `vendors` option or in the backend manifest when visitors should be able to
36
+ turn this vendor off inside a granted category. See
37
+ [granular consent](./granular-consent.md).
38
+
34
39
  ## Define revocation deliberately
35
40
 
36
41
  `onConsentChange` receives current permission information. Use it to update the
@@ -0,0 +1,167 @@
1
+ ---
2
+ title: Clear on revocation
3
+ description: Remove configured first-party cookies and Web Storage keys when
4
+ their consent category is denied.
5
+ group: integrations
6
+ ---
7
+
8
+ ## Configure cleanup
9
+
10
+ Add `clearOnRevocation` to your provider or runtime options. Declare only the
11
+ data owned by each optional category:
12
+
13
+ ```ts
14
+ import { hosted, type ClearOnRevocationConfig } from 'c15t';
15
+ import { createConsentRuntime } from 'c15t/runtime';
16
+
17
+ const clearOnRevocation = {
18
+ measurement: {
19
+ cookies: ['_ga', '_ga_*'],
20
+ localStorage: ['analytics:*'],
21
+ },
22
+ marketing: {
23
+ cookies: ['_fbp'],
24
+ sessionStorage: ['campaign-id'],
25
+ },
26
+ } satisfies ClearOnRevocationConfig;
27
+
28
+ const runtime = createConsentRuntime({
29
+ mode: hosted({ url: '/api/c15t' }),
30
+ clearOnRevocation,
31
+ });
32
+
33
+ // Start in the browser after mount. The endpoint must serve your c15t backend.
34
+ runtime.start();
35
+
36
+ // Call runtime.dispose() when the app no longer needs consent management.
37
+ ```
38
+
39
+ For React, pass the same configuration through `ConsentProvider.options`:
40
+
41
+ ```tsx
42
+ import type { ReactNode } from 'react';
43
+ import { ConsentProvider, hosted } from 'c15t/react';
44
+
45
+ const mode = hosted({ url: '/api/c15t' });
46
+
47
+ export function Consent({ children }: { children: ReactNode }) {
48
+ return (
49
+ <ConsentProvider
50
+ options={{
51
+ mode,
52
+ clearOnRevocation: {
53
+ measurement: { cookies: ['_ga', '_ga_*'] },
54
+ },
55
+ }}
56
+ >
57
+ {children}
58
+ </ConsentProvider>
59
+ );
60
+ }
61
+ ```
62
+
63
+ The same option is available in Next.js and TanStack Start `ConsentRoot` props, Vue and
64
+ Nuxt configuration, Svelte providers, and Astro integration options. Solid and
65
+ other headless integrations can use `createConsentRuntime` as shown above.
66
+ Every adapter uses the same cleanup module.
67
+
68
+ Omitting `clearOnRevocation` leaves cleanup disabled. The provider option is
69
+ initial-only. Remount the provider to replace its cleanup configuration. For
70
+ a shared runtime, configure the runtime owner rather than a borrowing provider.
71
+
72
+ ## Matching names and cookie scopes
73
+
74
+ Use an exact string or a nonempty prefix followed by `*`. `_ga_*` matches
75
+ `_ga_ABC123`; it does not match `_ga`. Regular expressions, wildcards in other
76
+ positions, and a bare `*` are unsupported. Web Storage keys may contain spaces,
77
+ Unicode, and punctuation. Cookie names use their raw spelling, without URL
78
+ decoding.
79
+
80
+ Cookies can share a name while having different domains or paths. Cleanup
81
+ tries the current host and its parent domains, and the current path and its
82
+ ancestors. To target a specific scope, use an object:
83
+
84
+ ```ts
85
+ const clearOnRevocation = {
86
+ measurement: {
87
+ cookies: [
88
+ { name: 'analytics-id', domain: 'example.com', path: '/' },
89
+ { name: 'checkout-metrics', domain: '', path: '/checkout' },
90
+ { name: 'partitioned-metrics', partitioned: true },
91
+ ],
92
+ },
93
+ };
94
+ ```
95
+
96
+ An empty `domain` means host-only. Explicit domains and paths replace the
97
+ automatic attempts for that field. Exact names can be deleted at a configured
98
+ path even when the current page cannot read that cookie. Prefix matching can
99
+ only discover cookie names visible to the current page, so use an exact name
100
+ for a cookie on another path.
101
+
102
+ Partitioned cookies require `partitioned: true`; ordinary targets remove
103
+ unpartitioned cookies. Cookie deletion preserves the browser's `__Secure-`
104
+ and `__Host-` prefix requirements. c15t protects its own consent, notice,
105
+ privacy, pending-save, and IAB consent records in cookies and localStorage,
106
+ including configured custom storage keys, even if your patterns match them.
107
+ c15t does not store consent in sessionStorage, so targeted entries there are
108
+ removed even when their names match consent storage keys.
109
+
110
+ ## When cleanup runs
111
+
112
+ The runtime attaches cleanup after persistence and the script loader. Cleanup
113
+ waits while the policy is pending. On the first settled snapshot, it removes
114
+ configured data for every denied category. This includes a new opt-in visitor
115
+ who has not made a choice and a returning visitor whose permission expired.
116
+
117
+ After that first pass, cleanup runs when a category changes from allowed to
118
+ denied. Saving a refusal, expiry, a policy change, Global Privacy Control,
119
+ or synchronized records can cause that transition. Under an opt-out policy,
120
+ an expired grant that remains effectively allowed does not trigger deletion.
121
+ The `necessary` category cannot be configured for cleanup.
122
+
123
+ Cleanup keeps waiting if a failed initial request leaves the policy pending.
124
+ If a previously settled policy falls back to denial after an initialization
125
+ failure, cleanup removes its configured data. A later successful retry cannot
126
+ restore deleted data.
127
+
128
+ Runtime construction, server rendering, draft checkbox edits, opening the
129
+ dialog, and disposal do not clear data. Cleanup does not poll storage or repeat
130
+ on unrelated UI updates.
131
+
132
+ ## Use an existing kernel
133
+
134
+ For a manually assembled integration, attach the module in the browser after
135
+ persistence hydration and script-loader setup:
136
+
137
+ ```ts
138
+ import { createClearOnRevocation } from 'c15t/modules/clear-on-revocation';
139
+
140
+ const cleanup = createClearOnRevocation({
141
+ kernel,
142
+ config: { measurement: { cookies: ['_ga', '_ga_*'] } },
143
+ storageConfig,
144
+ });
145
+
146
+ // Stop observing consent when this integration is torn down.
147
+ cleanup.dispose();
148
+ ```
149
+
150
+ Here `kernel` is your existing consent kernel. Pass the same `storageConfig`
151
+ used by persistence so cleanup protects custom record keys. Attaching the
152
+ module can immediately clear denied categories if the policy is already
153
+ settled. Do not also attach it when your provider or runtime owns cleanup.
154
+
155
+ ## Browser limits
156
+
157
+ Cleanup can remove JavaScript-accessible first-party cookies and keys in the
158
+ current origin's `localStorage` and `sessionStorage`. It cannot remove
159
+ `HttpOnly` cookies or another origin's data. Keep `HttpOnly` protections and
160
+ use your server to expire cookies that require server access. Cookie deletion
161
+ must match the cookie's scope. See the
162
+ [browser cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies).
163
+
164
+ Browser restrictions can prevent reads or deletions. Cleanup failures do not
165
+ block consent updates. A running SDK may write data again after a sweep, so
166
+ keep script gating and the integration's consent-change or teardown behavior
167
+ configured. Deleting a script element cannot undo JavaScript it already ran.