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

Avoid code duplication with shared modules

Each .styleframe.ts file is compiled independently. If multiple files import a shared module that registers variables or other styles, that code will be re-executed for each file, potentially causing duplicate CSS output.

To avoid this:

  • Define shared tokens in your global styleframe.config.ts instead
  • Keep .styleframe.ts files self-contained without shared registration functions
For example, if you create a useTokens() function that calls variable() and import it in multiple .styleframe.ts files, each file will register those variables separately.

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?recipe';

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?recipe';
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?recipe';

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