Custom Themes & Dark Mode
Consenti's entire visual layer is built on CSS custom properties. You can match it to your brand by overriding a handful of variables in your stylesheet — no build step, no Shadow DOM to pierce, no specificity battles.
How theming works
The widget reads CSS custom properties from the :root scope at render time. Override any --consenti-* token in your own stylesheet and the widget picks it up automatically. You can also pass a core.theme object to ConsentiSetup to set tokens via JavaScript — useful when your brand colours come from an API or theme context.
Option A — CSS custom property override
This is the recommended approach for static brand customisation. No CSS import is required —ConsentiSetup injects its own default <style> tag at runtime. Add your overrides in your own stylesheet, loaded normally in your page.
:root {
/* Primary button and active states */
--consenti-color-primary: #7c3aed;
--consenti-color-primary-text: #ffffff;
/* Secondary button */
--consenti-color-secondary: #f5f0ff;
--consenti-color-secondary-text: #7c3aed;
/* Banner & modal background */
--consenti-color-bg: #ffffff;
--consenti-color-text: #1a1a2e;
/* Border radius — pill buttons, rounded modal/banner corners */
--consenti-border-radius-btn: 999px;
--consenti-border-radius: 16px;
/* Typography */
--consenti-font-family: 'Inter', system-ui, sans-serif;
--consenti-font-size-base: 14px;
/* Toggle colours */
--consenti-toggle-bg-on: #7c3aed;
--consenti-toggle-bg-off: #d1d5db;
}Option B — JS theme config
Pass a core.theme object to ConsentiSetup. The widget translates each field into the corresponding CSS custom property at initialisation.
new ConsentiSetup({
compliance: { type: 'opt-in' },
core: {
theme: {
colorPrimary: '#7c3aed', // maps to --consenti-color-primary
colorPrimaryText: '#ffffff',
colorSecondary: '#f5f0ff',
colorSecondaryText: '#7c3aed',
borderRadius: '12px',
borderRadiusBtn: '999px', // pill buttons
fontFamily: 'Inter, system-ui, sans-serif',
fontSizeBase: '14px',
colorBg: '#ffffff',
colorText: '#1a1a2e',
},
},
})Runtime theme swapping
Use widget.setTheme()to hot-swap tokens after the widget is initialised. This merges the new values into the current theme — you don't need to pass the full theme object.
const widget = new ConsentiSetup({ /* ... */ })
// Later — e.g. when a theme switcher is toggled
widget.setTheme({ colorPrimary: '#e11d48' }) // swap accent colour onlyDark mode
Set darkMode: true in config, or let the widget follow the system preference:
// Follow system preference
new ConsentiSetup({
compliance: { type: 'opt-in' },
darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches,
})
// Or toggle at runtime
widget.setDarkMode() // toggle
widget.setDarkMode(true) // force dark
widget.setDarkMode(false) // force lightwidget.setDarkMode(isDark) whenever your theme changes.Full CSS token reference
| Token | Default | What it controls |
|---|---|---|
| --consenti-color-bg | #ffffff | Banner & modal background |
| --consenti-color-text | #1a2e4a | Main text colour |
| --consenti-color-text-muted | #949dab | Secondary/muted text |
| --consenti-color-primary | #04111f | Primary button background & accents |
| --consenti-color-primary-text | #ffffff | Primary button text |
| --consenti-color-secondary | #f0f4f8 | Secondary button/surface background |
| --consenti-color-secondary-text | #1a2e4a | Secondary button/surface text |
| --consenti-color-border | #dbe4ee | Default border colour |
| --consenti-color-overlay | #04111f | Full-screen modal backdrop |
| --consenti-color-accent | #d32f2f | Destructive/attention accent |
| --consenti-border-radius | 8px | Banner & modal corner radius |
| --consenti-border-radius-btn | 0 | Button corner radius |
| --consenti-shadow | 0 4px 24px rgba(21,101,192,.14) | Banner & modal box shadow |
| --consenti-toggle-bg-on | #43a047 | Toggle on-state colour |
| --consenti-toggle-bg-off | #9ca3af | Toggle off-state colour |
| --consenti-font-family | system-ui, sans-serif | Widget font family |
| --consenti-font-size-base | 14px | Base font size |