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
import './button.styleframe?css';
// Import recipe functions
import { button } from './button.styleframe?recipe';
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.tsinstead - Keep
.styleframe.tsfiles self-contained without shared registration functions
useTokens() function that calls variable() and import it in multiple .styleframe.ts files, each file will register those variables separately.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?recipe';
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?recipe';
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?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>
);
}
<script setup lang="ts">
import { computed } from 'vue';
import './button.styleframe?css';
import { button } from './button.styleframe?recipe';
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?recipe';
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 ?recipe (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.