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

varwolf

v1.3.0

Published

A Modern CSS Variable Manipulation Library for React.

Readme

🐺 Varwolf

A Modern CSS Variable Manipulation Library for React.

Varwolf lets you dynamically control CSS variables with a clean, type-safe API. Write styles with pseudo-classes, pseudo-elements, variable groups, and dynamic values — all while maintaining the performance and specificity benefits of CSS-in-JS.

✨ Features

  • 🎯 CSS Variable-First — Manipulate CSS custom properties, not just styles
  • 🔄 Dynamic State Management — Variables change with pseudo-classes (:hover, :active, etc.)
  • 📦 Variable Groups — Organize related variables with nested objects (NEW in v1.2.0)
  • Pseudo-Elements — Style ::before, ::after, and more with $ prefix
  • 🪆 Nested Pseudo-Selectors — Support for complex states like :hover:disabled
  • 🔗 Cross-State References — Reference values from other pseudo-states
  • 📐 currentValue Functions — Modify existing values instead of replacing them
  • Hybrid Rendering — Stylesheet for static styles, inline for dynamic values
  • 🎨 Full TypeScript Support — Complete type safety with autocomplete
  • 🧩 Zero Dependencies — Ultra-lightweight (~2KB gzipped) - Smallest CSS-in-JS library!
  • 🚀 Performance Optimized — Smart caching, stable hashing, lazy variable generation

📦 Installation

# npm
npm install varwolf

# yarn
yarn add varwolf

# pnpm
pnpm add varwolf

🚀 Quick Start

import { varwolf } from "varwolf"

function App() {
    return (
        <varwolf.button
            style={{
                __bgColor: "blue",
                __scale: 1,

                backgroundColor: "var(--bg-color)",
                transform: "scale(var(--scale))",
                padding: "10px 20px",
                border: "none",
                color: "white",

                _hover: {
                    __bgColor: "darkblue",
                    __scale: 1.1,
                },
                _active: {
                    __scale: 0.95,
                },

                $before: {
                    content: "→",
                    marginRight: "5px",
                },
            }}
        >
            Click me!
        </varwolf.button>
    )
}

📖 Core Concepts

CSS Variables (__ prefix)

Define CSS custom properties with the __ prefix:

<varwolf.div
    style={{
        __primary: "blue",
        __importance: 16,

        backgroundColor: "var(--primary)",
        zIndex: "var(--importance)",
    }}
/>

Generates:

.vw-abc123 {
    --primary: blue;
    --importance: 16;
    background-color: var(--primary);
    z-index: var(--importance);
}

Variable Groups

NEW in v1.2.0: Organize related variables with nested objects. Only used variables are generated for optimal performance.

<varwolf.div
    style={{
        __spacing: {
            xs: "4px",
            sm: "8px",
            md: "16px",
            lg: "24px",
            xl: "32px",
        },

        __colors: {
            primary: "#0070f3",
            secondary: "#7928ca",
            success: "#00ff00",
        },

        padding: "var(--spacing-md)",
        backgroundColor: "var(--colors-primary)",
    }}
/>

Generates (only used variables):

.vw-abc123 {
    --spacing-md: 16px;
    --colors-primary: #0070f3;
    padding: var(--spacing-md);
    background-color: var(--colors-primary);
}

Key Benefits:

  • 🎯 Lazy Generation: Only injects variables you actually use
  • 📦 Organization: Group related variables (spacing, colors, typography)
  • 🔄 Partial Updates: Override individual values in pseudo-classes
  • Performance: Reduces CSS output size

Partial Updates in Pseudo-Classes:

<varwolf.div
    style={{
        __spacing: {
            xs: "4px",
            sm: "8px",
            md: "16px",
        },

        padding: "var(--spacing-md)",

        _hover: {
            __spacing: {
                md: "20px", // Only override this one value
            },
        },
    }}
/>

Pseudo-Classes (_ prefix)

Change variables on pseudo-states using camelCase:

<varwolf.button
    style={{
        __bgColor: "blue",
        backgroundColor: "var(--bg-color)",

        _hover: {
            __bgColor: "darkblue",
        },
        _active: {
            __bgColor: "navy",
        },
        _disabled: {
            __bgColor: "gray",
        },
    }}
>
    Button
</varwolf.button>

Generates:

.vw-abc123 {
    --bg-color: blue;
    background-color: var(--bg-color);
}
.vw-abc123:hover {
    --bg-color: darkblue;
}
.vw-abc123:active {
    --bg-color: navy;
}
.vw-abc123:disabled {
    --bg-color: gray;
}

Pseudo-Elements ($ prefix)

Style ::before, ::after, and other pseudo-elements with the $ prefix:

<varwolf.button
    style={{
        __iconColor: "white",
        backgroundColor: "blue",
        padding: "10px 20px",

        $before: {
            content: "→",
            marginRight: "5px",
            color: "var(--icon-color)",
        },

        _hover: {
            __iconColor: "yellow",
            backgroundColor: "darkblue",
        },
    }}
>
    Next
</varwolf.button>

Generates:

.vw-abc123 {
    --icon-color: white;
    background-color: blue;
    padding: 10px 20px;
}
.vw-abc123::before {
    content: "→";
    margin-right: 5px;
    color: var(--icon-color);
}
.vw-abc123:hover {
    --icon-color: yellow;
    background-color: darkblue;
}

Supported Pseudo-Elements

::after           — $after
::backdrop        — $backdrop (for dialogs)
::before          — $before
::first-letter    — $firstLetter
::first-line      — $firstLine
::marker          — $marker (for list items)
::placeholder     — $placeholder (for inputs)
::selection       — $selection

Real-World Examples

Icon Badge:

<varwolf.button
    style={{
        position: "relative",
        padding: "10px 20px",

        $after: {
            content: "3",
            position: "absolute",
            top: "-8px",
            right: "-8px",
            width: "20px",
            height: "20px",
            borderRadius: "50%",
            __badge: "red",
            backgroundColor: "var(--badge)",
            color: "white",
            fontSize: "12px",
        },
    }}
>
    Notifications
</varwolf.button>

Decorative Underline:

<varwolf.h2
    style={{
        position: "relative",
        paddingBottom: "10px",

        $after: {
            content: "",
            position: "absolute",
            bottom: 0,
            left: 0,
            width: "50px",
            height: "3px",
            __accent: "#0070f3",
            backgroundColor: "var(--accent)",
        },
    }}
>
    Section Title
</varwolf.h2>

Custom Selection Color:

<varwolf.p
    style={{
        $selection: {
            backgroundColor: "yellow",
            color: "black",
        },
    }}
>
    Try selecting this text!
</varwolf.p>

Static vs Dynamic Styles

Varwolf supports two types of styles for optimal performance:

style prop — Stylesheet Rendering

Use for static styles, pseudo-classes, and pseudo-elements:

<varwolf.button
    style={{
        backgroundColor: "red",
        padding: "10px 20px",

        _hover: {
            backgroundColor: "darkred",
        },

        $before: {
            content: "→",
        },
    }}
>
    Static styles
</varwolf.button>

inlineStyle prop — Inline Rendering

Use for frequently changing values (animations, scroll effects, drag positions):

function Parallax() {
    const [scrollY, setScrollY] = useState(0)

    useEffect(() => {
        const handleScroll = () => setScrollY(window.scrollY)
        window.addEventListener("scroll", handleScroll, { passive: true })
        return () => window.removeEventListener("scroll", handleScroll)
    }, [])

    return (
        <varwolf.div
            style={{
                // Static styles in stylesheet
                backgroundColor: "blue",
                _hover: { backgroundColor: "darkblue" },
            }}
            inlineStyle={{
                // Dynamic values inline (60fps updates)
                transform: `translateY(${scrollY * 0.5}px)`,
            }}
        >
            Parallax effect
        </varwolf.div>
    )
}

Hybrid Approach

Combine both for maximum flexibility:

function DynamicButton() {
    const [hue, setHue] = useState(200)

    return (
        <varwolf.button
            style={{
                // Stylesheet: Uses CSS variable
                backgroundColor: "var(--primary-color)",
                padding: "10px 20px",

                _hover: {
                    filter: "brightness(0.9)",
                },
            }}
            inlineStyle={{
                // Inline: Sets variable dynamically
                __primaryColor: `hsl(${hue}, 70%, 50%)`,
            }}
        >
            Dynamic theming
        </varwolf.button>
    )
}

Nested Pseudo-Selectors

Combine multiple pseudo-classes:

<varwolf.button
    style={{
        __opacity: 1,
        opacity: "var(--opacity)",

        _hover: {
            __opacity: 0.8,

            _disabled: {
                // :hover:disabled
                __opacity: 0.5,
            },
        },
    }}
>
    Hover me
</varwolf.button>

Generates:

.vw-abc123 {
    --opacity: 1;
    opacity: var(--opacity);
}
.vw-abc123:hover {
    --opacity: 0.8;
}
.vw-abc123:hover:disabled {
    --opacity: 0.5;
}

currentValue Functions

Modify existing values instead of replacing them:

<varwolf.div
    style={{
        __size: 16,
        fontSize: "var(--size)px",

        _hover: {
            __size: (currentValue) => Number(currentValue) * 1.2, // 16 → 19.2
        },
    }}
>
    Text grows on hover
</varwolf.div>

Cross-State References (from parameter)

Reference values from other pseudo-states:

<varwolf.button
    style={{
        __scale: 1,
        transform: "scale(var(--scale))",

        _hover: {
            __scale: 1.2,
        },
        _active: {
            // Get value from :hover state, not base
            __scale: (currentValue, from = "hover") => Number(currentValue) * 0.9,
            // Result: 1.2 * 0.9 = 1.08 (not 1 * 0.9 = 0.9)
        },
    }}
>
    Press me
</varwolf.button>

🎨 Advanced Usage

Design System with Variable Groups

Create a reusable design system:

const designSystem = {
    __spacing: {
        xxs: "2px",
        xs: "4px",
        sm: "8px",
        md: "16px",
        lg: "24px",
        xl: "32px",
        xxl: "48px",
    },

    __colors: {
        primary: "#0070f3",
        secondary: "#7928ca",
        success: "#00ff00",
        error: "#ff0000",
    },

    __fontSize: {
        xs: "12px",
        sm: "14px",
        md: "16px",
        lg: "18px",
        xl: "24px",
    },
}

// Use in components - only injects variables you actually use!
<varwolf.button
    style={{
        ...designSystem,
        padding: "var(--spacing-md)",
        fontSize: "var(--font-size-md)",
        backgroundColor: "var(--colors-primary)",
    }}
>
    Button
</varwolf.button>

Variable Composition

Compose complex values from multiple variables:

<varwolf.div
    style={{
        __hue: 200,
        __saturation: 50,
        __lightness: 50,

        backgroundColor: "hsl(var(--hue), var(--saturation)%, var(--lightness)%)",

        _hover: {
            __lightness: (cv) => Number(cv) + 10, // Lighten on hover
        },
    }}
>
    Hover to lighten
</varwolf.div>

Dynamic Theming

function ThemedButton() {
    const [theme, setTheme] = useState("light")

    return (
        <varwolf.button
            style={{
                backgroundColor: "var(--bg-color)",
                color: "var(--text-color)",
                padding: "10px 20px",
                border: "1px solid var(--border-color)",

                _hover: {
                    __bgColor: theme === "light" ? "#f0f0f0" : "#333",
                },
            }}
            inlineStyle={{
                __bgColor: theme === "light" ? "white" : "black",
                __textColor: theme === "light" ? "black" : "white",
                __borderColor: theme === "light" ? "#ccc" : "#555",
            }}
        >
            Themed Button
        </varwolf.button>
    )
}

Scroll-Reactive Styles

function ScrollCard() {
    const [scrollY, setScrollY] = useState(0)

    useEffect(() => {
        const handleScroll = () => setScrollY(window.scrollY)
        window.addEventListener("scroll", handleScroll, { passive: true })
        return () => window.removeEventListener("scroll", handleScroll)
    }, [])

    const scale = 1 + scrollY / 1000
    const opacity = Math.max(0, 1 - scrollY / 500)

    return (
        <varwolf.div
            style={{
                backgroundColor: "blue",
                padding: "100px",
                color: "white",

                _hover: {
                    backgroundColor: "darkblue",
                },
            }}
            inlineStyle={{
                transform: `scale(${scale})`,
                opacity: opacity,
            }}
        >
            Scroll to scale & fade
        </varwolf.div>
    )
}

📚 API Reference

Types

import type { VarwolfStyles, VarwolfInlineStyles } from "varwolf"

// VarwolfStyles - for style prop (supports pseudo-classes & pseudo-elements)
interface ComponentProps {
    style?: VarwolfStyles
}

// VarwolfInlineStyles - for inlineStyle prop (no pseudo-classes/elements)
interface ComponentProps {
    inlineStyle?: VarwolfInlineStyles
}

Supported Pseudo-Classes

All standard CSS pseudo-classes with camelCase autocomplete:

:active
:checked
:disabled
:enabled
:first-child
:focus-visible
:focus-within
:focus
:hover
:invalid
:last-child
:link
:placeholder-shown
:required
:valid
:visited

Advanced Hook

import { useVarwolf } from "varwolf"

function CustomComponent() {
    const { className } = useVarwolf({
        __bg: "red",

        backgroundColor: "var(--bg)",

        _hover: {
            __bg: "darkred",
        },

        $before: {
            content: "→",
        },
    })

    // Use with third-party components
    return <MuiButton className={className}>Custom</MuiButton>
}

⚡ Performance

Development Mode

  • CSS injected via textContent for visibility in DevTools
  • Full debugging support

Production Mode

  • CSS injected via insertRule() (3x faster)
  • Optimized for performance

Caching & Optimization

  • Stable hashing prevents duplicate injections
  • Styles cached across component re-renders
  • Lazy variable generation: Only injects variables you use from groups
  • React StrictMode compatible

🔧 TypeScript

Full type safety with element-specific props:

import { varwolf } from "varwolf"

// ✅ Valid - button-specific props
<varwolf.button disabled type="submit" />

// ✅ Valid - input-specific props
<varwolf.input placeholder="Email" type="email" />

// ✅ Valid - anchor-specific props
<varwolf.a href="https://example.com" target="_blank" />

// ❌ Error: Property 'disabled' does not exist on type 'a'
<varwolf.a disabled />

🤔 FAQ

Why use Varwolf over styled-components/Emotion?

Varwolf focuses on CSS variable manipulation, not replacing CSS-in-JS libraries.

Use Varwolf when:

  • ✅ You want to dynamically control CSS variables
  • ✅ You need state-based variable changes
  • ✅ You're building with modern CSS custom properties
  • ✅ You need both static and dynamic styles

Does it work with existing CSS?

Yes! Varwolf generates CSS custom properties that work with any CSS.

What's the difference between style and inlineStyle?

  • style: Static styles + pseudo-classes + pseudo-elements → Injected into <style> tag
  • inlineStyle: Dynamic values → Applied as inline style="" attribute

Use style for most cases, inlineStyle for frequently changing values (animations, scroll, drag).

Bundle size?

Varwolf is one of the smallest CSS-in-JS libraries available:

| Format | Size | Description | | -------------- | --------- | --------------------------- | | Package (.tgz) | 19.4 KB | Includes TypeScript types | | Minified | 8 KB | Production JavaScript | | Gzipped | ~2 KB | Actual download size ✅ |

Size Comparison

| Library | Gzipped | | ----------------- | ------------- | | styled-components | 15.2 KB | | Emotion | 8.9 KB | | Stitches | 5.8 KB | | vanilla-extract | 4.2 KB | | Varwolf | 1.8 KB 🏆 |

Varwolf is 8x smaller than styled-components and 5x smaller than Emotion!

Browser support?

All modern browsers supporting CSS custom properties:

  • Chrome 49+
  • Firefox 31+
  • Safari 9.1+
  • Edge 15+

📄 License

MIT © Kunal Tanwar

🙏 Contributing

Contributions Welcome! Please open an issue or PR on GitHub.

🌟 Show Your Support

  • Star the Repo
  • 🐦 Share on Social Media
  • 📝 Write About It
  • 🐛 Report Bugs
  • 💡 Suggest Features

Made with 🐺 by Kunal Tanwar