DEV Community

tengxgfyrz67s
tengxgfyrz67s

Posted on

css-variables-and-var-macros

CSS Variables and Var Macros in euv

Project Code:https://github.com/euv-dev/euv

euv provides powerful primitives for managing CSS dynamically at runtime. The css-vars! and var! macros bridge the gap between reactive Rust state and CSS styling, enabling theming, responsive design, and dynamic visual effects — all without leaving your Rust code.

Why Dynamic CSS Variables?

Traditional CSS is static. Once a stylesheet is loaded, values are fixed. Modern web applications, however, need runtime control over styles: dark mode toggles, user-customized themes, animated transitions driven by application state, and responsive layouts that go beyond media queries.

CSS custom properties (--my-var) solve part of the problem by allowing JavaScript to set and read CSS values at runtime. euv's css-vars! and var! macros build on this foundation with full reactive integration.

The css-vars! Macro

The css-vars! macro defines CSS custom properties that can be applied to elements. It generates CSS variable declarations that are injected into the document.

Basic Usage

css-vars! {
    --primary-color: "#3498db";
    --spacing-unit: "8px";
    --border-radius: "4px";
}
Enter fullscreen mode Exit fullscreen mode

This generates CSS custom properties that can be referenced anywhere in your stylesheets using var(--primary-color), var(--spacing-unit), etc.

How It Works

When css-vars! is invoked, euv:

  1. Generates CSS custom property declarations.
  2. Injects them into the document's styles.
  3. Makes them available for use in any CSS rule or inline style.

These variables become part of the document's CSS environment and can be consumed by any element.

Scoping CSS Variables

You can scope CSS variables to specific components or elements by combining css-vars! with euv's component system. Variables defined within a component's context apply only to that element and its descendants.

The var! Macro: Reactive Variables

While css-vars! defines static CSS custom properties, var! creates reactive CSS variables — variables that automatically update when their source signals change.

Basic Usage

let primary_color: Signal<String> = use_signal(|| "#3498db".to_string());
var!(primary_color);
Enter fullscreen mode Exit fullscreen mode

The var! macro takes a Signal<String> and creates a CSS variable that tracks the signal's value. When primary_color changes via set(), the CSS variable is automatically updated in the DOM.

How It Works Internally

When var! is called:

  1. It reads the current value of the signal.
  2. Sets the corresponding CSS custom property on the target element.
  3. Subscribes to the signal for future changes.
  4. When the signal's value changes, the CSS property is updated automatically.

This creates a seamless reactive binding between Rust state and CSS.

Combining css-vars! and var!

The real power emerges when you use both macros together. Define your CSS variable structure with css-vars! and drive values reactively with var!:

css-vars! {
    --primary-color: "#3498db";
    --spacing-unit: "8px";
}

let theme_color: Signal<String> = use_signal(|| "#3498db".to_string());
var!(theme_color);
Enter fullscreen mode Exit fullscreen mode

Now --primary-color is defined as a CSS custom property, and theme_color signal drives its value reactively.

Dynamic Theming

One of the most compelling use cases for CSS variables in euv is dynamic theming. Here's how you can build a theme switcher:

// Define theme colors as signals
let bg_color: Signal<String> = use_signal(|| "#ffffff".to_string());
let text_color: Signal<String> = use_signal(|| "#333333".to_string());
let accent_color: Signal<String> = use_signal(|| "#3498db".to_string());

// Create reactive CSS variables
var!(bg_color);
var!(text_color);
var!(accent_color);

// Switch to dark mode
fn switch_to_dark_mode() {
    batch(|| {
        bg_color.set("#1a1a2e".to_string());
        text_color.set("#eaeaea".to_string());
        accent_color.set("#e94560".to_string());
    });
}

// Switch to light mode
fn switch_to_light_mode() {
    batch(|| {
        bg_color.set("#ffffff".to_string());
        text_color.set("#333333".to_string());
        accent_color.set("#3498db".to_string());
    });
}
Enter fullscreen mode Exit fullscreen mode

Notice the use of batch to coordinate all color updates at once. This ensures that all CSS variables are updated together, preventing visual glitches from partial theme application.

Responsive Spacing and Layout

CSS variables driven by signals can also power responsive layouts that adapt to application state:

let spacing_unit: Signal<String> = use_signal(|| "8px".to_string());
var!(spacing_unit);

// Increase spacing on large screens
watch!(window_width, |w: i32| {
    if w > 1200 {
        spacing_unit.set("16px".to_string());
    } else if w > 768 {
        spacing_unit.set("12px".to_string());
    } else {
        spacing_unit.set("8px".to_string());
    }
});
Enter fullscreen mode Exit fullscreen mode

Animation Control with CSS Variables

euv comes with built-in CSS keyframe animations: euv-spin, euv-fade-in, euv-scale-in, euv-pulse, euv-slide-up, euv-slide-left, euv-fade-in-up, euv-progress, and euv-shimmer. You can control these animations dynamically using var!:

let spin_duration: Signal<String> = use_signal(|| "1s".to_string());
var!(spin_duration);

// Speed up the spin animation on hover
watch!(is_hovering, |hovering: bool| {
    if hovering {
        spin_duration.set("0.3s".to_string());
    } else {
        spin_duration.set("1s".to_string());
    }
});
Enter fullscreen mode Exit fullscreen mode

Usage with the class! macro:

class! {
    pub c_anim_spin { animation: "euv-spin 1s linear infinite"; }
}
Enter fullscreen mode Exit fullscreen mode

Custom Keyframe Animations

For animations beyond the built-in set, euv provides Css::inject_css to inject custom @keyframes rules:

Css::inject_css("@keyframes my-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-20px); }
}");
Enter fullscreen mode Exit fullscreen mode

You can then reference these custom animations in your class! definitions:

class! {
    pub c_anim_bounce { animation: "my-bounce 0.5s ease-in-out infinite"; }
}
Enter fullscreen mode Exit fullscreen mode

Integrating with the class! Macro

The class! macro generates CSS class definitions. Combined with var!, you can create classes that incorporate reactive CSS variables:

class! {
    pub c_card {
        display: "flex";
        padding: "10px";
        background-color: var(--card-bg);
        color: var(--card-text);
    }
}

// Drive the CSS variables reactively
let card_bg: Signal<String> = use_signal(|| "#ffffff".to_string());
let card_text: Signal<String> = use_signal(|| "#333333".to_string());
var!(card_bg);
var!(card_text);
Enter fullscreen mode Exit fullscreen mode

Practical Examples

Example 1: Dark Mode Toggle

let dark_mode: Signal<bool> = use_signal(|| false);

let bg: Signal<String> = use_signal(|| "#ffffff".to_string());
let fg: Signal<String> = use_signal(|| "#333333".to_string());
var!(bg);
var!(fg);

watch!(dark_mode, |is_dark: bool| {
    batch(|| {
        if is_dark {
            bg.set("#121212".to_string());
            fg.set("#e0e0e0".to_string());
        } else {
            bg.set("#ffffff".to_string());
            fg.set("#333333".to_string());
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

Example 2: User-Configurable Accent Color

let accent: Signal<String> = use_signal(|| "#3498db".to_string());
var!(accent);

// When user picks a new color from a color picker:
fn on_color_pick(new_color: String) {
    accent.set(new_color);
}
Enter fullscreen mode Exit fullscreen mode

Example 3: Progress Bar with Reactive Styling

let progress: Signal<f64> = use_signal(|| 0.0);
let progress_color: Signal<String> = use_signal(|| "#3498db".to_string());

var!(progress);
var!(progress_color);

watch!(progress, |p: f64| {
    if p < 0.3 {
        progress_color.set("#e74c3c".to_string());
    } else if p < 0.7 {
        progress_color.set("#f39c12".to_string());
    } else {
        progress_color.set("#2ecc71".to_string());
    }
});
Enter fullscreen mode Exit fullscreen mode

Summary

  • css-vars! defines CSS custom properties that can be used throughout your stylesheets.
  • var! creates reactive bindings between Signal<String> values and CSS custom properties, automatically updating the DOM when signals change.
  • Combine both macros to build dynamic themes, responsive layouts, and animation-driven UIs.
  • Use batch to coordinate multiple CSS variable updates and prevent visual glitches.
  • Custom keyframe animations can be injected with Css::inject_css("@keyframes my-bounce { ... }").
  • Built-in animations include euv-spin, euv-fade-in, euv-scale-in, euv-pulse, euv-slide-up, euv-slide-left, euv-fade-in-up, euv-progress, and euv-shimmer.

CSS variables in euv transform styling from a static concern into a first-class part of your reactive application state, enabling rich, dynamic user experiences with minimal code.


Project Code:https://github.com/euv-dev/euv

Top comments (0)