Theming
The widget is customized through two complementary mechanisms:
--nrn-*CSS variables inside the Shadow DOM — over 60 tokens across the categoriessurface,text,border,primary,secondary,accent,radius,shadow,transition,z-indexandlayout.StyleOverridesfrom configuration (config.styles) — overrides any subset of the variables. Accepts hex (#06b6d4),rgb()or RGB-triplet (6 182 212).
Because the widget lives inside a Shadow DOM, host styles don't leak in and widget styles don't leak out. This is deliberate: it guarantees visual parity across stores and removes conflicts with host CSS frameworks (Bootstrap, theme Tailwind, etc.).
RGB-triplet format
The widget stores colors in R G B format (no commas, no rgb()) to compose with rgb() and runtime alpha:
.neuroon-widget {
--nrn-primary: 8 145 178; /* cyan-600 (default) */
--nrn-surface-base: 255 255 255; /* white (default) */
}
.button {
background-color: rgb(var(--nrn-primary));
/* dynamic alpha without re-declaring the color */
box-shadow: 0 0 0 4px rgb(var(--nrn-primary) / 0.18);
}
If you pass a hex (#06b6d4) in StyleOverrides, the widget converts it to the triplet automatically (colorToRgb in widget/src/styling/variables.ts).
Per-theme override
NeuroonWidget.init({
/* … */
styles: {
light: {
primary: '#06b6d4',
surfaceBase: '#ffffff',
textPrimary: '#0f172a',
},
dark: {
primary: '#22d3ee',
surfaceBase: '#0f172a',
textPrimary: '#f8fafc',
},
},
})
The widget detects the shape with isThemedStyles() and applies the block matching the active theme.
When the user switches theme (because prefers-color-scheme changes or you call widget.setTheme()), the widget re-injects the overrides for the active theme via injectCustomStyles().
Variable categories
| Category | Prefix | Examples |
|---|---|---|
| Surfaces | --nrn-surface-* | surface-base, surface-elevated, surface-overlay, surface-hover, surface-subtle, surface-sunken, surface-float |
| Text | --nrn-text-* | text-primary, text-secondary, text-tertiary, text-muted, text-disabled |
| Borders | --nrn-border-* | border-default, border-emphasis, border-subtle |
| Primary brand | --nrn-primary* | primary, primary-light, primary-dark, primary-hover, primary-text, primary-50…950 |
| Secondary brand | --nrn-secondary* | secondary, secondary-light, secondary-dark |
| Accent | --nrn-accent-* | accent-purple, accent-emerald, accent-amber (+ *-strong) |
| Semantic | — | --nrn-success, --nrn-error, --nrn-warning, --nrn-info |
| Typography | --nrn-font-* | font-size-{xs..xl}, font-weight-{normal..display}, font-letter-spacing-* |
| Spacing | --nrn-spacing-* | xs, sm, md, lg, xl |
| Radius | --nrn-radius-* | sm, md, lg, xl, 2xl |
| Shadow | --nrn-shadow-* | xs, sm, md, lg, xl, 2xl, primary, primary-lg |
| Animation | --nrn-duration-*, --nrn-ease-*, --nrn-motion-* | duration-{instant..slower}, ease-{out..bounce}, motion-{entrance..stagger} |
| Z-index | --nrn-z-* | dropdown, sticky, fixed, drawer, modal, popover, tooltip, toast, modal-fullscreen |
| Glass | --nrn-glass-* | glass-blur, glass-bg, glass-border-glass, glass-shadow, glass-inset |
| Layout | — | --nrn-container-max-width, --nrn-drawer-width, --nrn-dropdown-max-height… |
Exhaustive list with default values in
widget/src/tailwind.css.
End-to-end example
<div id="neuroon-search"></div>
<script
src="https://cdn.neuroon.ai/widget@0.9.10/widget.js"
data-token="WIDGET_TOKEN"
data-container="#neuroon-search"
data-theme="auto"
defer
></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
if (!window.NeuroonWidget?.getInstance) return
const w = window.NeuroonWidget.getInstance('#neuroon-search')
w?.setStyles({
light: {
primary: '#0f766e',
primaryLight: '#14b8a6',
radiusLg: '0.5rem',
fontFamily: '"Inter", system-ui, sans-serif',
},
dark: {
primary: '#5eead4',
surfaceBase: '#031c1a',
textPrimary: '#ecfeff',
},
})
})
</script>
Automatic host detection (theme: 'auto')
With theme: 'auto' (default), the widget queries window.matchMedia('(prefers-color-scheme: dark)') on mount and subscribes to its changes. Each change internally fires a CustomEvent('neuroon-theme-change') that re-injects the overrides for the active theme.
Force theme from the host
From host code, use the JavaScript API:
const w = window.NeuroonWidget.getInstance('#neuroon-search')
w?.setTheme('dark') // 'light' | 'dark' | 'auto'
Further reading
- Reference → CSS variables — exhaustive table with defaults.
- Configuration —
StyleOverridesformat. - Widget events —
neuroon-theme-change.