Theming
Customize colors, radius, and fonts without forking. Available since v0.6.0.
Built-in: light + dark
Two themes ship out of the box. Toggle via the useNeuTheme hook, or follow the user's OS preference:
<NeuProvider defaultTheme="dark" followSystemTheme>
<App />
</NeuProvider>import { useNeuTheme } from "neumorui";
function ThemeToggle() {
const { theme, toggleTheme } = useNeuTheme();
return <button onClick={toggleTheme}>{theme === "dark" ? "🌙" : "☀️"}</button>;
}Theme override prop (typed)
Pass a theme object to override colors, radius, or font globally. Every component that uses the affected CSS variables updates automatically.
<NeuProvider
theme={{
accent: "#ff6b6b",
danger: "#e74c3c",
success: "#27ae60",
radius: { sm: "8px", md: "16px", lg: "24px" },
fontFamily: '"Inter", system-ui, sans-serif',
}}
>
<App />
</NeuProvider>ThemeOverride shape
All fields are optional. Import the type for editor autocomplete:
import type { ThemeOverride } from "neumorui";
const myTheme: ThemeOverride = {
// Colors
accent: "#6c7ef8",
accentLight: "#8490fa",
accentDark: "#5a6cf5",
danger: "#f87c6c",
success: "#5ecba1",
warning: "#f9c74f",
info: "#5b9ee0",
bg: "#f0f4ff",
textPrimary: "#2a2d4a",
textSecondary: "#7880a8",
textMuted: "#b0b8d8",
border: "rgba(176,184,216,0.25)",
// Radius scale
radius: { sm: "10px", md: "14px", lg: "18px", xl: "24px" },
// Font
fontFamily: 'system-ui, sans-serif',
};CSS variables (escape hatch)
For variables not in the typed shape (e.g. shadow definitions, glow colors), use cssVars to set them directly:
<NeuProvider
cssVars={{
"--neu-shadow-raised": "10px 10px 24px rgba(0,0,0,.15), -8px -8px 20px rgba(255,255,255,.9)",
"--neu-accent-glow": "rgba(255, 107, 107, 0.5)",
}}
>
<App />
</NeuProvider>All available CSS variables
Set any of these directly via cssVars, or override in your own stylesheet:
/* Surfaces */
--neu-bg
--neu-bg-light
--neu-bg-dark
/* Shadows */
--neu-shadow-raised
--neu-shadow-raised-sm
--neu-shadow-raised-lg
--neu-shadow-inset
--neu-shadow-inset-sm
--neu-shadow-dark /* shadow tone */
--neu-shadow-light /* highlight tone */
/* Colors */
--neu-accent, --neu-accent-light, --neu-accent-dark, --neu-accent-glow
--neu-success, --neu-success-light, --neu-success-dark, --neu-success-glow
--neu-danger, --neu-danger-light, --neu-danger-dark, --neu-danger-glow
--neu-warning, --neu-warning-light, --neu-warning-dark, --neu-warning-glow
--neu-info, --neu-info-light, --neu-info-dark, --neu-info-glow
/* Text */
--neu-text-primary
--neu-text-secondary
--neu-text-muted
/* Tints (alerts, badges) */
--neu-tint-primary, --neu-tint-primary-text, --neu-tint-primary-border
--neu-tint-success, --neu-tint-success-text, --neu-tint-success-border
--neu-tint-danger, --neu-tint-danger-text, --neu-tint-danger-border
--neu-tint-warning, --neu-tint-warning-text, --neu-tint-warning-border
/* Radius */
--neu-radius-sm, --neu-radius-md, --neu-radius-lg, --neu-radius-xl, --neu-radius-full
/* Misc */
--neu-border
--neu-font-family
--neu-transitionAnimation control
Disable or reduce motion globally without waiting for user OS settings:
<NeuProvider defaultAnimation="reduced"> {/* short transitions, no fancy easing */}
<NeuProvider defaultAnimation="none"> {/* kill all motion entirely */}
<NeuProvider defaultAnimation="full"> {/* default — respects user OS prefers-reduced-motion */}Change at runtime via context:
const { animation, setAnimation } = useNeuTheme();
<button onClick={() => setAnimation("none")}>Disable motion</button>Quick accent presets
For common accent colors, use the defaultAccent shortcut:
<NeuProvider defaultAccent="violet"> {/* default */}
<NeuProvider defaultAccent="blue">
<NeuProvider defaultAccent="teal">
<NeuProvider defaultAccent="rose">
<NeuProvider defaultAccent="amber">For full control, use theme.accent with any hex/HSL value.