@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,79 +1,870 @@
1
1
  ---
2
- title: Banner styling recipes
3
- description: See real v3 components and reuse the exact theme configuration
4
- behind the examples.
2
+ title: Banner designs
3
+ description: Five consent banner designs built with c15t, from one prop to your
4
+ own markup, with tested code for React, Vue, Svelte, Astro and plain HTML.
5
5
  group: customization
6
6
  ---
7
7
 
8
- ## A branded floating card
8
+ ## Pick a design
9
9
 
10
- This theme uses a green primary action and a four-pixel large radius. Accept and
11
- Reject keep matching treatment. The component's required action behavior stays
12
- intact.
10
+ Each design below is a real c15t banner. The screenshots and the code come from
11
+ the same Storybook stories, which CI runs to check that each design renders and
12
+ that Accept and Reject record a choice.
13
13
 
14
- ![A v3 floating consent card with green Customize action, matching Accept and Reject buttons, and compact corners](../assets/v3/brand-card.png)
14
+ | Design | Customization level | Frameworks |
15
+ | ----------------------------- | ----------------------- | ------------------------- |
16
+ | [Bottom bar](#bottom-bar) | Props | React, Vue, Astro, Svelte |
17
+ | [Branded card](#branded-card) | Theme tokens | React |
18
+ | [Choice wall](#choice-wall) | Props | React |
19
+ | [Slim bar](#slim-bar) | Compound parts or slots | React, Vue, Svelte |
20
+ | [Headless bar](#headless-bar) | Headless | React, Vue, Svelte, HTML |
15
21
 
16
- ```ts
17
- import { defineTheme } from '@c15t/ui/theme';
22
+ Next.js and TanStack Start apps use the React code, with the same components
23
+ imported from `c15t/next` or `c15t/tanstack-start`. Nuxt uses the Vue code and
24
+ SvelteKit uses the Svelte code. [Customization](./overview.md)
25
+ explains when to use each level.
26
+
27
+ Every design keeps the policy in charge. The policy decides which buttons a
28
+ visitor gets, and none of these designs records consent until the visitor
29
+ clicks a button.
30
+
31
+ ## Bottom bar
32
+
33
+ ![A white bar across the bottom of a coffee shop page, with the heading "We value your privacy", one line of copy, and Reject All, Accept All and Customize buttons on the right](../assets/v3/bottom-bar.png)
34
+
35
+ The stock banner becomes a full-width bar along the bottom edge with one prop,
36
+ `variant="bar"`. The title and copy sit on the left and the buttons on the
37
+ right.
38
+
39
+ **React**
40
+
41
+ Render `CookieBanner` in place of `ConsentBanner`, inside your
42
+ `ConsentProvider` and next to `ConsentDialog`.
43
+
44
+ ```tsx title="src/cookie-banner.tsx"
45
+ import { ConsentBanner } from 'c15t/react';
46
+
47
+ /** The stock banner, drawn as a full-width bar along the bottom edge. */
48
+ export const CookieBanner = () => (
49
+ <ConsentBanner variant="bar" position="bottom" />
50
+ );
51
+ ```
52
+
53
+ **Vue**
54
+
55
+ Render `CookieBanner` and `ConsentManager` from
56
+ `c15t/vue/runtime/components/consent-manager.vue` in place of
57
+ `ConsentRoot`. To keep `ConsentRoot`, set `presentation.prompt` in the
58
+ `c15tVue` options instead, as in [customize](https://v3.c15t.com/docs/frameworks/vue/customize).
59
+
60
+ ```vue title="src/CookieBanner.vue"
61
+ <script setup lang="ts">
62
+ import ConsentBanner from 'c15t/vue/runtime/components/consent-banner.vue';
63
+ </script>
64
+
65
+ <template>
66
+ <!-- The stock banner, drawn as a full-width bar along the bottom edge. -->
67
+ <ConsentBanner variant="bar" position="bottom" />
68
+ </template>
69
+ ```
70
+
71
+ **Astro**
72
+
73
+ Add `presentation` to the `c15t()` options in `astro.config.mjs`. The
74
+ server renders the banner as a bar.
75
+
76
+ ```ts title="astro.config.mjs (partial)"
77
+ presentation: {
78
+ prompt: { position: 'bottom', variant: 'bar' },
79
+ },
80
+ ```
81
+
82
+ **Svelte**
83
+
84
+ Render `CookieBanner` in place of `ConsentBanner`, inside
85
+ `ConsentProvider` and next to `ConsentDialog`.
86
+
87
+ ```svelte title="src/lib/cookie-banner.svelte"
88
+ <script lang="ts">
89
+ import { ConsentBanner } from '@c15t/svelte';
90
+ </script>
91
+
92
+ <!-- The stock banner, drawn as a full-width bar along the bottom edge. -->
93
+ <ConsentBanner variant="bar" position="bottom" />
94
+ ```
95
+
96
+ The HTML script tag takes the same shape through `presentation.prompt`. See
97
+ [customize the script tag](https://v3.c15t.com/docs/frameworks/html/customize#change-the-banners-shape-and-position).
98
+
99
+ ## Branded card
100
+
101
+ ![A floating card in the bottom-left corner with a warm off-white surface, serif type, small corners, and green Reject All and Accept All buttons next to an outlined Customize button](../assets/v3/brand-card.png)
102
+
103
+ Theme tokens change the colors, type and radius of the stock card without
104
+ touching its markup. This theme sets a green primary color, an off-white
105
+ surface, a serif font and four-pixel corners, and fills the actions the policy
106
+ marks as primary. Here the policy marks both Reject All and Accept All as
107
+ primary, so neither choice stands out.
108
+
109
+ ```ts title="src/consent-theme.ts"
110
+ import { defineTheme } from 'c15t/react';
18
111
 
19
112
  export const brandTheme = defineTheme({
20
- colors: { primary: '#2f6f4e' },
21
- consentActions: {
22
- primary: { mode: 'filled', variant: 'primary' },
113
+ colors: {
114
+ border: '#e4dccf',
115
+ primary: '#2f6f4e',
116
+ primaryHover: '#24563c',
117
+ surface: '#fbf8f3',
118
+ textOnPrimary: '#ffffff',
23
119
  },
24
- radius: { lg: '4px' },
120
+ consentActions: { primary: { mode: 'filled', variant: 'primary' } },
121
+ radius: { lg: '4px', md: '4px' },
122
+ typography: { fontFamily: 'Georgia, "Times New Roman", serif' },
25
123
  });
26
-
27
124
  ```
28
125
 
29
- Install `@c15t/ui` to use `defineTheme` directly. Pass `brandTheme` as `theme` in
30
- your existing React or Next.js provider options. Keep your Inth mode and script
31
- configuration. The theme source above is also imported by the runnable example,
32
- so the rendered result and the documentation use the same values.
126
+ ```tsx title="src/consent-ui.tsx"
127
+ import { ConsentBanner, ConsentDialog, ConsentTheme } from 'c15t/react';
33
128
 
34
- ## Change the shape without rebuilding the banner
129
+ import { brandTheme } from './consent-theme';
130
+
131
+ /**
132
+ * Render inside your ConsentProvider, and pass `theme: brandTheme` in the
133
+ * provider options so the action styles apply too.
134
+ */
135
+ export const ConsentUI = () => (
136
+ <>
137
+ <ConsentTheme theme={brandTheme} />
138
+ <ConsentBanner />
139
+ <ConsentDialog />
140
+ </>
141
+ );
142
+ ```
143
+
144
+ `ConsentTheme` renders the tokens as a `<style>` element. In a server-rendered
145
+ app, render it from a Server Component or the root layout so the tokens arrive
146
+ with the HTML. The provider's `theme` option reads only `consentActions` and
147
+ slot styles, so pass the same object there for the filled buttons:
35
148
 
36
149
  ```tsx
37
- <ConsentBanner variant="bar" />
150
+ <ConsentProvider options={{ mode, theme: brandTheme }}>
38
151
  ```
39
152
 
40
- ![The same green theme on a full-width v3 consent bar with the actions arranged across its footer](../assets/v3/brand-bar.png)
153
+ The preferences dialog reads the same tokens:
41
154
 
42
- A bar changes the prompt's footprint. It does not change policy, grant categories
43
- or require a headless implementation.
155
+ ![The Privacy Settings dialog over the dimmed coffee shop page, with the same off-white surface, serif type and green Reject All and Accept All buttons](../assets/v3/brand-preferences.png)
44
156
 
45
- ```tsx
46
- <ConsentBanner variant="wall" />
157
+ Vue, Svelte, Astro and the script tag set the same tokens their own way. See
158
+ customize for [Vue](https://v3.c15t.com/docs/frameworks/vue/customize),
159
+ [Svelte](https://v3.c15t.com/docs/frameworks/svelte/customize),
160
+ [Astro](https://v3.c15t.com/docs/frameworks/astro/customize) or
161
+ [HTML](https://v3.c15t.com/docs/frameworks/html/customize), and
162
+ [theme tokens](./tokens.md) for every token.
163
+
164
+ ## Choice wall
165
+
166
+ ![A centered consent card over a dimmed coffee shop page, with Reject All, Accept All and Customize buttons](../assets/v3/choice-wall.png)
167
+
168
+ `variant="wall"` centers the stock banner over a backdrop, locks scrolling and
169
+ traps focus until the visitor chooses. Use it only where a policy requires a
170
+ choice before the page is usable.
171
+
172
+ ```tsx title="src/cookie-banner.tsx"
173
+ import { ConsentBanner } from 'c15t/react';
174
+
175
+ /** A centered prompt with a backdrop. The page waits for a choice. */
176
+ export const CookieBanner = () => <ConsentBanner variant="wall" />;
177
+ ```
178
+
179
+ A choice wall always blocks. A notice never blocks, so a wall requested for a
180
+ notice falls back to the floating card.
181
+
182
+ ## Slim bar
183
+
184
+ ![A thin white bar along the bottom of the page with the heading and one line of copy on the left, and Reject All, Accept All and Customize buttons on the same row](../assets/v3/slim-bar.png)
185
+
186
+ The slim bar puts the title, the copy and the buttons on one row, so it is
187
+ shorter than the stock bar. React builds it from the
188
+ `ConsentBanner` compound parts. Vue and Svelte have no compound banner parts,
189
+ so they add classes to the stock bar's card and header through slots. All
190
+ three use the same stylesheet, and all three keep the buttons the policy
191
+ requires.
192
+
193
+ **React**
194
+
195
+ `ConsentBanner.PolicyActions` renders the policy's buttons, so a required
196
+ Reject button stays. Render `CookieBanner` inside your `ConsentProvider`,
197
+ next to `ConsentDialog`.
198
+
199
+ ```tsx title="src/cookie-banner.tsx"
200
+ import { ConsentBanner } from 'c15t/react';
201
+
202
+ import './slim-bar.css';
203
+
204
+ /**
205
+ * The title, copy and the policy's buttons in a single row. The compound
206
+ * parts keep the policy's actions, so a required Reject button stays.
207
+ */
208
+ export const CookieBanner = () => (
209
+ <ConsentBanner.Root variant="bar">
210
+ <ConsentBanner.Card className="slim-bar">
211
+ <ConsentBanner.Header className="slim-bar__text">
212
+ <ConsentBanner.Title />
213
+ <ConsentBanner.Description />
214
+ </ConsentBanner.Header>
215
+ <ConsentBanner.PolicyActions />
216
+ </ConsentBanner.Card>
217
+ </ConsentBanner.Root>
218
+ );
219
+ ```
220
+
221
+ **Vue**
222
+
223
+ Spread `slimBar` into the options you pass to `app.use(c15tVue, ...)`.
224
+ `ConsentRoot` then renders the slim bar.
225
+
226
+ ```ts title="src/consent-options.ts"
227
+ import type { C15tVuePluginOptions } from 'c15t/vue/vue-plugin';
228
+
229
+ import './slim-bar.css';
230
+
231
+ /**
232
+ * Spread into the options you pass to `app.use(c15tVue, ...)`. The bar keeps
233
+ * the stock markup and actions; the slot classes put it on one row.
234
+ */
235
+ export const slimBar = {
236
+ components: {
237
+ banner: {
238
+ card: { class: 'slim-bar' },
239
+ header: { class: 'slim-bar__text' },
240
+ },
241
+ },
242
+ presentation: { prompt: { position: 'bottom', variant: 'bar' } },
243
+ } satisfies C15tVuePluginOptions;
244
+ ```
245
+
246
+ **Svelte**
247
+
248
+ Spread `slimBar` into the props you pass to `ConsentProvider`.
249
+ Svelte scopes component styles, so the classes live in a global
250
+ stylesheet.
251
+
252
+ ```ts title="src/lib/consent-options.ts"
253
+ import type { ConsentManagerOptions } from '@c15t/svelte';
254
+
255
+ import './slim-bar.css';
256
+
257
+ /**
258
+ * Spread into the options you pass to `ConsentProvider`. The bar
259
+ * keeps the stock markup and actions; the slot classes put it on one row.
260
+ */
261
+ export const slimBar = {
262
+ presentation: { prompt: { position: 'bottom', variant: 'bar' } },
263
+ theme: {
264
+ slots: {
265
+ consentBannerCard: 'slim-bar',
266
+ consentBannerHeader: 'slim-bar__text',
267
+ },
268
+ },
269
+ } satisfies Partial<ConsentManagerOptions>;
270
+ ```
271
+
272
+ ```css title="src/slim-bar.css"
273
+ .slim-bar {
274
+ box-sizing: border-box;
275
+ display: flex;
276
+ flex-wrap: wrap;
277
+ align-items: center;
278
+ gap: 0.5rem 1.5rem;
279
+ padding: 0.5rem 1.5rem;
280
+ }
281
+
282
+ /* Title and description share one line. */
283
+ .slim-bar__text {
284
+ display: flex;
285
+ flex: 1 1 20rem;
286
+ flex-direction: row;
287
+ flex-wrap: wrap;
288
+ align-items: baseline;
289
+ gap: 0 0.5rem;
290
+ padding: 0;
291
+ }
292
+
293
+ /* The actions share the text's row, so drop the footer's own box. */
294
+ .slim-bar > [data-testid='consent-banner-footer'] {
295
+ padding: 0;
296
+ border: 0;
297
+ background: none;
298
+ }
299
+ ```
300
+
301
+ ## Headless bar
302
+
303
+ ![A dark navy bar across the bottom of the page with one line of white copy, an underlined Preferences link, and white pill-shaped Reject all and Accept all buttons](../assets/v3/headless-bar.png)
304
+
305
+ The headless bar is your own markup and CSS, driven by c15t's headless API. It
306
+ shows the same design in every framework. The policy still decides which
307
+ buttons appear and in what order, the Preferences button is always there, and
308
+ a choice is recorded only when the visitor clicks.
309
+
310
+ **React**
311
+
312
+ `useHeadlessConsentUI()` from `c15t/react/headless` returns the banner
313
+ state and `performAction`. Render `CookieBar` in place of
314
+ `ConsentBanner`, and keep `ConsentDialog`, which Preferences opens.
315
+
316
+ ```tsx title="src/cookie-bar.tsx"
317
+ import { useHeadlessConsentUI } from 'c15t/react/headless';
318
+ import type { HeadlessConsentBannerAction } from 'c15t/react/headless';
319
+
320
+ import './cookie-bar.css';
321
+
322
+ const labels: Record<HeadlessConsentBannerAction, string> = {
323
+ accept: 'Accept all',
324
+ customize: 'Preferences',
325
+ dismiss: 'Got it',
326
+ reject: 'Reject all',
327
+ save: 'Save',
328
+ };
329
+
330
+ export const CookieBar = () => {
331
+ const { banner, openDialog, performAction } = useHeadlessConsentUI();
332
+
333
+ if (!banner.isVisible) {
334
+ return null;
335
+ }
336
+
337
+ // The policy decides which actions this visitor gets, and in what order.
338
+ // Preferences has its own button, so it is always there.
339
+ const actions = banner.orderedActions.filter(
340
+ (action) => action !== 'customize'
341
+ );
342
+
343
+ return (
344
+ <section className="cookie-bar" aria-label="Cookie consent">
345
+ <p className="cookie-bar__text">
346
+ We use cookies to measure traffic and improve this site. Choose which
347
+ ones can run.
348
+ </p>
349
+ <div className="cookie-bar__actions">
350
+ <button type="button" className="cookie-bar__link" onClick={openDialog}>
351
+ Preferences
352
+ </button>
353
+ {actions.map((action) => (
354
+ <button
355
+ key={action}
356
+ type="button"
357
+ className="cookie-bar__button"
358
+ onClick={() => performAction(action)}
359
+ >
360
+ {labels[action]}
361
+ </button>
362
+ ))}
363
+ </div>
364
+ </section>
365
+ );
366
+ };
367
+ ```
368
+
369
+ **Vue**
370
+
371
+ The composables from `c15t/vue/vue-plugin` give the policy's actions and
372
+ the active surface. Render `CookieBar` and `ConsentManager` in place of
373
+ `ConsentRoot`. Preferences sets `activeUI` to `'manager'`, which opens
374
+ `ConsentManager`.
375
+
376
+ ```vue title="src/CookieBar.vue"
377
+ <script setup lang="ts">
378
+ import {
379
+ useConsentActiveUI,
380
+ useConsentPolicyActions,
381
+ useConsentSave,
382
+ useDismissNotice,
383
+ usePromptRequirement,
384
+ } from 'c15t/vue/vue-plugin';
385
+ import { computed } from 'vue';
386
+
387
+ import './cookie-bar.css';
388
+
389
+ const labels: Record<string, string> = {
390
+ accept: 'Accept all',
391
+ dismiss: 'Got it',
392
+ reject: 'Reject all',
393
+ };
394
+
395
+ const activeUI = useConsentActiveUI();
396
+ const prompt = usePromptRequirement();
397
+ const { actionGroups } = useConsentPolicyActions('prompt');
398
+ const saveConsent = useConsentSave();
399
+ const dismissNotice = useDismissNotice();
400
+
401
+ const isVisible = computed(
402
+ () => activeUI.value === 'banner' && prompt.value.kind !== 'none'
403
+ );
404
+
405
+ // The policy decides which actions this visitor gets, and in what order.
406
+ // Preferences has its own button, so it is always there.
407
+ const actions = computed(() =>
408
+ actionGroups.value.flat().filter((action) => action in labels)
409
+ );
410
+
411
+ const run = (action: string) => {
412
+ if (action === 'accept') {
413
+ void saveConsent('all');
414
+ } else if (action === 'reject') {
415
+ void saveConsent('none');
416
+ } else if (action === 'dismiss') {
417
+ void dismissNotice();
418
+ }
419
+ };
420
+ </script>
421
+
422
+ <template>
423
+ <section v-if="isVisible" class="cookie-bar" aria-label="Cookie consent">
424
+ <p class="cookie-bar__text">
425
+ We use cookies to measure traffic and improve this site. Choose which ones
426
+ can run.
427
+ </p>
428
+ <div class="cookie-bar__actions">
429
+ <button
430
+ type="button"
431
+ class="cookie-bar__link"
432
+ @click="activeUI = 'manager'"
433
+ >
434
+ Preferences
435
+ </button>
436
+ <button
437
+ v-for="action in actions"
438
+ :key="action"
439
+ type="button"
440
+ class="cookie-bar__button"
441
+ @click="run(action)"
442
+ >
443
+ {{ labels[action] }}
444
+ </button>
445
+ </div>
446
+ </section>
447
+ </template>
448
+ ```
449
+
450
+ **Svelte**
451
+
452
+ `getHeadlessConsent()` returns the banner state and `performAction`.
453
+ Render `CookieBar` in place of `ConsentBanner`, inside
454
+ `ConsentProvider`, and keep `ConsentDialog`, which Preferences
455
+ opens.
456
+
457
+ ```svelte title="src/lib/cookie-bar.svelte"
458
+ <script lang="ts">
459
+ import { getHeadlessConsent } from '@c15t/svelte';
460
+ import type { PresentationAction } from '@c15t/svelte';
461
+
462
+ import './cookie-bar.css';
463
+
464
+ const consent = getHeadlessConsent();
465
+
466
+ const labels: Record<PresentationAction, string> = {
467
+ accept: 'Accept all',
468
+ customize: 'Preferences',
469
+ dismiss: 'Got it',
470
+ reject: 'Reject all',
471
+ save: 'Save',
472
+ };
473
+
474
+ // The policy decides which actions this visitor gets, and in what order.
475
+ // Preferences has its own button, so it is always there.
476
+ const actions = $derived(
477
+ consent.banner.orderedActions.filter((action) => action !== 'customize')
478
+ );
479
+ </script>
480
+
481
+ {#if consent.banner.isVisible}
482
+ <section class="cookie-bar" aria-label="Cookie consent">
483
+ <p class="cookie-bar__text">
484
+ We use cookies to measure traffic and improve this site. Choose which ones
485
+ can run.
486
+ </p>
487
+ <div class="cookie-bar__actions">
488
+ <button
489
+ type="button"
490
+ class="cookie-bar__link"
491
+ onclick={() => consent.openDialog()}
492
+ >
493
+ Preferences
494
+ </button>
495
+ {#each actions as action (action)}
496
+ <button
497
+ type="button"
498
+ class="cookie-bar__button"
499
+ onclick={() => consent.performAction(action)}
500
+ >
501
+ {labels[action]}
502
+ </button>
503
+ {/each}
504
+ </div>
505
+ </section>
506
+ {/if}
507
+ ```
508
+
509
+ **HTML**
510
+
511
+ The headless build, `c15t.headless.js`, keeps script gating and
512
+ `window.c15t` and ships no banner, dialog or CSS. Buttons with
513
+ `data-c15t-action` need no code. Because there is no stock dialog,
514
+ Preferences opens category switches inside the bar and a Save button
515
+ records them. Replace `https://your-project.inth.app` with the backend URL
516
+ from your Inth project, and put the block before `</body>`.
517
+
518
+ ```html title="index.html"
519
+ <script
520
+ src="https://your-project.inth.app/c15t.headless.js"
521
+ defer
522
+ ></script>
523
+
524
+ <style>
525
+ .cookie-bar {
526
+ position: fixed;
527
+ inset: auto 0 0;
528
+ z-index: 50;
529
+ display: flex;
530
+ flex-wrap: wrap;
531
+ align-items: center;
532
+ gap: 0.75rem 1.5rem;
533
+ padding: 0.875rem 1.5rem;
534
+ background: #14213d;
535
+ color: #f8f9fb;
536
+ font:
537
+ 0.9375rem/1.5 system-ui,
538
+ sans-serif;
539
+ box-shadow: 0 -4px 16px rgb(0 0 0 / 0.15);
540
+ }
541
+
542
+ .cookie-bar__text {
543
+ flex: 1 1 20rem;
544
+ margin: 0;
545
+ }
546
+
547
+ .cookie-bar__actions {
548
+ display: flex;
549
+ flex-wrap: wrap;
550
+ gap: 0.5rem;
551
+ }
552
+
553
+ .cookie-bar button {
554
+ padding: 0.5rem 1rem;
555
+ border: 1px solid #f8f9fb;
556
+ border-radius: 999px;
557
+ background: transparent;
558
+ color: inherit;
559
+ font: inherit;
560
+ cursor: pointer;
561
+ }
562
+
563
+ .cookie-bar .cookie-bar__button {
564
+ background: #f8f9fb;
565
+ color: #14213d;
566
+ font-weight: 600;
567
+ }
568
+
569
+ .cookie-bar .cookie-bar__link {
570
+ border-color: transparent;
571
+ text-decoration: underline;
572
+ text-underline-offset: 3px;
573
+ }
574
+
575
+ .cookie-bar button:focus-visible {
576
+ outline: 2px solid #fca311;
577
+ outline-offset: 2px;
578
+ }
579
+ /* On narrow screens, Reject and Accept share a row at equal width. */
580
+ @media (max-width: 30rem) {
581
+ .cookie-bar__actions {
582
+ width: 100%;
583
+ }
584
+
585
+ .cookie-bar__actions button {
586
+ flex: 1 1 40%;
587
+ }
588
+
589
+ .cookie-bar__actions .cookie-bar__link {
590
+ flex-basis: 100%;
591
+ }
592
+ }
593
+
594
+ /* The headless build has no dialog, so preferences open inside the bar. */
595
+ .cookie-bar[hidden],
596
+ .cookie-bar__prefs[hidden] {
597
+ display: none;
598
+ }
599
+
600
+ .cookie-bar__prefs {
601
+ display: flex;
602
+ flex-wrap: wrap;
603
+ gap: 0.5rem 1.25rem;
604
+ flex-basis: 100%;
605
+ margin: 0;
606
+ padding: 0;
607
+ border: 0;
608
+ }
609
+
610
+ .cookie-bar__prefs label {
611
+ display: flex;
612
+ align-items: center;
613
+ gap: 0.375rem;
614
+ }
615
+ </style>
616
+
617
+ <section
618
+ id="cookie-bar"
619
+ class="cookie-bar"
620
+ aria-label="Cookie consent"
621
+ hidden
622
+ >
623
+ <p class="cookie-bar__text">
624
+ We use cookies to measure traffic and improve this site. Choose which
625
+ ones can run.
626
+ </p>
627
+ <fieldset
628
+ class="cookie-bar__prefs"
629
+ hidden
630
+ >
631
+ <legend class="cookie-bar__text">Allow these cookies:</legend>
632
+ <label
633
+ ><input
634
+ type="checkbox"
635
+ name="functionality"
636
+ />
637
+ Functionality</label
638
+ >
639
+ <label
640
+ ><input
641
+ type="checkbox"
642
+ name="measurement"
643
+ />
644
+ Measurement</label
645
+ >
646
+ <label
647
+ ><input
648
+ type="checkbox"
649
+ name="experience"
650
+ />
651
+ Experience</label
652
+ >
653
+ <label
654
+ ><input
655
+ type="checkbox"
656
+ name="marketing"
657
+ />
658
+ Marketing</label
659
+ >
660
+ </fieldset>
661
+ <div class="cookie-bar__actions">
662
+ <!-- c15t wires data-c15t-action buttons itself; they need no code. -->
663
+ <button
664
+ type="button"
665
+ class="cookie-bar__link"
666
+ data-c15t-action="customize"
667
+ >
668
+ Preferences
669
+ </button>
670
+ <button
671
+ type="button"
672
+ class="cookie-bar__button"
673
+ data-c15t-action="dismiss"
674
+ data-notice-only
675
+ hidden
676
+ >
677
+ Got it
678
+ </button>
679
+ <button
680
+ type="button"
681
+ class="cookie-bar__button"
682
+ data-c15t-action="reject"
683
+ data-choice-only
684
+ >
685
+ Reject all
686
+ </button>
687
+ <button
688
+ type="button"
689
+ class="cookie-bar__button"
690
+ data-c15t-action="accept"
691
+ data-choice-only
692
+ >
693
+ Accept all
694
+ </button>
695
+ <button
696
+ type="button"
697
+ class="cookie-bar__button"
698
+ data-save
699
+ hidden
700
+ >
701
+ Save
702
+ </button>
703
+ </div>
704
+ </section>
705
+
706
+ <script>
707
+ const bar = document.getElementById('cookie-bar');
708
+ const prefs = bar.querySelector('.cookie-bar__prefs');
709
+ const saveButton = bar.querySelector('[data-save]');
710
+ const switches = prefs.querySelectorAll('input');
711
+ const choiceButtons = bar.querySelectorAll('[data-choice-only]');
712
+ const noticeButtons = bar.querySelectorAll('[data-notice-only]');
713
+
714
+ // The tag is deferred, so queue calls until window.c15t is the real API.
715
+ window.c15t = window.c15t || [];
716
+ const queue = (call) => {
717
+ if (Array.isArray(window.c15t)) {
718
+ window.c15t.push(call);
719
+ } else {
720
+ window.c15t[call[0]](...call.slice(1));
721
+ }
722
+ };
723
+
724
+ // c15t says which surface is due; the page decides what it looks like.
725
+ // 'banner' shows the bar, 'dialog' opens the switches, 'none' hides it.
726
+ queue([
727
+ 'on',
728
+ 'ui',
729
+ (surface) => {
730
+ const { kind } = c15t.getSnapshot().promptRequirement;
731
+ const editing = surface === 'dialog';
732
+ // A notice gets one acknowledgement button instead of Accept and Reject.
733
+ for (const button of choiceButtons) {
734
+ button.hidden = kind === 'notice' && !editing;
735
+ }
736
+ for (const button of noticeButtons) {
737
+ button.hidden = kind !== 'notice' || editing;
738
+ }
739
+ for (const input of switches) {
740
+ input.checked = c15t.has(input.name);
741
+ }
742
+ prefs.hidden = !editing;
743
+ saveButton.hidden = !editing;
744
+ bar.hidden =
745
+ surface === 'none' || (surface === 'banner' && kind === 'none');
746
+ },
747
+ ]);
748
+
749
+ // Save records exactly what the switches show.
750
+ saveButton.addEventListener('click', () => {
751
+ const choice = {};
752
+ for (const input of switches) {
753
+ choice[input.name] = input.checked;
754
+ }
755
+ c15t.save(choice);
756
+ });
757
+ </script>
758
+ ```
759
+
760
+ React, Vue and Svelte import the same stylesheet, `cookie-bar.css`. The HTML
761
+ block contains it inline.
762
+
763
+ ```css title="src/cookie-bar.css"
764
+ .cookie-bar {
765
+ position: fixed;
766
+ inset: auto 0 0;
767
+ z-index: 50;
768
+ display: flex;
769
+ flex-wrap: wrap;
770
+ align-items: center;
771
+ gap: 0.75rem 1.5rem;
772
+ padding: 0.875rem 1.5rem;
773
+ background: #14213d;
774
+ color: #f8f9fb;
775
+ font:
776
+ 0.9375rem/1.5 system-ui,
777
+ sans-serif;
778
+ box-shadow: 0 -4px 16px rgb(0 0 0 / 0.15);
779
+ }
780
+
781
+ .cookie-bar__text {
782
+ flex: 1 1 20rem;
783
+ margin: 0;
784
+ }
785
+
786
+ .cookie-bar__actions {
787
+ display: flex;
788
+ flex-wrap: wrap;
789
+ gap: 0.5rem;
790
+ }
791
+
792
+ .cookie-bar button {
793
+ padding: 0.5rem 1rem;
794
+ border: 1px solid #f8f9fb;
795
+ border-radius: 999px;
796
+ background: transparent;
797
+ color: inherit;
798
+ font: inherit;
799
+ cursor: pointer;
800
+ }
801
+
802
+ .cookie-bar .cookie-bar__button {
803
+ background: #f8f9fb;
804
+ color: #14213d;
805
+ font-weight: 600;
806
+ }
807
+
808
+ .cookie-bar .cookie-bar__link {
809
+ border-color: transparent;
810
+ text-decoration: underline;
811
+ text-underline-offset: 3px;
812
+ }
813
+
814
+ .cookie-bar button:focus-visible {
815
+ outline: 2px solid #fca311;
816
+ outline-offset: 2px;
817
+ }
818
+ /* On narrow screens, Reject and Accept share a row at equal width. */
819
+ @media (max-width: 30rem) {
820
+ .cookie-bar__actions {
821
+ width: 100%;
822
+ }
823
+
824
+ .cookie-bar__actions button {
825
+ flex: 1 1 40%;
826
+ }
827
+
828
+ .cookie-bar__actions .cookie-bar__link {
829
+ flex-basis: 100%;
830
+ }
831
+ }
47
832
  ```
48
833
 
49
- ![A centered v3 choice wall with a backdrop, using the same green action theme](../assets/v3/choice-wall.png)
834
+ On a 375-pixel screen the copy takes the full width, Preferences sits on its
835
+ own line, and Reject all and Accept all share a row at equal width:
50
836
 
51
- A choice wall blocks interaction with the page. A notice never blocks; requesting
52
- a wall for a notice falls back to a non-blocking presentation.
837
+ ![The dark headless bar on a narrow phone screen, with the copy on two lines, a centered Preferences link, and Reject all and Accept all buttons side by side](../assets/v3/headless-bar-mobile.png)
53
838
 
54
- ## Carry the theme into preferences
839
+ In the HTML version, Preferences opens the switches inside the bar:
55
840
 
56
- ![The v3 preferences dialog with optional category toggles and a green Save Settings action](../assets/v3/preferences.png)
841
+ ![The dark bar grown taller, with checkboxes for Functionality, Measurement, Experience and Marketing above Preferences, Reject all, Accept all and Save buttons](../assets/v3/headless-bar-html.png)
57
842
 
58
- Keep a preferences link after the banner closes. Test a reject action, reopen
59
- the dialog, change one category and save. A branded prompt is only part of the
60
- consent flow.
843
+ A headless UI owns its focus handling and labels. Test it with a keyboard and
844
+ a screen reader. The headless pages for [React](../frameworks/react/headless.md),
845
+ [Vue](https://v3.c15t.com/docs/frameworks/vue/headless) and [Svelte](https://v3.c15t.com/docs/frameworks/svelte/headless)
846
+ cover notices and custom preference forms.
61
847
 
62
- ## Check narrow screens
848
+ ## Check a design
63
849
 
64
- ![The branded v3 floating card at a 375-pixel viewport, with Accept and Reject sharing a row and Customize below](../assets/v3/mobile-card.png)
850
+ Run these checks after you adopt a design:
65
851
 
66
- This example fits a 375-pixel viewport without horizontal overflow. Test your
67
- own longest translations and font settings before adopting the same sizing.
852
+ 1. Clear site data and reload. The banner appears and no optional vendor
853
+ request appears in DevTools Network.
854
+ 2. Click Reject all, reload, and confirm the banner stays closed and the
855
+ vendors stay blocked.
856
+ 3. Open preferences from your footer link, allow one category, save, and
857
+ confirm only that category's vendors load.
858
+ 4. Test a 375-pixel viewport with your longest translation, and tab through
859
+ every button.
68
860
 
69
- ## Run the examples
861
+ [Verify consent](../guides/verify-consent.md) has the full list.
70
862
 
71
- The source repository contains resettable Storybook examples under
72
- `Docs / Customization`: Brand Card, Brand Bar and Choice Wall. Each demo uses
73
- in-memory consent and no analytics; Reset example returns it to a fresh choice.
74
- The screenshots above come from these v3 examples.
863
+ ## Run the designs
75
864
 
76
- The stories are suitable for isolated iframe previews on a version-matched
77
- Storybook deployment. Keep source code and the explanation on this page when
78
- embedding them, so the recipe remains usable in Markdown and offline package
79
- docs. A live preview must use the same v3 revision as the documentation.
865
+ The source repository renders each design in its Storybook under
866
+ `Docs / Banner designs`: `apps/storybook-react`, `apps/storybook-vue`,
867
+ `apps/storybook-svelte` and `apps/storybook-astro`. Each story uses an
868
+ in-memory policy and loads no analytics. The HTML headless bar runs at
869
+ `/consent-example/headless` in `internals/fixtures/script-tag`, which the example
870
+ acceptance tests cover.