Commonly Used Macros in UI/UX Development

Master CSS custom properties for consistent, scalable interfaces. Learn the patterns that power modern design systems.

What Are CSS Custom Properties?

CSS custom properties--commonly referred to as variables or "macros"--are entities defined by CSS authors that contain specific values to be reused throughout a document. Unlike preprocessor variables, CSS custom properties are true CSS values that cascade and inherit their value from parents, can be modified dynamically with JavaScript, and work in the browser at runtime.

The basic syntax uses two dashes as a prefix:

:root {
 --primary-color: #3b82f6;
 --spacing-unit: 8px;
 --border-radius: 4px;
}

.button {
 background-color: var(--primary-color);
 padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);
 border-radius: var(--border-radius);
}

Modern UI/UX development relies heavily on reusable, maintainable styling patterns. These native CSS features enable developers to define reusable values that cascade through stylesheets, respond to changes at runtime, and integrate seamlessly with JavaScript. Our web development services team implements these patterns to build scalable, maintainable interfaces for clients across industries.

Unlike Sass or Less variables, which are resolved at compile time, CSS custom properties provide true runtime flexibility. This means you can create themes that adapt to user preferences, respond to media queries at the document level, and enable sophisticated design systems that scale across large applications. By centralizing design decisions in a single source of truth, teams reduce inconsistencies and accelerate development cycles.

Color Macros

Color macros form the backbone of most design systems, enabling consistent theming and easy modifications:

:root {
 /* Brand colors */
 --color-primary: #2563eb;
 --color-primary-hover: #1d4ed8;
 --color-secondary: #64748b;

 /* Semantic colors */
 --color-success: #22c55e;
 --color-warning: #f59e0b;
 --color-error: #ef4444;

 /* Neutral palette */
 --color-text-primary: #1e293b;
 --color-text-secondary: #64748b;
 --color-background: #ffffff;
 --color-border: #e2e8f0;
}

A well-structured color system typically includes multiple layers of abstraction. Brand colors represent your core identity and should be used sparingly for maximum impact. Semantic colors communicate state and meaning--success for positive actions, warning for caution, error for problems. Neutral colors handle the majority of UI surface area, creating visual hierarchy through careful contrast management.

Our web development services often implement comprehensive color systems that integrate with component libraries, ensuring consistency across complex applications.

Spacing and Layout Macros

Spacing macros ensure visual rhythm and reduce inconsistent padding and margin values:

:root {
 /* Spacing scale */
 --space-1: 4px;
 --space-2: 8px;
 --space-3: 12px;
 --space-4: 16px;
 --space-5: 24px;
 --space-6: 32px;
 --space-8: 48px;
 --space-10: 64px;

 /* Container widths */
 --container-sm: 640px;
 --container-md: 768px;
 --container-lg: 1024px;
 --container-xl: 1280px;
}

Consistent spacing creates visual harmony across your interface and makes design decisions more systematic. A linear or geometric progression--such as 4px, 8px, 16px, 24px, 32px--provides predictable increments that work well for both padding and margin applications. Container width tokens ensure responsive breakpoints align with your design intent, preventing content from stretching too wide on large screens.

When building responsive layouts, these spacing macros integrate seamlessly with CSS Grid and Flexbox, enabling consistent component sizing that adapts across viewport sizes. Many teams find that implementing a spacing scale reduces design review friction and speeds up implementation. Implementing these patterns as part of a comprehensive web development strategy ensures cohesive user experiences across all touchpoints.

Typography Macros

Typography macros create consistent text rendering across applications:

:root {
 /* Type scale */
 --text-xs: 12px;
 --text-sm: 14px;
 --text-base: 16px;
 --text-lg: 18px;
 --text-xl: 20px;
 --text-2xl: 24px;
 --text-3xl: 30px;
 --text-4xl: 36px;

 /* Line heights */
 --leading-tight: 1.25;
 --leading-normal: 1.5;
 --leading-relaxed: 1.75;

 /* Font weights */
 --font-normal: 400;
 --font-medium: 500;
 --font-semibold: 600;
 --font-bold: 700;
}

A thoughtfully constructed type scale ensures readability and visual hierarchy throughout your application. The type scale often follows a mathematical ratio--such as 1.25 (major third) or 1.333 (perfect fourth)--creating harmonious size relationships between headings and body text. Line height tokens address the critical balance between readability and visual density, with tighter leading for headings and more generous leading for body text.

Design System Implementation

Token-Based Architecture

Design tokens represent the smallest pieces of a design system--colors, typography, spacing--and CSS macros serve as the implementation layer:

:root {
 /* Foundation tokens (primitive) */
 --blue-50: #eff6ff;
 --blue-100: #dbeafe;
 --blue-500: #3b82f6;
 --blue-600: #2563eb;
 --blue-700: #1d4ed8;

 /* Semantic tokens (component-agnostic) */
 --color-action-primary: var(--blue-600);
 --color-action-primary-hover: var(--blue-700);
 --color-action-primary-text: #ffffff;

 /* Component tokens */
 --button-padding-y: var(--space-2);
 --button-padding-x: var(--space-4);
 --button-border-radius: var(--radius-md);
}

Component-Level Scoping

Defining macros at the component level prevents conflicts and improves encapsulation:

.card {
 --card-background: var(--color-surface, #ffffff);
 --card-border: var(--color-border, #e2e8f0);
 --card-radius: var(--radius-lg);
 --card-shadow: var(--shadow-md);

 background: var(--card-background);
 border: 1px solid var(--card-border);
 border-radius: var(--card-radius);
 box-shadow: var(--card-shadow);
}

The three-tier token architecture--foundation, semantic, and component--creates a maintainable foundation for large-scale applications. Foundation tokens capture raw values, semantic tokens map those to meaning, and component tokens provide specific configurations. This layered approach allows global brand updates without touching individual components.

Component-level scoping through local custom properties creates encapsulated styling contexts. Components define their own defaults while still inheriting from global tokens when appropriate, preventing naming conflicts and enabling truly modular design systems that can be shared across projects.

Theming and Dark Mode

CSS macros excel at theming, particularly for dark mode support:

:root {
 --background-primary: #ffffff;
 --text-primary: #1e293b;
 --text-secondary: #64748b;
}

@media (prefers-color-scheme: dark) {
 :root {
 --background-primary: #0f172a;
 --text-primary: #f1f5f9;
 --text-secondary: #94a3b8;
 }
}

/* Manual toggle using data attribute */
[data-theme="dark"] {
 --background-primary: #0f172a;
 --text-primary: #f1f5f9;
}

Dynamic Theme Switching with JavaScript

const setTheme = (theme) => {
 document.documentElement.setAttribute('data-theme', theme);
 document.documentElement.style.setProperty('--color-primary', newColor);
};

const primaryColor = getComputedStyle(document.documentElement)
 .getPropertyValue('--color-primary').trim();

Implementing dark mode through CSS custom properties provides an elegant solution that respects user system preferences while allowing manual overrides. The prefers-color-scheme media query automatically adapts to operating system settings, while data attributes enable user-controlled theme switching. This dual approach accommodates both user preference and contextual needs, such as automatically darkening in low-light environments.

Advanced Patterns

Conditional Logic with Macros

CSS macros enable conditional styling without duplicate declarations:

.button {
 --button-bg: var(--color-primary);
 --button-text: #ffffff;
 background: var(--button-bg);
 color: var(--button-text);
}

.button:hover {
 --button-bg: var(--color-primary-hover);
}

.button:disabled {
 --button-bg: var(--color-text-secondary);
 opacity: 0.5;
}

Computed Values with calc()

:root {
 --base-size: 16px;
 --scale-ratio: 1.25;

 --text-xs: calc(var(--base-size) / var(--scale-ratio));
 --text-base: var(--base-size);
 --text-lg: calc(var(--base-size) * var(--scale-ratio));
}

Responsive Macros

:root {
 --container-padding: var(--space-4);
}

@media (min-width: 768px) {
 :root {
 --container-padding: var(--space-6);
 }
}

The conditional logic pattern using local custom properties significantly reduces code duplication in interactive components. By redefining --button-bg within pseudo-classes, you create a single rendering path that adapts based on state. This technique scales elegantly across complex components with multiple interactive states.

Combining calc() with custom properties enables mathematical relationships between design tokens, maintaining proportional consistency as base values change. Responsive value changes at the root level propagate throughout the application, ensuring consistent scaling across all components.

Best Practices

Naming Conventions

Effective macro naming ensures maintainability:

/* Avoid: implementation-specific names */
--blue-600: #2563eb;
--margin-16: 16px;

/* Prefer: semantic or functional names */
--color-action-primary: #2563eb;
--spacing-touch-target: 44px;

/* Use prefixes for categories */
--color-* for colors
--spacing-* for spacing
--font-* for typography
--radius-* for border radius
--shadow-* for shadows
--transition-* for animations

Organization Strategies

/* design-tokens.css */
:root {
 --color-brand: ...;
 --font-family: ...;
 --spacing-unit: ...;
}

/* component.css */
.component {
 color: var(--color-text-primary);
 padding: var(--spacing-4);
}

Performance Considerations

CSS macros have minimal performance overhead when used correctly:

  • Custom properties trigger style recalculation only for affected elements
  • Use will-change sparingly alongside macros for complex animations
  • Avoid over-nesting; shallow inheritance chains perform better

Following consistent naming conventions prevents ambiguity as your design system grows. Semantic names like --color-action-primary convey purpose rather than appearance, making updates easier when design requirements change prefixes. Category create predictable naming patterns that developers can navigate intuitively.

Organizing tokens into dedicated stylesheets--separate from component styles--creates clear architectural boundaries. Design tokens become a shared dependency that multiple components import, establishing a single source of truth. This approach simplifies maintenance and ensures all components reference consistent values. Our SEO services team often partners with development teams to ensure technical implementations like these contribute to overall site performance and discoverability.

JavaScript Integration

Getting and Setting Values

// Get value
const value = getComputedStyle(element).getPropertyValue('--variable-name');

// Set value
element.style.setProperty('--variable-name', 'new-value');

// Set on root
document.documentElement.style.setProperty('--color-primary', '#ff0000');

CSS Custom Properties API (@property)

@property --progress {
 syntax: '<percentage>';
 inherits: false;
 initial-value: 0%;
}

.progress-bar {
 width: var(--progress);
}

The @property at-rule allows you to define type-safe custom properties with explicit syntax, inheritance behavior, and initial values. This enables CSS animations on custom properties and provides developer tools with type information for better debugging. Browser devtools can now display custom property values with their proper types, improving the development experience.

JavaScript integration with CSS custom properties bridges the gap between dynamic application state and styling. Theme selection, user preferences, and contextual adaptations can all modify custom property values at runtime, enabling sophisticated personalization without component-level styling logic.

Conclusion

CSS custom properties have evolved from simple variables to powerful theming and design system tools. By establishing consistent macro patterns for colors, spacing, typography, and component-specific values, teams can maintain scalable, maintainable interfaces while enabling dynamic theming and responsive design.

The macros discussed here represent the most commonly used patterns in modern UI/UX development, forming the foundation of countless design systems and component libraries. Start implementing these patterns in your projects to achieve consistency, flexibility, and efficiency in your styling architecture.

When your team adopts these patterns, you'll find that design decisions become more intentional and easier to maintain. The initial investment in creating a comprehensive macro system pays dividends through reduced inconsistency, faster prototyping, and smoother design evolution. Partnering with experienced web development professionals can accelerate your design system implementation and ensure best practices are followed from the start.


Sources:

  1. CSS-Tricks: CSS Custom Properties Guide - Comprehensive guide covering syntax, cascading, JavaScript integration, and advanced patterns for CSS variables

  2. MDN Web Docs: Using CSS Custom Properties - Official documentation on declaring custom properties, inheritance, fallback values, and JavaScript usage

  3. Gorilla Logic: Building Design Systems with CSS Variables - Focus on using CSS variables for design systems, theming, and component libraries

Ready to Build Consistent, Scalable Interfaces?

Our UI/UX team specializes in design systems, component libraries, and consistent user experiences that drive engagement.