@slizzart/style-shifter
v1.0.2
Published
Dynamic CSS theming system with expression-based style overrides
Maintainers
Readme
StyleShifter
A lean, powerful TypeScript library for dynamic CSS theming with expression-based style overrides.
🚀 Live Interactive Demo
Experience StyleShifter in action! Switch between 6 different themes and see real-time CSS transformations.
Features
- 🎨 Dynamic Theming: Apply and switch themes at runtime
- 🔧 Expression-Based Overrides: Use special CSS comments to inject theme values
- 🎯 Type-Safe: Full TypeScript support
- 🔌 Extensible API: Add your own custom functions
- 📦 Lightweight: No dependencies
- 🌊 Theme Cascading: Inherit properties from base themes
Installation
npm install @slizzart/style-shifterQuick Start
1. Create a Theme
import { Theme, CSSProcessor } from '@slizzart/style-shifter';
const darkTheme = new Theme({
namespace: 'myapp',
name: 'dark',
data: {
primaryColor: '#1a1a1a',
textColor: '#ffffff',
accentColor: '#3b82f6',
fontSize: 16
}
});2. Add Theme Expressions to Your CSS
In your CSS/SCSS files, use the special /*![expression]*/ syntax:
.button {
/*![myapp.primaryColor]*/
background-color: #000000;
/*![myapp.textColor]*/
color: #fff;
/*![toPx(myapp.fontSize)]*/
font-size: 16px;
}
.accent {
/*![opacify(myapp.accentColor, 0.8)]*/
background-color: rgba(59, 130, 246, 0.8);
}3. Process and Apply the Theme
const processor = new CSSProcessor({ namespace: 'myapp' });
processor.addTheme(darkTheme);
// Apply theme to an element (adds class name)
darkTheme.applyTo(document.body);
// Remove theme
darkTheme.removeFrom(document.body);How It Works
StyleShifter scans your loaded CSS files for special comments in the format /*![expression]*/. When a theme is added:
- It parses these expressions
- Evaluates them using theme data and built-in functions
- Generates CSS overrides scoped to your theme class
- Injects the overrides into the document
<head>
The theme is applied by simply adding a CSS class (e.g., .myapp-dark) to an element.
Built-in API Functions
url(string)
Wraps a URL in url() for CSS.
/*![url(myapp.backgroundImage)]*/
background-image: url(image.png);toPx(value)
Converts a unitless value to pixels.
/*![toPx(myapp.spacing)]*/
padding: 16px;toRem(size, base?, initialBase?)
Converts px to rem or rebases rem values.
/*![toRem(myapp.fontSize, 16)]*/
font-size: 1rem;opacify(color, opacity)
Adds opacity to a color (hex or rgb).
/*![opacify(myapp.primaryColor, 0.5)]*/
background-color: rgba(26, 26, 26, 0.5);tint(baseColor, tintColor, amount)
Tints a color by mixing with another color.
/*![tint(myapp.backgroundColor, #ffffff, 0.1)]*/
background-color: rgba(30, 30, 30, 1);invert(color)
Inverts a color (255 - RGB).
/*![invert(myapp.primaryColor)]*/
color: rgba(235, 235, 235, 1);printf(format, ...args)
String formatting with placeholders.
/*![printf(%1 %2px solid %3, 2, myapp.borderWidth, myapp.borderColor)]*/
border: 2px solid #ccc;mapSvgColors(svgContent, originalColors, ...themeColors)
Maps SVG colors to theme colors and returns a base64 data URI.
/* Original colors separated by | */
/*![mapSvgColors('<svg>...</svg>', '#FF0000|#00FF00|#0000FF', myapp.primary, myapp.secondary, myapp.accent)]*/
background-image: url('data:image/svg+xml;base64,...');This function replaces specified colors in an inline SVG string and encodes it as a data URI, perfect for dynamically theming icon colors.
local(varName, value?)
Get or set a local variable (scoped to this processor instance).
/*![local(computed, tint(myapp.primary, #fff, 0.2))]*/
/*![local(computed)]*/
background: ...;global(varName, value?)
Get or set a global variable (shared across all processors).
setRuleScope(selector, position?)
Modify where the theme class is attached in the selector.
Custom API Functions
You can extend the API with your own functions:
import { CSSProcessor, APIFunction } from '@slizzart/style-shifter';
const processor = new CSSProcessor({ namespace: 'myapp' });
// Custom function to darken colors
const darken: APIFunction = (expression, theme, src, parserPos, args) => {
const color = args[0];
const amount = parseFloat(args[1] || '0.1');
// Your color manipulation logic here
return `rgba(...)`;
};
processor.registerFunction('darken', darken);Then use it in CSS:
.element {
/*![darken(myapp.primaryColor, 0.2)]*/
background-color: #000;
}Theme Cascading
Use ThemeRegistry to set up inheritance:
import { ThemeRegistry } from '@slizzart/style-shifter';
// Register base defaults
ThemeRegistry.registerCascade('myapp', {
fontSize: 14,
fontFamily: 'sans-serif',
spacing: 8
});
// Create theme - it will inherit these defaults
const theme = new Theme({
namespace: 'myapp',
name: 'custom',
data: {
primaryColor: '#ff0000'
// fontSize, fontFamily, spacing are inherited
}
});
// Apply cascades
ThemeRegistry.applyCascade('myapp', theme.data);Advanced Features
Preprocessors and Postprocessors
Transform override values before injection:
const processor = new CSSProcessor({
namespace: 'myapp',
preprocessors: [
(theme, override) => {
// Modify override.value before applying
return override.value;
}
],
postprocessors: [
(theme, override) => {
// Final transformations
return override.value;
}
]
});Font Loading
Themes can include custom fonts:
const theme = new Theme({
namespace: 'myapp',
name: 'custom',
data: { /* ... */ },
fonts: new Map([
['title-font', 'https://example.com/fonts/title.woff2'],
['body-font', 'https://example.com/fonts/body.woff2']
])
});Image Preloading
Ensure images are loaded before theme is ready:
const theme = new Theme({
namespace: 'myapp',
name: 'custom',
data: { /* ... */ },
preloadImages: [
'https://example.com/bg.jpg',
'https://example.com/logo.png'
]
});
theme.onComplete(() => {
console.log('Theme ready with all assets loaded!');
});TypeScript Support
Full type definitions are included:
import type { ThemeData, ThemeOptions, APIFunction } from '@slizzart/style-shifter';
const myThemeData: ThemeData = {
color: '#fff',
size: 16
};Browser Support
StyleShifter works in all modern browsers that support:
- ES2020
- DOM manipulation
- CSS injection
License
MIT
Contributing
Contributions are welcome! This library is designed to be extensible - feel free to add new API functions or features.
