The library supports two different theme matching strategies to accommodate different theming approaches.
Matches only the exact theme specified. Base styles are always included.
const styles = parse('color:blue; dark:color:white; light:color:black');
getStyle(styles, { theme: 'dark' });
// Returns: "color: white;"
// Only includes base + dark
getStyle(styles, { theme: 'dark', themeStrategy: 'strict' });
// Returns: "color: white;"
// Same as above (explicit)Use case: When you want precise control and each theme defines its own complete styles without borrowing from other themes.
Uses other available themes when the requested theme doesn't exist for a property. Enables graceful degradation across themes.
const styles = parse('color:blue; dark:color:white');
getStyle(styles, { theme: 'light', themeStrategy: 'fallback' });
// Returns: "color: white;"
// No light:color exists, falls back to dark:color
getStyle(styles, { theme: 'midnight', themeStrategy: 'fallback' });
// Returns: "color: white;"
// No midnight:color exists, falls back to dark:colorUse case: Flexible theming where you want themes to share styles when specific theme variants aren't defined.
When using themeStrategy: 'fallback', the priority is:
- Exact theme match (highest priority)
- Other theme fallback
- Base styles (lowest priority, always included)
However, the priority is context-aware based on breakpoints and states:
const styles = parse('background:gray; dark:background:black');
getStyle(styles, { theme: 'light', themeStrategy: 'fallback' });
// Returns: "background: black;"
// Falls back to dark theme (no base preferred)When a breakpoint is requested, base styles at that context are preferred over themed fallbacks:
const styles = parse(`
color:black;
md:padding:20px;
dark:color:white;
dark:md:padding:30px
`);
getStyle(styles, {
theme: 'light',
breakpoint: 'md',
themeStrategy: 'fallback'
});
// Returns: "color: black; padding: 20px;"
// Uses base (color:black, md:padding:20px)
// NOT dark fallback (dark:color:white has no breakpoint context)Rule: If a breakpoint is requested but the themed fallback doesn't have that breakpoint, prefer base styles.
Similarly, when a state is requested, base styles are preferred over themed fallbacks without that state:
const styles = parse(`
background:blue;
dark:background:lightblue;
active:transform:scale(0.98)
`);
getStyle(styles, {
theme: 'light',
states: ['active'],
themeStrategy: 'fallback'
});
// Returns: "background: blue; transform: scale(0.98);"
// Uses base background (not dark fallback without state context)When multiple themes exist and fallback is needed, the first matching theme in parse order is used:
const styles = parse('color:red; dark:color:blue; midnight:color:purple; sunset:color:orange');
getStyle(styles, { theme: 'light', themeStrategy: 'fallback' });
// Returns: "color: blue;"
// Uses dark:color (first theme found in parse order)Best Practice: Define themes in a consistent order (e.g., always dark before light) for predictable fallback behavior.
The library supports unlimited custom theme names beyond just dark and light:
const styles = parse(`
background:white;
dark:background:black;
midnight:background:#1a1a2e;
sunset:background:#ff6b6b;
ocean:background:#006994
`);
getStyle(styles, { theme: 'midnight', themeStrategy: 'strict' });
// Returns: "background: #1a1a2e;"
getStyle(styles, { theme: 'forest', themeStrategy: 'fallback' });
// Returns: "background: black;"
// No forest theme, falls back to dark (first available)Avoid naming themes the same as breakpoints or states:
// ❌ BAD - 'md' conflicts with breakpoint
const bad = parse('md:background:blue');
// Interpreted as breakpoint, not theme!
// ✅ GOOD - Use distinct theme names
const good = parse('medium-theme:background:blue');
// Clearly a custom themeCondition Resolution Order:
- If name matches known breakpoint → treat as breakpoint
- Else if name matches known state → treat as state
- Else → treat as custom theme
Known breakpoints: xs, sm, md, lg, xl, 2xl
Known states: hover, active, focus, visited, focus-visible, focus-within, disabled, enabled, checked
Theme strategies work seamlessly with breakpoint strategies:
const styles = parse(`
font-size:14px;
sm:font-size:16px;
md:font-size:18px;
dark:font-size:15px;
dark:md:font-size:20px
`);
// Mobile-first with theme fallback
getStyle(styles, {
theme: 'light',
breakpoint: 'md',
breakpointStrategy: 'mobile-first',
themeStrategy: 'fallback'
});
// Returns: "font-size: 18px;"
// Includes base → sm → md (mobile-first)
// Light theme doesn't have md:font-size, uses base md:font-sizeimport { parse, getStyle } from '@componentor/adaptive';
// Detect system preference
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
const buttonStyles = parse(`
background:blue;
color:white;
padding:10px 20px;
dark:background:#1e40af;
light:background:#3b82f6;
hover:opacity:0.9;
dark:hover:opacity:0.8
`);
const css = getStyle(buttonStyles, {
theme: systemTheme,
states: ['hover'],
themeStrategy: 'fallback'
});const brandStyles = parse(`
--primary:blue;
--secondary:gray;
brand-a:--primary:#ff6b6b;
brand-a:--secondary:#4ecdc4;
brand-b:--primary:#95e1d3;
brand-b:--secondary:#f38181;
dark:--primary:lightblue;
dark:--secondary:lightgray
`);
// Use brand theme with fallback to dark
function getBrandStyles(brand: string) {
return getStyle(brandStyles, {
theme: brand,
themeStrategy: 'fallback'
});
}
getBrandStyles('brand-a'); // Uses brand-a colors
getBrandStyles('brand-c'); // Falls back to dark colorsconst cardStyles = parse(`
background:white;
border:1px solid #e5e7eb;
padding:1rem;
border-radius:0.5rem;
dark:background:#1f2937;
dark:border:1px solid #374151;
light:background:#ffffff;
light:border:1px solid #d1d5db;
hover:shadow:0 4px 6px rgba(0,0,0,0.1);
dark:hover:shadow:0 4px 6px rgba(0,0,0,0.3)
`);
function Card({ theme = 'light', children }) {
const css = getStyle(cardStyles, {
theme,
states: isHovered ? ['hover'] : undefined,
themeStrategy: 'fallback'
});
return <div style={parseCSS(css)}>{children}</div>;
}When each theme should have complete, independent styles:
const strictStyles = parse(`
color:black;
background:white;
dark:color:white;
dark:background:black;
light:color:#111;
light:background:#fafafa
`);
// Each theme gets exactly what's defined
getStyle(strictStyles, { theme: 'dark', themeStrategy: 'strict' });When themes can share base styles:
const fallbackStyles = parse(`
padding:1rem;
margin:0.5rem;
dark:color:white;
dark:background:black
`);
// Custom themes inherit base layout, override colors
getStyle(fallbackStyles, { theme: 'custom', themeStrategy: 'fallback' });Define themes in a predictable order:
// ✅ GOOD - Consistent order
const good = parse(`
color:gray;
dark:color:white;
light:color:black;
midnight:color:purple
`);
// ❌ LESS IDEAL - Random order makes fallback unpredictable
const lessIdeal = parse(`
color:gray;
midnight:color:purple;
dark:color:white;
light:color:black
`);Keep a reference of custom theme names:
// themes.ts
export const THEMES = {
DARK: 'dark',
LIGHT: 'light',
MIDNIGHT: 'midnight',
SUNSET: 'sunset',
OCEAN: 'ocean'
} as const;
export type AppTheme = typeof THEMES[keyof typeof THEMES];Use breakpoint context to control fallback behavior:
const responsive = parse(`
width:100%;
padding:1rem;
md:width:768px;
md:padding:2rem;
dark:background:black;
dark:md:background:#1a1a1a
`);
// At 'md' breakpoint, prefer base layout over dark fallback
getStyle(responsive, {
theme: 'light',
breakpoint: 'md',
themeStrategy: 'fallback'
});If no themeStrategy is specified, the default is 'strict' for backwards compatibility:
// These are equivalent:
getStyle(styles, { theme: 'dark' });
getStyle(styles, { theme: 'dark', themeStrategy: 'strict' });Both strategies have similar performance characteristics. The fallback strategy performs additional checks but these are optimized for typical use cases:
// Both are fast (~0.001-0.003ms per operation)
getStyle(styles, { theme: 'dark', themeStrategy: 'strict' });
getStyle(styles, { theme: 'dark', themeStrategy: 'fallback' });For extremely large style sets (100+ properties), consider caching parsed results:
// Parse once, reuse many times
const parsed = parse(largeStyleString);
// Fast subsequent lookups
const darkCSS = getStyle(parsed, { theme: 'dark', themeStrategy: 'fallback' });
const lightCSS = getStyle(parsed, { theme: 'light', themeStrategy: 'fallback' });