UNPKG

varwolf

Version:

A Modern CSS Variable Manipulation Library for React.

822 lines (654 loc) 18.7 kB
# 🐺 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 ```bash # npm npm install varwolf # yarn yarn add varwolf # pnpm pnpm add varwolf ``` ## 🚀 Quick Start ```tsx 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: ```tsx <varwolf.div style={{ __primary: "blue", __importance: 16, backgroundColor: "var(--primary)", zIndex: "var(--importance)", }} /> ``` **Generates:** ```css .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. ```tsx <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):** ```css .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:** ```tsx <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: ```tsx <varwolf.button style={{ __bgColor: "blue", backgroundColor: "var(--bg-color)", _hover: { __bgColor: "darkblue", }, _active: { __bgColor: "navy", }, _disabled: { __bgColor: "gray", }, }} > Button </varwolf.button> ``` **Generates:** ```css .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: ```tsx <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:** ```css .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 ```plaintext ::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:** ```tsx <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:** ```tsx <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:** ```tsx <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: ```tsx <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): ```tsx 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: ```tsx 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: ```tsx <varwolf.button style={{ __opacity: 1, opacity: "var(--opacity)", _hover: { __opacity: 0.8, _disabled: { // :hover:disabled __opacity: 0.5, }, }, }} > Hover me </varwolf.button> ``` **Generates:** ```css .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: ```tsx <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: ```tsx <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: ```tsx 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: ```tsx <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 ```tsx 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 ```tsx 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 ```tsx 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: ```plaintext :active :checked :disabled :enabled :first-child :focus-visible :focus-within :focus :hover :invalid :last-child :link :placeholder-shown :required :valid :visited ``` ### Advanced Hook ```tsx 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: ```tsx 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-elementsInjected into `<style>` tag - **`inlineStyle`**: Dynamic valuesApplied 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](LICENSE) © Kunal Tanwar ## 🙏 Contributing Contributions Welcome! Please open an [issue](https://github.com/KunalTanwar/varwolf/issues) or [PR](https://github.com/KunalTanwar/varwolf/pulls) on [GitHub](https://github.com/KunalTanwar/varwolf). ## 🌟 Show Your Support - ⭐ [Star the Repo](https://github.com/KunalTanwar/varwolf) - 🐦 Share on Social Media - 📝 Write About It - 🐛 Report Bugs - 💡 Suggest Features **Made with 🐺 by [Kunal Tanwar](https://github.com/KunalTanwar)**