@c15t/nextjs 2.2.0 → 3.0.0-alpha.0

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 (220) hide show
  1. package/AGENTS.md +102 -142
  2. package/README.md +4 -4
  3. package/dist/api.js +1 -0
  4. package/dist/config.js +1 -0
  5. package/dist/devtools.js +2 -0
  6. package/dist/headers.js +1 -0
  7. package/dist/iab/styles.css +1 -1
  8. package/dist/iab/styles.tw3.css +133 -19
  9. package/dist/index.js +1 -1
  10. package/dist/middleware.js +1 -0
  11. package/dist/node-bridge.js +1 -0
  12. package/dist/pages.js +1 -0
  13. package/dist/proxy.js +1 -0
  14. package/dist/root.js +2 -0
  15. package/dist/server.js +1 -0
  16. package/dist/static.js +1 -0
  17. package/dist/styles.css +1 -1
  18. package/dist/styles.tw3.css +173 -29
  19. package/dist/version.js +1 -1
  20. package/dist-types/api.d.ts +88 -0
  21. package/dist-types/config.d.ts +110 -0
  22. package/dist-types/devtools.d.ts +1 -0
  23. package/dist-types/headers.d.ts +4 -0
  24. package/dist-types/index.d.ts +28 -10
  25. package/dist-types/middleware.d.ts +15 -0
  26. package/dist-types/node-bridge.d.ts +57 -0
  27. package/dist-types/pages.d.ts +88 -0
  28. package/dist-types/proxy.d.ts +34 -0
  29. package/dist-types/root.d.ts +86 -0
  30. package/dist-types/server.d.ts +164 -0
  31. package/dist-types/static.d.ts +42 -0
  32. package/dist-types/types.d.ts +5 -36
  33. package/dist-types/version.d.ts +1 -1
  34. package/docs/README.md +102 -142
  35. package/docs/assets/v3/brand-bar.png +0 -0
  36. package/docs/assets/v3/brand-card.png +0 -0
  37. package/docs/assets/v3/choice-wall.png +0 -0
  38. package/docs/assets/v3/mobile-card.png +0 -0
  39. package/docs/assets/v3/preferences.png +0 -0
  40. package/docs/customization/overview.md +45 -0
  41. package/docs/customization/recipes.md +79 -0
  42. package/docs/customization/slots.md +55 -0
  43. package/docs/customization/tokens.md +76 -0
  44. package/docs/customization/translations.md +49 -0
  45. package/docs/frameworks/next/api-reference/data-fetching.md +416 -0
  46. package/docs/frameworks/next/app-router.md +403 -0
  47. package/docs/frameworks/next/client-side.md +118 -0
  48. package/docs/frameworks/next/components/consent-banner.md +251 -211
  49. package/docs/frameworks/next/components/consent-dialog-link.md +96 -35
  50. package/docs/frameworks/next/components/consent-dialog-trigger.md +74 -149
  51. package/docs/frameworks/next/components/consent-dialog.md +189 -134
  52. package/docs/frameworks/next/components/consent-manager-provider.md +60 -318
  53. package/docs/frameworks/next/components/consent-widget.md +172 -114
  54. package/docs/frameworks/next/components/dev-tools.md +199 -40
  55. package/docs/frameworks/next/components/frame.md +137 -42
  56. package/docs/frameworks/next/concepts/consent-categories.md +24 -89
  57. package/docs/frameworks/next/concepts/policy-presets.md +142 -0
  58. package/docs/frameworks/next/content-security-policy.md +189 -0
  59. package/docs/frameworks/next/data-fetching.md +74 -0
  60. package/docs/frameworks/next/geography-headers.md +251 -0
  61. package/docs/frameworks/next/headless.md +95 -185
  62. package/docs/frameworks/next/hooks/use-consent-manager/overview.md +42 -163
  63. package/docs/frameworks/next/iab/overview.md +37 -107
  64. package/docs/frameworks/next/optimization.md +158 -194
  65. package/docs/frameworks/next/pages-router.md +296 -0
  66. package/docs/frameworks/next/quickstart.md +31 -132
  67. package/docs/frameworks/next/script-loader.md +134 -467
  68. package/docs/frameworks/next/server-side.md +97 -130
  69. package/docs/frameworks/next/static-export.md +164 -0
  70. package/docs/frameworks/next/styling/overview.md +166 -250
  71. package/docs/frameworks/next/troubleshooting.md +134 -144
  72. package/docs/guides/consent-state.md +60 -0
  73. package/docs/guides/data-fetching.md +163 -0
  74. package/docs/guides/deployment-modes.md +63 -0
  75. package/docs/guides/troubleshooting.md +68 -0
  76. package/docs/guides/verify-consent.md +62 -0
  77. package/docs/integrations/adobe-analytics.md +239 -105
  78. package/docs/integrations/ahrefs-analytics.md +238 -104
  79. package/docs/integrations/amplitude.md +219 -157
  80. package/docs/integrations/building-integrations.md +32 -224
  81. package/docs/integrations/clearbit.md +247 -86
  82. package/docs/integrations/cloudflare-web-analytics.md +250 -84
  83. package/docs/integrations/crisp.md +251 -97
  84. package/docs/integrations/databuddy.md +259 -153
  85. package/docs/integrations/fathom-analytics.md +239 -96
  86. package/docs/integrations/google-maps.md +328 -207
  87. package/docs/integrations/google-tag-manager.md +248 -96
  88. package/docs/integrations/google-tag.md +261 -90
  89. package/docs/integrations/heap.md +222 -149
  90. package/docs/integrations/hightouch.md +225 -131
  91. package/docs/integrations/hotjar.md +239 -90
  92. package/docs/integrations/intercom.md +239 -98
  93. package/docs/integrations/linkedin-insights.md +243 -113
  94. package/docs/integrations/logrocket.md +241 -123
  95. package/docs/integrations/matomo-analytics.md +256 -111
  96. package/docs/integrations/meta-pixel.md +197 -324
  97. package/docs/integrations/microsoft-clarity.md +233 -114
  98. package/docs/integrations/microsoft-uet.md +245 -110
  99. package/docs/integrations/mixpanel-analytics.md +252 -87
  100. package/docs/integrations/openai-pixel.md +441 -0
  101. package/docs/integrations/overview.md +95 -133
  102. package/docs/integrations/pirsch.md +249 -96
  103. package/docs/integrations/plausible-analytics.md +241 -100
  104. package/docs/integrations/posthog.md +353 -214
  105. package/docs/integrations/promptwatch.md +251 -81
  106. package/docs/integrations/reddit-pixel.md +226 -173
  107. package/docs/integrations/rudderstack.md +244 -187
  108. package/docs/integrations/rybbit-analytics.md +244 -91
  109. package/docs/integrations/segment.md +238 -92
  110. package/docs/integrations/snapchat-pixel.md +240 -110
  111. package/docs/integrations/tiktok-pixel.md +249 -81
  112. package/docs/integrations/umami-analytics.md +242 -95
  113. package/docs/integrations/vercel-analytics.md +242 -90
  114. package/docs/integrations/x-pixel.md +238 -104
  115. package/docs/integrations/youtube.md +354 -142
  116. package/docs/upgrade-v3.md +334 -0
  117. package/iab/styles.css +1 -1
  118. package/iab/styles.tw3.css +1 -1
  119. package/package.json +106 -65
  120. package/readme.json +3 -3
  121. package/src/iab/styles.css +1 -1
  122. package/src/iab/styles.tw3.css +1 -1
  123. package/src/styles.css +1 -1
  124. package/src/styles.tw3.css +1 -1
  125. package/styles.css +1 -1
  126. package/styles.tw3.css +1 -1
  127. package/client/components/consent-dialog-link.js +0 -3
  128. package/client/components/integrations.js +0 -3
  129. package/dist/components/integrations/index.cjs +0 -1
  130. package/dist/components/integrations/index.js +0 -1
  131. package/dist/headless.cjs +0 -1
  132. package/dist/index.cjs +0 -1
  133. package/dist/libs/browser-initial-data.cjs +0 -1
  134. package/dist/libs/browser-initial-data.js +0 -1
  135. package/dist/libs/initial-data.cjs +0 -1
  136. package/dist/libs/initial-data.js +0 -1
  137. package/dist/types.cjs +0 -1
  138. package/dist/version.cjs +0 -1
  139. package/dist-types/components/integrations/index.d.ts +0 -1
  140. package/dist-types/libs/browser-initial-data.d.ts +0 -9
  141. package/dist-types/libs/initial-data.d.ts +0 -33
  142. package/docs/frameworks/next/building-headless-components.md +0 -379
  143. package/docs/frameworks/next/callbacks.md +0 -186
  144. package/docs/frameworks/next/concepts/client-modes.md +0 -177
  145. package/docs/frameworks/next/concepts/consent-models.md +0 -117
  146. package/docs/frameworks/next/concepts/cookie-management.md +0 -122
  147. package/docs/frameworks/next/concepts/glossary.md +0 -24
  148. package/docs/frameworks/next/concepts/initialization-flow.md +0 -149
  149. package/docs/frameworks/next/concepts/policy-packs.md +0 -230
  150. package/docs/frameworks/next/hooks/use-color-scheme.md +0 -41
  151. package/docs/frameworks/next/hooks/use-consent-manager/checking-consent.md +0 -96
  152. package/docs/frameworks/next/hooks/use-consent-manager/location-info.md +0 -97
  153. package/docs/frameworks/next/hooks/use-consent-manager/setting-consent.md +0 -94
  154. package/docs/frameworks/next/hooks/use-draggable.md +0 -59
  155. package/docs/frameworks/next/hooks/use-focus-trap.md +0 -42
  156. package/docs/frameworks/next/hooks/use-reduced-motion.md +0 -37
  157. package/docs/frameworks/next/hooks/use-ssr-status.md +0 -32
  158. package/docs/frameworks/next/hooks/use-text-direction.md +0 -50
  159. package/docs/frameworks/next/hooks/use-translations.md +0 -55
  160. package/docs/frameworks/next/iab/consent-banner.md +0 -91
  161. package/docs/frameworks/next/iab/consent-dialog.md +0 -129
  162. package/docs/frameworks/next/iab/use-gvl-data.md +0 -21
  163. package/docs/frameworks/next/iframe-blocking.md +0 -106
  164. package/docs/frameworks/next/internationalization.md +0 -207
  165. package/docs/frameworks/next/network-blocker.md +0 -140
  166. package/docs/frameworks/next/policy-packs.md +0 -248
  167. package/docs/frameworks/next/styling/classnames.md +0 -94
  168. package/docs/frameworks/next/styling/color-scheme.md +0 -84
  169. package/docs/frameworks/next/styling/css-variables.md +0 -53
  170. package/docs/frameworks/next/styling/slots.md +0 -94
  171. package/docs/frameworks/next/styling/tailwind.md +0 -137
  172. package/docs/frameworks/next/styling/tokens.md +0 -156
  173. package/docs/shared/concepts/client-modes.md +0 -103
  174. package/docs/shared/concepts/consent-categories.md +0 -41
  175. package/docs/shared/concepts/consent-models.md +0 -72
  176. package/docs/shared/concepts/cookie-management.md +0 -88
  177. package/docs/shared/concepts/glossary.md +0 -24
  178. package/docs/shared/concepts/initialization-flow.md +0 -105
  179. package/docs/shared/concepts/policy-packs.md +0 -225
  180. package/docs/shared/react/components/consent-banner.md +0 -242
  181. package/docs/shared/react/components/consent-dialog-link.md +0 -45
  182. package/docs/shared/react/components/consent-dialog-trigger.md +0 -185
  183. package/docs/shared/react/components/consent-dialog.md +0 -119
  184. package/docs/shared/react/components/consent-manager-provider.md +0 -225
  185. package/docs/shared/react/components/consent-widget.md +0 -121
  186. package/docs/shared/react/components/dev-tools.md +0 -81
  187. package/docs/shared/react/components/frame.md +0 -52
  188. package/docs/shared/react/guides/building-headless-components.md +0 -110
  189. package/docs/shared/react/guides/callbacks.md +0 -89
  190. package/docs/shared/react/guides/headless.md +0 -31
  191. package/docs/shared/react/guides/iframe-blocking.md +0 -65
  192. package/docs/shared/react/guides/internationalization.md +0 -123
  193. package/docs/shared/react/guides/network-blocker.md +0 -72
  194. package/docs/shared/react/guides/optimization.md +0 -44
  195. package/docs/shared/react/guides/policy-packs.md +0 -173
  196. package/docs/shared/react/guides/script-loader.md +0 -311
  197. package/docs/shared/react/hooks/use-color-scheme.md +0 -31
  198. package/docs/shared/react/hooks/use-consent-manager/checking-consent.md +0 -95
  199. package/docs/shared/react/hooks/use-consent-manager/location-info.md +0 -96
  200. package/docs/shared/react/hooks/use-consent-manager/overview.md +0 -74
  201. package/docs/shared/react/hooks/use-consent-manager/setting-consent.md +0 -93
  202. package/docs/shared/react/hooks/use-draggable.md +0 -30
  203. package/docs/shared/react/hooks/use-focus-trap.md +0 -20
  204. package/docs/shared/react/hooks/use-reduced-motion.md +0 -33
  205. package/docs/shared/react/hooks/use-ssr-status.md +0 -16
  206. package/docs/shared/react/hooks/use-text-direction.md +0 -38
  207. package/docs/shared/react/hooks/use-translations.md +0 -15
  208. package/docs/shared/react/iab/consent-banner.md +0 -60
  209. package/docs/shared/react/iab/consent-dialog.md +0 -76
  210. package/docs/shared/react/iab/overview.md +0 -80
  211. package/docs/shared/react/iab/use-gvl-data.md +0 -21
  212. package/docs/shared/react/styling/classnames.md +0 -93
  213. package/docs/shared/react/styling/color-scheme.md +0 -35
  214. package/docs/shared/react/styling/css-variables.md +0 -53
  215. package/docs/shared/react/styling/overview.md +0 -261
  216. package/docs/shared/react/styling/slots.md +0 -93
  217. package/docs/shared/react/styling/stylesheet-entrypoint.md +0 -8
  218. package/docs/shared/react/styling/tailwind.md +0 -88
  219. package/docs/shared/react/styling/tokens.md +0 -155
  220. package/docs/shared/troubleshooting.md +0 -82
@@ -1,62 +1,123 @@
1
1
  ---
2
2
  title: ConsentDialogLink
3
- description: An inline trigger for opening the consent dialog from footers,
4
- legal pages, and account settings.
3
+ description: Open the preference center from a Next.js footer with
4
+ ConsentDialogLink, a Client Component that renders an unstyled button inside
5
+ ConsentRoot.
5
6
  group: frameworks
6
7
  ---
7
- `ConsentDialogLink` is an inline trigger for opening the consent dialog from places like site footers, legal pages, or account settings. It is unstyled by default, so it inherits your app's typography and link/button styles.
8
8
 
9
- ## Basic Usage
9
+ ## Reopen the preference center from your footer
10
10
 
11
- ```tsx
12
- import { ConsentDialogLink } from '@c15t/nextjs/components/consent-dialog-link';
11
+ `ConsentDialogLink` is an unstyled button that opens the consent dialog.
12
+ Put it where your site already keeps its legal links, so preferences stay
13
+ reachable after the banner closes. It is a Client Component and must render
14
+ inside the `ConsentRoot` from your
15
+ [App Router](../app-router.md) or
16
+ [Pages Router](../pages-router.md) setup, which also mounts
17
+ the `ConsentDialog` it opens.
18
+
19
+ ```tsx title="components/site-footer.tsx"
20
+ 'use client';
21
+
22
+ import Link from 'next/link';
23
+ import { ConsentDialogLink } from 'c15t/next';
13
24
 
14
25
  export function SiteFooter() {
15
26
  return (
16
27
  <footer>
17
- <ConsentDialogLink>
18
- Your privacy settings
19
- </ConsentDialogLink>
28
+ <nav aria-label="Legal">
29
+ <Link href="/privacy">Privacy policy</Link>
30
+ <Link href="/terms">Terms</Link>
31
+ <ConsentDialogLink className="footer-link">Privacy settings</ConsentDialogLink>
32
+ </nav>
20
33
  </footer>
21
34
  );
22
35
  }
23
36
  ```
24
37
 
25
- ## Footer Link (unstyled by default)
26
-
27
- ```tsx
28
- <footer>
29
- <ConsentDialogLink>
30
- Your privacy settings
31
- </ConsentDialogLink>
32
- </footer>
33
- ```
38
+ Render `SiteFooter` from the root layout as a child of the `Consent`
39
+ wrapper, in place of the inline footer the router guides show. The layout
40
+ stays a Server Component; only this file needs `'use client'`. The link
41
+ renders a `button`, so the CSS for `.footer-link` should reset button chrome
42
+ if your footer styles target anchors only.
34
43
 
35
- ## Render as an Anchor
36
-
37
- Use `asChild` to keep semantic anchor markup while still opening the dialog:
44
+ To reuse an existing link component, pass `asChild`:
38
45
 
39
46
  ```tsx
40
47
  <ConsentDialogLink asChild>
41
- <a href="#privacy-settings">Manage Preferences</a>
48
+ <a href="#cookie-preferences">Manage cookies</a>
42
49
  </ConsentDialogLink>
43
50
  ```
44
51
 
45
- ## Optional Styling Control
52
+ ## Props
46
53
 
47
- The component defaults to `noStyle={true}`. Set it to `false` if you want c15t button styles:
54
+ | Prop | Type | Default | Description |
55
+ | ---------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
56
+ | `children` | `ReactNode` | required | The visible label, such as "Privacy settings" or "Manage preferences". It is also the accessible name. |
57
+ | `asChild` | `boolean` | `false` | Renders your own element, such as an anchor, instead of a `button`, and attaches the click handler to it. |
58
+ | `noStyle` | `boolean` | `true` | Omits c15t button classes. Native browser styles remain unless your CSS resets them. Pass `false` to render it as a consent button with `variant` and `mode`. |
59
+ | `onClick` | `(event) => void` | none | Runs before the dialog opens. Call `event.preventDefault()` to keep it closed. |
48
60
 
49
- ```tsx
50
- <ConsentDialogLink noStyle={false}>
51
- Privacy Settings
52
- </ConsentDialogLink>
53
- ```
61
+ Other `button` attributes such as `className`, `style`, `id` and `aria-*`
62
+ pass through to the rendered element.
54
63
 
55
- ## Props
64
+ ## Behavior
65
+
66
+ `ConsentDialogLink` renders a `button` with `type="button"` and no c15t
67
+ styling by default. Clicking it makes `dialog` the active surface, which opens a
68
+ mounted `ConsentDialog`. It performs no save and does not change any
69
+ permission.
70
+
71
+ The element carries `data-c15t-rights` listing the rights of the active
72
+ policy rule, for example `disclosure opt-out preferences` under a US
73
+ opt-out rule and `disclosure preferences` under an opt-in rule, so a
74
+ stylesheet or a `::after` label can adapt the wording by region.
75
+
76
+ Without a resolved policy rule the link renders nothing and appears as soon
77
+ as a rule resolves, without a remount. A rule with `model: 'none'` and an
78
+ empty `rights` list owes no consent UI, so the link hides under it; a `none`
79
+ rule that lists any right, such as `['disclosure']`, shows the link and the
80
+ dialog opens as a settings route.
81
+
82
+ Mount exactly one `ConsentDialog` inside the same provider. If none is
83
+ mounted, the click still switches the active surface: nothing opens, and a
84
+ banner that was showing closes because the surface is no longer `banner`.
85
+
86
+ With `asChild`, the child's own `onClick` runs first; if it calls
87
+ `event.preventDefault()` the dialog stays closed. c15t does not prevent the
88
+ child's native behavior, so an anchor still follows its `href`. Use a
89
+ fragment or the current page as the destination, or render a `button`.
90
+
91
+ ### ConsentDialogLink or ConsentDialogTrigger
92
+
93
+ Both open the same dialog and carry `data-c15t-rights`. Use
94
+ `ConsentDialogLink` where the control belongs in your own layout: a footer,
95
+ a privacy page, a settings menu. It has no position, icon or visibility
96
+ rule of its own. Use `ConsentDialogTrigger` when you want a floating,
97
+ draggable button that positions itself in a corner and can wait until the
98
+ prompt is answered.
99
+
100
+ `ConsentDialogTrigger` has its own page: [ConsentDialogTrigger](./consent-dialog-trigger.md).
101
+
102
+ ## Accessibility
103
+
104
+ The rendered `button` is keyboard-focusable and activates with Enter and
105
+ Space; its accessible name is the text you pass as `children`, so give it
106
+ words that describe the destination rather than "click here". Because the
107
+ dialog it opens is labelled by its own title and, while blocking, returns
108
+ focus to the link on close, no additional `aria-haspopup` or
109
+ `aria-controls` is added; a non-blocking dialog manages no focus, so the
110
+ link does not regain it. With `asChild`,
111
+ focusability comes from your element: use a `button` or an anchor with an
112
+ `href`.
56
113
 
57
- |Property|Value|
58
- |:--|:--|
59
- |Type Name|\`ConsentDialogLinkProps\`|
60
- |Source Path|\`./packages/react/src/components/consent-preferences-link/consent-preferences-link.tsx\`|
114
+ ## Verify
61
115
 
62
- \*ExtractedTypeTable: Could not extract "ConsentDialogLinkProps" from "./packages/react/src/components/consent-preferences-link/consent-preferences-link.tsx" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
116
+ Scroll to the footer. With the footer button reset applied, the link uses
117
+ your footer's text styles and has no button chrome. Activate it with a click or with Enter: the preference
118
+ center opens as a centered dialog. Close it with Escape: with the default
119
+ blocking dialog, focus returns to the link. In your browser's element
120
+ inspector the link has
121
+ `data-c15t-rights`; change the visitor's region to a US opt-out rule and
122
+ the value includes `opt-out`. Under a rule with `model: 'none'` and no
123
+ rights, the link is absent.
@@ -1,44 +1,54 @@
1
1
  ---
2
2
  title: ConsentDialogTrigger
3
- description: A floating, draggable button that lets users re-open the consent
4
- dialog at any time.
3
+ description: Add the floating ConsentDialogTrigger button or toolbar to a
4
+ Next.js ConsentRoot so visitors can reopen the preference center.
5
5
  group: frameworks
6
6
  ---
7
- `ConsentDialogTrigger` is a floating button that opens the consent dialog when clicked. Users can drag it to any corner of the screen, and the position persists across sessions. Use it to give users a persistent way to manage their privacy settings.
8
7
 
9
- ## Basic Usage
8
+ ## Usage
10
9
 
11
- Use standalone or via the `showTrigger` prop on `ConsentDialog`:
10
+ `ConsentDialogTrigger` is a draggable floating button that opens the preference center. Place it once, next to the banner and dialog, so visitors can revisit their choices without a footer link.
12
11
 
13
- ```tsx
14
- import { ConsentDialogTrigger } from '@c15t/nextjs';
12
+ Keep the boundary, stylesheet and transport from your
13
+ [router setup](../quickstart.md). Add this component as a child
14
+ of the existing boundary, alongside its banner and dialog:
15
+
16
+ ```tsx title="components/privacy-trigger.tsx"
17
+ 'use client';
15
18
 
16
- // Standalone
17
- <ConsentDialogTrigger />
19
+ import { ConsentDialogTrigger } from 'c15t/next';
18
20
 
19
- // Via ConsentDialog
20
- <ConsentDialog showTrigger />
21
+ export function PrivacyTrigger() {
22
+ return <ConsentDialogTrigger showWhen="after-prompt" />;
23
+ }
21
24
  ```
22
25
 
23
- For inline footer links (instead of a floating button), use [`ConsentDialogLink`](/docs/frameworks/next/components/consent-dialog-link).
26
+ `showWhen="after-prompt"` keeps the button out of the way while a choice or notice is still owed and shows it once the visitor has answered, so the banner and the trigger never compete for the same corner. The default, `always`, shows it whenever the preference center is closed.
24
27
 
25
- ## Icon Options
28
+ The button carries `data-c15t-rights` with the rights the active policy guarantees, so a stylesheet can label it differently under an opt-out rule.
26
29
 
27
- ```tsx
28
- {/* Built-in icons */}
29
- <ConsentDialogTrigger icon="branding" /> {/* c15t logo (default) */}
30
- <ConsentDialogTrigger icon="fingerprint" /> {/* Privacy icon */}
31
- <ConsentDialogTrigger icon="settings" /> {/* Gear icon */}
30
+ ## Props
32
31
 
33
- {/* Custom icon */}
34
- <ConsentDialogTrigger icon={<MyCustomIcon />} />
35
- ```
32
+ | Prop | Type | Default | Description |
33
+ | ----------------- | ----------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------- |
34
+ | `icon` | `'branding' \|'fingerprint' \|'settings' \|ReactNode` | `'branding'` | Icon rendered inside the button. |
35
+ | `defaultPosition` | `'bottom-left' \|'bottom-right' \|'top-left' \|'top-right'` | `'bottom-right'` | Corner the button starts in. |
36
+ | `persistPosition` | `boolean` | `true` | Remember the corner the visitor dragged it to. |
37
+ | `showWhen` | `'always' \|'after-prompt' \|'never'` | `'always'` | When the button is visible. `after-prompt` waits until no choice or notice is owed. |
38
+ | `size` | `'sm' \|'md' \|'lg'` | `'md'` | Button size. |
39
+ | `ariaLabel` | `string` | `'Open privacy settings'` | Accessible name. |
40
+ | `noStyle` | `boolean` | `false` | Remove the default styling. |
41
+
42
+ ## Configurable toolbar
36
43
 
37
- ## Configurable Toolbar
44
+ Use `ConsentDialogTriggerToolbar` when you want app-owned controls beside the privacy trigger. It always renders exactly one built-in action that opens the preference center, so `actions` only holds controls your app owns.
38
45
 
39
- Use the separate `ConsentDialogTriggerToolbar` when you want to place app-owned controls beside the privacy trigger. It always renders exactly one built-in action that opens consent preferences, so `actions` only contains controls your app owns.
46
+ This partial example belongs in a Client Component inside the existing boundary.
47
+ Define the theme state, icons and callbacks in that component.
40
48
 
41
49
  ```tsx
50
+ import { ConsentDialogTriggerToolbar } from 'c15t/next';
51
+
42
52
  <ConsentDialogTriggerToolbar
43
53
  ariaLabel="Site controls"
44
54
  actions={[
@@ -63,140 +73,55 @@ Use the separate `ConsentDialogTriggerToolbar` when you want to place app-owned
63
73
  />
64
74
  ```
65
75
 
66
- Each custom action requires a stable `id`, accessible `label`, `icon`, and `onSelect` callback. Use `pressed` for toggle actions and `disabled` for unavailable actions. Your app remains responsible for state, integrations, and changing the icon or label. The preferences action automatically moves to the edge nearest the toolbar's snapped corner while custom actions keep their configured order.
67
-
68
- Toolbars are horizontal by default. Set `orientation="vertical"` to stack the actions and enable Up/Down arrow-key navigation:
69
-
70
- ```tsx
71
- <ConsentDialogTriggerToolbar
72
- orientation="vertical"
73
- actions={toolbarActions}
74
- />
75
- ```
76
-
77
- ### Toolbar Styling
78
-
79
- The toolbar follows the standard styling precedence: internal CSS module styles, provider theme slots, then direct component overrides. Use `className` and `style` on the toolbar, or on an individual action, and use `noStyle` for a fully custom implementation.
80
-
81
- ```tsx
82
- <ConsentManagerProvider
83
- options={{
84
- theme: {
85
- slots: {
86
- consentDialogTriggerToolbar: 'my-toolbar',
87
- consentDialogTriggerToolbarItem: 'my-toolbar-item',
88
- consentDialogTriggerToolbarIcon: 'my-toolbar-icon',
89
- },
90
- },
91
- }}
92
- >
93
- <ConsentDialogTriggerToolbar
94
- className="fixed-toolbar"
95
- style={{ '--cdtt-offset': '24px' }}
96
- actions={[
97
- {
98
- id: 'support',
99
- label: 'Open support chat',
100
- icon: <ChatIcon />,
101
- onSelect: openSupportChat,
102
- className: 'support-action',
103
- },
104
- ]}
105
- />
106
- </ConsentManagerProvider>
107
- ```
108
-
109
- Available toolbar theme slots are `consentDialogTriggerToolbar`, `consentDialogTriggerToolbarItem`, and `consentDialogTriggerToolbarIcon`.
110
-
111
- With `noStyle`, use `data-corner`, `data-dragging`, and `data-snapping` on the toolbar to style its current position and interaction state.
112
-
113
- ## Visibility
114
-
115
- Control when the trigger is visible:
76
+ Each custom action needs a stable `id`, an accessible `label`, an `icon`, and an `onSelect` callback. Use `pressed` for toggle actions and `disabled` for unavailable ones. Your app owns the state behind each action. The preferences action moves to the edge nearest the toolbar's snapped corner while custom actions keep their configured order.
116
77
 
117
- ```tsx
118
- {/* Always visible (default) */}
119
- <ConsentDialogTrigger showWhen="always" />
120
-
121
- {/* Only after user has made a consent choice */}
122
- <ConsentDialogTrigger showWhen="after-consent" />
123
-
124
- {/* Hidden (control visibility programmatically) */}
125
- <ConsentDialogTrigger showWhen="never" />
126
- ```
127
-
128
- ## Position
78
+ The toolbar does not depend on the consent policy. Your actions always render, so a theme toggle stays available while a banner is up. On the toolbar, `showWhen` applies only to the built-in preferences action: `after-prompt` hides that one item while a choice or notice is owed and shows it once the visitor has answered, and `never` leaves it out. The toolbar renders nothing only when it has no visible item. On the single `ConsentDialogTrigger`, `showWhen` applies to the whole control.
129
79
 
130
- Set the default corner and control persistence:
80
+ No policy, no UI. When no rule has resolved, because resolution failed, no rule matched and you set no default, or init is still withheld, the built-in preferences action and the single `ConsentDialogTrigger` render nothing; app-owned toolbar actions still do. They appear as soon as a rule resolves, without a remount. A rule with `model: 'none'` owes no rights, so the built-in action and the single trigger hide under it too, unless the rule adds `rights: ['preferences']`. The policy chooses what the control says, you choose where and when it sits, and it disappears only when there is nothing to manage.
131
81
 
132
- ```tsx
133
- <ConsentDialogTrigger
134
- defaultPosition="bottom-left"
135
- persistPosition={true} // Remembers user's drag position
136
- onPositionChange={(position) => console.log('Moved to:', position)}
137
- />
138
- ```
82
+ The built-in action names the strongest right the active rule guarantees. Under a rule that carries the `opt-out` right, such as a US opt-out notice, its accessible name is the translated "Do not sell or share my data" and the button carries `data-right="opt-out"`. Under any other rule it reads "Manage preferences" with `data-right="preferences"`. Either way it opens the preference center, and a `preferences.label` you pass replaces the default. The button also carries `data-c15t-rights` with every right on the rule.
139
83
 
140
- ## Size
84
+ Toolbars are horizontal by default. Set `orientation="vertical"` to stack the actions and switch arrow-key navigation to up and down:
141
85
 
142
86
  ```tsx
143
- <ConsentDialogTrigger size="sm" /> {/* Small */}
144
- <ConsentDialogTrigger size="md" /> {/* Medium (default) */}
145
- <ConsentDialogTrigger size="lg" /> {/* Large */}
87
+ <ConsentDialogTriggerToolbar orientation="vertical" actions={toolbarActions} />
146
88
  ```
147
89
 
148
- ## Compound Components
149
-
150
- Build fully custom trigger layouts using sub-components:
151
-
152
- ```tsx
153
- <ConsentDialogTrigger.Root defaultPosition="bottom-right">
154
- <ConsentDialogTrigger.Button size="md">
155
- <ConsentDialogTrigger.Icon icon="settings" />
156
- <ConsentDialogTrigger.Text>Privacy</ConsentDialogTrigger.Text>
157
- </ConsentDialogTrigger.Button>
158
- </ConsentDialogTrigger.Root>
90
+ ### Toolbar props
91
+
92
+ | Prop | Type | Default | Description |
93
+ | ----------------- | ---------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
94
+ | `actions` | `ConsentDialogTriggerToolbarAction[]` | `[]` | App-owned actions rendered beside the preferences action. |
95
+ | `preferences` | `ConsentDialogTriggerToolbarPreferences` | `{}` | Overrides for the built-in action: `icon`, `label`, `onSelect`, `className`, `style`. The label defaults to the opt-out or preferences right on the active rule. |
96
+ | `orientation` | `'horizontal' \|'vertical'` | `'horizontal'` | Layout direction. |
97
+ | `defaultPosition` | `CornerPosition` | `'bottom-right'` | Corner the toolbar starts in. |
98
+ | `persistPosition` | `boolean` | `true` | Remember the dragged corner. |
99
+ | `showWhen` | `'always' \|'after-prompt' \|'never'` | `'always'` | When the built-in preferences action is visible. App-owned actions always render. `after-prompt` waits until no choice or notice is owed. |
100
+ | `size` | `'sm' \|'md' \|'lg'` | `'md'` | Size of each action. |
101
+ | `ariaLabel` | `string` | `'Privacy controls'` | Accessible name for the toolbar group. |
102
+ | `noStyle` | `boolean` | `false` | Remove the default styling. |
103
+
104
+ ### Toolbar styling
105
+
106
+ The toolbar follows the standard precedence: bundled styles, then provider slots, then direct `className` and `style` on the toolbar or on one action. Use `noStyle` for a fully custom implementation.
107
+
108
+ Add these slot values to `options.components` on your existing
109
+ `ConsentRoot` or `ConsentProvider`. Merge them with any existing component
110
+ slots and retain the configured transport:
111
+
112
+ ```ts
113
+ const triggerSlots = {
114
+ toolbar: { className: 'my-toolbar' },
115
+ toolbarItem: { className: 'my-toolbar-item' },
116
+ toolbarIcon: { className: 'my-toolbar-icon' },
117
+ };
118
+
119
+ // In the existing options.components object:
120
+ // trigger: triggerSlots
159
121
  ```
160
122
 
161
- * `ConsentDialogTrigger.Root` — Portal wrapper with drag handling and position persistence
162
- * `ConsentDialogTrigger.Button` — Draggable button element with size variants
163
- * `ConsentDialogTrigger.Icon` — Icon display (branding, fingerprint, settings, or custom)
164
- * `ConsentDialogTrigger.Text` — Optional text label
165
-
166
- ## Props
167
-
168
- ### ConsentDialogTrigger
169
-
170
- |Property|Value|
171
- |:--|:--|
172
- |Type Name|\`ConsentDialogTriggerProps\`|
173
- |Source Path|\`./packages/react/src/components/consent-dialog-trigger/types.ts\`|
174
-
175
- \*ExtractedTypeTable: Could not extract "ConsentDialogTriggerProps" from "./packages/react/src/components/consent-dialog-trigger/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
176
-
177
- ### ConsentDialogTriggerToolbar
178
-
179
- |Property|Value|
180
- |:--|:--|
181
- |Type Name|\`ConsentDialogTriggerToolbarProps\`|
182
- |Source Path|\`./packages/react/src/components/consent-dialog-trigger/types.ts\`|
183
-
184
- \*ExtractedTypeTable: Could not extract "ConsentDialogTriggerToolbarProps" from "./packages/react/src/components/consent-dialog-trigger/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
185
-
186
- ### Toolbar Actions
187
-
188
- |Property|Value|
189
- |:--|:--|
190
- |Type Name|\`ConsentDialogTriggerToolbarAction\`|
191
- |Source Path|\`./packages/react/src/components/consent-dialog-trigger/types.ts\`|
192
-
193
- \*ExtractedTypeTable: Could not extract "ConsentDialogTriggerToolbarAction" from "./packages/react/src/components/consent-dialog-trigger/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
194
-
195
- ### Preferences Action
196
-
197
- |Property|Value|
198
- |:--|:--|
199
- |Type Name|\`ConsentDialogTriggerToolbarPreferences\`|
200
- |Source Path|\`./packages/react/src/components/consent-dialog-trigger/types.ts\`|
123
+ For one toolbar, pass `className="fixed-toolbar"` directly. Each app-owned
124
+ item also accepts `className`, such as `className: 'support-action'` on your
125
+ support action. Keep its icon and callback in the Client Component.
201
126
 
202
- \*ExtractedTypeTable: Could not extract "ConsentDialogTriggerToolbarPreferences" from "./packages/react/src/components/consent-dialog-trigger/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
127
+ The toolbar slot keys are `trigger.toolbar`, `trigger.toolbarItem`, and `trigger.toolbarIcon`. With `noStyle`, style the current position and interaction state through `data-corner`, `data-dragging`, and `data-snapping` on the toolbar element.