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.
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 }
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.
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'
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() }
css PropPass 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 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.
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
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() }
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.
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' } }
system: { spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32, '2xl': 48 } }
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 } } }
On this page