@c15t/nextjs 2.0.0-rc.7 → 2.0.0-rc.9

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 (55) hide show
  1. package/README.md +7 -0
  2. package/dist/iab/styles.css +12 -1
  3. package/dist/iab/styles.tw3.css +14 -1
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/styles.css +10 -1
  7. package/dist/styles.tw3.css +13 -1
  8. package/dist/version.cjs +1 -1
  9. package/dist/version.js +1 -1
  10. package/dist-types/headless.d.ts +1 -1
  11. package/dist-types/index.d.ts +2 -2
  12. package/dist-types/libs/browser-initial-data.d.ts +2 -2
  13. package/dist-types/libs/initial-data.d.ts +1 -1
  14. package/dist-types/types.d.ts +3 -3
  15. package/dist-types/version.d.ts +1 -1
  16. package/docs/building-headless-components.md +40 -22
  17. package/docs/callbacks.md +76 -9
  18. package/docs/components/consent-banner.md +83 -9
  19. package/docs/components/consent-dialog.md +12 -2
  20. package/docs/components/consent-manager-provider.md +3 -1
  21. package/docs/components/consent-widget.md +61 -8
  22. package/docs/concepts/client-modes.md +16 -4
  23. package/docs/concepts/initialization-flow.md +9 -2
  24. package/docs/concepts/policy-packs.md +2 -2
  25. package/docs/hooks/use-consent-manager/overview.md +17 -3
  26. package/docs/hooks/use-ssr-status.md +1 -1
  27. package/docs/hooks/use-translations.md +1 -0
  28. package/docs/iab/consent-banner.md +2 -5
  29. package/docs/iab/consent-dialog.md +3 -6
  30. package/docs/iab/overview.md +11 -5
  31. package/docs/integrations/building-integrations.md +405 -0
  32. package/docs/integrations/databuddy.md +22 -5
  33. package/docs/integrations/google-tag-manager.md +2 -2
  34. package/docs/integrations/google-tag.md +2 -29
  35. package/docs/integrations/linkedin-insights.md +1 -1
  36. package/docs/integrations/meta-pixel.md +1 -1
  37. package/docs/integrations/microsoft-uet.md +1 -1
  38. package/docs/integrations/overview.md +18 -2
  39. package/docs/integrations/posthog.md +39 -17
  40. package/docs/integrations/tiktok-pixel.md +1 -1
  41. package/docs/integrations/x-pixel.md +1 -1
  42. package/docs/optimization.md +68 -9
  43. package/docs/policy-packs.md +7 -7
  44. package/docs/quickstart.md +11 -5
  45. package/docs/script-loader.md +22 -1
  46. package/docs/server-side.md +1 -1
  47. package/docs/styling/tailwind.md +23 -17
  48. package/iab/styles.css +1 -0
  49. package/package.json +10 -8
  50. package/readme.json +4 -0
  51. package/src/iab/styles.css +6 -4
  52. package/src/iab/styles.tw3.css +8 -4
  53. package/src/styles.css +3 -3
  54. package/src/styles.tw3.css +7 -4
  55. package/styles.css +1 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: PostHog
3
3
  description: PostHog is an open-source product analytics platform for tracking user behavior, session replays, feature flags, and A/B testing. It supports cookieless tracking, allowing analytics to continue even without cookie consent.
4
- lastModified: 2025-09-19
4
+ lastModified: 2026-04-08
5
5
 
6
6
  icon: posthog
7
7
  ---
@@ -25,7 +25,7 @@ This is the recommended approach if you're using the Posthog JS SDK, this is com
25
25
  posthog.opt_out_capturing() // Avoids accidental tracking without consent till c15t has loaded
26
26
  ```
27
27
 
28
- 2. **Adding the SDK callback to c15t** The PostHog SDK approach uses callbacks.onConsentSet instead of scripts. This lets you toggle PostHog's opt-in/opt-out based on the user's consent choice.
28
+ 2. **Sync settled consent once, then subscribe to real changes** The recommended PostHog SDK approach uses two phases: run one initial sync after c15t has finished resolving consent, then subscribe to future real preference changes with subscribeToConsentChanges().
29
29
 
30
30
  > ℹ️ Info:
31
31
  >
@@ -35,20 +35,38 @@ This is the recommended approach if you're using the Posthog JS SDK, this is com
35
35
  import { getOrCreateConsentRuntime } from 'c15t';
36
36
  import { posthog } from 'posthog-js';
37
37
 
38
- getOrCreateConsentRuntime({
38
+ function syncPostHogMeasurementConsent(hasMeasurementConsent: boolean) {
39
+ if (hasMeasurementConsent) {
40
+ posthog.opt_in_capturing();
41
+ } else {
42
+ posthog.opt_out_capturing();
43
+ }
44
+ }
45
+
46
+ const runtime = getOrCreateConsentRuntime({
39
47
  mode: 'hosted',
40
48
  callbacks: {
41
- onConsentSet({ preferences }) {
42
- if (preferences.measurement) {
43
- posthog.opt_in_capturing();
44
- } else {
45
- posthog.opt_out_capturing();
46
- }
47
- }
48
- }
49
+ onBannerFetched() {
50
+ syncPostHogMeasurementConsent(
51
+ runtime.consentStore.getState().has('measurement')
52
+ );
53
+ },
54
+ },
49
55
  });
56
+
57
+ runtime.consentStore
58
+ .getState()
59
+ .subscribeToConsentChanges(({ allowedCategories }) => {
60
+ syncPostHogMeasurementConsent(
61
+ allowedCategories.includes('measurement')
62
+ );
63
+ });
50
64
  ```
51
65
 
66
+ > ℹ️ Info:
67
+ >
68
+ > Avoid using onConsentSet plus manual deduplication for PostHog. subscribeToConsentChanges() already gives you the exact change-only semantics most analytics SDKs need.
69
+
52
70
  ## PostHog Script Implementation
53
71
 
54
72
  If you want to load posthog via a script tag it's recommended to use this approach.
@@ -80,8 +98,13 @@ By default c15t will always load the script regardless of consent. This is becau
80
98
  posthog({
81
99
  id: 'phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
82
100
  apiHost: 'https://eu.i.posthog.com',
83
- defaults: '2025-05-24',
84
- options: { person_profiles: 'identified_only' }
101
+ scriptUrl: 'https://eu-assets.i.posthog.com/static/array.js',
102
+ initOptions: {
103
+ api_host: 'https://eu.i.posthog.com',
104
+ ui_host: 'https://eu.i.posthog.com',
105
+ autocapture: false,
106
+ person_profiles: 'identified_only',
107
+ }
85
108
  })
86
109
  ```
87
110
 
@@ -92,10 +115,9 @@ By default c15t will always load the script regardless of consent. This is becau
92
115
  |Property|Type|Description|Default|Required|
93
116
  |:--|:--|:--|:--|:--:|
94
117
  |id|string|Your posthog id, begins with 'phc\_'.|-|✅ Required|
95
- |apiHost|string|Your posthog api host.|'https\://eu.i.posthog.com'|✅ Required|
96
- |defaults|string|The defaults for the posthog script.|-|✅ Required|
97
- |options|Record\<string, unknown>|Other optional options for the posthog script.|-|✅ Required|
98
- |script|Script \|undefined|Override or extend the default script values. Options: \`id\`: 'posthog-consent'; \`category\`: 'measurement'|-|Optional|
118
+ |apiHost|string \|undefined|Your posthog api host.|'https\://eu.i.posthog.com'|Optional|
119
+ |scriptUrl|string \|undefined|The PostHog array loader URL.|-|Optional|
120
+ |initOptions|Record\<string, unknown> \|undefined|PostHog init options passed to \`posthog.init(...)\`.|-|Optional|
99
121
 
100
122
  ### Script
101
123
 
@@ -31,7 +31,7 @@ tiktokPixel({
31
31
  |Property|Type|Description|Default|Required|
32
32
  |:--|:--|:--|:--|:--:|
33
33
  |pixelId|string|Your TikTok Pixel ID|-|✅ Required|
34
- |script|Script \|undefined|Override or extend the default script values. Options: \`id\`: 'tiktok-pixel'; \`src\`: \`https\://analytics.tiktok.com/i18n/pixel/events.js\`; \`category\`: 'marketing'|-|Optional|
34
+ |scriptSrc|string \|undefined|TikTok Pixel loader base URL.|-|Optional|
35
35
 
36
36
  ### Script
37
37
 
@@ -37,7 +37,7 @@ xPixelEvent('tw-xxxx-xxxx', { value: 10.00, currency: 'USD' });
37
37
  |Property|Type|Description|Default|Required|
38
38
  |:--|:--|:--|:--|:--:|
39
39
  |pixelId|string|Your X Pixel ID|-|✅ Required|
40
- |script|Script \|undefined|Override or extend the default script values. Options: \`id\`: 'x-pixel'; \`src\`: \`https\://static.ads-twitter.com/uwt.js\`; \`category\`: 'marketing'|-|Optional|
40
+ |scriptSrc|string \|undefined|X Pixel loader URL.|-|Optional|
41
41
 
42
42
  ### Script
43
43
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Optimization
3
3
  description: Improve c15t startup performance in Next.js with rewrites, static prefetching, and rendering tradeoffs.
4
- lastModified: 2026-03-17
4
+ lastModified: 2026-04-09
5
5
  ---
6
6
  Use this guide when you care about banner visibility speed, route static-ness, and reducing backend round-trip cost.
7
7
 
@@ -48,7 +48,7 @@ Why this helps:
48
48
 
49
49
  |Goal|Strategy|Tradeoff|
50
50
  |--|--|--|
51
- |Keep routes fully static|`C15tPrefetch` + `getPrefetchedInitialData()`|Still client-side fetch, but starts earlier|
51
+ |Keep routes fully static|`C15tPrefetch`|Still client-side fetch, but starts earlier|
52
52
  |Fastest first banner on dynamic routes|`fetchInitialData()` in a Server Component (do not await)|Route becomes dynamic because of `next/headers`|
53
53
  |Simplest setup|Client-only init (no prefetch)|Banner appears later on slow networks|
54
54
 
@@ -60,9 +60,72 @@ In production benchmarks with a same-origin rewrite, prefetching strategies show
60
60
  |Browser prefetch|\~1.3x faster|\~2.6x earlier|\~1.25x faster|
61
61
  |Server prefetch|\~2x faster|before page loads|\~1.9x faster|
62
62
 
63
+ ### Dynamic Routes: Fetch On The Server And Stream
64
+
65
+ Use `fetchInitialData()` in a Server Component when you want the fastest first banner and can accept the route becoming dynamic.
66
+
67
+ ```tsx title="app/layout.tsx"
68
+ import { fetchInitialData } from '@c15t/nextjs';
69
+ import ConsentManager from '@/components/consent-manager';
70
+
71
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
72
+ const ssrData = fetchInitialData({
73
+ backendURL: process.env.NEXT_PUBLIC_C15T_URL!,
74
+ });
75
+
76
+ return (
77
+ <html lang="en">
78
+ <body>
79
+ <ConsentManager ssrData={ssrData}>{children}</ConsentManager>
80
+ </body>
81
+ </html>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ```tsx title="components/consent-manager/provider.tsx"
87
+ 'use client';
88
+
89
+ import { type ReactNode } from 'react';
90
+ import {
91
+ ConsentManagerProvider,
92
+ ConsentBanner,
93
+ ConsentDialog,
94
+ type InitialDataPromise,
95
+ } from '@c15t/nextjs';
96
+
97
+ export default function ConsentManager({
98
+ children,
99
+ ssrData,
100
+ }: {
101
+ children: ReactNode;
102
+ ssrData?: InitialDataPromise;
103
+ }) {
104
+ return (
105
+ <ConsentManagerProvider
106
+ options={{
107
+ mode: 'hosted',
108
+ backendURL: '/api/c15t',
109
+ store: { ssrData },
110
+ }}
111
+ >
112
+ <ConsentBanner />
113
+ <ConsentDialog />
114
+ {children}
115
+ </ConsentManagerProvider>
116
+ );
117
+ }
118
+ ```
119
+
120
+ > ℹ️ **Info:**
121
+ > Do not await fetchInitialData(). Pass the unresolved Promise to the provider so Next.js can stream the route while /init runs in parallel.
122
+ >
123
+ > ℹ️ **Info:**
124
+ > For fetchInitialData(), prefer a direct backend URL such as https\://your-instance.c15t.dev instead of a rewrite to avoid an extra server-side proxy hop. See Server-Side Data Fetching for the full flow.
125
+
63
126
  ### Static Routes: Start Fetch Early In The Browser
64
127
 
65
- Use `C15tPrefetch` in your layout and consume the same Promise in the provider.
128
+ Use `C15tPrefetch` in your layout. Matching prefetched data is consumed automatically by the runtime during first store initialization.
66
129
 
67
130
  ```tsx title="app/layout.tsx"
68
131
  import { C15tPrefetch } from '@c15t/nextjs';
@@ -92,7 +155,6 @@ import {
92
155
  ConsentManagerProvider,
93
156
  ConsentBanner,
94
157
  ConsentDialog,
95
- getPrefetchedInitialData,
96
158
  } from '@c15t/nextjs';
97
159
 
98
160
  export default function ConsentManagerClient({ children }: { children: React.ReactNode }) {
@@ -101,10 +163,7 @@ export default function ConsentManagerClient({ children }: { children: React.Rea
101
163
  options={{
102
164
  mode: 'hosted',
103
165
  backendURL: '/api/c15t',
104
- ssrData: getPrefetchedInitialData({
105
- backendURL: '/api/c15t',
106
- overrides: { country: 'DE', region: 'BE', language: 'de' },
107
- }),
166
+ overrides: { country: 'DE', region: 'BE', language: 'de' },
108
167
  }}
109
168
  >
110
169
  <ConsentBanner />
@@ -119,7 +178,7 @@ export default function ConsentManagerClient({ children }: { children: React.Rea
119
178
  > C15tPrefetch uses Next.js beforeInteractive script loading, so the /init request can start before hydration. In App Router streaming output, this may appear as Next.js script bootstrap data (for example self.\_\_next\_s.push(...)) instead of a literal \<head>\<script> tag.
120
179
  >
121
180
  > ℹ️ **Info:**
122
- > If you use overrides or custom credentials in C15tPrefetch, pass the same values to getPrefetchedInitialData(...) so it resolves the matching prefetched request.
181
+ > If overrides.gpc conflicts with the browser's ambient GPC signal, the prefetched entry is not reused and c15t falls back to a normal client /init.
123
182
 
124
183
  ## Keep The Provider Mounted Across Navigation
125
184
 
@@ -6,7 +6,7 @@ Policy packs configure how c15t handles regional consent — which model (opt-in
6
6
 
7
7
  **For most apps, you just need a `ConsentManagerProvider` pointing at your backend with presets configured there.** The frontend receives the resolved policy via the `/init` response — no client-side policy config required.
8
8
 
9
- When a backend isn't available — local development, static previews, Storybook, or as a resilience fallback — you can pass policies directly to the provider via `offlinePolicy.policyPacks` and c15t resolves them locally.
9
+ When a backend isn't available — local development, static previews, Storybook, automated tests, or as a resilience fallback during a temporary outage — you can pass policies directly to the provider via `offlinePolicy.policyPacks` and c15t resolves them locally.
10
10
 
11
11
  > ℹ️ **Info:**
12
12
  > For QA and testing, use the c15t DevTools to simulate different regions and policy responses against your real backend, rather than switching to offline mode.
@@ -29,9 +29,9 @@ When using consent.io or a self-hosted backend, the provider connects automatica
29
29
 
30
30
  The backend resolves the correct policy based on the visitor's geo data and returns it in the `/init` response. Configure your presets on the backend side.
31
31
 
32
- ## Offline Presets (Fallback)
32
+ ## Offline Presets (Development and Fallback)
33
33
 
34
- When no backend is available, pass presets directly to the provider:
34
+ Use offline presets mainly for local development, Storybook, deterministic tests, or temporary backend outages:
35
35
 
36
36
  ```tsx
37
37
  import { policyPackPresets } from '@c15t/react';
@@ -109,7 +109,7 @@ For production Next.js apps, you can optionally hydrate the first `/init` respon
109
109
 
110
110
  ## Offline / Fallback
111
111
 
112
- For local development, static sites, or when the backend is unreachable, pass policies directly:
112
+ For local development, previews, automated tests, or when the backend is temporarily unreachable, pass policies directly:
113
113
 
114
114
  ```tsx title="components/consent-manager/provider.tsx"
115
115
  'use client';
@@ -150,7 +150,7 @@ export function ConsentManager({ children }: { children: ReactNode }) {
150
150
  ## Provider Shape
151
151
 
152
152
  Configure packs through `offlinePolicy.policyPacks`. Add `offlinePolicy.i18n`
153
- when you want offline mode to mirror hosted policy-profile language behavior:
153
+ when you want local previews or fallback behavior to mirror hosted policy-profile language behavior:
154
154
 
155
155
  ```tsx
156
156
  <ConsentManagerProvider
@@ -209,7 +209,7 @@ With that setup, offline mode resolves language the same way as hosted mode:
209
209
  |`offlinePolicy: { policyPacks: [] }`|Explicit no-banner mode|
210
210
  |Non-empty pack, no match, no default|Explicit no-banner mode|
211
211
 
212
- Omitting the option gives you a safe opt-in default for local development and outage scenarios. Providing it tells c15t you want policy-driven behavior exactly as configured.
212
+ Omitting the option gives you a safe opt-in default for local development and outage scenarios. Providing it tells c15t you want deterministic preview or fallback behavior exactly as configured.
213
213
 
214
214
  ## QA and Debugging
215
215
 
@@ -221,7 +221,7 @@ For deeper inspection:
221
221
  * Open the DevTools Policy panel to inspect matcher resolution and fingerprints
222
222
  * Compare your frontend preview with the backend `/init` response before shipping
223
223
 
224
- If you need fully deterministic resolution without a backend (e.g., in automated tests or Storybook), pair `offlinePolicy.policyPacks` with `overrides`:
224
+ If you need fully deterministic resolution without a backend during testing or preview work (for example, in automated tests or Storybook), pair `offlinePolicy.policyPacks` with `overrides`:
225
225
 
226
226
  ```tsx
227
227
  options={{
@@ -33,15 +33,17 @@ availableIn:
33
33
  |yarn|`yarn add @c15t/nextjs`|
34
34
  |bun|`bun add @c15t/nextjs`|
35
35
 
36
- 2. **Import styles** Import the prebuilt component stylesheet in your root layout. This is required for styled components to render correctly.
36
+ 2. **Import styles** Import the prebuilt component stylesheet in your app-level CSS entrypoint. This is required for styled components to render correctly.
37
37
 
38
- ```tsx
39
- import '@c15t/nextjs/styles.css';
38
+ ```css
39
+ @import "@c15t/nextjs/styles.css";
40
40
  ```
41
41
 
42
+ Keeping the c15t stylesheet in your global CSS entrypoint makes layer and cascade order explicit. JS/TSX side-effect imports can load in a different order across framework and Tailwind tooling, which makes style regressions harder to debug.
43
+
42
44
  > ℹ️ Info:
43
45
  >
44
- > If you are using the headless API or fully custom styling, you can skip this import.
46
+ > If you are using the headless API or fully custom styling, you can skip this import. Your root layout should continue importing ./globals.css as usual.
45
47
 
46
48
  3. **Create ConsentManager components** Create a provider component with the consent UI and a wrapper that re-exports it. This initializes the consent store and makes consent state available to all child components.
47
49
 
@@ -85,7 +87,11 @@ availableIn:
85
87
 
86
88
  > ℹ️ Info:
87
89
  >
88
- > Don't have a backend yet? You can use mode: 'offline' for local-only consent storage, but review the browser-only storage consequences before choosing it for production.
90
+ > Hosted mode is the recommended production setup because the backend resolves jurisdiction and policy, keeps durable consent records, and lets c15t recover from temporary network failures by re-syncing later.
91
+ >
92
+ > ℹ️ Info:
93
+ >
94
+ > Don't have a backend yet? You can use mode: 'offline' for local-only consent storage, but it gives up backend audit history, server-side consent awareness, and automatic jurisdiction detection. Review the browser-only storage consequences before choosing it for production.
89
95
 
90
96
  4. **Mount ConsentManager at the app root** Wrap your app tree with ConsentManager so all routes/components can access consent state.
91
97
 
@@ -4,7 +4,7 @@ description: Gate third-party scripts behind consent - load Google Analytics, Me
4
4
  ---
5
5
  The script loader manages third-party scripts based on consent state. Scripts are defined in the provider's `scripts` option and are automatically loaded when their required consent category is granted, and unloaded when consent is revoked.
6
6
 
7
- c15t has a collection of premade scripts available on the @c15t/scripts package. It's recomended to check if a pre-built integration exists before manually creating a script, see the [integrations overview](/docs/integrations/overview).
7
+ c15t has a collection of premade scripts available in `@c15t/scripts`. Check the [integrations overview](/docs/integrations/overview) first before manually building a script.
8
8
 
9
9
  |Package manager|Command|
10
10
  |:--|:--|
@@ -15,6 +15,12 @@ c15t has a collection of premade scripts available on the @c15t/scripts package.
15
15
 
16
16
  > ℹ️ **Info:**
17
17
  > We recommend using the pre-built integrations when possible.
18
+ >
19
+ > ℹ️ **Info:**
20
+ > If you need a vendor we do not ship yet, see the custom integration guide. It covers both one-off Script objects and reusable manifest-backed integrations.
21
+ >
22
+ > ℹ️ **Info:**
23
+ > For app-specific scripts, use a plain Script object. For reusable integrations, prefer a manifest-backed helper so startup phases, consent signaling, and future server-side loading support stay structured.
18
24
 
19
25
  ## Basic Usage
20
26
 
@@ -47,6 +53,21 @@ export function ConsentManager({ children }: { children: ReactNode }) {
47
53
  }
48
54
  ```
49
55
 
56
+ ## Choose the Right Approach
57
+
58
+ * Use a plain `Script` for one-off app code.
59
+ * Use a manifest-backed helper in `@c15t/scripts` for reusable integrations, contributions, or anything that needs structured startup behavior.
60
+
61
+ If you are building something reusable, start with the [custom integration guide](/docs/integrations/building-integrations) before using raw callbacks.
62
+
63
+ ## Reusable Integrations
64
+
65
+ For app-specific use, raw `Script` objects are usually enough.
66
+
67
+ For reusable integrations, c15t uses a manifest-backed model in `@c15t/scripts`. That keeps startup phases, consent signaling, and vendor-specific boot logic structured instead of hidden inside large callback bodies.
68
+
69
+ If you are building an integration for multiple apps or contributing upstream, use the [custom integration guide](/docs/integrations/building-integrations).
70
+
50
71
  ## Script Types
51
72
 
52
73
  ### Standard Scripts
@@ -29,7 +29,7 @@ const ssrData = fetchInitialData({
29
29
  > Do not await fetchInitialData() in Server Components. Pass the Promise directly to your client component so Next.js can stream the page while the consent data loads in parallel.
30
30
  >
31
31
  > ℹ️ **Info:**
32
- > Need fully static routes? Use C15tPrefetch in your layout and getPrefetchedInitialData() in your client provider instead of fetchInitialData(). See Optimization.
32
+ > Need fully static routes? Use C15tPrefetch in your layout. Matching prefetched data is consumed automatically by the runtime instead of using fetchInitialData(). See Optimization.
33
33
 
34
34
  ### How Streaming Works
35
35
 
@@ -6,41 +6,47 @@ c15t works with Tailwind CSS out of the box. Use the `slots` theme option to app
6
6
 
7
7
  ## Setup
8
8
 
9
- Import the standard c15t stylesheet once at the root of your app:
9
+ Import the standard c15t stylesheet once in your app-level CSS entrypoint:
10
10
 
11
- ```tsx
12
- // React
13
- import '@c15t/react/styles.css';
11
+ ```css
12
+ /* React: src/index.css */
13
+ @import "@c15t/react/styles.css";
14
14
 
15
- // Next.js
16
- import '@c15t/nextjs/styles.css';
15
+ /* Next.js: app/globals.css */
16
+ @import "@c15t/nextjs/styles.css";
17
17
  ```
18
18
 
19
+ Keeping the c15t stylesheet in your global CSS entrypoint makes layer and cascade order explicit. JS/TSX side-effect imports can load in a different order across framework and Tailwind tooling, which makes style regressions harder to debug.
20
+
19
21
  ### Tailwind v4
20
22
 
21
- Tailwind v4 automatically scans your source files. Import Tailwind normally. c15t component styles join Tailwind's `components` layer automatically, so no extra c15t-specific layer declaration is needed:
23
+ Tailwind v4 automatically scans your source files. Import Tailwind normally, then place the c15t stylesheet immediately after it. c15t component styles join Tailwind's `components` layer automatically, so no extra c15t-specific layer declaration is needed:
22
24
 
23
- ```css
25
+ ```css title="src/index.css"
24
26
  @import "tailwindcss";
27
+ @import "@c15t/react/styles.css";
25
28
  ```
26
29
 
27
- ### Tailwind v3
30
+ ```css title="app/globals.css"
31
+ @import "tailwindcss";
32
+ @import "@c15t/nextjs/styles.css";
33
+ ```
28
34
 
29
- Import the Tailwind 3-compatible c15t stylesheet before your app Tailwind globals, then keep your standard Tailwind directives in the app stylesheet:
35
+ ### Tailwind v3
30
36
 
31
- ```tsx title="app/layout.tsx"
32
- import '@c15t/react/styles.tw3.css';
33
- import './globals.css';
34
- ```
37
+ Import the Tailwind 3-compatible c15t stylesheet after `@tailwind components;` and before `@tailwind utilities;`:
35
38
 
36
- ```tsx title="app/layout.tsx"
37
- import '@c15t/nextjs/styles.tw3.css';
38
- import './globals.css';
39
+ ```css title="src/index.css"
40
+ @tailwind base;
41
+ @tailwind components;
42
+ @import "@c15t/react/styles.tw3.css";
43
+ @tailwind utilities;
39
44
  ```
40
45
 
41
46
  ```css title="app/globals.css"
42
47
  @tailwind base;
43
48
  @tailwind components;
49
+ @import "@c15t/nextjs/styles.tw3.css";
44
50
  @tailwind utilities;
45
51
  ```
46
52
 
package/iab/styles.css ADDED
@@ -0,0 +1 @@
1
+ @import "../dist/iab/styles.css";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c15t/nextjs",
3
- "version": "2.0.0-rc.7",
3
+ "version": "2.0.0-rc.9",
4
4
  "description": "Developer-first CMP for Next.js: cookie banner, consent manager, preferences centre. GDPR ready with minimal setup and rich customization.",
5
5
  "keywords": [
6
6
  "nextjs",
@@ -33,9 +33,9 @@
33
33
  "type": "module",
34
34
  "exports": {
35
35
  "./styles.css": "./dist/styles.css",
36
- "./styles.tw3.css": "./src/styles.tw3.css",
36
+ "./styles.tw3.css": "./dist/styles.tw3.css",
37
37
  "./iab/styles.css": "./dist/iab/styles.css",
38
- "./iab/styles.tw3.css": "./src/iab/styles.tw3.css",
38
+ "./iab/styles.tw3.css": "./dist/iab/styles.tw3.css",
39
39
  "./headless": {
40
40
  "types": "./dist-types/headless.d.ts",
41
41
  "import": "./dist/headless.js",
@@ -60,6 +60,8 @@
60
60
  "docs",
61
61
  "dist-types",
62
62
  "client",
63
+ "styles.css",
64
+ "iab",
63
65
  "src/styles.css",
64
66
  "src/styles.tw3.css",
65
67
  "src/iab/styles.css",
@@ -67,10 +69,10 @@
67
69
  ],
68
70
  "scripts": {
69
71
  "prebuild": "genversion --esm --semi src/version.ts",
70
- "build": "bun prebuild && rslib build && bun ../../scripts/agent-docs/generate-package-docs.ts @c15t/nextjs",
72
+ "build": "bun prebuild && rslib build && bun ../../scripts/normalize-dist-types.mjs && bun scripts/generate-distribution-css.ts && bun ../../scripts/agent-docs/generate-package-docs.ts @c15t/nextjs",
71
73
  "build:agent-docs": "bun ../../scripts/agent-docs/generate-package-docs.ts @c15t/nextjs",
72
74
  "check-types": "bun prebuild && tsc --noEmit",
73
- "dev": "bun prebuild && rslib build",
75
+ "dev": "bun prebuild && rslib build && bun ../../scripts/normalize-dist-types.mjs && bun scripts/generate-distribution-css.ts",
74
76
  "fmt": "bun biome format --write . && bun biome check --formatter-enabled=false --linter-enabled=false --write",
75
77
  "lint": "bun biome lint ./src",
76
78
  "prepack": "cd ../.. && bunx turbo run build --filter=@c15t/nextjs",
@@ -78,9 +80,9 @@
78
80
  "test:watch": "bun prebuild && vitest --passWithNoTests"
79
81
  },
80
82
  "dependencies": {
81
- "@c15t/react": "2.0.0-rc.7",
82
- "@c15t/translations": "2.0.0-rc.5",
83
- "c15t": "2.0.0-rc.6"
83
+ "@c15t/react": "2.0.0-rc.9",
84
+ "@c15t/translations": "2.0.0-rc.8",
85
+ "c15t": "2.0.0-rc.8"
84
86
  },
85
87
  "devDependencies": {
86
88
  "@c15t/typescript-config": "0.0.1-beta.1",
package/readme.json CHANGED
@@ -21,6 +21,10 @@
21
21
  "",
22
22
  "```bash\npnpm add @c15t/nextjs\n```",
23
23
  "",
24
+ "Then add the prebuilt stylesheet to your app-level CSS entrypoint:",
25
+ "",
26
+ "```css\n/* app/globals.css */\n@import \"@c15t/nextjs/styles.css\";\n```",
27
+ "",
24
28
  "To manually install, follow the guide in our [docs – manual setup](https://c15t.com/docs/frameworks/nextjs/quickstart#manual-setup)."
25
29
  ],
26
30
  "usage": [
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * @c15t/nextjs — IAB TCF component styles.
3
3
  *
4
- * Import this stylesheet when using IAB consent components in Next.js.
5
- * Self-contained includes shared primitives, no need for base styles.
4
+ * Add this stylesheet to the same global CSS entrypoint as your base c15t styles
5
+ * when using IAB consent components in Next.js. Do not import either stylesheet
6
+ * from JS/TSX.
6
7
  *
7
- * Usage:
8
- * import '@c15t/nextjs/iab/styles.css';
8
+ * Usage (app/globals.css):
9
+ * @import "@c15t/nextjs/styles.css";
10
+ * @import "@c15t/nextjs/iab/styles.css";
9
11
  */
10
12
  @import "@c15t/react/iab/styles.css";
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * @c15t/nextjs/iab — Tailwind 3-compatible IAB component styles.
3
3
  *
4
- * Import this stylesheet before your app Tailwind globals so utility classes
5
- * can come after the c15t base rules.
4
+ * Add this stylesheet to the same global CSS entrypoint as your base c15t styles.
5
+ * Do not import either stylesheet from JS/TSX files.
6
6
  *
7
- * Usage:
8
- * import '@c15t/nextjs/iab/styles.tw3.css';
7
+ * Usage (app/globals.css):
8
+ * @tailwind base;
9
+ * @tailwind components;
10
+ * @import "@c15t/nextjs/styles.tw3.css";
11
+ * @import "@c15t/nextjs/iab/styles.tw3.css";
12
+ * @tailwind utilities;
9
13
  */
10
14
  @import "@c15t/react/iab/styles.tw3.css";
package/src/styles.css CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @c15t/nextjs — Non-IAB prebuilt component styles.
3
3
  *
4
- * Import this stylesheet once in your root layout when using
4
+ * Import this stylesheet once from your app-level CSS entrypoint when using
5
5
  * prebuilt (styled) consent components.
6
6
  *
7
- * Usage:
8
- * import '@c15t/nextjs/styles.css';
7
+ * Usage (app/globals.css):
8
+ * @import "@c15t/nextjs/styles.css";
9
9
  */
10
10
  @import "@c15t/react/styles.css";
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * @c15t/nextjs — Tailwind 3-compatible prebuilt component styles.
3
3
  *
4
- * Import this stylesheet before your app Tailwind globals so utility classes
5
- * can come after the c15t base rules.
4
+ * Import this stylesheet in the same global CSS entrypoint as Tailwind 3,
5
+ * after `@tailwind components;` and before `@tailwind utilities;`.
6
6
  *
7
- * Usage:
8
- * import '@c15t/nextjs/styles.tw3.css';
7
+ * Usage (app/globals.css):
8
+ * @tailwind base;
9
+ * @tailwind components;
10
+ * @import "@c15t/nextjs/styles.tw3.css";
11
+ * @tailwind utilities;
9
12
  */
10
13
  @import "@c15t/react/styles.tw3.css";
package/styles.css ADDED
@@ -0,0 +1 @@
1
+ @import "./dist/styles.css";