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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@crescendolab/css-var-ts

v1.0.1

Published

Type-safe utilities for CSS Custom Properties

Downloads

14

Readme

🌟 @crescendolab/css-var-ts

Type-safe, ergonomic utilities for authoring, registering, and consuming CSS Custom Properties (CSS Variables) in TypeScript.

npm (scoped)


🚀 Features

  • ✅ Strongly typed CSS variable keys & values
  • ✅ Auto–generated collision‑resistant variable names (slug + short random id)
  • ✅ Convenient .cssProps map you can spread into inline styles / style objects
  • ✅ Easy integration with: @emotion/css, @emotion/react (css prop), @mui/system (sx prop)
  • ✅ Compose semantic variables from a base palette safely (getValuevar(--token))
  • ✅ Advanced: custom variable key generator via cssVarUtils.create
  • ✅ Advanced: works with @property at‑rule registration

📦 Installation

pnpm add @crescendolab/css-var-ts
# or
npm i @crescendolab/css-var-ts
# or
yarn add @crescendolab/css-var-ts

⚡ Quick Start

import { cssVarUtils } from "@crescendolab/css-var-ts";

// 1. Define a base palette
const palette = cssVarUtils.define({
  primaryBlue: "#0074D9",
  accentPink: "#F012BE",
  neutralBg: "#FFFFFF",
  neutralFg: "#111111",
});

// 2. Define semantic tokens referencing the palette (type‑safe)
const semantic = cssVarUtils.define({
  brand: palette.getValue("primaryBlue"),
  text: palette.getValue("neutralFg"),
  background: palette.getValue("neutralBg"),
});

// 3. Use in styles
const style: React.CSSProperties = {
  // ...palette.cssProps, // Optional: variables have fallback values via `getValue`
  // ...semantic.cssProps,
  color: semantic.getValue("text"),
  backgroundColor: semantic.getValue("background"),
};

Resulting (example) generated variable keys (random 8‑char suffix) look like:

--primaryblue-a1b2c3d4
--accentpink-9fe012ab

🧩 Basic Usage (from Storybook “01_basic”)

import { cssVarUtils } from "@crescendolab/css-var-ts";

// Base palette
const paletteDefinition = cssVarUtils.define({
  navy: "#001F3F",
  blue: "#0074D9",
  aqua: "#7FDBFF",
  black: "#111111",
});

// Semantic tokens referencing base palette
const semanticDefinition = cssVarUtils.define({
  primary: paletteDefinition.getValue("navy"),
  foreground: paletteDefinition.getValue("black"),
});

// Override one semantic var dynamically
const dynamicStyle = {
  ...paletteDefinition.cssProps,
  ...semanticDefinition.cssProps,
  [semanticDefinition.getKey("primary")]: paletteDefinition.getValue("blue"),
  color: semanticDefinition.getValue("foreground"),
};

🎨 Integrations

Emotion (@emotion/css)

import { css } from "@emotion/css";
import {
  gruvboxCssVarBaseDefinition,
  gruvboxCssVarLightDefinition,
} from "./styles";

const container = css({
  ...gruvboxCssVarBaseDefinition.cssProps,
  ...gruvboxCssVarLightDefinition.cssProps,
  color: gruvboxCssVarLightDefinition.getValue("fg"),
});

Emotion (css prop)

import { css } from "@emotion/react";
const button = css({
  color: gruvboxCssVarLightDefinition.getValue("fg"),
  backgroundColor: gruvboxCssVarLightDefinition.getValue("bg"),
});

MUI (sx prop)

<Box
  sx={{
    ...gruvboxCssVarBaseDefinition.cssProps,
    ...gruvboxCssVarLightDefinition.cssProps,
    color: gruvboxCssVarLightDefinition.getValue("fg"),
  }}
/>

See live Storybook demos below for full examples including light/dark variants and status colors.


🛠️ Advanced

Custom Variable Key Strategy

Use createCssVarUtils to fully control how variable names are produced (e.g. ephemeral / randomized keys).

import { cssVarUtils, randomString, slugify } from "@crescendolab/css-var-ts";

const myCssVarUtils = cssVarUtils.create({
  recordKeyToCssVarKey: (key) =>
    `--my-${slugify(key)}-${randomString(8)}` as const,
});

const myDefinition = myCssVarUtils.define({
  primary: "#0074D9",
});

myDefinition.getKey("primary"); // different each load

Static (Deterministic) Keys

If you prefer fully readable, deterministic variable names (no random suffix) you can supply a static strategy. Be sure to manually ensure uniqueness across packages / bundles when using this approach.

import { cssVarUtils, slugify } from "@crescendolab/css-var-ts";

const staticCssVarUtils = cssVarUtils.create({
  recordKeyToCssVarKey: (key) => `--static-${slugify(key)}` as const,
});

const staticDefinition = staticCssVarUtils.define({
  primary: "#0074D9",
  accent: "#F012BE",
});

staticDefinition.getKey("primary"); // "--static-primary"
staticDefinition.getValue("primary"); // "var(--static-primary, #0074D9)"

@property Registration

You can register variables with the CSS Typed OM for transitions, inheritance, etc.

const definition = cssVarUtils.define({ primaryColor: "#F012BE" });

CSS.registerProperty({
  name: definition.getKey("primaryColor"),
  syntax: "<color>",
  inherits: true,
  initialValue: "#F012BE",
});

Recommendations for Large CSS-in-JS Apps

For large-scale web applications (mono-repos, micro frontends, dynamic plugin architectures) you should take extra precautions to avoid accidental variable name collisions and to harden your design system surface.

  1. Strengthen uniqueness: Provide a custom recordKeyToCssVarKey that injects a namespace (package name) plus a short random suffix. (You can optionally add build / commit info if desired.)

    import {
      cssVarUtils,
      randomString,
      slugify,
    } from "@crescendolab/css-var-ts";
    
    const namespace = process.env.APP_NAMESPACE ?? "app"; // e.g. marketing, analytics
    
    const scopedCssVarUtils = cssVarUtils.create({
      recordKeyToCssVarKey: (key) =>
        `--${namespace}-${slugify(key)}-${randomString(8)}` as const,
    });

    For deterministic builds replace randomString(8) with a stable hash (e.g. of namespace + key).

  2. Strongly recommended: Register core design tokens via @property to enforce syntax (e.g. <color>, <length>) and enable smoother transitions & validation.

  3. Expose only semantic tokens to feature teams; keep raw palette tokens private to your design system package.

  4. Document namespace conventions so new packages follow the same pattern.

  5. Periodically audit generated variable names (e.g. collect with a build script) to detect drift or duplication.

These measures reduce the chance of silent styling regressions when independently deployed bundles are combined at runtime.


🔍 API Reference

cssVarUtils

The default exported utility bundle.

const definition = cssVarUtils.define({ accent: "#F012BE" });
definition.raw; // [{ accent: "#F012BE" }]
// example suffix will differ each run (8 random hex chars):
definition.cssProps; // { "--accent-a1b2c3d4": "#F012BE" }
definition.getKey("accent"); // "--accent-a1b2c3d4"
definition.getValue("accent"); // "var(--accent-a1b2c3d4, #F012BE)"

Each call to define() returns an object:

| Key | Type | Description | | ---------------- | ------------------------- | ------------------------------------------------------------- | | raw | [base, ...exts] | Array of raw token records (base + extensions) | | cssProps | Record<cssVarKey, string> | Object you can spread into style systems to declare variables | | getKey(name) | string | Generated CSS variable name (e.g. --accent-…) | | getValue(name) | var(--token, val) | Proper var() usage string |

cssVarUtils.create(options)

Low‑level factory to customize naming.

const custom = cssVarUtils.create({
  recordKeyToCssVarKey: (k) => `--my-${k}` as const,
});

Helper Exports

| Export | Purpose | | -------------- | -------------------------------------------------------------- | | slugify | Deterministic slug for record keys | | randomString | Cryptographically strong random id (hex) for custom strategies |


📚 Storybook Examples

| Category | Story | Code | Live Demo | | ------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Basic: Simple | Palette only | 01_basic/01_simple | Demo | | Basic: Extend | .extend() usage | 01_basic/02_extend | Demo | | Basic: Reset | Nested reset | 01_basic/03_reset | Demo | | Emotion (class) | @emotion/css | 02_integration/01_emotion/01_emotion_css | Demo | | Emotion (css prop) | @emotion/react | 02_integration/01_emotion/02_css_prop | Demo | | MUI | sx prop | 02_integration/02_mui_sx_prop | Demo | | Advanced | Static custom keys | 03_advanced/01_staticCssVarKey | Demo | | Advanced | @property | 03_advanced/02_@property_atRule | Demo |


🤔 Why add a random suffix?

Adding a short random suffix mitigates accidental collisions when multiple packages / microfrontends define the same token names. It keeps names mostly human readable while providing lightweight namespacing. For fully deterministic readable names use a static strategy; for strict isolation include a package or build id.

Strategy Summary

List of approaches:

  • Default (cssVarUtils): Slug + random 8‑char id = collision‑resistant and readable.
  • Static custom (see story): --static-${slug} for fully readable tokens; ensure uniqueness manually.
  • Random / ephemeral: cssVarUtils.create + randomString / build hash for experiments, multi‑tenant isolation, A/B variants.

🧪 Testing Strategy

Library surface is pure & easily unit testable (see randomString.test.ts for an example). Add tests as you add helpers: focus on stability of generated keys and referential integrity between getKey and getValue.


🛠 Release Automation

This repo uses changesets + GitHub Actions. On merge to main, a version PR is created / updated. Approve & merge to publish.

Ensure org settings allow the workflow to create & approve PRs: Settings → Code and automation → Actions → General → Workflow permissions:

  • Read & write permissions
  • Allow GitHub Actions to create and approve pull requests

🤝 Contributing

PRs welcome! See the contributing guide.

Suggested areas:

  • New integrations (e.g. Tailwind plugin example)
  • Additional DX helpers
  • Documentation improvements

📜 License

Apache-2.0

Copyright (c) 2025 Crescendo Lab


Made with ❤️ to make CSS variables first-class citizens in TypeScript.