Design Tokens

Spacing

Create and manage spacing design tokens with CSS variables for consistent layout spacing, padding, margins, and gaps across your application.

Overview

The spacing composables help you create consistent spacing systems with minimal code. They generate spacing variables that can be easily referenced throughout your application, enabling flexible theming and consistent spatial relationships between elements.

Why use spacing composables?

Spacing composables help you:

  • Centralize spacing values: Define all your spacing units in one place for easy management.
  • Enable flexible theming: Override spacing values to instantly update layouts across your application.
  • Maintain consistency: Use semantic names to ensure consistent spacing usage throughout your design system.
  • Create harmonious scales: Integrate with modular scales to generate mathematically proportional spacing systems.

useSpacing

The useSpacing() function creates a set of spacing variables from a simple object of spacing value definitions.

import { styleframe } from 'styleframe';
import { useSpacing } from '@styleframe/theme';

const s = styleframe();

const {
    spacing,
    spacingXs,
    spacingSm,
    spacingMd,
    spacingLg,
    spacingXl,
} = useSpacing(s, {
    default: '1rem',
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
} as const);

export default s;

Each key in the object becomes a spacing variable with the prefix spacing, and the export name is automatically converted to camelCase (e.g., defaultspacing, xsspacingXs, mdspacingMd).

Pro tip: Use the default key for your base spacing unit. It will create a variable named --spacing without any suffix, making it the natural choice for standard spacing throughout your application.

Integration with useMultiplier

The real power of useSpacing comes when combined with useMultiplier() and modular scales. This allows you to create mathematically harmonious spacing systems that maintain consistent proportions.

Create a spacing scale based on a modular scale ratio:

import { styleframe } from 'styleframe';
import { useScale, useScalePowers, useMultiplier, useSpacing } from '@styleframe/theme';

const s = styleframe();

// Use the Major Second scale (1.125) for subtle spacing differences
const { scale } = useScale(s);

// Define base spacing unit
const { spacing } = useSpacing(s, { default: '1rem' });

// Create spacing scale powers
const scalePowers = useScalePowers(s, scale);

// Generate spacing variables automatically
const {
    spacingXs,
    spacingSm,
    spacingMd,
    spacingLg,
    spacingXl,
    spacing2xl,
} = useMultiplier(s, spacing, {
    xs: scalePowers[-2],   // ~0.79rem
    sm: scalePowers[-1],   // ~0.89rem
    md: scalePowers[0],    // 1rem (base)
    lg: scalePowers[1],    // ~1.13rem
    xl: scalePowers[2],    // ~1.27rem
    '2xl': scalePowers[3], // ~1.42rem
});

export default s;

The useMultiplier() function multiplies your base spacing by the scale powers, creating a harmonious progression of spacing values. This ensures consistent proportional relationships throughout your design system.

Read more about design scales and take advantage of the flexibility they offer.

Pro tip: Using useMultiplier() with scales means you can change your entire spacing system's proportions by simply adjusting the scale ratio. Try different scales like Perfect Fourth (1.333) for more dramatic spacing differences or Major Second (1.125) for subtle variations.

Using Spacing Variables

Once created, spacing variables can be used anywhere in your styles:

import { styleframe } from 'styleframe';
import { useSpacing } from '@styleframe/theme';

const s = styleframe();
const { ref, selector, css } = s;

const { spacing, spacingSm, spacingLg } = useSpacing(s, {
    default: '1rem',
    sm: '0.5rem',
    lg: '1.5rem',
} as const);

selector('.card', {
    padding: ref(spacing),
    marginBottom: ref(spacingLg),
});

selector('.button', {
    padding: css`${ref(spacingSm)} ${ref(spacing)}`,
});

selector('.container', {
    gap: ref(spacing),
});

export default s;

Examples

Semantic Spacing Names

Use semantic names to make spacing intent clear:

import { styleframe } from 'styleframe';
import { useSpacing } from '@styleframe/theme';

const s = styleframe();

const {
    spacing,
    spacingTight,
    spacingComfortable,
    spacingLoose,
} = useSpacing(s, {
    default: '1rem',
    tight: '0.5rem',
    comfortable: '1.5rem',
    loose: '2rem',
} as const);

export default s;

Complete Spacing System

Here's a comprehensive example showing a full spacing system with multiple scales:

import { styleframe } from 'styleframe';
import { useScale, useScalePowers, useMultiplier, useSpacing } from '@styleframe/theme';

const s = styleframe();

// Use Minor Third scale (1.2) for balanced spacing
const { scale } = useScale(s, 'minor-third');

// Define base spacing units
const { spacing, spacingGutter, spacingSection } = useSpacing(s, {
    default: '1rem',        // Base unit for general spacing
    gutter: '1.5rem',       // For grid gutters
    section: '4rem',        // For section spacing
} as const);

// Create scale powers for the base spacing
const scalePowers = useScalePowers(s, scale, [-3, -2, -1, 0, 1, 2, 3, 4]);

// Generate base spacing scale
const {
    spacing3xs,
    spacing2xs,
    spacingXs,
    spacingSm,
    spacingMd,
    spacingLg,
    spacingXl,
    spacing2xl,
} = useMultiplier(s, spacing, {
    '3xs': scalePowers[-3],  // ~0.58rem
    '2xs': scalePowers[-2],  // ~0.69rem
    xs: scalePowers[-1],     // ~0.83rem
    sm: scalePowers[0],      // 1rem (base)
    md: scalePowers[1],      // ~1.2rem
    lg: scalePowers[2],      // ~1.44rem
    xl: scalePowers[3],      // ~1.73rem
    '2xl': scalePowers[4],   // ~2.07rem
});

export default s;

Best Practices

  • Start with a base unit: Use 1rem (16px) as your base spacing unit and build everything else from it.
  • Use the default key: This creates a clean --spacing variable that's perfect for general-purpose spacing.
  • Integrate with scales: Combine useSpacing() with useMultiplier() for mathematically harmonious spacing systems.
  • Limit your scale: Too many spacing options can lead to inconsistency. Aim for 5-8 spacing values.
  • Consider both directions: Think about vertical rhythm (margins, padding-block) and horizontal spacing (gaps, padding-inline).
  • Test with real content: Ensure your spacing works across different component sizes and content densities.
Good to know: We use as const to ensure the object is treated as a constant type. This helps TypeScript infer the return type of the composables and provides better type safety and autocomplete support.

FAQ