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

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 (406) hide show
  1. package/AGENTS.md +96 -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/panel/atoms/card.js +1 -1
  27. package/dist/components/panel/atoms/overlay.js +1 -1
  28. package/dist/components/panel/atoms/root.js +1 -1
  29. package/dist/components/panel/panel.js +1 -1
  30. package/dist/components/panel-trigger/atoms/button.js +1 -1
  31. package/dist/components/panel-trigger/atoms/icon.js +1 -1
  32. package/dist/components/panel-trigger/atoms/root.js +1 -1
  33. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  34. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
  35. package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
  36. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  37. package/dist/components/preferences/atoms/accordion.js +1 -1
  38. package/dist/components/preferences/atoms/root.js +1 -1
  39. package/dist/components/preferences/atoms/vendor-list.js +1 -0
  40. package/dist/components/preferences/index.js +1 -1
  41. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  42. package/dist/components/prompt/atoms/overlay.js +1 -1
  43. package/dist/components/prompt/atoms/root.js +1 -1
  44. package/dist/components/prompt/components.js +1 -1
  45. package/dist/components/prompt/policy-actions.js +1 -1
  46. package/dist/components/prompt/prompt.js +1 -1
  47. package/dist/components/shared/dialog-sheets.js +1 -0
  48. package/dist/components/shared/first-paint-sheets.js +1 -0
  49. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  50. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  51. package/dist/components/shared/libs/compose-refs.js +1 -0
  52. package/dist/components/shared/libs/slot.js +1 -1
  53. package/dist/components/shared/primitives/button.js +1 -1
  54. package/dist/components/shared/primitives/legal-links/index.js +1 -1
  55. package/dist/components/shared/streamed-surface.js +2 -0
  56. package/dist/components/shared/surface-styles.js +2 -0
  57. package/dist/components/shared/ui/dialog/dialog.js +1 -1
  58. package/dist/components/shared/ui/icon.js +1 -1
  59. package/dist/components/shared/ui/logo.js +1 -1
  60. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  61. package/dist/consent-gate.js +2 -0
  62. package/dist/consent-theme.js +1 -0
  63. package/dist/context/consent-tracking-context.js +1 -1
  64. package/dist/context/index.js +1 -1
  65. package/dist/context.js +1 -1
  66. package/dist/devtools-launcher.js +2 -0
  67. package/dist/devtools.js +1 -1
  68. package/dist/draft-context.js +2 -0
  69. package/dist/draft.js +1 -1
  70. package/dist/external-iab-context.js +1 -1
  71. package/dist/gpp.js +2 -0
  72. package/dist/headless.js +1 -1
  73. package/dist/hooks/use-color-scheme.js +1 -1
  74. package/dist/hooks/use-committed-ref.js +2 -0
  75. package/dist/hooks/use-focus-trap.js +1 -1
  76. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  77. package/dist/hooks/use-iab-policy-guard.js +1 -0
  78. package/dist/hooks/use-late-entry.js +2 -0
  79. package/dist/hooks.js +1 -1
  80. package/dist/iab/styles.tw3.css +5 -45
  81. package/dist/iab-context.js +1 -1
  82. package/dist/index-parts/consent-banner.js +1 -0
  83. package/dist/index-parts/consent-dialog-link.js +1 -0
  84. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  85. package/dist/index-parts/consent-dialog.js +1 -0
  86. package/dist/index-parts/consent-gate.js +1 -0
  87. package/dist/index-parts/core.js +1 -0
  88. package/dist/index-parts/draft.js +1 -0
  89. package/dist/index-parts/frame.js +1 -0
  90. package/dist/index-parts/hooks.js +1 -0
  91. package/dist/index-parts/hosted.js +1 -0
  92. package/dist/index-parts/module-hooks.js +1 -0
  93. package/dist/index-parts/offline.js +1 -0
  94. package/dist/index-parts/presets.js +1 -0
  95. package/dist/index-parts/provider.js +1 -0
  96. package/dist/index-parts/theme.js +1 -0
  97. package/dist/index-parts/translations.js +1 -0
  98. package/dist/index.js +1 -1
  99. package/dist/kernel-selector.js +1 -0
  100. package/dist/modes.js +1 -0
  101. package/dist/module-hooks/iframe-blocker.js +1 -1
  102. package/dist/module-hooks/network-blocker.js +1 -1
  103. package/dist/module-hooks/network-hold.js +2 -0
  104. package/dist/module-hooks/persistence.js +1 -1
  105. package/dist/panel-link.js +1 -1
  106. package/dist/panel-trigger.js +1 -1
  107. package/dist/panel.js +1 -1
  108. package/dist/postcss-tailwind3.js +1 -0
  109. package/dist/preferences.js +1 -2
  110. package/dist/provider.js +1 -1
  111. package/dist/server/fetch-ssr-data.js +1 -1
  112. package/dist/server/headers.js +1 -1
  113. package/dist/server/normalize-url.js +1 -1
  114. package/dist/styles.tw3.css +33 -51
  115. package/dist/transports/hosted.js +1 -0
  116. package/dist/transports/manifest.js +1 -0
  117. package/dist/transports/offline.js +1 -1
  118. package/dist/utils/deferred-module.js +2 -0
  119. package/dist/utils/merge-slot-props.js +1 -1
  120. package/dist/utils/theme-utils.js +1 -1
  121. package/dist/version.js +1 -1
  122. package/dist-types/aggregate-components.d.ts +2 -2
  123. package/dist-types/chunk-warming.d.ts +45 -8
  124. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +5 -5
  125. package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
  126. package/dist-types/components/consent-gate/atoms.d.ts +11 -0
  127. package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
  128. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  129. package/dist-types/components/consent-gate/index.d.ts +25 -0
  130. package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
  131. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  132. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  133. package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
  134. package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
  135. package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
  136. package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
  137. package/dist-types/components/iab-panel/iab-panel.d.ts +3 -3
  138. package/dist-types/components/iab-panel/index.d.ts +29 -26
  139. package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
  140. package/dist-types/components/iab-prompt/iab-prompt.d.ts +3 -3
  141. package/dist-types/components/iab-prompt/index.d.ts +19 -19
  142. package/dist-types/components/panel/atoms/card.d.ts +5 -4
  143. package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
  144. package/dist-types/components/panel/index.d.ts +8 -8
  145. package/dist-types/components/panel/panel.d.ts +3 -3
  146. package/dist-types/components/panel-link/index.d.ts +1 -1
  147. package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
  148. package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
  149. package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
  150. package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
  151. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +25 -0
  152. package/dist-types/components/panel-trigger/index.d.ts +4 -4
  153. package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
  154. package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -17
  155. package/dist-types/components/panel-trigger/types.d.ts +1 -1
  156. package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
  157. package/dist-types/components/preferences/atoms/button.d.ts +1 -1
  158. package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
  159. package/dist-types/components/preferences/atoms/root.d.ts +1 -1
  160. package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
  161. package/dist-types/components/preferences/index.d.ts +14 -12
  162. package/dist-types/components/preferences/policy-actions.d.ts +2 -2
  163. package/dist-types/components/preferences/preferences.d.ts +1 -1
  164. package/dist-types/components/preferences/types.d.ts +1 -1
  165. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  166. package/dist-types/components/prompt/atoms/root.d.ts +4 -0
  167. package/dist-types/components/prompt/components.d.ts +9 -3
  168. package/dist-types/components/prompt/index.d.ts +12 -12
  169. package/dist-types/components/prompt/policy-actions.d.ts +2 -2
  170. package/dist-types/components/prompt/prompt.d.ts +5 -1
  171. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  172. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  173. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  174. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  175. package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
  176. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  177. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  178. package/dist-types/components/shared/primitives/button.d.ts +2 -2
  179. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  180. package/dist-types/components/shared/streamed-surface.d.ts +23 -0
  181. package/dist-types/components/shared/surface-styles.d.ts +36 -0
  182. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  183. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  184. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  185. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  186. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  187. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  188. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  189. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  190. package/dist-types/components/shared/ui/icon.d.ts +2 -1
  191. package/dist-types/components/shared/ui/logo.d.ts +0 -10
  192. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  193. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +13 -0
  194. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  195. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  196. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  197. package/dist-types/consent-gate.d.ts +1 -0
  198. package/dist-types/consent-theme.d.ts +38 -0
  199. package/dist-types/context/consent-tracking-context.d.ts +10 -0
  200. package/dist-types/context/index.d.ts +3 -3
  201. package/dist-types/context/theme-context.d.ts +3 -3
  202. package/dist-types/context.d.ts +25 -1
  203. package/dist-types/devtools-launcher.d.ts +29 -0
  204. package/dist-types/devtools.d.ts +13 -2
  205. package/dist-types/draft-context.d.ts +31 -0
  206. package/dist-types/draft.d.ts +81 -9
  207. package/dist-types/gpp.d.ts +27 -0
  208. package/dist-types/headless.d.ts +6 -7
  209. package/dist-types/hooks/use-committed-ref.d.ts +24 -0
  210. package/dist-types/hooks/use-focus-trap.d.ts +3 -1
  211. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  212. package/dist-types/hooks/use-iab-manager.d.ts +5 -5
  213. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  214. package/dist-types/hooks/use-late-entry.d.ts +14 -0
  215. package/dist-types/hooks/use-theme.d.ts +1 -1
  216. package/dist-types/hooks.d.ts +106 -11
  217. package/dist-types/iab.d.ts +7 -7
  218. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  219. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  220. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  221. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  222. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  223. package/dist-types/index-parts/core.d.ts +7 -0
  224. package/dist-types/index-parts/draft.d.ts +7 -0
  225. package/dist-types/index-parts/frame.d.ts +7 -0
  226. package/dist-types/index-parts/hooks.d.ts +7 -0
  227. package/dist-types/index-parts/hosted.d.ts +7 -0
  228. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  229. package/dist-types/index-parts/offline.d.ts +7 -0
  230. package/dist-types/index-parts/presets.d.ts +7 -0
  231. package/dist-types/index-parts/provider.d.ts +7 -0
  232. package/dist-types/index-parts/theme.d.ts +8 -0
  233. package/dist-types/index-parts/translations.d.ts +7 -0
  234. package/dist-types/index.d.ts +32 -28
  235. package/dist-types/kernel-selector.d.ts +68 -0
  236. package/dist-types/modes.d.ts +26 -0
  237. package/dist-types/module-hooks/iframe-blocker.d.ts +33 -1
  238. package/dist-types/module-hooks/network-blocker.d.ts +23 -0
  239. package/dist-types/module-hooks/network-hold.d.ts +47 -0
  240. package/dist-types/module-hooks.d.ts +4 -4
  241. package/dist-types/panel-link.d.ts +1 -1
  242. package/dist-types/panel-trigger.d.ts +1 -1
  243. package/dist-types/panel.d.ts +13 -1
  244. package/dist-types/postcss-tailwind3.d.ts +21 -0
  245. package/dist-types/preferences.d.ts +12 -1
  246. package/dist-types/primitives/accordion.d.ts +1 -1
  247. package/dist-types/primitives/button.d.ts +1 -1
  248. package/dist-types/primitives/collapsible.d.ts +1 -1
  249. package/dist-types/primitives/dialog.d.ts +1 -1
  250. package/dist-types/primitives/preference-item.d.ts +1 -1
  251. package/dist-types/primitives/switch.d.ts +1 -1
  252. package/dist-types/primitives/tabs.d.ts +1 -1
  253. package/dist-types/primitives.d.ts +7 -7
  254. package/dist-types/prompt.d.ts +1 -1
  255. package/dist-types/provider.d.ts +169 -28
  256. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  257. package/dist-types/server/headers.d.ts +14 -4
  258. package/dist-types/server/index.d.ts +5 -5
  259. package/dist-types/server/normalize-url.d.ts +14 -7
  260. package/dist-types/server/types.d.ts +12 -1
  261. package/dist-types/theme-provider.d.ts +2 -2
  262. package/dist-types/transports/hosted.d.ts +36 -0
  263. package/dist-types/transports/manifest.d.ts +48 -0
  264. package/dist-types/transports/offline.d.ts +7 -16
  265. package/dist-types/types/index.d.ts +3 -3
  266. package/dist-types/types/manager.d.ts +1 -1
  267. package/dist-types/types/theme/index.d.ts +1 -1
  268. package/dist-types/ui-config-context.d.ts +14 -2
  269. package/dist-types/utils/deferred-module.d.ts +13 -0
  270. package/dist-types/utils/index.d.ts +4 -4
  271. package/dist-types/utils/merge-slot-props.d.ts +17 -3
  272. package/dist-types/utils/merge-styles.d.ts +1 -1
  273. package/dist-types/utils/theme-utils.d.ts +18 -3
  274. package/dist-types/version.d.ts +1 -1
  275. package/docs/README.md +96 -70
  276. package/docs/assets/v3/bottom-bar.png +0 -0
  277. package/docs/assets/v3/brand-card.png +0 -0
  278. package/docs/assets/v3/brand-preferences.png +0 -0
  279. package/docs/assets/v3/choice-wall.png +0 -0
  280. package/docs/assets/v3/headless-bar-html.png +0 -0
  281. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  282. package/docs/assets/v3/headless-bar.png +0 -0
  283. package/docs/assets/v3/slim-bar.png +0 -0
  284. package/docs/concepts/choose-your-setup.md +138 -0
  285. package/docs/concepts/consent-categories.md +84 -0
  286. package/docs/concepts/consent-state.md +421 -0
  287. package/docs/concepts/data-fetching.md +270 -0
  288. package/docs/concepts/how-consent-works.md +123 -0
  289. package/docs/concepts/modes.md +278 -0
  290. package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +8 -60
  291. package/docs/customization/class-names.md +202 -0
  292. package/docs/customization/dark-mode.md +157 -0
  293. package/docs/customization/motion.md +128 -0
  294. package/docs/customization/overview.md +68 -33
  295. package/docs/customization/recipes.md +838 -47
  296. package/docs/customization/slots.md +217 -35
  297. package/docs/customization/stylesheets.md +248 -0
  298. package/docs/customization/tailwind.md +863 -0
  299. package/docs/customization/tokens.md +167 -36
  300. package/docs/customization/translations.md +87 -3
  301. package/docs/frameworks/react/callbacks.md +118 -0
  302. package/docs/frameworks/react/clear-on-revocation.md +106 -0
  303. package/docs/frameworks/react/components/consent-banner.md +48 -27
  304. package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
  305. package/docs/frameworks/react/components/consent-dialog-trigger.md +86 -76
  306. package/docs/frameworks/react/components/consent-dialog.md +111 -58
  307. package/docs/frameworks/react/components/{frame.md → consent-gate.md} +70 -49
  308. package/docs/frameworks/react/components/consent-provider.md +140 -0
  309. package/docs/frameworks/react/components/consent-widget.md +69 -24
  310. package/docs/frameworks/react/components/dev-tools.md +76 -85
  311. package/docs/frameworks/react/components.md +113 -0
  312. package/docs/frameworks/react/compose.md +354 -0
  313. package/docs/frameworks/react/content-security-policy.md +143 -0
  314. package/docs/frameworks/react/customize.md +258 -0
  315. package/docs/frameworks/react/embeds.md +89 -0
  316. package/docs/frameworks/react/gpp.md +163 -0
  317. package/docs/frameworks/react/headless.md +4 -4
  318. package/docs/frameworks/react/hooks.md +70 -0
  319. package/docs/frameworks/react/iab.md +259 -0
  320. package/docs/frameworks/react/network-blocker.md +140 -0
  321. package/docs/frameworks/react/quickstart.md +220 -102
  322. package/docs/frameworks/react/rendering.md +263 -0
  323. package/docs/frameworks/react/scripts.md +114 -0
  324. package/docs/frameworks/react/translations.md +155 -0
  325. package/docs/frameworks/react/troubleshooting.md +188 -61
  326. package/docs/frameworks/react/upgrade-v3.md +651 -0
  327. package/docs/frameworks/react/vendor-consent.md +188 -0
  328. package/docs/guides/banner-experiments.md +672 -0
  329. package/docs/guides/troubleshooting.md +192 -54
  330. package/docs/guides/verify-consent.md +96 -49
  331. package/docs/integrations/adobe-analytics.md +177 -183
  332. package/docs/integrations/ahrefs-analytics.md +162 -178
  333. package/docs/integrations/amplitude.md +172 -180
  334. package/docs/integrations/building-integrations.md +137 -33
  335. package/docs/integrations/clearbit.md +164 -178
  336. package/docs/integrations/cloudflare-web-analytics.md +166 -180
  337. package/docs/integrations/cloudflare-zaraz.md +333 -0
  338. package/docs/integrations/crisp.md +174 -182
  339. package/docs/integrations/databuddy.md +167 -197
  340. package/docs/integrations/fathom-analytics.md +168 -180
  341. package/docs/integrations/front-chat.md +308 -0
  342. package/docs/integrations/google-maps.md +142 -108
  343. package/docs/integrations/google-tag-manager.md +258 -185
  344. package/docs/integrations/google-tag.md +226 -182
  345. package/docs/integrations/heap.md +173 -179
  346. package/docs/integrations/hightouch.md +171 -181
  347. package/docs/integrations/hotjar.md +169 -179
  348. package/docs/integrations/intercom.md +193 -177
  349. package/docs/integrations/klaviyo.md +473 -0
  350. package/docs/integrations/linkedin-insights.md +184 -174
  351. package/docs/integrations/logrocket.md +170 -180
  352. package/docs/integrations/matomo-analytics.md +198 -202
  353. package/docs/integrations/meta-pixel.md +198 -174
  354. package/docs/integrations/microsoft-clarity.md +173 -179
  355. package/docs/integrations/microsoft-uet.md +158 -178
  356. package/docs/integrations/mixpanel-analytics.md +165 -184
  357. package/docs/integrations/one-dollar-stats.md +292 -0
  358. package/docs/integrations/openai-pixel.md +214 -325
  359. package/docs/integrations/overview.md +152 -83
  360. package/docs/integrations/pinterest-tag.md +315 -0
  361. package/docs/integrations/pirsch.md +179 -183
  362. package/docs/integrations/plausible-analytics.md +182 -182
  363. package/docs/integrations/posthog.md +323 -264
  364. package/docs/integrations/promptwatch.md +164 -178
  365. package/docs/integrations/reddit-pixel.md +195 -181
  366. package/docs/integrations/rudderstack.md +211 -210
  367. package/docs/integrations/rybbit-analytics.md +181 -184
  368. package/docs/integrations/segment.md +192 -178
  369. package/docs/integrations/sentry.md +402 -0
  370. package/docs/integrations/snapchat-pixel.md +196 -180
  371. package/docs/integrations/tiktok-pixel.md +181 -174
  372. package/docs/integrations/umami-analytics.md +173 -182
  373. package/docs/integrations/vercel-analytics.md +176 -181
  374. package/docs/integrations/x-pixel.md +186 -174
  375. package/docs/integrations/youtube.md +150 -111
  376. package/package.json +48 -17
  377. package/dist/component-hooks/use-manager.js +0 -2
  378. package/dist/components/frame/atoms.js +0 -1
  379. package/dist/components/frame/frame.js +0 -2
  380. package/dist/components/frame/index.js +0 -2
  381. package/dist/frame.js +0 -2
  382. package/dist/ui-save.js +0 -1
  383. package/dist-types/component-hooks/use-manager.d.ts +0 -40
  384. package/dist-types/components/frame/atoms.d.ts +0 -11
  385. package/dist-types/components/frame/frame.d.ts +0 -2
  386. package/dist-types/components/frame/index.d.ts +0 -24
  387. package/dist-types/frame.d.ts +0 -1
  388. package/dist-types/ui-save.d.ts +0 -5
  389. package/docs/assets/v3/brand-bar.png +0 -0
  390. package/docs/assets/v3/mobile-card.png +0 -0
  391. package/docs/assets/v3/preferences.png +0 -0
  392. package/docs/frameworks/react/components/consent-manager-provider.md +0 -71
  393. package/docs/frameworks/react/concepts/consent-categories.md +0 -33
  394. package/docs/frameworks/react/hooks/use-consent-manager/overview.md +0 -58
  395. package/docs/frameworks/react/iab/overview.md +0 -53
  396. package/docs/frameworks/react/script-loader.md +0 -63
  397. package/docs/frameworks/react/styling/overview.md +0 -217
  398. package/docs/guides/consent-state.md +0 -60
  399. package/docs/guides/data-fetching.md +0 -163
  400. package/docs/guides/deployment-modes.md +0 -63
  401. package/docs/integrations/clear-on-revocation.md +0 -167
  402. package/docs/upgrade-v3.md +0 -334
  403. package/readme.json +0 -48
  404. package/src/iab/styles.css +0 -11
  405. package/src/styles.css +0 -10
  406. /package/dist/components/{frame → consent-gate}/types.js +0 -0
@@ -0,0 +1,278 @@
1
+ ---
2
+ title: Consent modes
3
+ description: Reference for manifest(), hosted() and offline() in every c15t
4
+ framework package, where each one resolves the visitor's policy, what it adds
5
+ to first-load JavaScript, the build-time manifest fetch and its failure
6
+ policy, and the backend URL variable each framework reads.
7
+ group: concepts
8
+ ---
9
+
10
+ ## Choose a mode
11
+
12
+ A mode decides how c15t finds the policy that applies to a visitor. Every
13
+ framework package exports the same three factories, `manifest()`, `hosted()`
14
+ and `offline()`. Consent choices go to the backend in `manifest()` and
15
+ `hosted()`, whether that backend is [Inth](https://inth.com) or a
16
+ [self-hosted c15t backend](https://v3.c15t.com/docs/self-host/quickstart).
17
+
18
+ | Mode | Where the policy resolves | Backend requests on a first visit | Use it when |
19
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
20
+ | `manifest()`, the default | Your server, from the policy the build downloaded. With `resolve: 'browser'`, or in a single-page app, the browser. | None to resolve the policy. Choices post to `/subjects`. | Most apps. Rebuild to ship policy changes, or set `source: 'runtime'`. |
21
+ | `hosted()` | The backend's `GET /init` | One `/init` per page that resolves consent. | Static hosting without a server, or a policy that changes without a rebuild and needs the backend's geolocation. |
22
+ | `offline()` | The browser, from rules in your bundle | None. Choices stay in the browser. | Local development, tests and demos. Not recommended for production environments. |
23
+
24
+ `manifest()` is the default in Next.js, TanStack Start, Nuxt, Astro and
25
+ SvelteKit. React, Vue, Svelte and `@c15t/browser` have no default: pass a
26
+ mode yourself. Their quickstarts use `manifest()`. The script tag picks its
27
+ mode from the bundle file; see
28
+ [choose a mode](https://v3.c15t.com/docs/frameworks/html/attributes-and-api#choose-a-mode).
29
+
30
+ [Data fetching](./data-fetching.md) compares these paths with the
31
+ custom transport and same-origin proxies.
32
+
33
+ ## Import the factories from your framework package
34
+
35
+ There are two kinds of factory, and each framework package exports the kind it
36
+ needs under the same three names.
37
+
38
+ | Framework | Import from | Pass the mode to | Kind |
39
+ | -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------- |
40
+ | Next.js | `c15t/next` | `mode` in `c15t.config.ts` | Data |
41
+ | TanStack Start | `c15t/tanstack-start` | `createConsentStateHandler({ mode })` | Data |
42
+ | Nuxt | `c15t/vue` | `c15t.mode` in `nuxt.config.ts` | Data |
43
+ | Astro | `c15t/astro` | `c15t({ mode })` in `astro.config.mjs` | Data |
44
+ | SvelteKit | `@c15t/svelte/kit` | `c15tHandle({ mode })` in `src/hooks.server.ts` | Data |
45
+ | React | `c15t/react` | `ConsentProvider`'s `options.mode` | Transport |
46
+ | Vue | `c15t/vue/vue-plugin` | `app.use(c15tVue, { mode })` | Transport |
47
+ | Svelte | `@c15t/svelte` | `<ConsentProvider mode={...}>` | Transport |
48
+ | JavaScript | `@c15t/browser` | `init({ mode })` | Transport |
49
+ | Script tag | none | The bundle file: `c15t.js` is hosted, `c15t.offline.js` offline. The headless and IAB scripts also take `data-mode="manifest"`. | Name |
50
+
51
+ **Data factories** return a plain object such as `{ type: 'manifest' }`.
52
+ Server-rendered frameworks read their config on the server and in the
53
+ browser, and Nuxt and Astro serialize it, so the mode has to be data. The
54
+ framework turns it into a transport in the browser and loads the code of
55
+ any mode other than the default with `import()`. The same factories are
56
+ available from `c15t/modes` (or `@c15t/core/modes`) for shared code.
57
+
58
+ **Transport factories** return the transport itself, with the same `type` and
59
+ options attached as properties. A single-page app has one environment, so
60
+ the bundler keeps only the mode you import and drops the others.
61
+
62
+ A server-rendered root that receives a transport factory still works, for
63
+ example `hosted()` from `c15t/react` passed to Next.js `ConsentRoot` as
64
+ `options.mode`. Outside production it warns that the transport's code is now
65
+ in the first-load bundle. `custom(transport)` exists only as a transport, so
66
+ it cannot go in a serializable config. Pass it through the root's
67
+ `options.mode` instead.
68
+
69
+ ## manifest()
70
+
71
+ `manifest()` resolves the visitor's policy from the backend's public consent
72
+ manifest instead of asking the backend's `/init`. A
73
+ [consent manifest](./data-fetching.md#what-is-a-consent-manifest)
74
+ holds your policy rules and copy, and no visitor data.
75
+
76
+ | Option | Default | Behavior |
77
+ | ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
78
+ | `source` | `'build'` | `'build'` uses the snapshot the build integration downloaded. Without one, such as in dev after a failed fetch, it fetches the manifest at runtime. `'runtime'` always fetches at runtime, so policy edits apply without a rebuild. |
79
+ | `snapshot` | none | A manifest you supply. You can pass `snapshot` or `source`, not both. |
80
+ | `resolve` | `'server'` | `'server'` resolves each visitor on the server and ships no resolver to the browser. `'browser'` resolves in the browser, for pages the server does not render per visitor. Server-rendered frameworks only. |
81
+ | `manifestURL` | see below | Where to fetch the manifest at runtime. On the server, `${backendURL}/manifest`. In the browser, `${routePrefix}/manifest`, else `${backendURL}/manifest`. |
82
+ | `geoURL` | none | A same-origin route that answers with the visitor's `{ country, region }`. Browser resolution only. |
83
+ | `inputs` | none | `{ country, region }` when the page already knows the visitor's location, for example from an edge worker. Browser resolution only. |
84
+
85
+ The transport `manifest()` from `c15t/react`, `c15t/vue/vue-plugin`,
86
+ `@c15t/svelte` and `@c15t/browser` always resolves in the browser, so it has
87
+ no `resolve` option. It also takes `backendURL`, `headers`, `credentials`,
88
+ `domain`, `fetch` and `initFallback`. With no options it reads the snapshot
89
+ and the backend URL your build integration downloaded. With `manifestURL`,
90
+ the browser fetches that URL when the page loads and the build's snapshot is
91
+ not used. Saves still go to `backendURL`, or the build's backend URL.
92
+
93
+ When the policy depends on where the visitor is, and the browser does not
94
+ know the country or region, browser resolution asks `geoURL`, then the
95
+ backend's `/init`. Set `initFallback: false` to resolve for an unknown location
96
+ instead. A policy with location rules therefore saves no request in a
97
+ single-page app unless you pass `inputs` or `geoURL`. The `consentManifest()`
98
+ plugins from `c15t/build`, `c15t/vue/vite` and `@c15t/svelte/vite` warn
99
+ about this when a single-page app build bundles such a policy.
100
+
101
+ ## hosted()
102
+
103
+ `hosted()` asks the backend's `GET /init` for each visitor's policy and posts
104
+ choices to `${backendURL}/subjects`.
105
+
106
+ | Option | Default | Behavior |
107
+ | ------------ | --------------------------- | ------------------------------------------------------ |
108
+ | `backendURL` | the framework's backend URL | Backend URL, absolute or relative such as `/api/c15t`. |
109
+ | `headers` | none | Headers sent with `GET /init`. |
110
+
111
+ The transport `hosted()` also takes `initURL`, `assertDecisionInputs`,
112
+ `domain` and `fetch`. `initURL` sends `GET /init` to another URL, usually a
113
+ same-origin route that resolves the manifest, while saves keep going to
114
+ `${backendURL}/subjects`. `assertDecisionInputs` defaults to `true` when
115
+ `initURL` is set, so each save carries the policy decision it was made
116
+ against and the backend can reject a save made against a stale policy.
117
+
118
+ `hosted()` from `c15t` (`@c15t/core`) is the plain transport and requires
119
+ `backendURL`. The framework exports fill it in from your config or your
120
+ build.
121
+
122
+ ## offline()
123
+
124
+ Not recommended for production environments.
125
+
126
+ `offline()` resolves policy rules in the browser and stores choices in browser
127
+ storage. Nothing reaches a backend, so there is no consent record, no
128
+ cross-device history and no IP geolocation.
129
+
130
+ | Option | Default | Behavior |
131
+ | ------------- | ------------------------- | ----------------------------------------------------------- |
132
+ | `policyRules` | the recommended rule pack | Rules to resolve. Passing rules replaces the pack entirely. |
133
+
134
+ Unknown country and region are real inputs: test the missing-location case
135
+ against your rules. See [policy rules](./policies.md).
136
+
137
+ ## What each mode adds to first-load JavaScript
138
+
139
+ In a server-rendered framework, the browser receives the resolved state with
140
+ the page. It only needs the code to save a choice, and loads the rest when it
141
+ runs:
142
+
143
+ | Mode | First-load JavaScript | Loaded with `import()` when it runs |
144
+ | ---------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
145
+ | `manifest()` | The code that saves choices and builds an init request | The hosted init path, if the browser has to resolve consent again |
146
+ | `manifest({ resolve: 'browser' })` | The code that saves choices | The browser resolver, English copy and the fetch of `${routePrefix}/manifest` or `${backendURL}/manifest`. Other languages load per language. |
147
+ | `hosted()` | The code that saves choices | The hosted init path |
148
+ | `offline()` | Nothing mode-specific | The offline transport, the rule pack and its copy |
149
+
150
+ With `manifest()` resolved on the server, the manifest snapshot, the resolver,
151
+ the offline rule pack and other languages stay out of the browser bundle.
152
+
153
+ In a single-page app the bundle contains the mode you imported:
154
+
155
+ | Mode | In the bundle |
156
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
157
+ | `manifest()` | The snapshot, when the build downloaded one, and the code that saves choices. The resolver starts loading when `manifest()` runs, unless the policy needs a location the page does not know. English copy is bundled; other languages load the first time a visitor needs one. |
158
+ | `hosted()` | The hosted transport |
159
+ | `offline()` | The offline transport, the recommended rule pack and its copy |
160
+
161
+ ## Set the backend URL
162
+
163
+ Each framework reads its backend URL from a public environment variable, so
164
+ the quickstarts never write it in code. Put it in `.env` or your host's build
165
+ settings. The value is public configuration, not a secret.
166
+
167
+ | Framework | Variable | Inth alternative |
168
+ | --------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
169
+ | Next.js | `NEXT_PUBLIC_C15T_BACKEND_URL` | `NEXT_PUBLIC_INTH_PROJECT_URL` |
170
+ | Nuxt | `NUXT_PUBLIC_C15T_BACKEND_URL` | `NUXT_PUBLIC_INTH_PROJECT_URL` |
171
+ | Astro | `PUBLIC_C15T_BACKEND_URL` | `PUBLIC_INTH_PROJECT_URL` |
172
+ | SvelteKit and Svelte | `PUBLIC_C15T_BACKEND_URL`, then `VITE_C15T_BACKEND_URL` | `PUBLIC_INTH_PROJECT_URL`, then `VITE_INTH_PROJECT_URL` |
173
+ | TanStack Start, React, Vue and JavaScript with Vite | `VITE_C15T_BACKEND_URL` | `VITE_INTH_PROJECT_URL` |
174
+ | Script tag | `data-backend-url` on the script element | – |
175
+
176
+ Set either variable. The Inth one holds your Inth project URL, so other Inth
177
+ SDKs in the same app can read it too. When both are set, the c15t variable
178
+ wins.
179
+
180
+ A backend URL resolves in this order: the mode's own `backendURL`, then the
181
+ framework's top-level `backendURL` option, then the c15t variable, then the
182
+ Inth variable. The build reads each variable from the process environment,
183
+ then from `.env.[mode].local`, `.env.[mode]`, `.env.local` and `.env` in the
184
+ project root, so a c15t variable in `.env` still beats an Inth variable in
185
+ the environment. The Vite plugins set an unset `VITE_C15T_BACKEND_URL` to the
186
+ URL they used, so `import.meta.env.VITE_C15T_BACKEND_URL` in app code reads
187
+ the same value. `withConsentManifest()` does the same for
188
+ `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js.
189
+
190
+ Next.js inlines `NEXT_PUBLIC_` variables at build time. Changing the variable
191
+ on a built app has no effect until you rebuild.
192
+
193
+ ## Download the manifest at build time
194
+
195
+ `manifest()` with the default `source: 'build'` resolves from a snapshot that a
196
+ build integration downloads from `${backendURL}/manifest`:
197
+
198
+ | Framework | Build integration | Name in messages |
199
+ | -------------------- | ------------------------------------------------------------------- | ---------------------------- |
200
+ | Next.js | `withConsentManifest()` from `c15t/next/build`, in `next.config.ts` | `@c15t/nextjs/build` |
201
+ | TanStack Start | `consentManifest()` from `c15t/tanstack-start/build` | `@c15t/tanstack-start/build` |
202
+ | React and JavaScript | `consentManifest()` from `c15t/build` | `@c15t/core/build` |
203
+ | Vue | `consentManifest()` from `c15t/vue/vite` | `@c15t/vue/vite` |
204
+ | Svelte and SvelteKit | `consentManifest()` from `@c15t/svelte/vite` | `@c15t/svelte/vite` |
205
+ | Nuxt | the `c15t/vue` module | `@c15t/vue` |
206
+ | Astro | the `c15t()` integration | `@c15t/astro` |
207
+
208
+ The integration downloads the manifest when the production build or the dev
209
+ server starts, and waits at most 10 seconds. Nothing is written into your
210
+ source tree. App code reads the result from `c15t/generated`:
211
+
212
+ ```ts
213
+ import { backendURL, snapshot } from 'c15t/generated';
214
+ ```
215
+
216
+ Both exports are `undefined` when the build has no snapshot, so imports
217
+ compile on a fresh checkout. In the browser bundle of a server-rendered
218
+ framework, `snapshot` is always `undefined`. Next.js goes further: a client
219
+ component that imports `c15t/generated` fails the build.
220
+
221
+ Rebuild after you change policies, translations or vendors in your project. If
222
+ your CI caches build output, force a fresh build.
223
+
224
+ ## What happens when the download fails
225
+
226
+ The same policy applies in every framework:
227
+
228
+ | Command | Default when the download fails or no backend URL is set |
229
+ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
230
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
231
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning. The server, or a single-page app's browser, fetches the manifest at runtime. |
232
+
233
+ `onBuildError` picks one behavior for both commands:
234
+
235
+ * `'fail'` stops dev too.
236
+ * `'runtime'` lets a production build finish without a snapshot. The manifest
237
+ is then fetched at runtime.
238
+
239
+ Pass it where you configure the integration: the second argument of
240
+ `withConsentManifest()`, `consentManifest({ onBuildError })`, the `c15t` key
241
+ in `nuxt.config.ts`, or `c15t({ onBuildError })` in Astro.
242
+
243
+ The `C15T_ON_BUILD_ERROR` environment variable overrides the option, so you
244
+ can deploy during a backend outage without a code change:
245
+
246
+ ```sh
247
+ C15T_ON_BUILD_ERROR=runtime npm run build
248
+ ```
249
+
250
+ Any value other than `fail` or `runtime` stops the command. Turborepo's strict
251
+ environment mode hides undeclared variables from tasks, so list
252
+ `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
253
+
254
+ A missing backend URL follows the same policy as a failed download: a
255
+ production build stops, dev warns, and an explicit `'runtime'` logs a notice
256
+ and continues. Astro and Nuxt are stricter in `manifest()` mode, including
257
+ `manifest({ snapshot })`: the browser saves consent to the backend, so a
258
+ missing backend URL stops `astro dev` and `nuxt dev` as well.
259
+
260
+ The download is skipped without an error when the build cannot use a
261
+ snapshot:
262
+
263
+ * The backend URL is relative, such as `/api/c15t`. With an explicit
264
+ `onBuildError: 'fail'`, a relative URL stops the build instead.
265
+ * Next.js builds with `output: 'export'`, which has no server.
266
+ * The mode is `hosted()`, `offline()`, `manifest({ snapshot })` or
267
+ `manifest({ source: 'runtime' })` in Next.js, Nuxt and Astro. Next.js reads
268
+ the mode from `c15t.config.ts`.
269
+ * A single-page app built with Vite (React, Vue, Svelte, JavaScript) does
270
+ not use `manifest()`. The mode is set in app code, so the plugin downloads
271
+ the manifest only once the bundle turns out to read it, after unused code
272
+ is dropped. A `hosted()` or `offline()` build never contacts the backend.
273
+ SvelteKit and TanStack Start choose the mode on the server at runtime, so
274
+ their builds always download it.
275
+
276
+ A failed download never reuses an older snapshot. The
277
+ [troubleshooting guide](../guides/troubleshooting.md#why-does-the-build-fail-to-download-the-manifest)
278
+ lists each message and its fix.
@@ -1,8 +1,8 @@
1
1
  ---
2
- title: Policy presets
3
- description: Understand how policy rules affect React prompts, permissions and
4
- persistent privacy controls.
5
- group: frameworks
2
+ title: Policies
3
+ description: How policy models, prompts and scope decide what c15t asks
4
+ visitors, where to change the rules, and why a banner may not appear.
5
+ group: concepts
6
6
  ---
7
7
 
8
8
  ## What the policy controls
@@ -29,20 +29,20 @@ must do the same.
29
29
  Use `effectivePermissions` to gate a script or embed. A `true` value under an
30
30
  opt-out rule does not mean the visitor clicked Accept. Read `explicitChoice`
31
31
  when you need the visitor's recorded decision. See
32
- [consent state](../../../guides/consent-state.md) for these distinctions.
32
+ [how consent works](./how-consent-works.md#a-permission-is-not-a-recorded-choice) for these distinctions.
33
33
 
34
34
  ## Where to change the rules
35
35
 
36
36
  For Inth, configure policy rules on your hosted project. The app receives those
37
- rules through the configured [data-fetching path](../../../guides/data-fetching.md). Importing
37
+ rules through the configured [data-fetching path](./data-fetching.md). Importing
38
38
  `recommendedPolicyRules()` or changing browser styling does not override the
39
39
  hosted policy.
40
40
 
41
41
  For a backend you operate, use the
42
- [policy packs guide](https://c15t.com/docs/self-host/guides/policy-packs). It covers
42
+ [policy packs guide](https://v3.c15t.com/docs/self-host/guides/policy-packs). It covers
43
43
  `manifest.policyRules`, preset selection and custom matchers. Browser-only
44
44
  setups own their rules locally; see
45
- [deployment modes](../../../guides/deployment-modes.md).
45
+ [choose your setup](./choose-your-setup.md).
46
46
 
47
47
  Presets are starting configurations. Select them for your actual processing,
48
48
  and review their assumptions before allowing optional categories by default.
@@ -69,55 +69,3 @@ default for other known locations. An unknown country uses its strict opt-in
69
69
  fallback. A US country with a missing state gets the US opt-out fallback.
70
70
  These are the local pack's defaults, not a promise about your Inth project.
71
71
  Browser-only mode does not discover a visitor's country from their IP address.
72
-
73
- ## Inspect the active policy
74
-
75
- Place this diagnostic component inside your existing consent
76
- provider. It reads state without recording a choice.
77
-
78
- ```tsx title="src/consent-policy-debug.tsx"
79
- import { useSnapshot } from 'c15t/react';
80
-
81
- export function ConsentPolicyDebug() {
82
- const snapshot = useSnapshot();
83
- const resolution = snapshot.resolution;
84
-
85
- return (
86
- <pre>
87
- {JSON.stringify(
88
- {
89
- status: resolution.status,
90
- policy: resolution.status === 'matched' ? resolution.policy.id : null,
91
- prompt: snapshot.promptRequirement,
92
- permissions: snapshot.effectivePermissions,
93
- location: snapshot.location,
94
- privacySignals: snapshot.privacySignals,
95
- },
96
- null,
97
- 2,
98
- )}
99
- </pre>
100
- );
101
- }
102
- ```
103
-
104
- The diagnostic subscribes to consent changes. Remove it after verification.
105
- For a production feature gate, use `useConsent('marketing')` or the category
106
- your feature needs instead of subscribing to the whole snapshot.
107
-
108
- ## Change appearance without changing policy
109
-
110
- Keep one banner and one dialog in your existing
111
- provider. Use
112
- [styling](../styling/overview.md) to change their appearance,
113
- or [headless UI](../headless.md) for custom markup.
114
- Presentation settings change layout and emphasis; policy rules decide which
115
- actions and privacy rights the app must expose.
116
-
117
- ## Verify policy behavior
118
-
119
- Use a fresh browser profile to check the intended location, then repeat with a
120
- saved rejection and GPC enabled. Confirm that the expected prompt and
121
- preferences controls appear, and that denied scripts and embeds make no
122
- requests. Test missing location inputs too. Follow
123
- [verify consent](../../../guides/verify-consent.md) for the full flow.
@@ -0,0 +1,202 @@
1
+ ---
2
+ title: Class names and CSS-in-JS
3
+ description: Style c15t's component parts with CSS Modules, vanilla-extract,
4
+ StyleX, Emotion or plain class names, and see which approach works in each
5
+ framework.
6
+ group: customization
7
+ ---
8
+
9
+ ## Pass a class to a part
10
+
11
+ CSS Modules, vanilla-extract, StyleX, Emotion and Tailwind all end up as a class
12
+ on an element and a rule in a stylesheet. Give c15t the class through your
13
+ framework's part API, and the rule wins over c15t's own rule for that part:
14
+ c15t's rules sit in the `components` cascade layer, and an unlayered rule from
15
+ your stylesheet outranks any layered one.
16
+
17
+ Each example below adds a 3px colored border and 4px corners to the banner card.
18
+ The React examples run as Storybook stories in CI, which check that each class
19
+ lands on the card and that the computed border comes from the class, not from
20
+ c15t's own card rule. The script tag examples run in the example app's browser
21
+ tests.
22
+
23
+ | Framework | Part API | Class key | Inline `style` | Where the class's CSS must load |
24
+ | ------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------- | -------------- | ------------------------------------------------------------------------------------ |
25
+ | Next.js, TanStack Start, React | `components.<component>.<part>`, or `theme.slots` | `className` | Yes | Anywhere on the page |
26
+ | Nuxt, Vue | `components.<component>.<part>`, or `theme.slots` | `class` (`className` in `theme.slots`) | Yes | Anywhere on the page |
27
+ | Svelte, SvelteKit | `theme.slots`, or `class` on a component | A string, or `className` in a slot object | Yes | A global stylesheet, or `:global()` |
28
+ | Astro | `theme.slots` in the integration options, or `class` on `ConsentBanner` | A string, or `className` in a slot object | Yes | A global stylesheet |
29
+ | HTML, JavaScript | `ui.theme.slots` | A string, or `className` in a slot object | Yes | Inside the shadow root through `ui.stylesheetURLs`, or anywhere with `shadow: false` |
30
+
31
+ [Component parts](./slots.md) lists the parts and their keys in
32
+ each framework.
33
+
34
+ ## CSS Modules
35
+
36
+ Import the module and pass its class. This works wherever your bundler compiles
37
+ CSS Modules:
38
+
39
+ ```css title="src/banner.module.css"
40
+ .card {
41
+ border: 3px solid rgb(219 39 119);
42
+ border-radius: 4px;
43
+ }
44
+ ```
45
+
46
+ ```ts title="src/consent-components.ts"
47
+ import type { ConsentProviderOptions } from 'c15t/react';
48
+
49
+ import styles from './banner.module.css';
50
+
51
+ /** Pass as `components` in your ConsentProvider options. */
52
+ export const components: ConsentProviderOptions['components'] = {
53
+ banner: { card: { className: styles.card } },
54
+ };
55
+ ```
56
+
57
+ Pass `components` in your `ConsentProvider` options, or in `options` on
58
+ `ConsentRoot` in Next.js and TanStack Start.
59
+
60
+ ## vanilla-extract
61
+
62
+ vanilla-extract compiles `style()` calls in `.css.ts` files to static CSS at
63
+ build time. Add its plugin for your bundler, such as
64
+ `@vanilla-extract/vite-plugin`, then pass the class:
65
+
66
+ ```ts title="src/consent-components.css.ts"
67
+ import { style } from '@vanilla-extract/css';
68
+ import type { ConsentProviderOptions } from 'c15t/react';
69
+
70
+ const card = style({ border: '3px solid rgb(37 99 235)', borderRadius: 4 });
71
+
72
+ /** Pass as `components` in your ConsentProvider options. */
73
+ export const components: ConsentProviderOptions['components'] = {
74
+ banner: { card: { className: card } },
75
+ };
76
+ ```
77
+
78
+ ## StyleX
79
+
80
+ `stylex.props()` returns `className` and, for dynamic values, `style`. A React
81
+ part accepts both, so spread the result into the part. Add the StyleX compiler
82
+ plugin for your bundler, such as `@stylexjs/unplugin`:
83
+
84
+ ```ts title="src/consent-components.ts"
85
+ import * as stylex from '@stylexjs/stylex';
86
+ import type { ConsentProviderOptions } from 'c15t/react';
87
+
88
+ const styles = stylex.create({
89
+ card: {
90
+ borderColor: 'rgb(22 163 74)',
91
+ borderRadius: 4,
92
+ borderStyle: 'solid',
93
+ borderWidth: 3,
94
+ },
95
+ });
96
+
97
+ /**
98
+ * Pass as `components` in your ConsentProvider options. `stylex.props()`
99
+ * returns `className` and, for dynamic styles, `style`; the slot takes both.
100
+ */
101
+ export const components: ConsentProviderOptions['components'] = {
102
+ banner: { card: stylex.props(styles.card) },
103
+ };
104
+ ```
105
+
106
+ StyleX writes atomic classes into an unlayered stylesheet, so they outrank
107
+ c15t's layered rules without extra specificity.
108
+
109
+ ## Emotion
110
+
111
+ `css()` from `@emotion/css` returns a class name and inserts its rule into a
112
+ `<style>` element in the document `<head>` when your code runs:
113
+
114
+ ```ts title="src/consent-components.ts"
115
+ import { css } from '@emotion/css';
116
+ import type { ConsentProviderOptions } from 'c15t/react';
117
+
118
+ const card = css({ border: '3px solid rgb(234 88 12)', borderRadius: 4 });
119
+
120
+ /** Pass as `components` in your ConsentProvider options. */
121
+ export const components: ConsentProviderOptions['components'] = {
122
+ banner: { card: { className: card } },
123
+ };
124
+ ```
125
+
126
+ Emotion inserts its rules into the page, not into a shadow root. With the HTML
127
+ script tag or `@c15t/browser`, set `ui: { shadow: false }` so the UI renders
128
+ in the page where those rules reach it. `ui.stylesheetURLs` cannot carry them,
129
+ because they have no URL.
130
+
131
+ ## Use other frameworks' part APIs
132
+
133
+ Vue and Nuxt bind a part's object to the element with `v-bind`, so write
134
+ `class` rather than `className`. A class string from CSS Modules,
135
+ vanilla-extract or Emotion works the same way. To use StyleX, map its
136
+ `className` to `class`.
137
+
138
+ Svelte scopes component styles, so a class defined in a component's `<style>`
139
+ block does not reach a c15t part. Define it in a global stylesheet or with
140
+ `:global(.your-class)`. `theme.slots` accepts a string or
141
+ `{ className, style }`, and `class` on `ConsentBanner` goes on the banner root.
142
+
143
+ Astro serializes its integration options, so `theme.slots` in
144
+ `astro.config.mjs` takes plain strings and objects. Use class names from a
145
+ global stylesheet or Tailwind there. Build-time tools that export class names
146
+ from a module, such as CSS Modules, cannot reach `astro.config.mjs`.
147
+
148
+ ## Style the script tag's shadow root
149
+
150
+ The script tag and `init()` from `@c15t/browser` render into a shadow root. A
151
+ class from your page's stylesheet reaches a part only if the stylesheet reaches
152
+ the shadow root. Pick one:
153
+
154
+ * **`ui.stylesheetURLs`.** c15t links each URL inside the shadow root, after
155
+ its own stylesheet. Link the stylesheet that holds your classes, such as your
156
+ CSS Modules or Tailwind build.
157
+ * **`::part()`.** Every part with a slot key carries it in a `part` attribute.
158
+ Page CSS can style it with `[data-c15t-ui]::part(consentBannerCard)`,
159
+ without any class.
160
+ * **`ui.css`.** A string of CSS that c15t adds inside the shadow root.
161
+ * **`shadow: false`.** c15t renders into the page and every page stylesheet
162
+ applies, including Emotion's.
163
+
164
+ This example links a Tailwind build into the shadow root and puts utilities on
165
+ two parts:
166
+
167
+ ```html title="index.html"
168
+ <link
169
+ rel="stylesheet"
170
+ href="/tailwind.css"
171
+ />
172
+ <script>
173
+ window.c15t = window.c15t || [];
174
+ c15t.push([
175
+ 'config',
176
+ {
177
+ ui: {
178
+ // The banner renders in a shadow root, where the page's
179
+ // stylesheets do not reach. Link your Tailwind build into it
180
+ // too, and keep the page's own link: Tailwind 4 registers
181
+ // variables with @property, which only works in the page.
182
+ stylesheetURLs: ['/tailwind.css'],
183
+ theme: {
184
+ slots: {
185
+ consentBannerCard: 'rounded-none border-4 border-sky-600',
186
+ consentBannerTitle: 'uppercase tracking-wide',
187
+ },
188
+ },
189
+ },
190
+ },
191
+ ]);
192
+ </script>
193
+ ```
194
+
195
+ ## Check the result
196
+
197
+ 1. Open the page in a private window so the banner shows.
198
+ 2. Select the banner card in the Elements panel. It carries your class.
199
+ 3. In the Styles panel, your rule applies and c15t's rule for the same
200
+ property is struck out. If your class is on the element but its rule is
201
+ missing, the stylesheet does not reach the part. Check the "Where the class's CSS
202
+ must load" column in [Pass a class to a part](#pass-a-class-to-a-part).