@c15t/scripts 3.0.0-alpha.2 → 3.0.0-alpha.3

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 (300) hide show
  1. package/AGENTS.md +129 -63
  2. package/README.md +8 -29
  3. package/SKILL.md +33 -0
  4. package/dist/adobe-analytics.js +2 -0
  5. package/dist/ahrefs-analytics.js +2 -0
  6. package/dist/amplitude.js +2 -0
  7. package/dist/clearbit.js +2 -0
  8. package/dist/cloudflare-web-analytics.js +2 -0
  9. package/dist/cloudflare-zaraz.js +2 -0
  10. package/dist/crisp.js +2 -0
  11. package/dist/databuddy.js +2 -0
  12. package/dist/e2e-test-utils.js +2 -139
  13. package/dist/engine/compile.js +2 -89
  14. package/dist/engine/runtime.js +2 -448
  15. package/dist/events.js +2 -218
  16. package/dist/fathom-analytics.js +2 -0
  17. package/dist/front-chat.js +2 -0
  18. package/dist/google-tag-manager.js +2 -0
  19. package/dist/google-tag.js +2 -0
  20. package/dist/heap.js +2 -0
  21. package/dist/hightouch.js +2 -0
  22. package/dist/hotjar.js +2 -0
  23. package/dist/intercom.js +2 -0
  24. package/dist/klaviyo.js +2 -0
  25. package/dist/linkedin-insights.js +2 -0
  26. package/dist/logrocket.js +2 -0
  27. package/dist/matomo-analytics.js +2 -0
  28. package/dist/meta-pixel.js +2 -0
  29. package/dist/microsoft-clarity.js +2 -0
  30. package/dist/microsoft-uet.js +2 -0
  31. package/dist/mixpanel-analytics.js +2 -0
  32. package/dist/one-dollar-stats.js +2 -0
  33. package/dist/openai-pixel.js +2 -0
  34. package/dist/pinterest-tag.js +2 -0
  35. package/dist/pirsch.js +2 -0
  36. package/dist/plausible-analytics.js +2 -0
  37. package/dist/posthog.js +2 -0
  38. package/dist/promptwatch.js +2 -0
  39. package/dist/reddit-pixel.js +2 -0
  40. package/dist/registry.js +2 -422
  41. package/dist/resolve.js +2 -33
  42. package/dist/rudderstack.js +2 -0
  43. package/dist/rybbit-analytics.js +2 -0
  44. package/dist/segment.js +2 -0
  45. package/dist/snapchat-pixel.js +2 -0
  46. package/dist/tiktok-pixel.js +2 -0
  47. package/dist/types.js +2 -16
  48. package/dist/umami-analytics.js +2 -0
  49. package/dist/vendors/_shared/attributes.js +2 -14
  50. package/dist/vendors/_shared/google-consent.js +2 -27
  51. package/dist/vendors/_shared/install-builders.js +2 -21
  52. package/dist/vendors/_shared/required-id.js +2 -0
  53. package/dist/vendors/_shared/script-url.js +2 -28
  54. package/dist/vendors/ads-and-pixels/linkedin-insights.js +2 -48
  55. package/dist/vendors/ads-and-pixels/meta-pixel.js +2 -153
  56. package/dist/vendors/ads-and-pixels/microsoft-uet.js +2 -110
  57. package/dist/vendors/ads-and-pixels/openai-pixel.js +2 -88
  58. package/dist/vendors/ads-and-pixels/pinterest-tag.js +2 -123
  59. package/dist/vendors/ads-and-pixels/reddit-pixel.js +2 -107
  60. package/dist/vendors/ads-and-pixels/snapchat-pixel.js +2 -87
  61. package/dist/vendors/ads-and-pixels/tiktok-pixel.js +2 -89
  62. package/dist/vendors/ads-and-pixels/x-pixel.js +2 -48
  63. package/dist/vendors/analytics/adobe-analytics.js +2 -49
  64. package/dist/vendors/analytics/ahrefs-analytics.js +2 -27
  65. package/dist/vendors/analytics/amplitude.js +2 -134
  66. package/dist/vendors/analytics/clearbit.js +2 -28
  67. package/dist/vendors/analytics/cloudflare-web-analytics.js +2 -32
  68. package/dist/vendors/analytics/databuddy.js +2 -103
  69. package/dist/vendors/analytics/fathom-analytics.js +2 -35
  70. package/dist/vendors/analytics/google-tag.js +2 -78
  71. package/dist/vendors/analytics/heap.js +2 -134
  72. package/dist/vendors/analytics/hightouch.js +2 -109
  73. package/dist/vendors/analytics/hotjar.js +2 -44
  74. package/dist/vendors/analytics/logrocket.js +2 -58
  75. package/dist/vendors/analytics/matomo-analytics.js +2 -191
  76. package/dist/vendors/analytics/microsoft-clarity.js +2 -100
  77. package/dist/vendors/analytics/mixpanel-analytics.js +2 -93
  78. package/dist/vendors/analytics/one-dollar-stats.js +2 -30
  79. package/dist/vendors/analytics/pirsch.js +2 -67
  80. package/dist/vendors/analytics/plausible-analytics.js +2 -81
  81. package/dist/vendors/analytics/posthog.js +2 -200
  82. package/dist/vendors/analytics/promptwatch.js +2 -29
  83. package/dist/vendors/analytics/rudderstack.js +2 -183
  84. package/dist/vendors/analytics/rybbit-analytics.js +2 -63
  85. package/dist/vendors/analytics/segment.js +2 -65
  86. package/dist/vendors/analytics/umami-analytics.js +2 -39
  87. package/dist/vendors/analytics/vercel-analytics.js +2 -53
  88. package/dist/vendors/email-and-sms/klaviyo.js +2 -0
  89. package/dist/vendors/functional/crisp.js +2 -100
  90. package/dist/vendors/functional/front-chat.js +2 -64
  91. package/dist/vendors/functional/intercom.js +2 -45
  92. package/dist/vendors/tag-managers/cloudflare-zaraz.js +2 -98
  93. package/dist/vendors/tag-managers/google-tag-manager.js +2 -73
  94. package/dist/vercel-analytics.js +2 -0
  95. package/dist/x-pixel.js +2 -0
  96. package/dist-types/adobe-analytics.d.ts +2 -0
  97. package/dist-types/ahrefs-analytics.d.ts +2 -0
  98. package/dist-types/amplitude.d.ts +2 -0
  99. package/dist-types/clearbit.d.ts +2 -0
  100. package/dist-types/cloudflare-web-analytics.d.ts +2 -0
  101. package/dist-types/cloudflare-zaraz.d.ts +2 -0
  102. package/dist-types/crisp.d.ts +2 -0
  103. package/dist-types/databuddy.d.ts +2 -0
  104. package/dist-types/e2e-test-utils.d.ts +2 -0
  105. package/dist-types/engine/compile.d.ts +2 -3
  106. package/dist-types/engine/runtime.d.ts +2 -3
  107. package/dist-types/events.d.ts +2 -46
  108. package/dist-types/fathom-analytics.d.ts +2 -0
  109. package/dist-types/front-chat.d.ts +2 -0
  110. package/dist-types/google-tag-manager.d.ts +2 -0
  111. package/dist-types/google-tag.d.ts +2 -0
  112. package/dist-types/heap.d.ts +2 -0
  113. package/dist-types/hightouch.d.ts +2 -0
  114. package/dist-types/hotjar.d.ts +2 -0
  115. package/dist-types/intercom.d.ts +2 -0
  116. package/dist-types/klaviyo.d.ts +2 -0
  117. package/dist-types/linkedin-insights.d.ts +2 -0
  118. package/dist-types/logrocket.d.ts +2 -0
  119. package/dist-types/matomo-analytics.d.ts +2 -0
  120. package/dist-types/meta-pixel.d.ts +2 -0
  121. package/dist-types/microsoft-clarity.d.ts +2 -0
  122. package/dist-types/microsoft-uet.d.ts +2 -0
  123. package/dist-types/mixpanel-analytics.d.ts +2 -0
  124. package/dist-types/one-dollar-stats.d.ts +2 -0
  125. package/dist-types/openai-pixel.d.ts +2 -0
  126. package/dist-types/pinterest-tag.d.ts +2 -0
  127. package/dist-types/pirsch.d.ts +2 -0
  128. package/dist-types/plausible-analytics.d.ts +2 -0
  129. package/dist-types/posthog.d.ts +2 -0
  130. package/dist-types/promptwatch.d.ts +2 -0
  131. package/dist-types/reddit-pixel.d.ts +2 -0
  132. package/dist-types/registry.d.ts +2 -485
  133. package/dist-types/resolve.d.ts +2 -9
  134. package/dist-types/rudderstack.d.ts +2 -0
  135. package/dist-types/rybbit-analytics.d.ts +2 -0
  136. package/dist-types/segment.d.ts +2 -0
  137. package/dist-types/snapchat-pixel.d.ts +2 -0
  138. package/dist-types/tiktok-pixel.d.ts +2 -0
  139. package/dist-types/types.d.ts +2 -314
  140. package/dist-types/umami-analytics.d.ts +2 -0
  141. package/dist-types/vendors/_shared/attributes.d.ts +2 -35
  142. package/dist-types/vendors/_shared/google-consent.d.ts +2 -47
  143. package/dist-types/vendors/_shared/install-builders.d.ts +2 -30
  144. package/dist-types/vendors/_shared/required-id.d.ts +2 -0
  145. package/dist-types/vendors/_shared/script-url.d.ts +2 -75
  146. package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +2 -92
  147. package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +2 -289
  148. package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +2 -105
  149. package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +2 -211
  150. package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +2 -295
  151. package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +2 -210
  152. package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +2 -171
  153. package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +2 -106
  154. package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +2 -183
  155. package/dist-types/vendors/analytics/adobe-analytics.d.ts +2 -75
  156. package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +2 -62
  157. package/dist-types/vendors/analytics/amplitude.d.ts +2 -234
  158. package/dist-types/vendors/analytics/clearbit.d.ts +2 -60
  159. package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +2 -67
  160. package/dist-types/vendors/analytics/databuddy.d.ts +2 -147
  161. package/dist-types/vendors/analytics/fathom-analytics.d.ts +2 -90
  162. package/dist-types/vendors/analytics/google-tag.d.ts +2 -95
  163. package/dist-types/vendors/analytics/heap.d.ts +2 -316
  164. package/dist-types/vendors/analytics/hightouch.d.ts +2 -285
  165. package/dist-types/vendors/analytics/hotjar.d.ts +2 -73
  166. package/dist-types/vendors/analytics/logrocket.d.ts +2 -101
  167. package/dist-types/vendors/analytics/matomo-analytics.d.ts +2 -41
  168. package/dist-types/vendors/analytics/microsoft-clarity.d.ts +2 -97
  169. package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +2 -113
  170. package/dist-types/vendors/analytics/one-dollar-stats.d.ts +2 -39
  171. package/dist-types/vendors/analytics/pirsch.d.ts +2 -96
  172. package/dist-types/vendors/analytics/plausible-analytics.d.ts +2 -122
  173. package/dist-types/vendors/analytics/posthog.d.ts +2 -175
  174. package/dist-types/vendors/analytics/promptwatch.d.ts +2 -36
  175. package/dist-types/vendors/analytics/rudderstack.d.ts +2 -330
  176. package/dist-types/vendors/analytics/rybbit-analytics.d.ts +2 -82
  177. package/dist-types/vendors/analytics/segment.d.ts +2 -164
  178. package/dist-types/vendors/analytics/umami-analytics.d.ts +2 -93
  179. package/dist-types/vendors/analytics/vercel-analytics.d.ts +2 -66
  180. package/dist-types/vendors/email-and-sms/klaviyo.d.ts +2 -0
  181. package/dist-types/vendors/functional/crisp.d.ts +2 -78
  182. package/dist-types/vendors/functional/front-chat.d.ts +2 -62
  183. package/dist-types/vendors/functional/intercom.d.ts +2 -135
  184. package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +2 -39
  185. package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +2 -96
  186. package/dist-types/vercel-analytics.d.ts +2 -0
  187. package/dist-types/x-pixel.d.ts +2 -0
  188. package/docs/README.md +129 -63
  189. package/docs/assets/v3/bottom-bar.png +0 -0
  190. package/docs/assets/v3/brand-card.png +0 -0
  191. package/docs/assets/v3/brand-preferences.png +0 -0
  192. package/docs/assets/v3/choice-wall.png +0 -0
  193. package/docs/assets/v3/headless-bar-html.png +0 -0
  194. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  195. package/docs/assets/v3/headless-bar.png +0 -0
  196. package/docs/assets/v3/slim-bar.png +0 -0
  197. package/docs/concepts/choose-your-setup.md +87 -0
  198. package/docs/concepts/consent-categories.md +84 -0
  199. package/docs/{guides → concepts}/consent-state.md +71 -101
  200. package/docs/{guides → concepts}/data-fetching.md +31 -27
  201. package/docs/concepts/how-consent-works.md +123 -0
  202. package/docs/concepts/policies.md +71 -0
  203. package/docs/customization/class-names.md +202 -0
  204. package/docs/customization/dark-mode.md +157 -0
  205. package/docs/customization/motion.md +119 -0
  206. package/docs/customization/overview.md +67 -34
  207. package/docs/customization/recipes.md +839 -49
  208. package/docs/customization/slots.md +216 -35
  209. package/docs/customization/stylesheets.md +147 -0
  210. package/docs/customization/tailwind.md +842 -0
  211. package/docs/customization/tokens.md +163 -96
  212. package/docs/customization/translations.md +61 -3
  213. package/docs/frameworks/astro/embeds.md +160 -0
  214. package/docs/frameworks/astro/network-blocker.md +86 -0
  215. package/docs/frameworks/astro/scripts.md +146 -0
  216. package/docs/frameworks/html/embeds.md +142 -0
  217. package/docs/frameworks/html/network-blocker.md +105 -0
  218. package/docs/frameworks/html/scripts.md +164 -0
  219. package/docs/frameworks/javascript/scripts.md +119 -0
  220. package/docs/frameworks/next/embeds.md +90 -0
  221. package/docs/frameworks/next/network-blocker.md +153 -0
  222. package/docs/frameworks/next/scripts.md +196 -0
  223. package/docs/frameworks/nuxt/embeds.md +81 -0
  224. package/docs/frameworks/nuxt/network-blocker.md +97 -0
  225. package/docs/frameworks/nuxt/scripts.md +89 -0
  226. package/docs/frameworks/react/embeds.md +89 -0
  227. package/docs/frameworks/react/network-blocker.md +140 -0
  228. package/docs/frameworks/react/scripts.md +115 -0
  229. package/docs/frameworks/svelte/embeds.md +96 -0
  230. package/docs/frameworks/svelte/network-blocker.md +141 -0
  231. package/docs/frameworks/svelte/scripts.md +137 -0
  232. package/docs/frameworks/sveltekit/embeds.md +103 -0
  233. package/docs/frameworks/sveltekit/network-blocker.md +149 -0
  234. package/docs/frameworks/sveltekit/scripts.md +141 -0
  235. package/docs/frameworks/tanstack-start/embeds.md +96 -0
  236. package/docs/frameworks/tanstack-start/network-blocker.md +145 -0
  237. package/docs/frameworks/tanstack-start/scripts.md +103 -0
  238. package/docs/frameworks/vue/embeds.md +84 -0
  239. package/docs/frameworks/vue/network-blocker.md +99 -0
  240. package/docs/frameworks/vue/scripts.md +93 -0
  241. package/docs/guides/banner-experiments.md +654 -0
  242. package/docs/guides/troubleshooting.md +120 -47
  243. package/docs/guides/verify-consent.md +81 -49
  244. package/docs/integrations/adobe-analytics.md +167 -159
  245. package/docs/integrations/ahrefs-analytics.md +152 -154
  246. package/docs/integrations/amplitude.md +162 -156
  247. package/docs/integrations/building-integrations.md +136 -37
  248. package/docs/integrations/clearbit.md +154 -154
  249. package/docs/integrations/cloudflare-web-analytics.md +156 -156
  250. package/docs/integrations/cloudflare-zaraz.md +209 -261
  251. package/docs/integrations/crisp.md +164 -158
  252. package/docs/integrations/databuddy.md +157 -173
  253. package/docs/integrations/fathom-analytics.md +158 -156
  254. package/docs/integrations/front-chat.md +167 -167
  255. package/docs/integrations/google-maps.md +118 -83
  256. package/docs/integrations/google-tag-manager.md +178 -163
  257. package/docs/integrations/google-tag.md +163 -160
  258. package/docs/integrations/heap.md +163 -155
  259. package/docs/integrations/hightouch.md +161 -157
  260. package/docs/integrations/hotjar.md +159 -155
  261. package/docs/integrations/intercom.md +183 -153
  262. package/docs/integrations/klaviyo.md +486 -0
  263. package/docs/integrations/linkedin-insights.md +174 -150
  264. package/docs/integrations/logrocket.md +160 -156
  265. package/docs/integrations/matomo-analytics.md +188 -178
  266. package/docs/integrations/meta-pixel.md +188 -150
  267. package/docs/integrations/microsoft-clarity.md +163 -155
  268. package/docs/integrations/microsoft-uet.md +148 -154
  269. package/docs/integrations/mixpanel-analytics.md +155 -160
  270. package/docs/integrations/one-dollar-stats.md +166 -165
  271. package/docs/integrations/openai-pixel.md +204 -301
  272. package/docs/integrations/overview.md +137 -80
  273. package/docs/integrations/pinterest-tag.md +191 -183
  274. package/docs/integrations/pirsch.md +169 -159
  275. package/docs/integrations/plausible-analytics.md +172 -158
  276. package/docs/integrations/posthog.md +226 -241
  277. package/docs/integrations/promptwatch.md +154 -154
  278. package/docs/integrations/reddit-pixel.md +185 -157
  279. package/docs/integrations/rudderstack.md +201 -186
  280. package/docs/integrations/rybbit-analytics.md +171 -160
  281. package/docs/integrations/segment.md +182 -154
  282. package/docs/integrations/snapchat-pixel.md +186 -156
  283. package/docs/integrations/tiktok-pixel.md +171 -150
  284. package/docs/integrations/umami-analytics.md +163 -158
  285. package/docs/integrations/vercel-analytics.md +166 -157
  286. package/docs/integrations/x-pixel.md +176 -150
  287. package/docs/integrations/youtube.md +121 -86
  288. package/docs/upgrade-v3.md +475 -467
  289. package/package.json +10 -257
  290. package/dist-types/__tests__/helpers.d.ts +0 -141
  291. package/docs/assets/v3/brand-bar.png +0 -0
  292. package/docs/assets/v3/mobile-card.png +0 -0
  293. package/docs/assets/v3/preferences.png +0 -0
  294. package/docs/frameworks/javascript/script-loader.md +0 -100
  295. package/docs/frameworks/next/script-loader.md +0 -216
  296. package/docs/frameworks/react/script-loader.md +0 -69
  297. package/docs/guides/deployment-modes.md +0 -75
  298. package/docs/guides/shared-consent-controls.md +0 -158
  299. package/docs/integrations/clear-on-revocation.md +0 -167
  300. package/docs/integrations/granular-consent.md +0 -210
@@ -0,0 +1,842 @@
1
+ ---
2
+ title: Tailwind CSS
3
+ description: Load c15t's styles next to Tailwind CSS 4 or 3 in every framework,
4
+ put utilities on c15t component parts, and use Tailwind's dark variant with
5
+ c15t.
6
+ group: customization
7
+ ---
8
+
9
+ ## How c15t's styles meet Tailwind
10
+
11
+ c15t's component rules live in the CSS cascade layer `components`. Each c15t
12
+ stylesheet starts with Tailwind CSS 4's layer order,
13
+ `@layer properties, theme, base, components, utilities;`, and cascade layers
14
+ rank by the order they are first named. On Tailwind 4, c15t's rules stay above
15
+ Tailwind's preflight in `base` and below your utilities in `utilities`,
16
+ whichever stylesheet loads first. A plain utility such as `p-2` on a c15t part
17
+ wins without `!important`.
18
+
19
+ Tailwind CSS 3 has no cascade layers. It treats `@layer components` as its own
20
+ directive and fails the build on a stylesheet that uses it without a matching
21
+ `@tailwind components`. c15t's Tailwind 3 PostCSS plugin removes the layer
22
+ wrappers from c15t's stylesheets before Tailwind 3 runs. c15t's rules then
23
+ win by specificity, as they did in c15t v2, so a utility on a c15t part needs
24
+ Tailwind's important modifier, such as `!p-2`.
25
+
26
+ The setups below come from fixtures that CI builds for each framework and
27
+ checks in Chromium. The Next.js snippets come from the Next.js 16 Turbopack
28
+ fixture. The Next.js 15 and webpack fixtures use the same files. The check
29
+ confirms that c15t's banner and dialog keep their styles under Tailwind's
30
+ preflight, that a padding utility on the banner root
31
+ beats c15t's own padding, and that the `dark:` variant applies. The fixtures run
32
+ without a backend, so they use `offline()` mode or a local `backendURL`. Keep the
33
+ `mode` or `backendURL` from your quickstart.
34
+
35
+ ## Set up Tailwind CSS 4
36
+
37
+ Install Tailwind's plugin for your build tool, add it to the build, and load
38
+ c15t's stylesheet in the same CSS entry as Tailwind. Vue, Nuxt and Astro load
39
+ c15t's styles for you, so their CSS entry holds only Tailwind.
40
+
41
+ **Next.js**
42
+
43
+ Next.js runs Tailwind 4 through `@tailwindcss/postcss`:
44
+
45
+ ```js title="postcss.config.mjs"
46
+ export default { plugins: { '@tailwindcss/postcss': {} } };
47
+ ```
48
+
49
+ Import c15t after Tailwind in the global stylesheet that `app/layout.tsx`
50
+ or `pages/_app.tsx` loads:
51
+
52
+ ```css title="app/globals.css"
53
+ @import 'tailwindcss';
54
+ @import 'c15t/next/styles.css';
55
+
56
+ @custom-variant dark (&:where(.dark, .dark *));
57
+ ```
58
+
59
+ **TanStack Start**
60
+
61
+ Add `@tailwindcss/vite` before the TanStack Start plugin, then import
62
+ Tailwind and c15t from the stylesheet your root route links:
63
+
64
+ ```ts title="vite.config.ts"
65
+ import tailwindcss from '@tailwindcss/vite';
66
+ import { tanstackStart } from '@tanstack/react-start/plugin/vite';
67
+ import react from '@vitejs/plugin-react';
68
+ import { defineConfig } from 'vite';
69
+
70
+ export default defineConfig({
71
+ plugins: [
72
+ tailwindcss(),
73
+ tanstackStart({ prerender: { enabled: true } }),
74
+ react(),
75
+ ],
76
+ });
77
+ ```
78
+
79
+ Import Tailwind and c15t from the stylesheet your root route links:
80
+
81
+ ```css title="src/styles.css"
82
+ @import 'tailwindcss';
83
+ @import 'c15t/tanstack-start/styles.css';
84
+
85
+ @custom-variant dark (&:where(.dark, .dark *));
86
+ ```
87
+
88
+ **React**
89
+
90
+ ```ts title="vite.config.ts"
91
+ import tailwindcss from '@tailwindcss/vite';
92
+ import react from '@vitejs/plugin-react';
93
+ import { defineConfig } from 'vite';
94
+
95
+ export default defineConfig({ plugins: [react(), tailwindcss()] });
96
+ ```
97
+
98
+ Import this stylesheet from your entry module in place of a separate
99
+ `c15t/react/styles.css` import:
100
+
101
+ ```css title="src/app.css"
102
+ @import 'tailwindcss';
103
+ @import 'c15t/react/styles.css';
104
+
105
+ @custom-variant dark (&:where(.dark, .dark *));
106
+ ```
107
+
108
+ **Nuxt**
109
+
110
+ Add `@tailwindcss/vite` under `vite.plugins` and list your CSS entry in
111
+ `css`. The Vue components load c15t's styles themselves:
112
+
113
+ ```ts title="nuxt.config.ts"
114
+ import tailwindcss from '@tailwindcss/vite';
115
+
116
+ export default defineNuxtConfig({
117
+ c15t: { backendURL: '/api/c15t' },
118
+ compatibilityDate: '2026-07-04',
119
+ css: ['~/assets/css/main.css'],
120
+ modules: ['c15t/vue'],
121
+ ssr: false,
122
+ vite: { plugins: [tailwindcss()] },
123
+ });
124
+ ```
125
+
126
+ Your CSS entry imports Tailwind and declares the dark variant:
127
+
128
+ ```css title="app/assets/css/main.css"
129
+ @import 'tailwindcss';
130
+
131
+ @custom-variant dark (&:where(.dark, .dark *));
132
+ ```
133
+
134
+ **Vue**
135
+
136
+ The Vue components load their own stylesheets, so the CSS entry holds only
137
+ Tailwind:
138
+
139
+ ```ts title="vite.config.ts"
140
+ import tailwindcss from '@tailwindcss/vite';
141
+ import vue from '@vitejs/plugin-vue';
142
+ import c15tVue from 'c15t/vue/vite';
143
+ import { defineConfig } from 'vite';
144
+
145
+ export default defineConfig({ plugins: [vue(), c15tVue(), tailwindcss()] });
146
+ ```
147
+
148
+ Import this stylesheet from `src/main.ts`:
149
+
150
+ ```css title="src/style.css"
151
+ @import 'tailwindcss';
152
+
153
+ @custom-variant dark (&:where(.dark, .dark *));
154
+ ```
155
+
156
+ **Astro**
157
+
158
+ The integration adds `c15t/astro/styles.css` to every page, so the CSS
159
+ entry holds only Tailwind. Add `@tailwindcss/vite` under `vite.plugins`:
160
+
161
+ ```js title="astro.config.mjs"
162
+ import svelte from '@astrojs/svelte';
163
+ import tailwindcss from '@tailwindcss/vite';
164
+ import { defineConfig } from 'astro/config';
165
+ import c15t, { offline } from 'c15t/astro';
166
+
167
+ export default defineConfig({
168
+ integrations: [
169
+ svelte(),
170
+ c15t({
171
+ mode: offline({
172
+ policyRules: [
173
+ {
174
+ id: 'tailwind-matrix',
175
+ match: { isDefault: true },
176
+ model: 'opt-in',
177
+ prompt: 'choice',
178
+ },
179
+ ],
180
+ }),
181
+ }),
182
+ ],
183
+ vite: { plugins: [tailwindcss()] },
184
+ });
185
+ ```
186
+
187
+ Import this stylesheet from your layout:
188
+
189
+ ```css title="src/styles/global.css"
190
+ @import 'tailwindcss';
191
+
192
+ @custom-variant dark (&:where(.dark, .dark *));
193
+ ```
194
+
195
+ **Svelte**
196
+
197
+ ```ts title="vite.config.ts"
198
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
199
+ import tailwindcss from '@tailwindcss/vite';
200
+ import { defineConfig } from 'vite';
201
+
202
+ export default defineConfig({ plugins: [svelte(), tailwindcss()] });
203
+ ```
204
+
205
+ Import this stylesheet from `src/main.ts` in place of a separate
206
+ `@c15t/svelte/styles.css` import:
207
+
208
+ ```css title="src/app.css"
209
+ @import 'tailwindcss';
210
+ @import '@c15t/svelte/styles.css';
211
+
212
+ @custom-variant dark (&:where(.dark, .dark *));
213
+ ```
214
+
215
+ **SvelteKit**
216
+
217
+ ```ts title="vite.config.ts"
218
+ import { sveltekit } from '@sveltejs/kit/vite';
219
+ import tailwindcss from '@tailwindcss/vite';
220
+ import { defineConfig } from 'vite';
221
+
222
+ export default defineConfig({ plugins: [tailwindcss(), sveltekit()] });
223
+ ```
224
+
225
+ Import this stylesheet from your root `+layout.svelte`:
226
+
227
+ ```css title="src/app.css"
228
+ @import 'tailwindcss';
229
+ @import '@c15t/svelte/styles.css';
230
+
231
+ @custom-variant dark (&:where(.dark, .dark *));
232
+ ```
233
+
234
+ **HTML**
235
+
236
+ The script tag renders its UI in a shadow root with its own copy of c15t's
237
+ stylesheet. Build your Tailwind CSS as usual and link it from the page:
238
+
239
+ ```css title="style.css"
240
+ @import 'tailwindcss';
241
+
242
+ @custom-variant dark (&:where(.dark, .dark *));
243
+ ```
244
+
245
+ To put utilities on c15t's parts, link the same build into the shadow root
246
+ with `ui.stylesheetURLs`, as in
247
+ [put utilities on c15t parts](#put-utilities-on-c15t-parts).
248
+
249
+ **JavaScript**
250
+
251
+ `init()` from `@c15t/browser` renders into a shadow root, the same way as
252
+ the script tag. Build Tailwind as usual for your page. To put utilities on
253
+ c15t's parts, pass the URL of your built stylesheet in
254
+ `ui.stylesheetURLs`, as in
255
+ [put utilities on c15t parts](#put-utilities-on-c15t-parts).
256
+
257
+ ## Set up Tailwind CSS 3
258
+
259
+ Tailwind 3 loads the same `styles.css` as Tailwind 4 and needs one PostCSS
260
+ plugin. Every c15t package that ships a stylesheet exports it as
261
+ `<package>/postcss-tailwind3`, so use the one from the package you installed:
262
+
263
+ | You installed | Plugin name |
264
+ | --------------------------------------------------------- | --------------------------------- |
265
+ | `c15t` (React, Next.js, TanStack Start, Vue, Nuxt, Astro) | `c15t/postcss-tailwind3` |
266
+ | `@c15t/svelte` (Svelte, SvelteKit) | `@c15t/svelte/postcss-tailwind3` |
267
+ | `@c15t/browser` (script tag in the light DOM) | `@c15t/browser/postcss-tailwind3` |
268
+
269
+ They are the same plugin. A config that already lists
270
+ `@c15t/ui/postcss-tailwind3` keeps working.
271
+
272
+ Make three changes:
273
+
274
+ 1. Add the plugin to your PostCSS plugins, before `tailwindcss`. Write the plugins as an object, as the examples below do.
275
+ PostCSS runs them in the order of the keys. Next.js also accepts an array
276
+ of plugin names, but Vite's PostCSS loader rejects that form.
277
+ 2. Where you import c15t's stylesheet yourself, put the import above the
278
+ `@tailwind` directives, whatever your build tool. Vite drops an `@import`
279
+ that follows another rule, with the warning
280
+ `@import must precede all other statements`, and the banner renders
281
+ unstyled. Next.js 16 with Turbopack fails the build.
282
+ 3. Set `darkMode: 'class'` so Tailwind's `dark:` variant follows the same
283
+ `dark` class c15t reads.
284
+
285
+ The plugin also processes the stylesheets c15t loads for you, such as the
286
+ dialog's lazy stylesheet and Astro's injected one. Without it, Tailwind 3 stops
287
+ the build with "`@layer components` is used but no matching
288
+ `@tailwind components` directive is present", or purges c15t's rules, because
289
+ c15t's class names never appear in your source.
290
+
291
+ Tailwind 3 has no cascade layers, so c15t's selectors outrank a single utility
292
+ class. Add the important modifier to utilities you pass to c15t parts:
293
+ `!bg-red-500`, `hover:!bg-red-500`. A bare `bg-red-500` has no effect. Theme
294
+ tokens work the same on Tailwind 3 and 4.
295
+
296
+ In React and Next.js apps, `c15t setup` makes the first two changes when it
297
+ finds Tailwind 3. It replaces an existing
298
+ `styles.tw3.css` import from an earlier setup with `styles.css`. When it
299
+ cannot edit your PostCSS config, it prints the step to do by hand. See
300
+ [setup](https://c15t.com/docs/cli/commands/setup#set-up-styles-and-tailwind-css).
301
+
302
+ Create React App ignores PostCSS config files, so Tailwind 3 cannot run the
303
+ plugin and the build fails on c15t's dialog stylesheet. `c15t setup` warns
304
+ about this. Add the plugin before `tailwindcss` through CRACO, eject, or move
305
+ the app to Vite.
306
+
307
+ **Next.js**
308
+
309
+ Next.js loads `postcss.config.mjs` itself:
310
+
311
+ ```js title="postcss.config.mjs"
312
+ export default {
313
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
314
+ };
315
+ ```
316
+
317
+ Scan `app/` and turn on the class-based dark variant:
318
+
319
+ ```ts title="tailwind.config.ts"
320
+ import type { Config } from 'tailwindcss';
321
+
322
+ export default {
323
+ content: ['./app/**/*.{ts,tsx}'],
324
+ darkMode: 'class',
325
+ } satisfies Config;
326
+ ```
327
+
328
+ Import `c15t/next/styles.css` above the directives. Turbopack fails the
329
+ build when the import sits below them. The same order works with
330
+ webpack:
331
+
332
+ ```css title="app/globals.css"
333
+ /* Turbopack rejects an @import below other rules, so it goes first. */
334
+ @import 'c15t/next/styles.css';
335
+
336
+ @tailwind base;
337
+ @tailwind components;
338
+ @tailwind utilities;
339
+ ```
340
+
341
+ **TanStack Start**
342
+
343
+ ```js title="postcss.config.mjs"
344
+ export default {
345
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
346
+ };
347
+ ```
348
+
349
+ Scan your source and turn on the class-based dark variant:
350
+
351
+ ```ts title="tailwind.config.ts"
352
+ import type { Config } from 'tailwindcss';
353
+
354
+ export default {
355
+ content: ['./src/**/*.{ts,tsx}'],
356
+ darkMode: 'class',
357
+ } satisfies Config;
358
+ ```
359
+
360
+ Import `c15t/tanstack-start/styles.css` above the directives, in the
361
+ stylesheet your root route links:
362
+
363
+ ```css title="src/styles.css"
364
+ /* Vite ignores an @import below other rules, so it goes first. */
365
+ @import 'c15t/tanstack-start/styles.css';
366
+
367
+ @tailwind base;
368
+ @tailwind components;
369
+ @tailwind utilities;
370
+ ```
371
+
372
+ **React**
373
+
374
+ ```js title="postcss.config.mjs"
375
+ export default {
376
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
377
+ };
378
+ ```
379
+
380
+ Scan your source and turn on the class-based dark variant:
381
+
382
+ ```ts title="tailwind.config.ts"
383
+ import type { Config } from 'tailwindcss';
384
+
385
+ export default {
386
+ content: ['./index.html', './src/**/*.{ts,tsx}'],
387
+ darkMode: 'class',
388
+ } satisfies Config;
389
+ ```
390
+
391
+ Import `c15t/react/styles.css` above the directives, and import this
392
+ stylesheet from your entry module:
393
+
394
+ ```css title="src/app.css"
395
+ /* Vite ignores an @import below other rules, so it goes first. */
396
+ @import 'c15t/react/styles.css';
397
+
398
+ @tailwind base;
399
+ @tailwind components;
400
+ @tailwind utilities;
401
+ ```
402
+
403
+ **Nuxt**
404
+
405
+ Nuxt reads PostCSS plugins from `postcss.plugins` in `nuxt.config.ts`, in
406
+ the order listed:
407
+
408
+ ```ts title="nuxt.config.ts"
409
+ export default defineNuxtConfig({
410
+ c15t: { backendURL: '/api/c15t' },
411
+ compatibilityDate: '2026-07-04',
412
+ css: ['~/assets/css/main.css'],
413
+ modules: ['c15t/vue'],
414
+ postcss: {
415
+ plugins: {
416
+ 'c15t/postcss-tailwind3': {},
417
+ tailwindcss: {},
418
+ autoprefixer: {},
419
+ },
420
+ },
421
+ ssr: false,
422
+ });
423
+ ```
424
+
425
+ Scan `app/` and turn on the class-based dark variant:
426
+
427
+ ```ts title="tailwind.config.ts"
428
+ import type { Config } from 'tailwindcss';
429
+
430
+ export default {
431
+ content: ['./app/**/*.{vue,ts}'],
432
+ darkMode: 'class',
433
+ } satisfies Config;
434
+ ```
435
+
436
+ The Vue components load c15t's styles themselves, and the plugin processes
437
+ them too, so the CSS entry holds only the directives:
438
+
439
+ ```css title="app/assets/css/main.css"
440
+ @tailwind base;
441
+ @tailwind components;
442
+ @tailwind utilities;
443
+ ```
444
+
445
+ **Vue**
446
+
447
+ ```js title="postcss.config.mjs"
448
+ export default {
449
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
450
+ };
451
+ ```
452
+
453
+ Scan your source and turn on the class-based dark variant:
454
+
455
+ ```ts title="tailwind.config.ts"
456
+ import type { Config } from 'tailwindcss';
457
+
458
+ export default {
459
+ content: ['./index.html', './src/**/*.{vue,ts}'],
460
+ darkMode: 'class',
461
+ } satisfies Config;
462
+ ```
463
+
464
+ The Vue components load their own stylesheets, and the plugin processes
465
+ them too, so the CSS entry holds only the directives:
466
+
467
+ ```css title="src/style.css"
468
+ @tailwind base;
469
+ @tailwind components;
470
+ @tailwind utilities;
471
+ ```
472
+
473
+ **Astro**
474
+
475
+ ```js title="postcss.config.mjs"
476
+ export default {
477
+ plugins: { 'c15t/postcss-tailwind3': {}, tailwindcss: {}, autoprefixer: {} },
478
+ };
479
+ ```
480
+
481
+ Scan your source and turn on the class-based dark variant:
482
+
483
+ ```ts title="tailwind.config.ts"
484
+ import type { Config } from 'tailwindcss';
485
+
486
+ export default {
487
+ content: ['./src/**/*.{astro,html,ts}'],
488
+ darkMode: 'class',
489
+ } satisfies Config;
490
+ ```
491
+
492
+ The plugin processes the stylesheet the integration injects, so the CSS
493
+ entry holds only the directives:
494
+
495
+ ```css title="src/styles/global.css"
496
+ @tailwind base;
497
+ @tailwind components;
498
+ @tailwind utilities;
499
+ ```
500
+
501
+ **Svelte**
502
+
503
+ ```js title="postcss.config.mjs"
504
+ export default {
505
+ plugins: {
506
+ '@c15t/svelte/postcss-tailwind3': {},
507
+ tailwindcss: {},
508
+ autoprefixer: {},
509
+ },
510
+ };
511
+ ```
512
+
513
+ Scan your source and turn on the class-based dark variant:
514
+
515
+ ```ts title="tailwind.config.ts"
516
+ import type { Config } from 'tailwindcss';
517
+
518
+ export default {
519
+ content: ['./index.html', './src/**/*.{svelte,ts}'],
520
+ darkMode: 'class',
521
+ } satisfies Config;
522
+ ```
523
+
524
+ Import `@c15t/svelte/styles.css` above the directives, and import this
525
+ stylesheet from `src/main.ts`:
526
+
527
+ ```css title="src/app.css"
528
+ /* Vite ignores an @import below other rules, so it goes first. */
529
+ @import '@c15t/svelte/styles.css';
530
+
531
+ @tailwind base;
532
+ @tailwind components;
533
+ @tailwind utilities;
534
+ ```
535
+
536
+ **SvelteKit**
537
+
538
+ ```js title="postcss.config.mjs"
539
+ export default {
540
+ plugins: {
541
+ '@c15t/svelte/postcss-tailwind3': {},
542
+ tailwindcss: {},
543
+ autoprefixer: {},
544
+ },
545
+ };
546
+ ```
547
+
548
+ Scan your source and turn on the class-based dark variant:
549
+
550
+ ```ts title="tailwind.config.ts"
551
+ import type { Config } from 'tailwindcss';
552
+
553
+ export default {
554
+ content: ['./src/**/*.{html,svelte,ts}'],
555
+ darkMode: 'class',
556
+ } satisfies Config;
557
+ ```
558
+
559
+ Import `@c15t/svelte/styles.css` above the directives, and import this
560
+ stylesheet from your root `+layout.svelte`:
561
+
562
+ ```css title="src/app.css"
563
+ /* Vite ignores an @import below other rules, so it goes first. */
564
+ @import '@c15t/svelte/styles.css';
565
+
566
+ @tailwind base;
567
+ @tailwind components;
568
+ @tailwind utilities;
569
+ ```
570
+
571
+ **HTML**
572
+
573
+ In the default shadow root, c15t's stylesheet never meets Tailwind, so the
574
+ page's Tailwind 3 setup needs nothing from c15t:
575
+
576
+ ```css title="style.css"
577
+ @tailwind base;
578
+ @tailwind components;
579
+ @tailwind utilities;
580
+ ```
581
+
582
+ To render the UI in the page instead, see
583
+ [render the UI in the light DOM](#render-the-ui-in-the-light-dom).
584
+
585
+ **JavaScript**
586
+
587
+ `init()` renders into a shadow root by default, where c15t's stylesheet
588
+ never meets Tailwind. To render the UI in the page and build c15t's rules
589
+ into your Tailwind CSS, see
590
+ [render the UI in the light DOM](#render-the-ui-in-the-light-dom).
591
+
592
+ ## Put utilities on c15t parts
593
+
594
+ Pass utilities to a component part through your framework's part API. The
595
+ fixtures put padding on the banner root, which c15t also pads, with a different
596
+ value in dark mode. On Tailwind 4 a plain utility wins. On Tailwind 3, add `!`,
597
+ as in `!p-[7px] dark:!p-[11px]`, because c15t's unlayered rules outrank a plain
598
+ utility. [Component parts](./slots.md) lists the parts each
599
+ framework exposes.
600
+
601
+ Tailwind only generates classes it finds in the files its `content` globs or
602
+ `@source` rules scan. Keep part classes in a scanned file.
603
+
604
+ **Next.js**
605
+
606
+ Add the classes to `options.components` on `ConsentRoot`, in the
607
+ `'use client'` wrapper from your quickstart:
608
+
609
+ ```tsx title="app/consent.tsx"
610
+ options={{
611
+ components: {
612
+ banner: {
613
+ root: { className: 'p-[7px] dark:p-[11px]' },
614
+ },
615
+ },
616
+ mode,
617
+ }}
618
+ ```
619
+
620
+ **TanStack Start**
621
+
622
+ ```tsx title="src/routes/__root.tsx"
623
+ options={{
624
+ components: {
625
+ banner: {
626
+ root: { className: 'p-[7px] dark:p-[11px]' },
627
+ },
628
+ },
629
+ mode,
630
+ }}
631
+ ```
632
+
633
+ **React**
634
+
635
+ ```tsx title="src/main.tsx"
636
+ const options = {
637
+ components: { banner: { root: { className: 'p-[7px] dark:p-[11px]' } } },
638
+ mode,
639
+ };
640
+ ```
641
+
642
+ **Nuxt**
643
+
644
+ Tailwind scans `app/`, not `nuxt.config.ts`, so put part classes in
645
+ `app/app.config.ts` under the `c15t` key. The module merges it with the
646
+ `c15t` options in `nuxt.config.ts`. Vue takes `class`, not `className`:
647
+
648
+ ```ts title="app/app.config.ts"
649
+ // Tailwind scans `app/`, so keep slot classes here rather than in
650
+ // nuxt.config.ts.
651
+ export default defineAppConfig({
652
+ c15t: {
653
+ components: { banner: { root: { class: 'p-[7px] dark:p-[11px]' } } },
654
+ },
655
+ });
656
+ ```
657
+
658
+ **Vue**
659
+
660
+ Add the classes to `components` in the `c15tVue` options. Vue takes
661
+ `class`, not `className`:
662
+
663
+ ```ts title="src/main.ts"
664
+ .use(c15tVue, {
665
+ backendURL: '/api/c15t',
666
+ components: {
667
+ banner: {
668
+ root: { class: 'p-[7px] dark:p-[11px]' },
669
+ },
670
+ },
671
+ })
672
+ ```
673
+
674
+ **Astro**
675
+
676
+ `class` on `ConsentBanner` goes on the banner root:
677
+
678
+ ```astro title="src/pages/index.astro"
679
+ <ConsentBanner class="p-[7px] dark:p-[11px]" />
680
+ ```
681
+
682
+ **Svelte**
683
+
684
+ `class` on `ConsentBanner` goes on the banner root:
685
+
686
+ ```svelte title="src/App.svelte"
687
+ <ConsentBanner class="p-[7px] dark:p-[11px]" />
688
+ ```
689
+
690
+ **SvelteKit**
691
+
692
+ ```svelte title="src/routes/+layout.svelte"
693
+ <ConsentBanner class="p-[7px] dark:p-[11px]" />
694
+ ```
695
+
696
+ **HTML**
697
+
698
+ Put the classes on parts with `ui.theme.slots`, and link your Tailwind
699
+ build into the shadow root with `ui.stylesheetURLs`. Keep the page's own
700
+ link too. Tailwind 4 registers some of its variables with `@property`,
701
+ which only takes effect in the page's stylesheets, not a shadow root's:
702
+
703
+ ```html title="index.html"
704
+ <link
705
+ rel="stylesheet"
706
+ href="/tailwind.css"
707
+ />
708
+ <script>
709
+ window.c15t = window.c15t || [];
710
+ c15t.push([
711
+ 'config',
712
+ {
713
+ ui: {
714
+ // The banner renders in a shadow root, where the page's
715
+ // stylesheets do not reach. Link your Tailwind build into it
716
+ // too, and keep the page's own link: Tailwind 4 registers
717
+ // variables with @property, which only works in the page.
718
+ stylesheetURLs: ['/tailwind.css'],
719
+ theme: {
720
+ slots: {
721
+ consentBannerCard: 'rounded-none border-4 border-sky-600',
722
+ consentBannerTitle: 'uppercase tracking-wide',
723
+ },
724
+ },
725
+ },
726
+ },
727
+ ]);
728
+ </script>
729
+ ```
730
+
731
+ Tailwind must scan the file that holds the slot classes.
732
+
733
+ **JavaScript**
734
+
735
+ Pass `ui.stylesheetURLs` and `ui.theme.slots` to `init()`. Keep a link to
736
+ the same stylesheet on the page, because Tailwind 4's `@property` rules
737
+ only take effect there:
738
+
739
+ ```ts title="src/consent.ts (partial)"
740
+ ui: {
741
+ stylesheetURLs: ['/tailwind.css'],
742
+ theme: {
743
+ slots: {
744
+ consentBannerCard: 'rounded-none border-4 border-sky-600',
745
+ },
746
+ },
747
+ },
748
+ ```
749
+
750
+ ## Use the dark variant
751
+
752
+ The fixtures make Tailwind's `dark:` variant follow a `dark` class on `<html>`:
753
+ `@custom-variant dark (&:where(.dark, .dark *));` on Tailwind 4 and
754
+ `darkMode: 'class'` on Tailwind 3. c15t's stylesheet reads the same class, and
755
+ in React, Next.js, TanStack Start, Vue, Nuxt, Svelte and SvelteKit an unset
756
+ `colorScheme` also copies it into `c15t-dark`. One toggle then switches your
757
+ utilities and c15t's dark tokens together.
758
+
759
+ Astro and the script tag follow the system setting by default. [Dark mode](./dark-mode.md) covers each
760
+ case.
761
+
762
+ ## Render the UI in the light DOM
763
+
764
+ The script tag and `init()` render into a shadow root, so your page's CSS does
765
+ not reach c15t and c15t's CSS does not reach your page. To style the UI with
766
+ your page's Tailwind build directly, render it in the page with
767
+ `data-shadow="false"` or `ui: { shadow: false }`.
768
+
769
+ On Tailwind 4, that is the whole change. c15t still injects its layered
770
+ stylesheet, and the layer order keeps it above preflight:
771
+
772
+ ```html title="light-dom.html"
773
+ <script
774
+ src="/c15t.js"
775
+ data-mode="offline"
776
+ data-country="DE"
777
+ data-shadow="false"
778
+ defer
779
+ ></script>
780
+ ```
781
+
782
+ On Tailwind 3, keep the default shadow root. Tailwind 3's preflight is
783
+ unlayered, so in the page it beats c15t's layered rules whatever their
784
+ specificity and removes the button padding and card borders. Linking
785
+ `c15t.css` from the page does not help, because nothing removes its layers.
786
+
787
+ To render in the light DOM anyway, turn the injected sheet off with
788
+ `styles: false`, and build c15t's rules into your Tailwind CSS with the plugin
789
+ instead:
790
+
791
+ ```html title="light-dom.html"
792
+ <script>
793
+ window.c15t = [['config', { ui: { shadow: false, styles: false } }]];
794
+ </script>
795
+ <script
796
+ src="/c15t.js"
797
+ data-mode="offline"
798
+ data-country="DE"
799
+ defer
800
+ ></script>
801
+ ```
802
+
803
+ Import `@c15t/browser/styles.css` above the directives in your Tailwind entry.
804
+ Install `@c15t/browser@alpha`, which provides both the stylesheet and
805
+ `@c15t/browser/postcss-tailwind3`:
806
+
807
+ ```css title="style.css"
808
+ /*
809
+ * With `shadow: false`, c15t's injected stylesheet is layered and Tailwind
810
+ * 3's preflight is not, so preflight wins. Turn the injected sheet off and
811
+ * build c15t's rules into your Tailwind CSS instead.
812
+ */
813
+ @import '@c15t/browser/styles.css';
814
+
815
+ @tailwind base;
816
+ @tailwind components;
817
+ @tailwind utilities;
818
+ ```
819
+
820
+ ```js title="postcss.config.mjs"
821
+ export default {
822
+ plugins: {
823
+ '@c15t/browser/postcss-tailwind3': {},
824
+ tailwindcss: {},
825
+ autoprefixer: {},
826
+ },
827
+ };
828
+ ```
829
+
830
+ In the light DOM your page's global rules reach the banner too, so check
831
+ buttons and headings after the switch.
832
+
833
+ ## Check the result
834
+
835
+ 1. Build the app and open it in a private window. The banner has its card
836
+ background, border and button padding, so preflight did not reset it.
837
+ 2. Inspect the part you gave a utility. In the Styles panel, your utility is
838
+ applied and c15t's rule for the same property is struck out.
839
+ 3. Add the `dark` class to `<html>`. Your `dark:` utility applies and, where
840
+ c15t follows the class, the banner switches to dark colors.
841
+ 4. Open the preference dialog. Its rules come from a separate stylesheet in
842
+ most frameworks, so check that it is styled too.