API Essentials

Imports

Learn the two ways to import Styleframe styles into your application—global imports for centralized design systems and per-file imports for component-scoped styling.

Overview

Styleframe provides two patterns for importing styles into your application:

  1. Global imports: A centralized styleframe.config.ts file that generates styles for your entire application
  2. Per-file imports: Individual *.styleframe.ts files co-located with your components

Both approaches use the same Styleframe API and can be combined in a single project. This guide helps you understand when to use each pattern and how they work together.

Global Imports

Global imports use a single styleframe.config.ts configuration file at your project root. All styles are compiled together and imported via virtual modules.

Setup

styleframe.config.ts
import { styleframe } from 'styleframe';

const s = styleframe();
const { variable, utility, recipe, ref } = s;

// Design tokens
const colorPrimary = variable('color.primary', '#3b82f6');
const colorSecondary = variable('color.secondary', '#64748b');
const spacingMd = variable('spacing.md', '1rem');

// Utilities
utility('background', ({ value }) => ({ backgroundColor: value }));
utility('padding', ({ value }) => ({ padding: value }));

// Recipes
recipe({
    name: 'button',
    base: { padding: ref(spacingMd) },
    variants: {
        color: {
            primary: { background: ref(colorPrimary) },
            secondary: { background: ref(colorSecondary) },
        },
    },
});

export default s;

When to use global imports

  • Building a centralized design system with shared tokens across your application
  • Defining global styles that apply everywhere
  • Working with a smaller application where code splitting isn't critical
  • Preferring a single source of truth for all styling decisions

Per-File Imports

Per-file imports use individual *.styleframe.ts files placed alongside your components. Each file is compiled independently and imported directly.

Setup

src/components/button.styleframe.ts
import { styleframe } from 'styleframe';

const s = styleframe();
const { variable, utility, recipe, ref } = s;

// Component-specific tokens
const buttonPrimary = variable('button.primary', '#3b82f6');
const buttonSecondary = variable('button.secondary', '#64748b');
const buttonPadding = variable('button.padding', '1rem');

// Component utilities
utility('background', ({ value }) => ({ backgroundColor: value }));
utility('padding', ({ value }) => ({ padding: value }));

// Component recipe
recipe({
    name: 'button',
    base: { padding: ref(buttonPadding) },
    variants: {
        color: {
            primary: { background: ref(buttonPrimary) },
            secondary: { background: ref(buttonSecondary) },
        },
    },
});

export default s;

When to use per-file imports

  • Building a component library with self-contained components
  • Working on a large application with independent feature modules
  • Preferring co-located styles next to components
  • Wanting automatic code splitting for styles
Shared modules are cached automaticallyThe Styleframe plugin uses a serialized loader with module caching, so shared dependencies (like a useTokens() helper) are only compiled once and reused across all .styleframe.ts files. This prevents duplicate CSS output and improves build performance.However, each .styleframe.ts file still creates its own styleframe() instance, so if your shared module registers variables on the passed instance, those variables will be registered once per file that calls it. For truly global tokens, consider defining them in your styleframe.config.ts instead.

Comparison

AspectGlobal ImportsPer-File Imports
ConfigurationSingle styleframe.config.tsMultiple .styleframe.ts files
Import pathvirtual:styleframe.css./component.styleframe.css
ScopeApplication-wideComponent-level
Code splittingSingle bundleAutomatic per-component
Token sharingBuilt-inRequires shared modules
Best forDesign systems, shared tokensComponent libraries, isolation

Combining Both Patterns

You can use both patterns in the same project. A common approach is to define shared tokens and utilities globally, while keeping component-specific recipes in per-file imports:

src/main.ts
// Global design tokens and base utilities
import 'virtual:styleframe.css';
src/components/Button.tsx
// Component-specific styles and recipes
import './button.styleframe.css';
import { button } from './button.styleframe';

export function Button({ variant, children }) {
    return (
        <button className={button({ variant })}>
            {children}
        </button>
    );
}
When combining both patterns, be mindful of potential naming conflicts between utilities or variables defined in different files.

Import Types

Both patterns support two types of imports:

CSS Import

Imports the compiled CSS containing variables, selectors, utilities, and recipe class definitions.

// Global
import 'virtual:styleframe.css';

// Per-file
import './component.styleframe.css';

TypeScript Import

Imports the compiled recipe functions for runtime variant selection. Only needed if you define recipes.

// Global
import { button, card } from 'virtual:styleframe';

// Per-file
import { button } from './button.styleframe';
If you're not using recipes, you only need the CSS import. The TypeScript import is specifically for recipe functions that handle runtime variant selection.

Sharing Tokens Between Files

When using per-file imports, you may want to share tokens across components. Extract shared values into a regular TypeScript file:

src/tokens.ts
// Shared design tokens (regular TS file, not a .styleframe.ts)
export const colors = {
    primary: '#3b82f6',
    secondary: '#64748b',
    white: '#ffffff',
};

export const spacing = {
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
};
src/components/button.styleframe.ts
import { styleframe } from 'styleframe';
import { colors, spacing } from '../tokens';

const s = styleframe();
const { variable } = s;

// Use shared tokens
const buttonPrimary = variable('button.primary', colors.primary);
const buttonPadding = variable('button.padding', spacing.md);

// ... rest of component styles
export default s;

Framework Usage Examples

import { useMemo } from 'react';
import './button.styleframe.css';
import { button } from './button.styleframe';

interface ButtonProps {
    variant?: 'primary' | 'secondary';
    children: React.ReactNode;
}

export function Button({ variant, children }: ButtonProps) {
    const className = useMemo(() => button({ variant }), [variant]);

    return (
        <button className={className}>
            {children}
        </button>
    );
}

FAQ