API Essentials

Composables

Styleframe composables are reusable functions that provide design system components like variables, selectors, and utilities. They enable modular, consistent styling patterns across your application.

Overview

Composables in Styleframe are reusable functions that take a styleframe instance as their first parameter and provide pre-configured variables, selectors, utilities, or other styling patterns. They serve as the building blocks of your design system, enabling you to create consistent, modular, and maintainable styling solutions.

Why use composables?

Composables help you:

  • Organize your design system: Group related styling logic into focused, reusable functions.
  • Ensure consistency: Define design tokens and patterns once, use them everywhere.
  • Improve maintainability: Centralize styling logic in composable functions that can be easily updated and tested.
  • Scale your design system: Build complex design systems from simple, composable building blocks.

Defining Composables

Variable Composables

Variable composables are functions that provide reusable design tokens for your design system that:

  • Typically follow the use<Context>Variables naming pattern (e.g. useColorVariables).
  • Take a styleframe instance (s: Styleframe) as their first parameter.
  • Define variables using default: true to ensure they are only defined once, even if the composable is called multiple times.
  • Return the defined variables to be used in selectors or utilities.
import { styleframe } from 'styleframe';
import type { Styleframe } from 'styleframe';

export function useSpacingVariables(s: Styleframe) {
    const { variable } = s;

    const spacing = variable('spacing', '1rem', { default: true });
    
    const spacingXs = variable('spacing--xs', 
        css`calc(${ref(spacing)} * 0.25)`, { default: true });
    const spacingSm = variable('spacing--sm', 
        css`calc(${ref(spacing)} * 0.5)`, { default: true });
    const spacingMd = variable('spacing--md', 
        ref(spacing), { default: true });
    const spacingLg = variable('spacing--lg', 
        css`calc(${ref(spacing)} * 1.5)`, { default: true });
    const spacingXl = variable('spacing--xl', 
        css`calc(${ref(spacing)} * 2)`, { default: true });

    return {
        spacing,
        spacingXs,
        spacingSm,
        spacingMd,
        spacingLg,
        spacingXl,
    };
}

Selector Composables

Selector composables are functions that provide reusable component styles for your design system that:

  • Typically follow the use<Context>Selectors naming pattern (e.g. useButtonSelectors, useCardSelectors).
  • Take a styleframe instance (s: Styleframe) as their first parameter.
  • Import design tokens from variable composables using their returned references.
import { styleframe } from 'styleframe';
import type { Styleframe } from 'styleframe';

export function useCardSelectors(s: Styleframe) {
    const { selector, ref } = s;

    const { colorNeutral100, colorNeutral500 } = useColorVariables(s);
    const { spacingMd, spacingLg } = useSpacingVariables(s);
    
    const card = selector('.card', {
        backgroundColor: ref(colorNeutral100),
        borderRadius: '0.5rem',
        padding: ref(spacingLg),
        boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)',
        
        '.card-title': {
            fontSize: '1.25rem',
            fontWeight: '600',
            marginBottom: ref(spacingMd),
        },
        
        '.card-content': {
            color: ref(colorNeutral500),
            lineHeight: '1.6',
        },
    });
}

Utility Composables

Utility composables provide atomic CSS classes for your design system. They typically follow the use<Context>Utilities naming pattern:

import { styleframe, type Styleframe } from 'styleframe';

export function useSpacingUtilities(s: Styleframe) {
    const { utility, ref } = s;

    const { spacingXs, spacingSm, spacingMd, spacingLg, spacingXl } = useSpacingVariables(s);
    
    const spacingMap = {
        'xs': ref(spacingXs),
        'sm': ref(spacingSm),
        'md': ref(spacingMd),
        'lg': ref(spacingLg),
        'xl': ref(spacingXl),
    };
    
    const createPaddingUtility = utility('padding', (value) => ({
        padding: value,
    }));
    
    const createMarginUtility = utility('margin', (value) => ({
        margin: value,
    }));
    
    createPaddingUtility(spacingMap);
    createMarginUtility(spacingMap);
    
    return {
        createPaddingUtility,
        createMarginUtility,
    };
}

Recipe Composables

Recipe composables are functions that encapsulate complex styling patterns or component styles. They can combine variables, selectors, and utilities into a cohesive design system component.

  • They typically follow the use<Context>Recipe naming pattern.
  • They take a styleframe instance (s: Styleframe) as their first parameter.
import { styleframe } from 'styleframe';
import type { Styleframe } from 'styleframe';

export function useButtonRecipe(s: Styleframe) {
    const { recipe } = s;
    
    recipe('button', {
        borderWidth: 'thin',
        borderStyle: 'solid',
    }, {
        color: {
            primary: {
                background: 'primary',
                color: 'white',
                borderColor: 'primary',
            },
            secondary: {
                background: 'secondary',
                color: 'white',
                borderColor: 'secondary',
            }
        },
        size: {
            sm: {
                padding: 'sm',
                fontSize: 'sm',
            },
            md: {
                padding: 'md',
                fontSize: 'md',
            },
            lg: {
                padding: 'lg',
                fontSize: 'lg',
            }
        }
    });
}

Theme Composables

Combine themes with composables for organized, reusable theming patterns:

import type { Styleframe } from 'styleframe';

export function useColorVariables(s: Styleframe) {
    const colorPrimary = variable('color--primary', 
        '#4850ec', { default: true });
    const colorSecondary = variable('color--secondary', 
        '#7297f4', { default: true });
    
    return { colorPrimary, colorSecondary };
}

export function useVibrantBrandTheme(s: Styleframe) {
    const { theme } = s;
    const { colorPrimary, colorSecondary } = useColorVariables(s);
    
    theme('brand-vibrant', (ctx) => {
        const { variable } = ctx;
        
        variable(colorPrimary, '#ec4899');
        variable(colorSecondary, '#f472b6');
    });
}

export function useMinimalBrandTheme(s: Styleframe) {
    const { theme } = s;
    const { colorPrimary, colorSecondary } = useColorVariables(s);
    
    theme('brand-minimal', (ctx) => {
        const { variable } = ctx;
        
        variable(colorPrimary, '#374151');
        variable(colorSecondary, '#6b7280');
    });
}

Using Composables

To use composables in your project, simply call them with your styleframe instance:

import { styleframe } from 'styleframe';
import { useColorVariables, useSpacingVariables } from './design-system/variables';
import { useButtonSelectors, useCardSelectors } from './design-system/selectors';
import { useSpacingUtilities } from './design-system/utilities';
import { useButtonRecipe } from './design-system/recipes';

const s = styleframe();

// Variable composables
useColorVariables(s);
useSpacingVariables(s);

// Selector composables
useButtonSelectors(s);
useCardSelectors(s);

// Utility composables
useSpacingUtilities(s);

// Recipe composables
useButtonRecipe(s);

export default s;
Pro tip: Call variable composables first, then selectors and utilities that depend on those variables. This ensures all dependencies are available when needed.

Examples

Composable Composition

Composables can call other composables to build complex design systems:

import { type Styleframe } from 'styleframe';

export function useDesignSystem(s: Styleframe) {
    // Foundation variables
    useColorVariables(s);
    useSpacingVariables(s);
    useTypographyVariables(s);

    // Component selectors
    useButtonSelectors(s);
    useCardSelectors(s);
    useFormSelectors(s);
    
    // Utility classes
    useSpacingUtilities(s);
    useLayoutUtilities(s);
    useColorUtilities(s);
}

Configurable Composables

Create composables that accept configuration options:

import { type Styleframe } from 'styleframe';

type DesignSystemColor = 'primary' | 'secondary' | 'accent';
type ColorConfig = Partial<Record<DesignSystemColor, string>>;

export function useColorVariables(s: Styleframe, config: ColorConfig = {}) {
    const { variable } = s;

    const colorPrimary = variable('color--primary', 
        config.primary ?? '#006cff', { default: true });
    const colorSecondary = variable('color--secondary', 
        config.secondary ?? '#ff6b6b', { default: true });
    const colorAccent = variable('color--accent', 
        config.accent ?? '#4ecdc4', { default: true });
    
    return { colorPrimary, colorSecondary, colorAccent };
}

Best Practices

  • Use the default option for variables in composables to prevent accidental overwrites when the composable is called multiple times.
  • Follow naming conventions: Use use<Context>Variables, use<Context>Selectors, use<Context>Utilities patterns.
  • Return references from variable composables so other composables can reuse them.
  • Keep composables focused on a single responsibility (colors, spacing, buttons, etc.).
  • Document your composables with clear descriptions of what they provide and any dependencies.

FAQ