Imports
Overview
Styleframe provides two patterns for importing styles into your application:
- Global imports: A centralized
styleframe.config.tsfile that generates styles for your entire application - Per-file imports: Individual
*.styleframe.tsfiles 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
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;
// Import all generated CSS
import 'virtual:styleframe.css';
// Import recipe functions (if using recipes)
import { button } from 'virtual:styleframe';
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
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;
// Import compiled CSS (note the .css extension)
import './button.styleframe.css';
// Import recipe functions (note: no extension)
import { button } from './button.styleframe';
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
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
| Aspect | Global Imports | Per-File Imports |
|---|---|---|
| Configuration | Single styleframe.config.ts | Multiple .styleframe.ts files |
| Import path | virtual:styleframe.css | ./component.styleframe.css |
| Scope | Application-wide | Component-level |
| Code splitting | Single bundle | Automatic per-component |
| Token sharing | Built-in | Requires shared modules |
| Best for | Design systems, shared tokens | Component 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:
// Global design tokens and base utilities
import 'virtual:styleframe.css';
// 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>
);
}
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';
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:
// 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',
};
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>
);
}
<script setup lang="ts">
import { computed } from 'vue';
import './button.styleframe.css';
import { button } from './button.styleframe';
const props = defineProps<{
variant?: 'primary' | 'secondary';
}>();
const className = computed(() => button({ variant: props.variant }));
</script>
<template>
<button :class="className">
<slot />
</button>
</template>
<script lang="ts">
import './button.styleframe.css';
import { button } from './button.styleframe';
export let variant: 'primary' | 'secondary' = 'primary';
$: className = button({ variant });
</script>
<button class={className}>
<slot />
</button>
FAQ
@styleframe/plugin automatically handles .styleframe.ts files. Just create your file and import it with either .css (for styles) or without an extension (for recipe functions).Both patterns support HMR in development. When you modify any Styleframe file:
- CSS changes are hot-reloaded without a full page refresh
- TypeScript changes (recipes) trigger component re-renders
This provides instant feedback on style changes regardless of which pattern you use.
Global imports bundle all styles together, which is efficient for smaller applications but includes unused styles.
Per-file imports enable automatic code splitting—only the styles for imported components are included. This can reduce initial bundle size for larger applications.
Both patterns use the tree-shakeable @styleframe/runtime package for recipe functions.
Yes. Both patterns work with any build tool supported by @styleframe/plugin:
- Vite
- Nuxt
- webpack
- Rollup
- esbuild
The plugin handles virtual module resolution and compilation for all supported bundlers.
Composables
Composables are design patterns for building reusable, shareable design system building blocks. They provide idempotency, configurability, and composition for variables, selectors, utilities, and recipes.
Merging
Combine multiple Styleframe instances into a single unified configuration. Perfect for composing design systems, sharing configurations across projects, or building modular styling architectures.