DEV Community

Cover image for Introducing Varwolf 🐺 - Dynamic CSS Variables for React
Kunal Tanwar
Kunal Tanwar

Posted on • Edited on • Originally published at github.com

Introducing Varwolf 🐺 - Dynamic CSS Variables for React

I just shipped Varwolf v1.0 - a lightweight library (2KB gzipped!) that lets you control CSS variables dynamically with pseudo-classes like :hover, :focus, and :active in React.

💡 The Problem

Ever tried to make a CSS variable change on hover?

// ❌ This doesn't work
<div 
  style={{ 
    '--color': 'red',
    backgroundColor: 'var(--color)'
  }}
  onMouseEnter={() => /* how to change --color? */}
/>
Enter fullscreen mode Exit fullscreen mode

You'd need state management, event handlers, and lots of boilerplate just to change one variable on hover.

✨ The Solution

import { varwolf } from "varwolf"

<varwolf.button
  style={{
    __bg: "red",
    backgroundColor: "var(--bg)",
    _hover: {
      __bg: "blue"
    }
  }}
>
  Hover me!
</varwolf.button>
Enter fullscreen mode Exit fullscreen mode

The __ prefix creates CSS variables, and _hover makes them change on hover. Pure CSS, no JavaScript!


🚀 Key Features

1. Pseudo-Classes with Underscore Prefix

Use camelCase with _ prefix for any pseudo-class:

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

    _hover: { __opacity: 0.8 },
    _active: { __opacity: 0.6 },
    _focus: { __opacity: 0.9 },
    _disabled: { __opacity: 0.3 }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

2. Nested Selectors

Combine multiple states:

<varwolf.button
  style={{
    __color: "black",
    _hover: {
      __color: "blue",
      _disabled: {
        __color: "gray"  // :hover:disabled
      }
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

3. currentValue Functions

Modify existing values instead of replacing:

<varwolf.div
  style={{
    __size: "100px",
    width: "var(--size)",
    _hover: {
      __size: (currentValue) => `calc(${currentValue} * 1.2)`
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

4. Cross-State References

Reference values from other pseudo-states:

<varwolf.button
  style={{
    __bg: "red",
    _hover: { __bg: "blue" },
    _active: {
      __bg: (cv, from = "hover") => cv  // Use hover's value
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

5. Hybrid Rendering

Combine CSS variables with dynamic inline styles:

const [scrollY, setScrollY] = useState(0)

<varwolf.div
  style={{
    __bg: "red",
    _hover: { __bg: "blue" }
  }}
  inlineStyle={{
    transform: `translateY(${scrollY}px)`
  }}
/>
Enter fullscreen mode Exit fullscreen mode

📊 Why Varwolf?

Size Comparison

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

vs Competition

Library Gzipped vs Varwolf
styled-components 15.2 KB 8.4x larger
Emotion 8.9 KB 4.9x larger
Stitches 5.8 KB 3.2x larger
vanilla-extract 4.2 KB 2.3x larger
Varwolf 1.8 KB Winner! 🏆

💻 Real-World Example

Here's an animated button with hover, active, and disabled states:

import { varwolf } from "varwolf"
import { useState } from "react"

function AnimatedButton() {
  const [loading, setLoading] = useState(false)

  return (
    <varwolf.button
      style={{
        __bg: "blue",
        __scale: "1",

        backgroundColor: "var(--bg)",
        transform: "scale(var(--scale))",
        padding: "10px 20px",
        border: "none",
        color: "white",
        cursor: "pointer",
        transition: "all 0.3s ease",

        _hover: {
          __bg: "darkblue",
          __scale: "1.05"
        },

        _active: {
          __scale: "0.95"
        },

        _disabled: {
          __bg: "gray",
          cursor: "not-allowed"
        }
      }}
      disabled={loading}
      onClick={() => setLoading(true)}
    >
      {loading ? "Loading..." : "Click me!"}
    </varwolf.button>
  )
}
Enter fullscreen mode Exit fullscreen mode

🔧 How It Works

Varwolf injects CSS rules into a <style> tag:

.varwolf-abc123 {
  --bg: red;
}

.varwolf-abc123:hover {
  --bg: blue;
}
Enter fullscreen mode Exit fullscreen mode

Your component gets the class name, and CSS variables update on interaction. No JavaScript event handlers needed!


📦 Installation

npm install varwolf
Enter fullscreen mode Exit fullscreen mode

⚡ Features at a Glance

  • 2KB gzipped - Smallest CSS-in-JS library
  • Zero dependencies - No bloat
  • TypeScript - Full type safety
  • Pseudo-classes - :hover, :focus, :active, etc.
  • Nested selectors - :hover:disabled
  • Dynamic values - currentValue functions
  • Cross-state refs - Reference other states
  • Performance optimized - Smart caching & hashing

🎯 Use Cases

Theme Switching

<varwolf.div
  style={{
    __theme: darkMode ? "dark" : "light",
    __bg: "white",
    __color: "black",

    '[data-theme="dark"]': {
      __bg: "black",
      __color: "white"
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Loading States

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

    '[aria-busy="true"]': {
      __opacity: 0.5
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Responsive Hover Effects

<varwolf.card
  style={{
    __elevation: "2px",
    boxShadow: "0 var(--elevation) 10px rgba(0,0,0,0.1)",

    _hover: {
      __elevation: "8px"
    }
  }}
/>
Enter fullscreen mode Exit fullscreen mode

🔮 What's Next?

Planned features for future releases:

  • Media query support (_md, _lg)
  • Animation helpers
  • Theme presets
  • Container queries
  • More pseudo-classes

💬 Feedback Welcome!

This is v1.0, and I'd love your thoughts:

  • What features would you add?
  • How can the API be improved?
  • Found any bugs?

Drop a comment or open an issue on GitHub!


🔗 Links


🙏 Thanks!

If you find varwolf useful:

  • ⭐ Star it on GitHub
  • 💬 Share your feedback
  • 🐛 Report bugs
  • 🚀 Try it in your project

Built with 🐺 by Kunal Tanwar


Have you tried CSS variables in React? What challenges have you faced? Let me know in the comments! 👇

Top comments (0)