Theming — MeoNode UI
Context-based theming with automatic value resolution. Define your theme object, wrap your app with ThemeProvider, and
reference theme tokens anywhere using string paths—no prop drilling required.
Theme Structure
Every theme requires two root properties:
type Theme = { mode: 'light' | 'dark' | string system: { primary: { default: string; content: string } secondary: { default: string; content: string } accent: { default: string; content: string } warning: { default: string; content: string } base: { default: string; content: string; accent?: string } // Add custom tokens as needed } } & Partial<{ [key: string]: string | number | boolean | Theme | Record<string, any> }>
Required:
mode— Theme mode identifier (light, dark, or custom)system— Design tokens (colors, spacing, typography, etc.)
Example Theme:
export const lightTheme: Theme = { mode: 'light', system: { primary: { default: '#FF6B6B', content: '#4A0000', }, secondary: { default: '#6BCB77', content: '#0A3B0F', }, accent: { default: '#4ECDC4', content: '#1A4A47', }, warning: { default: '#FFE66D', content: '#665A00', }, base: { default: '#F8F8F8', content: '#333333', accent: '#88B04B', }, }, // Add custom properties for your design system }
ThemeProvider
Wrap your app with ThemeProvider to enable theme access throughout the component tree:
import { ThemeProvider, Column, H1, Text } from '@meonode/ui' import { lightTheme } from './theme' const AppContent = () => Column({ padding: 20, children: [ H1('Themed Application', { color: 'theme.primary', fontSize: '2.5rem', }), Text('Automatic theme resolution', { backgroundColor: 'theme.base', color: 'theme.base.content', padding: 10, }), ], }).render() const App = () => ThemeProvider({ theme: lightTheme, children: AppContent(), }).render()
All MeoNode components inside ThemeProvider automatically access the theme—no prop passing required.
Accessing Theme Values
String Path Notation
Reference theme tokens using dot-separated paths prefixed with theme.:
import { Column, Button } from '@meonode/ui' Column({ backgroundColor: 'theme.primary', padding: 'theme.base.accent', children: Button('Submit', { backgroundColor: 'theme.secondary', color: 'theme.secondary.content', }), })
Default Key Resolution:
Partial paths (e.g., 'theme.primary') automatically resolve to the default key within that object. If no default
exists, it throws an error.
// These are equivalent: backgroundColor: 'theme.primary' backgroundColor: 'theme.primary.default'
Direct Object Access
Use the useTheme hook for direct access with full TypeScript support:
import { useTheme, Column, Text } from '@meonode/ui' const ThemedComponent = () => { const { theme } = useTheme() return Column({ padding: theme.system.accent.default, backgroundColor: theme.system.base.default, children: Text('Direct access', { color: theme.system.base.content, }), }).render() }
Theme Functions in css Prop
Pass functions to css prop properties for dynamic theme-based computations:
import { Div } from '@meonode/ui' import tinycolor from 'tinycolor2' Div({ backgroundColor: 'theme.primary', css: { // Simple theme access color: theme => theme.system.primary.content, // Computed values boxShadow: theme => `0 4px 14px 0 ${tinycolor(theme.system.primary.default).setAlpha(0.38).toString()}`, }, })
This provides maximum flexibility for complex styling logic based on theme tokens.
Function Children
Function children automatically inherit theme context:
import { ThemeProvider, Column } from '@meonode/ui' ThemeProvider({ theme: myTheme, children: () => Column({ color: 'theme.accent', padding: 'theme.spacing.md', }), })
The child function's returned nodes receive the theme automatically.
Complete Example
import { ThemeProvider, Column, H1, Button, Text } from '@meonode/ui' const lightTheme = { mode: 'light', system: { primary: { default: '#3B82F6', content: '#FFFFFF' }, secondary: { default: '#10B981', content: '#FFFFFF' }, base: { default: '#F9FAFB', content: '#1F2937' }, spacing: { sm: 8, md: 16, lg: 24 }, }, } const ThemedApp = () => Column({ padding: 'theme.spacing.lg', backgroundColor: 'theme.base', children: [ H1('MeoNode Theming', { color: 'theme.primary', marginBottom: 'theme.spacing.md', }), Text('Context-based theme system with automatic resolution', { color: 'theme.base.content', marginBottom: 'theme.spacing.md', }), Button('Primary Action', { backgroundColor: 'theme.primary', color: 'theme.primary.content', padding: '12px 24px', borderRadius: 8, }), ], }).render() const App = () => ThemeProvider({ theme: lightTheme, children: ThemedApp(), }).render() export default App
Multiple Themes
Switch themes dynamically or nest ThemeProvider for different theme contexts:
import { useState } from 'react' import { ThemeProvider, Column, Button } from '@meonode/ui' const darkTheme = { mode: 'dark', system: { primary: { default: '#60A5FA', content: '#1E3A8A' }, base: { default: '#1F2937', content: '#F9FAFB' }, }, } const App = () => { const [theme, setTheme] = useState(lightTheme) return ThemeProvider({ theme, children: Column({ children: [ Button('Toggle Theme', { onClick: () => setTheme(theme.mode === 'light' ? darkTheme : lightTheme), }), ], }), }).render() }
Best Practices
Structure system Property
Organize design tokens logically: colors, spacing, typography, shadows, borders.
Use Semantic Names
primary, secondary, success, warning over blue, green.
Default Keys for Variants
Use default as the base value, add content, hover, active for variants.
system: { primary: { default: '#3B82F6', content: '#FFFFFF', hover: '#2563EB', active: '#1D4ED8' } }
TypeScript for Type Safety
Define theme types for autocomplete and compile-time validation.
Function-Based Computations
Use theme functions in css prop for dynamic color transformations or complex calculations.
Common Patterns
Color System
system: { // Core colors primary: { default: '#3B82F6', content: '#FFFFFF' }, secondary: { default: '#10B981', content: '#FFFFFF' }, // Semantic colors success: { default: '#10B981', content: '#FFFFFF' }, warning: { default: '#F59E0B', content: '#78350F' }, error: { default: '#EF4444', content: '#FFFFFF' }, // Surface colors base: { default: '#FFFFFF', content: '#1F2937' }, surface: { default: '#F9FAFB', content: '#374151' }, overlay: { default: 'rgba(0, 0, 0, 0.5)', content: '#FFFFFF' } }
Spacing Scale
system: { spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32, '2xl': 48 } }
Typography
system: { typography: { fontFamily: { sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto', mono: 'ui-monospace, SFMono-Regular, Menlo' }, fontSize: { sm: '0.875rem', base: '1rem', lg: '1.125rem', xl: '1.25rem', '2xl': '1.5rem' }, fontWeight: { normal: 400, medium: 500, semibold: 600, bold: 700 } } }
Next Steps
- Framework Integration — Next.js, Vite, Remix configuration
- FAQ — Common patterns, troubleshooting, migration strategies
- Release Notes — Changelog, breaking changes, upgrade guides
On this page
- Theming — MeoNode UI