@c15t/react 3.0.0-alpha.1 → 3.0.0-alpha.11

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 (413) hide show
  1. package/AGENTS.md +97 -70
  2. package/README.md +21 -17
  3. package/SKILL.md +34 -0
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/chunk-warming.js +1 -1
  6. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  7. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  8. package/dist/component-hooks/use-panel-trigger.js +1 -1
  9. package/dist/components/consent-gate/atoms.js +1 -0
  10. package/dist/components/consent-gate/consent-gate.js +2 -0
  11. package/dist/components/consent-gate/frame.js +2 -0
  12. package/dist/components/consent-gate/index.js +2 -0
  13. package/dist/components/consent-preferences-link/consent-preferences-link.js +1 -1
  14. package/dist/components/iab-panel/atoms/card.js +1 -1
  15. package/dist/components/iab-panel/atoms/feature-item.js +2 -0
  16. package/dist/components/iab-panel/atoms/header.js +1 -1
  17. package/dist/components/iab-panel/atoms/purpose-item.js +1 -1
  18. package/dist/components/iab-panel/atoms/root.js +1 -1
  19. package/dist/components/iab-panel/atoms/stack-item.js +1 -1
  20. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  21. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  22. package/dist/components/iab-panel/iab-panel.js +1 -1
  23. package/dist/components/iab-panel/index.js +1 -1
  24. package/dist/components/iab-prompt/atoms/overlay.js +1 -1
  25. package/dist/components/iab-prompt/atoms/root.js +1 -1
  26. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  27. package/dist/components/panel/atoms/card.js +1 -1
  28. package/dist/components/panel/atoms/overlay.js +1 -1
  29. package/dist/components/panel/atoms/root.js +1 -1
  30. package/dist/components/panel/panel.js +1 -1
  31. package/dist/components/panel-trigger/atoms/button.js +1 -1
  32. package/dist/components/panel-trigger/atoms/icon.js +1 -1
  33. package/dist/components/panel-trigger/atoms/root.js +1 -1
  34. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  35. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
  36. package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
  37. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  38. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  39. package/dist/components/preferences/atoms/accordion.js +1 -1
  40. package/dist/components/preferences/atoms/root.js +1 -1
  41. package/dist/components/preferences/atoms/vendor-list.js +1 -0
  42. package/dist/components/preferences/index.js +1 -1
  43. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  44. package/dist/components/prompt/atoms/overlay.js +1 -1
  45. package/dist/components/prompt/atoms/root.js +1 -1
  46. package/dist/components/prompt/components.js +1 -1
  47. package/dist/components/prompt/policy-actions.js +1 -1
  48. package/dist/components/prompt/prompt.js +1 -1
  49. package/dist/components/shared/dialog-sheets.js +1 -0
  50. package/dist/components/shared/first-paint-sheets.js +1 -0
  51. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  52. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  53. package/dist/components/shared/libs/compose-refs.js +1 -0
  54. package/dist/components/shared/libs/slot.js +1 -1
  55. package/dist/components/shared/primitives/button.js +1 -1
  56. package/dist/components/shared/primitives/legal-links/index.js +1 -1
  57. package/dist/components/shared/streamed-surface.js +2 -0
  58. package/dist/components/shared/surface-styles.js +2 -0
  59. package/dist/components/shared/ui/dialog/dialog.js +1 -1
  60. package/dist/components/shared/ui/icon.js +1 -1
  61. package/dist/components/shared/ui/logo.js +1 -1
  62. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  63. package/dist/consent-gate.js +2 -0
  64. package/dist/consent-theme.js +1 -0
  65. package/dist/context/consent-tracking-context.js +1 -1
  66. package/dist/context/index.js +1 -1
  67. package/dist/context.js +1 -1
  68. package/dist/devtools-launcher.js +2 -0
  69. package/dist/devtools.js +1 -1
  70. package/dist/draft-context.js +2 -0
  71. package/dist/draft.js +1 -1
  72. package/dist/external-iab-context.js +1 -1
  73. package/dist/gpp.js +2 -0
  74. package/dist/headless.js +1 -1
  75. package/dist/hooks/use-color-scheme.js +1 -1
  76. package/dist/hooks/use-committed-ref.js +2 -0
  77. package/dist/hooks/use-focus-trap.js +1 -1
  78. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  79. package/dist/hooks/use-iab-policy-guard.js +1 -0
  80. package/dist/hooks/use-late-entry.js +2 -0
  81. package/dist/hooks.js +1 -1
  82. package/dist/iab/styles.tw3.css +5 -45
  83. package/dist/iab-context.js +1 -1
  84. package/dist/iab-dialog-deferred.js +2 -0
  85. package/dist/iab.js +1 -1
  86. package/dist/index-parts/consent-banner.js +1 -0
  87. package/dist/index-parts/consent-dialog-link.js +1 -0
  88. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  89. package/dist/index-parts/consent-dialog.js +1 -0
  90. package/dist/index-parts/consent-gate.js +1 -0
  91. package/dist/index-parts/core.js +1 -0
  92. package/dist/index-parts/draft.js +1 -0
  93. package/dist/index-parts/frame.js +1 -0
  94. package/dist/index-parts/hooks.js +1 -0
  95. package/dist/index-parts/hosted.js +1 -0
  96. package/dist/index-parts/module-hooks.js +1 -0
  97. package/dist/index-parts/offline.js +1 -0
  98. package/dist/index-parts/presets.js +1 -0
  99. package/dist/index-parts/provider.js +1 -0
  100. package/dist/index-parts/theme.js +1 -0
  101. package/dist/index-parts/translations.js +1 -0
  102. package/dist/index.js +1 -1
  103. package/dist/kernel-selector.js +1 -0
  104. package/dist/modes.js +1 -0
  105. package/dist/module-hooks/iframe-blocker.js +1 -1
  106. package/dist/module-hooks/network-blocker.js +1 -1
  107. package/dist/module-hooks/network-hold.js +2 -0
  108. package/dist/module-hooks/persistence.js +1 -1
  109. package/dist/panel-link.js +1 -1
  110. package/dist/panel-trigger.js +1 -1
  111. package/dist/panel.js +1 -1
  112. package/dist/postcss-tailwind3.js +1 -0
  113. package/dist/preferences.js +1 -2
  114. package/dist/provider.js +1 -1
  115. package/dist/server/fetch-ssr-data.js +1 -1
  116. package/dist/server/headers.js +1 -1
  117. package/dist/server/normalize-url.js +1 -1
  118. package/dist/styles.tw3.css +33 -51
  119. package/dist/transports/hosted.js +1 -0
  120. package/dist/transports/manifest.js +1 -0
  121. package/dist/transports/offline.js +1 -1
  122. package/dist/utils/deferred-module.js +2 -0
  123. package/dist/utils/merge-slot-props.js +1 -1
  124. package/dist/utils/theme-utils.js +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist-types/aggregate-components.d.ts +2 -2
  127. package/dist-types/chunk-warming.d.ts +42 -8
  128. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +5 -5
  129. package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
  130. package/dist-types/components/consent-gate/atoms.d.ts +11 -0
  131. package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
  132. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  133. package/dist-types/components/consent-gate/index.d.ts +25 -0
  134. package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
  135. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  136. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  137. package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
  138. package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
  139. package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
  140. package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
  141. package/dist-types/components/iab-panel/iab-panel.d.ts +3 -3
  142. package/dist-types/components/iab-panel/index.d.ts +29 -26
  143. package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
  144. package/dist-types/components/iab-prompt/iab-prompt.d.ts +3 -3
  145. package/dist-types/components/iab-prompt/index.d.ts +19 -19
  146. package/dist-types/components/panel/atoms/card.d.ts +5 -4
  147. package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
  148. package/dist-types/components/panel/index.d.ts +8 -8
  149. package/dist-types/components/panel/panel.d.ts +3 -3
  150. package/dist-types/components/panel-link/index.d.ts +1 -1
  151. package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
  152. package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
  153. package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
  154. package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
  155. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +25 -0
  156. package/dist-types/components/panel-trigger/index.d.ts +4 -4
  157. package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
  158. package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -17
  159. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  160. package/dist-types/components/panel-trigger/types.d.ts +1 -1
  161. package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
  162. package/dist-types/components/preferences/atoms/button.d.ts +1 -1
  163. package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
  164. package/dist-types/components/preferences/atoms/root.d.ts +1 -1
  165. package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
  166. package/dist-types/components/preferences/index.d.ts +14 -12
  167. package/dist-types/components/preferences/policy-actions.d.ts +2 -2
  168. package/dist-types/components/preferences/preferences.d.ts +1 -1
  169. package/dist-types/components/preferences/types.d.ts +1 -1
  170. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  171. package/dist-types/components/prompt/atoms/root.d.ts +4 -0
  172. package/dist-types/components/prompt/components.d.ts +9 -3
  173. package/dist-types/components/prompt/index.d.ts +12 -12
  174. package/dist-types/components/prompt/policy-actions.d.ts +2 -2
  175. package/dist-types/components/prompt/prompt.d.ts +5 -1
  176. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  177. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  178. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  179. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  180. package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
  181. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  182. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  183. package/dist-types/components/shared/primitives/button.d.ts +2 -2
  184. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  185. package/dist-types/components/shared/streamed-surface.d.ts +23 -0
  186. package/dist-types/components/shared/surface-styles.d.ts +60 -0
  187. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  188. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  189. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  190. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  191. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  192. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  193. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  194. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  195. package/dist-types/components/shared/ui/icon.d.ts +2 -1
  196. package/dist-types/components/shared/ui/logo.d.ts +0 -10
  197. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  198. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +13 -0
  199. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  200. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  201. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  202. package/dist-types/consent-gate.d.ts +1 -0
  203. package/dist-types/consent-theme.d.ts +38 -0
  204. package/dist-types/context/consent-tracking-context.d.ts +10 -0
  205. package/dist-types/context/index.d.ts +3 -3
  206. package/dist-types/context/theme-context.d.ts +3 -3
  207. package/dist-types/context.d.ts +25 -1
  208. package/dist-types/devtools-launcher.d.ts +29 -0
  209. package/dist-types/devtools.d.ts +13 -2
  210. package/dist-types/draft-context.d.ts +31 -0
  211. package/dist-types/draft.d.ts +81 -9
  212. package/dist-types/gpp.d.ts +27 -0
  213. package/dist-types/headless.d.ts +6 -7
  214. package/dist-types/hooks/use-committed-ref.d.ts +24 -0
  215. package/dist-types/hooks/use-focus-trap.d.ts +3 -1
  216. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  217. package/dist-types/hooks/use-iab-manager.d.ts +5 -5
  218. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  219. package/dist-types/hooks/use-late-entry.d.ts +14 -0
  220. package/dist-types/hooks/use-theme.d.ts +1 -1
  221. package/dist-types/hooks.d.ts +106 -11
  222. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  223. package/dist-types/iab.d.ts +8 -7
  224. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  225. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  226. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  227. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  228. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  229. package/dist-types/index-parts/core.d.ts +7 -0
  230. package/dist-types/index-parts/draft.d.ts +7 -0
  231. package/dist-types/index-parts/frame.d.ts +7 -0
  232. package/dist-types/index-parts/hooks.d.ts +7 -0
  233. package/dist-types/index-parts/hosted.d.ts +7 -0
  234. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  235. package/dist-types/index-parts/offline.d.ts +7 -0
  236. package/dist-types/index-parts/presets.d.ts +7 -0
  237. package/dist-types/index-parts/provider.d.ts +7 -0
  238. package/dist-types/index-parts/theme.d.ts +8 -0
  239. package/dist-types/index-parts/translations.d.ts +7 -0
  240. package/dist-types/index.d.ts +32 -28
  241. package/dist-types/kernel-selector.d.ts +68 -0
  242. package/dist-types/modes.d.ts +26 -0
  243. package/dist-types/module-hooks/iframe-blocker.d.ts +33 -1
  244. package/dist-types/module-hooks/network-blocker.d.ts +23 -0
  245. package/dist-types/module-hooks/network-hold.d.ts +47 -0
  246. package/dist-types/module-hooks.d.ts +4 -4
  247. package/dist-types/panel-link.d.ts +1 -1
  248. package/dist-types/panel-trigger.d.ts +1 -1
  249. package/dist-types/panel.d.ts +13 -1
  250. package/dist-types/postcss-tailwind3.d.ts +21 -0
  251. package/dist-types/preferences.d.ts +12 -1
  252. package/dist-types/primitives/accordion.d.ts +1 -1
  253. package/dist-types/primitives/button.d.ts +1 -1
  254. package/dist-types/primitives/collapsible.d.ts +1 -1
  255. package/dist-types/primitives/dialog.d.ts +1 -1
  256. package/dist-types/primitives/preference-item.d.ts +1 -1
  257. package/dist-types/primitives/switch.d.ts +1 -1
  258. package/dist-types/primitives/tabs.d.ts +1 -1
  259. package/dist-types/primitives.d.ts +7 -7
  260. package/dist-types/prompt.d.ts +1 -1
  261. package/dist-types/provider.d.ts +169 -28
  262. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  263. package/dist-types/server/headers.d.ts +14 -4
  264. package/dist-types/server/index.d.ts +5 -5
  265. package/dist-types/server/normalize-url.d.ts +14 -7
  266. package/dist-types/server/types.d.ts +12 -1
  267. package/dist-types/theme-provider.d.ts +2 -2
  268. package/dist-types/transports/hosted.d.ts +36 -0
  269. package/dist-types/transports/manifest.d.ts +48 -0
  270. package/dist-types/transports/offline.d.ts +7 -16
  271. package/dist-types/types/index.d.ts +3 -3
  272. package/dist-types/types/manager.d.ts +1 -1
  273. package/dist-types/types/theme/index.d.ts +1 -1
  274. package/dist-types/ui-config-context.d.ts +14 -2
  275. package/dist-types/utils/deferred-module.d.ts +13 -0
  276. package/dist-types/utils/index.d.ts +4 -4
  277. package/dist-types/utils/merge-slot-props.d.ts +17 -3
  278. package/dist-types/utils/merge-styles.d.ts +1 -1
  279. package/dist-types/utils/theme-utils.d.ts +18 -3
  280. package/dist-types/version.d.ts +1 -1
  281. package/docs/README.md +97 -70
  282. package/docs/assets/v3/bottom-bar.png +0 -0
  283. package/docs/assets/v3/brand-card.png +0 -0
  284. package/docs/assets/v3/brand-preferences.png +0 -0
  285. package/docs/assets/v3/choice-wall.png +0 -0
  286. package/docs/assets/v3/headless-bar-html.png +0 -0
  287. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  288. package/docs/assets/v3/headless-bar.png +0 -0
  289. package/docs/assets/v3/slim-bar.png +0 -0
  290. package/docs/concepts/choose-your-setup.md +141 -0
  291. package/docs/concepts/consent-categories.md +84 -0
  292. package/docs/concepts/consent-state.md +421 -0
  293. package/docs/concepts/data-fetching.md +284 -0
  294. package/docs/concepts/how-consent-works.md +123 -0
  295. package/docs/concepts/modes.md +281 -0
  296. package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +8 -60
  297. package/docs/customization/class-names.md +202 -0
  298. package/docs/customization/dark-mode.md +157 -0
  299. package/docs/customization/motion.md +128 -0
  300. package/docs/customization/overview.md +68 -33
  301. package/docs/customization/recipes.md +838 -47
  302. package/docs/customization/slots.md +217 -35
  303. package/docs/customization/stylesheets.md +248 -0
  304. package/docs/customization/tailwind.md +863 -0
  305. package/docs/customization/tokens.md +167 -36
  306. package/docs/customization/translations.md +87 -3
  307. package/docs/frameworks/react/callbacks.md +118 -0
  308. package/docs/frameworks/react/clear-on-revocation.md +106 -0
  309. package/docs/frameworks/react/components/consent-banner.md +48 -27
  310. package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
  311. package/docs/frameworks/react/components/consent-dialog-trigger.md +86 -76
  312. package/docs/frameworks/react/components/consent-dialog.md +111 -58
  313. package/docs/frameworks/react/components/{frame.md → consent-gate.md} +70 -49
  314. package/docs/frameworks/react/components/consent-provider.md +143 -0
  315. package/docs/frameworks/react/components/consent-widget.md +69 -24
  316. package/docs/frameworks/react/components/dev-tools.md +76 -85
  317. package/docs/frameworks/react/components.md +113 -0
  318. package/docs/frameworks/react/compose.md +354 -0
  319. package/docs/frameworks/react/content-security-policy.md +149 -0
  320. package/docs/frameworks/react/customize.md +258 -0
  321. package/docs/frameworks/react/embeds.md +89 -0
  322. package/docs/frameworks/react/gpp.md +163 -0
  323. package/docs/frameworks/react/headless.md +4 -4
  324. package/docs/frameworks/react/hooks.md +70 -0
  325. package/docs/frameworks/react/iab.md +278 -0
  326. package/docs/frameworks/react/network-blocker.md +140 -0
  327. package/docs/frameworks/react/quickstart.md +171 -107
  328. package/docs/frameworks/react/rendering.md +285 -0
  329. package/docs/frameworks/react/scripts.md +114 -0
  330. package/docs/frameworks/react/translations.md +155 -0
  331. package/docs/frameworks/react/troubleshooting.md +189 -61
  332. package/docs/frameworks/react/upgrade-v3.md +652 -0
  333. package/docs/frameworks/react/vendor-consent.md +188 -0
  334. package/docs/guides/banner-experiments.md +688 -0
  335. package/docs/guides/troubleshooting.md +192 -54
  336. package/docs/guides/verify-consent.md +96 -49
  337. package/docs/integrations/adobe-analytics.md +177 -183
  338. package/docs/integrations/ahrefs-analytics.md +162 -178
  339. package/docs/integrations/amplitude.md +172 -180
  340. package/docs/integrations/building-integrations.md +137 -33
  341. package/docs/integrations/clearbit.md +164 -178
  342. package/docs/integrations/cloudflare-web-analytics.md +166 -180
  343. package/docs/integrations/cloudflare-zaraz.md +333 -0
  344. package/docs/integrations/crisp.md +174 -182
  345. package/docs/integrations/databuddy.md +167 -197
  346. package/docs/integrations/fathom-analytics.md +168 -180
  347. package/docs/integrations/front-chat.md +308 -0
  348. package/docs/integrations/google-maps.md +142 -108
  349. package/docs/integrations/google-tag-manager.md +258 -185
  350. package/docs/integrations/google-tag.md +226 -182
  351. package/docs/integrations/heap.md +173 -179
  352. package/docs/integrations/hightouch.md +171 -181
  353. package/docs/integrations/hotjar.md +169 -179
  354. package/docs/integrations/intercom.md +193 -177
  355. package/docs/integrations/klaviyo.md +473 -0
  356. package/docs/integrations/linkedin-insights.md +184 -174
  357. package/docs/integrations/logrocket.md +170 -180
  358. package/docs/integrations/matomo-analytics.md +198 -202
  359. package/docs/integrations/meta-pixel.md +198 -174
  360. package/docs/integrations/microsoft-clarity.md +173 -179
  361. package/docs/integrations/microsoft-uet.md +158 -178
  362. package/docs/integrations/mixpanel-analytics.md +165 -184
  363. package/docs/integrations/one-dollar-stats.md +292 -0
  364. package/docs/integrations/openai-pixel.md +214 -325
  365. package/docs/integrations/overview.md +153 -83
  366. package/docs/integrations/pinterest-tag.md +315 -0
  367. package/docs/integrations/pirsch.md +179 -183
  368. package/docs/integrations/plausible-analytics.md +182 -182
  369. package/docs/integrations/posthog.md +323 -264
  370. package/docs/integrations/promptwatch.md +164 -178
  371. package/docs/integrations/reddit-pixel.md +195 -181
  372. package/docs/integrations/rudderstack.md +211 -210
  373. package/docs/integrations/rybbit-analytics.md +181 -184
  374. package/docs/integrations/segment.md +192 -178
  375. package/docs/integrations/sentry.md +402 -0
  376. package/docs/integrations/snapchat-pixel.md +196 -180
  377. package/docs/integrations/tiktok-pixel.md +181 -174
  378. package/docs/integrations/umami-analytics.md +173 -182
  379. package/docs/integrations/vercel-analytics.md +176 -181
  380. package/docs/integrations/vercel-speed-insights.md +306 -0
  381. package/docs/integrations/x-pixel.md +186 -174
  382. package/docs/integrations/youtube.md +150 -111
  383. package/package.json +48 -17
  384. package/dist/component-hooks/use-manager.js +0 -2
  385. package/dist/components/frame/atoms.js +0 -1
  386. package/dist/components/frame/frame.js +0 -2
  387. package/dist/components/frame/index.js +0 -2
  388. package/dist/frame.js +0 -2
  389. package/dist/ui-save.js +0 -1
  390. package/dist-types/component-hooks/use-manager.d.ts +0 -40
  391. package/dist-types/components/frame/atoms.d.ts +0 -11
  392. package/dist-types/components/frame/frame.d.ts +0 -2
  393. package/dist-types/components/frame/index.d.ts +0 -24
  394. package/dist-types/frame.d.ts +0 -1
  395. package/dist-types/ui-save.d.ts +0 -5
  396. package/docs/assets/v3/brand-bar.png +0 -0
  397. package/docs/assets/v3/mobile-card.png +0 -0
  398. package/docs/assets/v3/preferences.png +0 -0
  399. package/docs/frameworks/react/components/consent-manager-provider.md +0 -71
  400. package/docs/frameworks/react/concepts/consent-categories.md +0 -33
  401. package/docs/frameworks/react/hooks/use-consent-manager/overview.md +0 -58
  402. package/docs/frameworks/react/iab/overview.md +0 -53
  403. package/docs/frameworks/react/script-loader.md +0 -63
  404. package/docs/frameworks/react/styling/overview.md +0 -217
  405. package/docs/guides/consent-state.md +0 -60
  406. package/docs/guides/data-fetching.md +0 -163
  407. package/docs/guides/deployment-modes.md +0 -63
  408. package/docs/integrations/clear-on-revocation.md +0 -167
  409. package/docs/upgrade-v3.md +0 -334
  410. package/readme.json +0 -48
  411. package/src/iab/styles.css +0 -11
  412. package/src/styles.css +0 -10
  413. /package/dist/components/{frame → consent-gate}/types.js +0 -0
@@ -1,14 +1,14 @@
1
1
  ---
2
- title: Frame
3
- description: Gate a YouTube or map iframe behind consent with Frame in React,
4
- showing a placeholder that opens the preference center until the category is
5
- allowed.
2
+ title: ConsentGate
3
+ description: Gate a YouTube or map iframe behind consent with ConsentGate in
4
+ React, showing a placeholder that opens the preference center until the
5
+ category is allowed.
6
6
  group: frameworks
7
7
  ---
8
8
 
9
9
  ## Gate an embed behind consent
10
10
 
11
- `Frame` mounts its children only while the effective permission for one
11
+ `ConsentGate` mounts its children only while the effective permission for one
12
12
  consent category is granted, and shows a placeholder otherwise. Wrap any
13
13
  iframe or third-party widget that would set cookies or contact a vendor on
14
14
  load. Render it anywhere inside the `ConsentProvider` from your
@@ -16,30 +16,30 @@ load. Render it anywhere inside the `ConsentProvider` from your
16
16
  placeholder button opens.
17
17
 
18
18
  ```tsx title="src/product-video.tsx"
19
- import { Frame } from 'c15t/react';
19
+ import { ConsentGate } from 'c15t/react';
20
20
 
21
21
  export function ProductVideo() {
22
- return (
23
- <Frame category="marketing" className="video-frame">
24
- <iframe
25
- src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
26
- title="Product tour"
27
- loading="lazy"
28
- allowFullScreen
29
- style={{ width: '100%', aspectRatio: '16 / 9', border: 0 }}
30
- />
31
- </Frame>
32
- );
22
+ return (
23
+ <ConsentGate category="marketing" className="video-frame">
24
+ <iframe
25
+ src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
26
+ title="Product tour"
27
+ loading="lazy"
28
+ allowFullScreen
29
+ style={{ width: '100%', aspectRatio: '16 / 9', border: 0 }}
30
+ />
31
+ </ConsentGate>
32
+ );
33
33
  }
34
34
  ```
35
35
 
36
36
  `marketing` must be in your policy scope. If you set a non-empty
37
37
  `options.consentCategories` list on `ConsentProvider`, include `marketing`
38
- there too so the preference center can display and save it. `Frame` does
38
+ there too so the preference center can display and save it. `ConsentGate` does
39
39
  not add categories to that list.
40
40
 
41
41
  The placeholder names the category using its title from your translations. Set `className` or `style`
42
- on `Frame` to reserve the embed's space, so the page does not shift when the
42
+ on `ConsentGate` to reserve the embed's space, so the page does not shift when the
43
43
  placeholder is replaced.
44
44
 
45
45
  ## Props
@@ -51,51 +51,54 @@ placeholder is replaced.
51
51
  | `placeholder` | `ReactNode` | built-in placeholder | Replaces the built-in title and button while the category is not allowed. Falsy values such as `null`, `false`, `0` and an empty string use the built-in placeholder. Pass an empty fragment, `<></>`, to render no placeholder content. |
52
52
 
53
53
  Other `div` attributes such as `className`, `style` and `ref` apply to the
54
- wrapper element that stays in the page in both states. `FrameProps` also
54
+ wrapper element that stays in the page in both states. `ConsentGateProps` also
55
55
  declares `noStyle` and `theme`, but the component does not apply them yet;
56
- style the wrapper with `className` and theme the placeholder through the
57
- provider `theme` option.
56
+ style the wrapper with `className`, and style the built-in placeholder
57
+ through its parts in the provider options.
58
58
 
59
59
  ## Behavior
60
60
 
61
- `Frame` renders a `div` wrapper. Inside it, when the effective permission for
61
+ `ConsentGate` renders a `div` wrapper. Inside it, when the effective permission for
62
62
  `category` is granted, it renders the children; otherwise it renders the
63
63
  placeholder. Permission is the same value `useConsent(category)` returns,
64
64
  so it can be granted under an opt-out rule before the visitor records a
65
65
  choice, and a recorded grant can be overridden by a privacy signal.
66
66
 
67
67
  While effective permission is denied, the children are absent from the DOM,
68
- so an iframe or a third-party widget inside `Frame` sends no requests. When the visitor later revokes it, the
69
- children unmount and the embed disappears. `Frame` does not depend on the
68
+ so an iframe or a third-party widget inside `ConsentGate` sends no requests. When the visitor later revokes it, the
69
+ children unmount and the embed disappears. `ConsentGate` does not depend on the
70
70
  network blocker or the iframe blocker modules; use those for markup you
71
71
  cannot wrap in a component.
72
72
 
73
- The built-in placeholder shows the title `frame.title`, "Accept {category}
74
- consent to view this content.", with `{category}` replaced by
75
- `consentTypes.<category>.title`, and a button labelled `frame.actionButton`,
76
- "Enable {category} consent". The button opens the preference center; it
73
+ The built-in placeholder shows the title `consentGate.title`,
74
+ `Accept {category} consent to view this content.`, with `{category}`
75
+ replaced by `consentTypes.<category>.title`, and a button labelled
76
+ `consentGate.actionButton`, `Enable {category} consent`. The button opens the
77
+ preference center; it
77
78
  does not grant the category by itself, because the visitor still has to
78
79
  save. Mount a `ConsentDialog` in the same provider so the button has
79
80
  something to open.
80
81
 
81
82
  Under a policy with `scopeMode: 'strict'`, a category outside the rule's
82
- scope cannot be granted. The built-in placeholder then shows `frame.policyBlocked`,
83
+ scope cannot be granted. The built-in placeholder then shows `consentGate.policyBlocked`,
83
84
  "This content is unavailable under your region's consent policy.", without
84
85
  the button, and a stored grant for that category stays blocked.
85
86
 
86
87
  When the provider starts from a snapshot prefetched on the server, the
87
- server renders the children or placeholder according to effective permission.
88
- A grant in the request cookie allows the embed only when policy and privacy
89
- signals permit it. Browser privacy signals detected during hydration can
88
+ server renders the placeholder for a denied category. For a granted
89
+ category it renders the empty wrapper, and the children mount once
90
+ hydration completes, so an iframe loads once even when the page streams
91
+ inside a `Suspense` boundary. A grant in the request cookie allows the
92
+ embed only when policy and privacy signals permit it. Browser privacy signals detected during hydration can
90
93
  withdraw that permission. In a browser-only setup the
91
94
  provider has no permission until it resolves policy on the client, so
92
- `Frame` shows the placeholder first and swaps in the embed after resolution
95
+ `ConsentGate` shows the placeholder first and swaps in the embed after resolution
93
96
  when the category is already granted.
94
97
 
95
- `Frame` is one component on this page; the per-vendor embed guides for
98
+ `ConsentGate` is one component on this page; the per-vendor embed guides for
96
99
  [YouTube](../../../integrations/youtube.md) and
97
100
  [Google Maps](../../../integrations/google-maps.md) show a complete embed
98
- configuration with sizing, titles and the same `Frame` usage across
101
+ configuration with sizing, titles and the same `ConsentGate` usage across
99
102
  frameworks.
100
103
 
101
104
  To read the same permission in your own components, use `useConsent` from
@@ -104,34 +107,50 @@ also import from there.
104
107
 
105
108
  ## Composition
106
109
 
107
- The placeholder parts are available as `Frame.Root`, `Frame.Title` and
108
- `Frame.Button` for a custom placeholder that keeps the built-in copy and
109
- behavior. `Frame.Title` and `Frame.Button` accept `category` and fill in the
110
+ The placeholder parts are available as `ConsentGate.Root`, `ConsentGate.Title` and
111
+ `ConsentGate.Button` for a custom placeholder that keeps the built-in copy and
112
+ behavior. `ConsentGate.Title` and `ConsentGate.Button` accept `category` and fill in the
110
113
  translated text; pass children to either to replace it.
111
114
 
112
115
  ```tsx
113
- <Frame
116
+ <ConsentGate
114
117
  category="marketing"
115
118
  placeholder={
116
- <Frame.Root>
117
- <Frame.Title category="marketing" />
119
+ <ConsentGate.Root>
120
+ <ConsentGate.Title category="marketing" />
118
121
  <p>The video is also available on our channel.</p>
119
- <Frame.Button category="marketing">Choose cookies</Frame.Button>
120
- </Frame.Root>
122
+ <ConsentGate.Button category="marketing">Choose cookies</ConsentGate.Button>
123
+ </ConsentGate.Root>
121
124
  }
122
125
  >
123
126
  <iframe src="https://www.youtube-nocookie.com/embed/..." title="Product tour" />
124
- </Frame>
127
+ </ConsentGate>
125
128
  ```
126
129
 
127
- The built-in placeholder carries `data-testid="frame-placeholder"` and its
128
- button `data-testid="frame-open-dialog"`.
130
+ To restyle the built-in placeholder without replacing it, set its parts
131
+ under `components['consent-gate']` in the provider options. `root` is the
132
+ card, and `title` and `button` are its text and button. The `consentGate`, `consentGateTitle` and
133
+ `consentGateButton` theme slots reach the same parts. The button part
134
+ applies on top of `button.primary`.
135
+
136
+ ```tsx
137
+ components: {
138
+ 'consent-gate': {
139
+ root: { className: 'rounded-none' },
140
+ button: { className: 'font-semibold' },
141
+ },
142
+ },
143
+ ```
144
+
145
+ The built-in placeholder carries `data-testid="consent-gate-placeholder"`,
146
+ its title `data-testid="consent-gate-title"` and its button
147
+ `data-testid="consent-gate-button"`.
129
148
 
130
149
  ## Accessibility
131
150
 
132
151
  The placeholder is plain text and a `button`, so it is readable and
133
152
  operable without the embed. Give the iframe a descriptive `title`, and keep a
134
- transcript, address or link outside the `Frame` for visitors who decline
153
+ transcript, address or link outside the `ConsentGate` for visitors who decline
135
154
  the category. A denied category may be fixed by policy, so opening the
136
155
  preference center does not guarantee that the visitor can grant it.
137
156
 
@@ -142,7 +161,9 @@ center and is not restricted by policy or privacy signals such as GPC.
142
161
  Load the page with the category denied. The placeholder text names the
143
162
  category and the network panel shows no request to the embed's host. With a
144
163
  prefetched snapshot, the server HTML contains the placeholder for a denied
145
- category or the embed for a granted category. Browser-only initialization
164
+ category and neither placeholder nor embed for a granted category; the
165
+ embed appears after hydration and the network panel shows one request for
166
+ its document. Browser-only initialization
146
167
  shows the placeholder before policy resolves, then replaces it with the
147
168
  embed if the category is granted. After policy resolves, activate the
148
169
  placeholder button: the preference center opens. Turn the category on and
@@ -0,0 +1,143 @@
1
+ ---
2
+ title: ConsentProvider
3
+ description: Mount ConsentProvider in a React app and configure its backend,
4
+ scripts, storage, blocking and presentation options.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Mount one provider
9
+
10
+ `ConsentProvider` creates the consent runtime, loads the policy, stores the
11
+ visitor's choice and loads consent-gated scripts. Render one provider at the
12
+ root of your app and put the banner, dialog and a preferences link inside it.
13
+ The [quickstart](../quickstart.md) does this in
14
+ `src/consent.tsx`:
15
+
16
+ ```tsx title="src/consent.tsx"
17
+ import { posthog } from '@c15t/integrations/posthog';
18
+ import {
19
+ ConsentBanner,
20
+ ConsentDialog,
21
+ ConsentDialogLink,
22
+ ConsentProvider,
23
+ hosted,
24
+ } from 'c15t/react';
25
+ import type { ReactNode } from 'react';
26
+
27
+ const options = {
28
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
29
+ mode: hosted(),
30
+ scripts: [
31
+ posthog({
32
+ id: 'phc_your_project_key',
33
+ initOptions: { cookieless_mode: 'never' },
34
+ loadMode: 'after-consent',
35
+ }),
36
+ ],
37
+ };
38
+
39
+ export const Consent = ({ children }: { children: ReactNode }) => (
40
+ <ConsentProvider options={options}>
41
+ {children}
42
+ <ConsentBanner />
43
+ <ConsentDialog />
44
+ <footer>
45
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
46
+ </footer>
47
+ </ConsentProvider>
48
+ );
49
+ ```
50
+
51
+ `hosted()` from `c15t/react` asks the backend's `/init` for the visitor's
52
+ policy on each page load, and sends consent choices to the same backend. It
53
+ uses the backend URL `consentManifest()` read from `VITE_C15T_BACKEND_URL` or
54
+ `VITE_INTH_PROJECT_URL`. Set one to the backend URL from your Inth project. To
55
+ resolve a bundled policy in the browser instead, pass `manifest()` from
56
+ `c15t/react`; see
57
+ [rendering](../rendering.md#bundle-the-policy-at-build-time).
58
+ The Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
59
+ this provider and passes it server-resolved state.
60
+
61
+ ## Provider options
62
+
63
+ Keep the provider mounted across client navigation. It reads `mode`,
64
+ `prefetch`, `persistence`, `storageConfig`, `clearOnRevocation`, `i18n` and
65
+ `experiment` once, when it mounts. Outside production, changing `mode`,
66
+ `i18n`, `experiment`, `persistence` or `storageConfig` logs a warning. Remount
67
+ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
68
+ `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
69
+ `enabled` apply when they change.
70
+
71
+ | Option | Purpose |
72
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `offline()` for local policies, all from `c15t/react`, or `custom()` for your own transport. See [consent modes](../../../concepts/modes.md) |
74
+ | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
75
+ | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
76
+ | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
77
+ | `scriptLoader` | Options for the script loader module |
78
+ | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
79
+ | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
80
+ | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
81
+ | `storageConfig` | Storage key and cookie settings for the stored choice |
82
+ | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
83
+ | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
84
+ | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
85
+ | `overrides` | Force the country, region or language the policy is resolved for, for testing |
86
+ | `user` | Link consent records to a signed-in user |
87
+ | `callbacks` | Choice, permission and error events |
88
+ | `i18n` | Languages and message overrides |
89
+ | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
90
+ | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
91
+ | `components` | Slot attributes per component part |
92
+ | `presentation` | Banner and dialog shape, position and blocking |
93
+ | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
94
+ | `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows) |
95
+ | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
96
+ | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
97
+ | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
98
+ | `nonce` | Content Security Policy nonce for script elements c15t inserts |
99
+ | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
100
+
101
+ `enabled: false` grants categories and allows gated loading while suppressing
102
+ consent UI. It is not a way to fix failed initialization or hide a banner in
103
+ production.
104
+
105
+ ## Observe changes without inventing choices
106
+
107
+ `onChoiceRecorded` reports explicit accept, reject and save actions.
108
+ `onPermissionsChanged` also covers changes caused by policy, expiry or privacy
109
+ signals. Hydration and notice dismissal do not become explicit choices.
110
+
111
+ An externally owned runtime can be passed through `runtime`. Its owner must
112
+ start and dispose it. A provider borrowing that runtime must not initialize or
113
+ dispose a second copy. Passing a different `runtime` moves the provider and its
114
+ hooks to the new runtime's kernel and unsubscribes them from the previous one.
115
+ The previous runtime keeps running until its owner disposes it. Switch the
116
+ provider to another runtime, or unmount it, before disposing the runtime it
117
+ renders. Changing between a borrowed runtime and a provider-created one still
118
+ requires remounting the provider. IAB actions taken before its handle is ready
119
+ wait for that handle. If the provider unmounts or switches runtimes first, a
120
+ pending `save()` rejects with an `AbortError` instead of waiting indefinitely.
121
+ See [how consent works](../../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice) and
122
+ [choose your setup](../../../concepts/choose-your-setup.md).
123
+
124
+ ## Reload after revocation
125
+
126
+ Removing a script cannot stop code that already ran. A vendor's listeners,
127
+ timers and widgets keep working until the page unloads. When an accept, reject
128
+ or save turns off a category or vendor that was granted, the provider reloads
129
+ the page once the save request settles, so the next page runs only permitted
130
+ code. `onBeforeConsentRevocationReload` runs just before the reload.
131
+
132
+ Otherwise, expiry, policy changes and privacy signals do not reload the page. Set
133
+ `reloadOnConsentRevoked: false` only when every gated vendor stops itself on
134
+ revocation, for example through its own opt-out API.
135
+
136
+ ## Clear data when consent is denied
137
+
138
+ Set `options.clearOnRevocation` to declare cookies, localStorage keys, and
139
+ sessionStorage keys by optional consent category. Cleanup waits for policy
140
+ resolution, then clears denied categories and later allowed-to-denied
141
+ transitions. This option is initial-only. See
142
+ [clear on revocation](../clear-on-revocation.md) for examples,
143
+ cookie scopes, and browser limits.
@@ -8,7 +8,7 @@ group: frameworks
8
8
  ## Embed the preference center inline
9
9
 
10
10
  `ConsentWidget` renders the preference center in the page flow instead of in
11
- a modal: the category switches and the Reject all, Accept all and Save
11
+ a modal: the category switches and the Reject All, Accept All and Save Settings
12
12
  actions, without a title, backdrop or portal. Use it on a privacy or
13
13
  settings page where the visitor expects the controls to be part of the
14
14
  content. Render it anywhere inside the `ConsentProvider` from your
@@ -19,16 +19,16 @@ banner's Customize button and preference links still have a dialog to open.
19
19
  import { ConsentWidget } from 'c15t/react';
20
20
 
21
21
  export function PrivacyPage() {
22
- return (
23
- <main>
24
- <h1>Privacy</h1>
25
- <p>Choose which optional categories this site may use.</p>
26
- <section aria-labelledby="cookie-preferences">
27
- <h2 id="cookie-preferences">Cookie preferences</h2>
28
- <ConsentWidget />
29
- </section>
30
- </main>
31
- );
22
+ return (
23
+ <main>
24
+ <h1>Privacy</h1>
25
+ <p>Choose which optional categories this site may use.</p>
26
+ <section aria-labelledby="cookie-preferences">
27
+ <h2 id="cookie-preferences">Cookie preferences</h2>
28
+ <ConsentWidget />
29
+ </section>
30
+ </main>
31
+ );
32
32
  }
33
33
  ```
34
34
 
@@ -49,13 +49,16 @@ or dialog by itself.
49
49
 
50
50
  The widget is the preference center without the modal around it: an
51
51
  accordion with one row per category, each with a switch, followed by the
52
- Reject all, Accept all and Save actions. `ConsentDialog` renders this same
52
+ Reject All, Accept All and Save Settings actions. `ConsentDialog` renders this same
53
53
  widget inside its card. Standalone, the widget renders in place, takes part
54
54
  in server rendering, and does not touch the active surface, so it suits a
55
55
  privacy page or an account settings screen.
56
56
 
57
57
  The rows are `necessary` plus the categories in the active policy's scope,
58
- narrowed to the provider `options.consentCategories` when that list is non-empty.
58
+ narrowed to the categories the provider declares: `options.consentCategories`
59
+ and the categories of its scripts, network rules, vendors and discovered
60
+ frames. When nothing is declared, a permissive policy shows only `necessary`
61
+ and a strict one its whole scope.
59
62
  The `necessary` switch is on and disabled. Row titles and descriptions come
60
63
  from `consentTypes.<category>.title` and `.description`; the action labels
61
64
  from `common.rejectAll`, `common.acceptAll` and `common.save`. One row is
@@ -68,8 +71,8 @@ until Save. The draft seeds each category from the recorded choice; without
68
71
  one it uses `presentation.preferences.defaults` from the provider options,
69
72
  and without that it is on under an opt-out rule and on only for the rule's
70
73
  preselected categories under an opt-in rule. Save records the displayed
71
- categories, then reseeds the draft from the new record. Accept all and
72
- Reject all record immediately without a separate Save. A save answers a
74
+ categories, then reseeds the draft from the new record. Accept All and
75
+ Reject All record immediately without a separate Save. A save answers a
73
76
  choice prompt, so an open choice banner closes; it does not dismiss a
74
77
  `notice` prompt, which keeps its own acknowledgement record. Unsaved toggles
75
78
  are lost when the widget unmounts.
@@ -85,10 +88,35 @@ Privacy Control overrides shows a note under its row: "Your saved choice is
85
88
  restricted by the current privacy settings." The switch still reflects the
86
89
  saved value; the effective permission is off.
87
90
 
91
+ ### Vendors
92
+
93
+ When the provider declares `vendors`, each category's expanded description
94
+ ends with one card per vendor whose category condition names that category,
95
+ styled like a category row: the vendor's name and a switch on the header,
96
+ and its description and privacy policy link behind the card's own expand
97
+ control. Cards start collapsed, so a long list stays one line per vendor,
98
+ and the category list scrolls inside the dialog rather than growing it.
99
+ A collapsed category or card renders its content element empty and mounts
100
+ the content the first time it opens, then keeps it mounted, so a long
101
+ vendor list costs nothing until a visitor expands its category.
102
+ Vendor switches edit the same draft as the category
103
+ switches and record on Save. Saving with one vendor off denies that vendor
104
+ only; the category stays granted and its other vendors keep loading. While
105
+ the category is off in the draft the vendor switches are disabled, with the
106
+ hint "Turn on this category to choose vendors." A vendor declared
107
+ `disabled`, or one that only falls under `necessary`, is listed without a
108
+ switch. Accept All and Reject All clear every vendor denial, including one
109
+ staged but not yet saved. Under an `iab` policy no vendor rows render.
110
+ Vendors that are only referenced by a script slug, without a name and
111
+ privacy policy URL, still gate loading but are not listed, and so does a
112
+ vendor whose condition negates a category, since no category row can
113
+ host it. See
114
+ [vendor consent](../vendor-consent.md).
115
+
88
116
  ### Actions
89
117
 
90
- The preference center always renders Reject all, Accept all and Save, with
91
- Save as the primary action. `presentation.preferences.layout`,
118
+ The preference center always renders Reject All, Accept All and Save Settings, with
119
+ Save Settings as the primary action. `presentation.preferences.layout`,
92
120
  `primaryActions`, `direction` and `uiProfile` in the provider options
93
121
  reorder and restyle them; a layout that omits one of the three has it
94
122
  restored. Customize and dismiss actions never render here.
@@ -102,8 +130,12 @@ such as `['disclosure']`, renders the widget and Save completes without
102
130
  writing a consent record.
103
131
 
104
132
  For a fully custom preference center, `useConsentDraft()` returns the same
105
- draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
106
- `update`, `acceptAll`, `rejectAll`, `save` and `reset`.
133
+ draft: `values`, `displayedCategories`, `vendors`, `isDirty`, `isStale`,
134
+ `set`, `update`, `setVendor`, `acceptAll`, `rejectAll`, `save` and `reset`.
135
+ A custom vendor control alone takes `useVendorDraft()`, the vendor slice of
136
+ that draft: `vendors`, `setVendor`, `isDirty`, `isStale`, `save` and `reset`.
137
+ `useDeclaredVendors()`, `useVendorChoice()` and `useVendorAllowed(id)` read
138
+ the declared list, the recorded denials and one vendor's effective result.
107
139
 
108
140
  `useConsentDraft` and `ConsentDraftProvider` import from `c15t/react`.
109
141
 
@@ -112,7 +144,10 @@ draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
112
144
  Each row is a disclosure: a button that expands the category description
113
145
  and a switch beside it, so a visitor can read the description without
114
146
  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
147
+ name. A category's vendor cards sit in a `region` named "Vendors" with the
148
+ count; each card's expand button carries `aria-expanded`, each vendor
149
+ switch is named "Allow" followed by the vendor name, and is described by
150
+ the vendor's name element. The policy-change alert uses `role="alert"`, and a restriction note is
116
151
  an `output` element linked to its switch through `aria-describedby`. The
117
152
  root's `dir` attribute follows the active language.
118
153
 
@@ -122,7 +157,8 @@ Every part is available as `ConsentWidget.<Part>`. `Root` provides the
122
157
  draft and accepts `noStyle`, `disableAnimation` and `uiSource`. `Accordion`
123
158
  and `AccordionItems` render the stock rows; `AccordionItem`,
124
159
  `AccordionTrigger`, `AccordionTriggerInner`, `AccordionContent`,
125
- `AccordionArrow` and `Switch` build your own. `PolicyActions` renders the
160
+ `AccordionArrow` and `Switch` build your own; `VendorList` renders one
161
+ category's vendor rows and takes a `category` prop. `PolicyActions` renders the
126
162
  resolved actions in `Footer` and `FooterSubGroup` and accepts `renderAction`
127
163
  to replace one button; `AcceptAllButton`, `RejectButton`, `SaveButton` and
128
164
  `CustomizeButton` are the individual buttons.
@@ -155,11 +191,20 @@ export function PreferenceCenter() {
155
191
  Provider component slots for the stock structure are `manager.root`,
156
192
  `manager.footer`, `manager.actionGroup`, `accordion.root`,
157
193
  `accordion.triggerRow`, `accordion.title`, `accordion.control`,
158
- `accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`
159
- and `tag.manager`.
194
+ `accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`,
195
+ `vendor-list.root`, `vendor-list.trigger`, `vendor-list.content`,
196
+ `vendor-list.item`, `vendor-list.header`,
197
+ `vendor-list.name`, `vendor-list.description`, `vendor-list.link`,
198
+ `vendor-list.control` and `tag.manager`.
199
+
200
+ `ConsentWidget` from the package root and from the
201
+ `c15t/react/consent-widget` subpath loads its code in a separate chunk the
202
+ first time it renders, so a page that never renders it does not download
203
+ it. `c15t/react/components/consent-widget` exports the widget without that
204
+ deferral, plus each part as a named export.
160
205
 
161
206
  The compound parts import from `c15t/react` as properties of `ConsentWidget`.
162
- See [Styling](../styling/overview.md) for slots and tokens.
207
+ See [customize](../customize.md) for slots and tokens.
163
208
 
164
209
  ## Verify
165
210