npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@slizzart/style-shifter

v1.0.2

Published

Dynamic CSS theming system with expression-based style overrides

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-shifter

Quick 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:

  1. It parses these expressions
  2. Evaluates them using theme data and built-in functions
  3. Generates CSS overrides scoped to your theme class
  4. 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.