Documentation
Styling
c15t's rules live in the cascade layer components, and each <style>
element the components render opens with Tailwind CSS 4's layer order,
@layer properties, theme, base, components, utilities;. Unlayered CSS,
Tailwind 4 utilities and StyleX's atomic classes override c15t's rules
without specificity tricks, whichever stylesheet loads first.
Stylesheets and CSS layers explains the
order and which file holds the dialog's rules.
Tailwind CSS 3 has no cascade layers, and its unlayered preflight beats
c15t's layered rules. Set styles: false in the provider options, import
styles.css above the @tailwind directives, and add the
c15t/postcss-tailwind3 PostCSS plugin before tailwindcss. Utilities
you pass to c15t parts then need the important modifier, such as
!bg-red-500. Tailwind CSS
shows the tested setup for each framework.
Colors, radii, shadows, typography, and motion come from --c15t-* custom
properties. The rules the components render set the default values, as
does styles.css when you import it with styles: false. To change them,
render ConsentTheme with your theme where your app renders on the server,
or override the variables directly in your CSS.
ConsentTheme renders a <style id="c15t-theme"> element with the
variables for theme. It is not a client component: rendered from a Server
Component or another server-only file, the theme generator stays out of the
browser bundle. Rendered from a client component it still works, but the
browser downloads the generator (about 1.7 KB gzip). The provider no longer
turns theme tokens into CSS. It still reads consentActions and slot
styles from its theme option, and warns in development when theme holds
tokens but the page has no c15t-theme stylesheet.
ConsentTheme prop | Purpose |
|---|---|
theme | Colors, dark colors, typography, spacing, radius, shadows and motion. Defaults to the built-in theme. |
colorScheme | 'dark' makes the dark tokens the default before hydration. 'system' follows prefers-color-scheme with a CSS media query. Omit it to follow a dark or c15t-dark class on <html>. |
nonce | Content Security Policy nonce for the <style> element. |
Pass the same colorScheme to ConsentTheme and to the provider. If your
app manages dark mode through a root class, such as next-themes' dark
class, omit colorScheme and include that class in the server HTML.
Dark mode covers theme.dark, null and
a dark first paint.
To switch between different token sets at runtime, render ConsentTheme
from a client component and change its props, which ships the generator.
Outside a component, call generateThemeCSS(theme, colorScheme) from
c15t/react/utils, or @c15t/react/utils with the scoped package. It
writes the same CSS as ConsentTheme. Outside React, import it from
@c15t/ui/theme. Call it on the server or at build time and put the result in a
<style> element or your stylesheet. It escapes <, so the result is safe
inside <style>.
Earlier v3 alphas generated theme CSS in the browser from the provider's
theme option. To migrate:
- Move the theme into a module without
'use client', so server and client files can both import it. - Render
<ConsentTheme theme={theme} />next to the consent root, from a Server Component or another server-rendered file. PasscolorSchemeandnonceif you set them on the provider. - Keep passing
themeto the provider only forconsentActionsand slot styles. A theme with tokens only can be removed from the provider. - The default tokens the provider used to inject now come with the rules
the components render. If you import
styles.csswithstyles: false, keep that import.
consentActions decides how each action role looks: default applies to
every button, primary to whichever actions the policy or your props mark
primary, and accept, reject, customize, and dismiss to one role each.
Per-action keys win over primary, which wins over default.
Banner sizing has its own variables. Override them in CSS when a variant needs a different footprint:
| Variable | Default | Applies to |
|---|---|---|
--consent-banner-max-width | 440px | floating cards |
--consent-banner-widget-max-width | 20rem | widget chips |
--consent-banner-wall-max-width | 30rem | wall cards |
The footer lays out its actions by the card's width, not the viewport's.
With the default compact profile, a card narrower than 22rem puts Reject
and Accept on one row and Customize on a full-width row below them.
Colors by consent model
The primary action can change while the brand color stays the same. Set
consentActions.primary once, then select primaryButton per policy as
shown in ConsentBanner.
To give opt-in and opt-out banners different colors, scope the tokens to the root's attributes. Include hover and foreground colors when overriding CSS tokens directly:
These colors apply to the action marked primary. Button layout and color changes do not change the policy or expire a saved choice.
Slots
Each component part is a slot. A slot accepts any attributes the element
takes, so the contract is the same whether you write class strings or pass an
object with className and style. Set slots on the provider under
components, keyed by component and part.
The root also carries data-variant, so you can restyle one shape without
touching the others. Tailwind, giving a bar a brand-colored top edge and
tighter text:
data-variant sits on the root, so child slots use Tailwind's group
prefix to read it.
Class names and CSS-in-JS shows CSS Modules, vanilla-extract, StyleX and Emotion classes on a part.
Set noStyle on a component to drop the built-in classes from every part and
keep only what your slots pass. Set noStyle in the provider options to do it
for every component.
Component parts lists every part key under
components and the data-* attributes each part carries. Action buttons
carry data-action, so a single rule such as [data-action='dismiss']
styles one role across every surface. Right links are underlined text by
default, so the only button on a notice is the primary action.