Customization
Customize the interface
Climb the ladder one step at a time
Each step keeps everything the step below it gives you. Stop at the first one that makes the change you need:
- Props and presentation. Pick a shape, a position, button order or blocking. c15t keeps its markup, styles and behavior.
- Theme tokens. Change colors, type, radius, spacing, shadows and motion everywhere at once. Dark mode is a second set of tokens.
- Parts and classes. Add a class or inline style to one part of one component, such as the banner card. Tailwind, CSS Modules and CSS-in-JS classes go here.
- Compose. Rebuild a component from c15t's parts, in your own layout, while c15t still renders the actions the policy requires.
- Headless. Render your own markup from c15t's state and actions. You own the layout, focus handling and labels.
Most brand work needs tokens and a few part classes. See banner designs for five banners built at different steps, with screenshots and tested code.
Where each step lives in your framework
| Framework | Props and presentation | Tokens | Parts and classes | Compose | Headless |
|---|---|---|---|---|---|
| Next.js | ConsentBanner props, options.presentation | ConsentTheme in a Server Component | options.components, className | Compose | Headless |
| TanStack Start | ConsentBanner props, options.presentation | ConsentTheme in the root route | options.components, className | Compose | Headless |
| React | ConsentBanner props, presentation | ConsentTheme, or CSS variables | components, className | Compose | Headless |
| Nuxt | presentation in nuxt.config.ts | tokens or theme in the module options | components, class | None | Headless |
| Vue | presentation in the c15tVue options | tokens or theme in the plugin options | components, class | None | Headless |
| Astro | presentation in the integration options | theme in the integration options | theme.slots, class on ConsentBanner | None | None |
| Svelte | ConsentBanner props, presentation | CSS variables, or generateThemeCSS() | theme.slots, class on ConsentBanner | Primitives | Headless |
| SvelteKit | ConsentBanner props, presentation | generateThemeCSS() in a server load | theme.slots, class on ConsentBanner | Primitives | Headless |
| HTML | presentation.prompt in config | ui.theme | ui.theme.slots, ::part(), ui.css | None | Headless |
| JavaScript | presentation.prompt in init() | ui.theme | ui.theme.slots, ::part(), ui.css | None | Headless |
Configuration shapes differ between frameworks. React's
components.banner.card, Vue's components.banner.card with class, and the
consentBannerCard key in theme.slots target the same part through
different APIs. Check your framework's customize page before you move a
configuration from one framework to another.
Keep behavior and appearance separate
Presentation controls the prompt's shape, position and blocking. The policy controls which actions and rights the visitor gets. Changing colors or button order does not change a saved choice or the policy's scope.
A choice wall always blocks. A notice never blocks and does not become a wall
because you asked for variant: 'wall'. The preference dialog stays centered,
whatever position the banner has. If a custom layout leaves out an action the
policy requires, c15t puts it back.
Read the rest of this section
- Banner designs: five designs with tested code.
- Theme tokens: every
--c15t-*variable and its theme key. - Dark mode:
colorScheme, dark tokens and a dark first paint. - Motion and animation: duration and easing
tokens,
disableAnimationand reduced motion. - Stylesheets and CSS layers: how c15t
loads its rules, when to import
styles.cssyourself, when the dialog's CSS loads, and how to run without c15t's styles. - Component parts: every part, its keys in each
framework, and the
data-*attributes to select on. - Class names and CSS-in-JS: CSS Modules, vanilla-extract, StyleX and Emotion on c15t's parts.
- Tailwind CSS: Tailwind 4 and 3 in every framework.
- Copy and translations: labels, languages and right-to-left text.