DEV Community

Cover image for Introducing Varwolf ๐Ÿบ - Dynamic CSS Variables for React
Kunal Tanwar
Kunal Tanwar

Posted 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)