Styleframe is a type-safe, composable CSS-in-TypeScript framework for building design systems. It compiles design tokens, utility classes, component recipes, and themes into CSS, TypeScript runtime code, and type declarations.
styleframe.config.ts → Global Styleframe instance (single source of truth)
*.styleframe.ts files → Extend the global instance (tokens, recipes, utilities)
@styleframe/plugin → Build tool integration (Vite, Webpack, Nuxt, Astro, etc.)
@styleframe/transpiler → Compiles AST to CSS + TypeScript + .d.ts
@styleframe/runtime → Lightweight browser runtime for recipe class generation (~1.4KB)
virtual:styleframe → Auto-generated TypeScript with recipe functions and selectors
virtual:styleframe.css → Auto-generated CSS with all styles
All *.styleframe.ts extension files share one Styleframe instance from styleframe.config.ts. Extension files import the instance from virtual:styleframe, add tokens/recipes/utilities, and export it as default. Application code imports compiled recipe functions and selectors from virtual:styleframe.
engine/
├── core/ # @styleframe/core — Token AST, factory methods (variable, ref, selector, utility, modifier, recipe, theme, css, keyframes, media, atRule, merge)
├── loader/ # @styleframe/loader — Runtime config loading, module loading, HMR, build
├── runtime/ # @styleframe/runtime — Browser-side recipe class name generation
├── scanner/ # @styleframe/scanner — Content scanning for utility class extraction
├── styleframe/ # styleframe — Barrel package re-exporting all engine APIs
└── transpiler/ # @styleframe/transpiler — AST-to-CSS/TS/DTS code generation
theme/ # @styleframe/theme — Design token composables, modifiers, utilities, recipes, presets
tooling/
├── cli/ # @styleframe/cli — CLI for init, build, and Figma sync
├── figma/ # @styleframe/figma — Bidirectional Figma variable sync via DTCG format
└── plugin/ # @styleframe/plugin — Unplugin build integration (Vite, Webpack, Nuxt, Astro, Rollup, Rspack, esbuild, Farm)
config/ # @styleframe/config-typescript, @styleframe/config-vite — Shared build configs
apps/
├── docs/ # Documentation site (Nuxt Content + Markdown)
├── app/ # Customer dashboard (Nuxt 3 + Supabase)
├── shared/ # Shared Nuxt layer for doc apps
└── storybook/ # Storybook 10 + Vue 3 design system showcase
testing/
└── integration/ # Playwright end-to-end tests across Chromium, Firefox, WebKit
Import rule: Import from 'styleframe' (the barrel package), not @styleframe/* sub-packages. Use 'styleframe/plugin/vite' for plugins, 'styleframe/loader' for loader, 'styleframe/transpiler' for transpiler.
ALWAYS create a Styleframe instance and destructure the methods:
import { styleframe } from 'styleframe';
const s = styleframe();
const { variable, ref, selector, utility, modifier, recipe, theme, atRule, keyframes, media, css } = s;
export default s;- Use dot notation:
color.primarybecomes CSS--color--primary - ALWAYS use
ref()to reference variables in declarations - For string references, use the
@prefix syntax:"@spacing.md","@color.primary","@font-size.sm" - Use
{ default: true }for variables in reusable composables - NEVER hardcode values that should be tokens
- Generated format:
_property-name:value(e.g.,_margin:md) - Modifier prefix format:
_modifier:property:value(e.g.,_hover:background:primary) - Boolean true omits value:
_display - Multiple modifiers chain:
_dark:hover:background:primary
- Default:
[data-theme="name"] - Apply themes via
data-themeattribute on HTML elements
styleframe.config.ts— Global config at project root. MUST export instance as default.*.styleframe.ts— Extension files that extend the global instance. Import fromvirtual:styleframe..styleframe/— Auto-generated type declarations directory. Add to.gitignore.
Creates a CSS custom property. Dot notation in name becomes -- in CSS.
const colorPrimary = variable('color.primary', '#006cff');
const spacing = variable('spacing', '1rem', { default: true }); // For composables
const colorAccent = variable('color.accent', ref(colorPrimary)); // Reference another variableCreates a var(--name) reference. Accepts a Variable token or dot-notation string.
backgroundColor: ref(colorPrimary)
color: ref('color.text', '#000') // String ref with fallback
color: "@color.primary" // @-prefixed string ref (shorthand)Creates a CSS selector rule. Supports nested selectors (&:hover), nested @media, and callback form.
selector('.button', {
padding: ref(spacing),
'&:hover': { opacity: 0.9 },
'@media (min-width: 768px)': { padding: '2rem' },
});
// Callback form for complex nesting
selector('.container', ({ selector, media }) => {
selector('.inner', { padding: '1rem' });
return { display: 'flex' };
});Creates a utility class generator. Returns a creator function that MUST be called.
const createPadding = utility('padding', ({ value }) => ({ padding: value }));
// Object syntax (explicit keys)
createPadding({ sm: ref(spacingSm), md: ref(spacingMd) });
// Array syntax (auto-generated keys)
createPadding([ref(spacingSm), '@spacing.md', '1rem']);
// With modifiers
createPadding({ sm: ref(spacingSm) }, [hover, focus]);Critical: Always invoke the creator function. Defining a utility without calling it produces no CSS.
Creates a reusable modifier that wraps utility declarations.
const hover = modifier('hover', ({ declarations }) => ({
'&:hover': declarations,
}));
// Multi-key modifier (for breakpoints)
const responsive = modifier(['sm', 'md', 'lg'], ({ key, declarations }) => ({
[`@media (min-width: ${breakpoints[key]}px)`]: declarations,
}));Creates a component variant system. At runtime, recipe({ color: 'primary' }) returns a class string.
recipe({
name: 'button',
base: { borderWidth: ref(borderWidthThin), borderStyle: 'solid' },
variants: {
color: {
primary: { background: ref(colorPrimary), color: ref(colorWhite) },
secondary: { background: ref(colorSecondary) },
},
size: {
sm: { padding: ref(spacingSm) },
md: { padding: ref(spacingMd) },
},
},
defaultVariants: { color: 'primary', size: 'md' },
compoundVariants: [
{
match: { color: 'primary', disabled: false },
css: { hover: { background: '@color.primary-dark' } },
},
],
});Creates a theme variant scoped to [data-theme="name"].
theme('dark', (ctx) => {
ctx.variable(colorBackground, '#18181b');
ctx.variable(colorText, '#ffffff');
ctx.selector('.card', { boxShadow: '0 4px 6px rgba(0,0,0,0.3)' });
});HTML: <div data-theme="dark">Dark themed content</div>
Interpolates token values into CSS strings for complex expressions.
padding: css`${ref(spacingSm)} ${ref(spacingMd)}`
width: css`calc(100% - ${ref(sidebarWidth)})`
background: css`linear-gradient(135deg, ${ref(colorPrimary)} 0%, ${ref(colorSecondary)} 100%)`Defines a CSS @keyframes animation. Returns an AtRule token with .rule for the animation name.
const fadeIn = keyframes('fade-in', {
'0%': { opacity: 0, transform: 'translateY(10px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
});
selector('.animated', {
animation: `${fadeIn.rule} 0.3s ease-out`,
});Creates @media rules. Also usable inline in selectors via object keys.
media('(min-width: 768px)', ({ selector }) => {
selector('.container', { maxWidth: '750px' });
});Creates arbitrary CSS at-rules (@supports, @font-face, @layer, @container, @property).
atRule('supports', '(display: grid)', ({ selector }) => {
selector('.grid', { display: 'grid' });
});Combines multiple Styleframe instances. Variables override; arrays (utilities, modifiers, recipes) concatenate; same-name themes merge.
import { merge } from 'styleframe';
const s = merge(base, components, themes); // General → specificimport { styleframe } from 'styleframe';
import { useDesignTokensPreset, useModifiersPreset, useUtilitiesPreset } from '@styleframe/theme';
const s = styleframe();
const tokens = useDesignTokensPreset(s); // All design token variables
const modifiers = useModifiersPreset(s); // All pseudo-state/element/media modifiers
useUtilitiesPreset(s); // All utility classes
export default s;| Category | Composables |
|---|---|
| Colors | useColor, useColorLightness, useColorShade, useColorTint |
| Scales | useScale, useScalePowers |
| Spacing | useSpacing, useMultiplier |
| Typography | useFontFamily, useFontSize, useFontWeight, useFontStyle, useLineHeight, useLetterSpacing |
| Borders/Effects | useBorderWidth, useBorderRadius, useBorderStyle, useBorderColor, useBoxShadow |
| Breakpoints | useBreakpoint |
| Easing | useEasing |
All composables take s (Styleframe instance) as first argument, accept optional custom values, and return typed token objects.
import { useColor, useSpacing, useMultiplier, useScale, useScalePowers } from '@styleframe/theme';
const { colorPrimary } = useColor(s, { primary: '#006cff' } as const);
const { spacing } = useSpacing(s, { default: '1rem' } as const);
const { scale } = useScale(s, { default: '@minor-third' });
const scalePowers = useScalePowers(s, scale);
const { spacingSm, spacingMd } = useMultiplier(s, spacing, {
sm: scalePowers[-1],
md: scalePowers[0],
});| Composable | Examples |
|---|---|
usePseudoStateModifiers |
hover, focus, focusWithin, focusVisible, active, visited |
usePseudoElementModifiers |
before, after, placeholder, selection, firstLetter |
useFormStateModifiers |
disabled, enabled, checked, required, valid, invalid |
useAriaStateModifiers |
ariaExpanded, ariaSelected, ariaDisabled, ariaPressed |
useMediaPreferenceModifiers |
dark, motionSafe, motionReduce, contrastMore, print |
useStructuralModifiers |
first, last, only, odd, even, empty |
useDirectionalModifiers |
rtl, ltr |
Typography, Backgrounds, Borders, Effects, Filters, Flexbox & Grid, Layout, Sizing, Interactivity, Transforms, Transitions, Accessibility, SVG, Tables.
import { useButtonRecipe, useBadgeRecipe } from '@styleframe/theme';
useButtonRecipe(s); // Colors: primary/secondary/success/info/warning/danger
// Variants: solid/outline/soft/subtle/ghost/link
// Sizes: xs/sm/md/lg/xl
useBadgeRecipe(s); // Colors: same, Variants: solid/outline/soft/subtle, Sizes: sameimport { useShorthandUtilitiesPreset } from '@styleframe/theme';
useShorthandUtilitiesPreset(s); // Tailwind-style: m, p, w, text, etc.Each bundler has a dedicated adapter. Plugins are default exports.
// vite.config.ts
import styleframe from 'styleframe/plugin/vite';
export default defineConfig({ plugins: [styleframe()] });
// nuxt.config.ts
export default defineNuxtConfig({ modules: ['styleframe/plugin/nuxt'] });
// astro.config.mjs
import styleframe from 'styleframe/plugin/astro';
export default { integrations: [styleframe()] };styleframe({
entry: './styleframe.config.ts', // Config file path
include: ['**/*.styleframe.ts'], // Extension file globs
loadOrder: 'alphabetical', // or 'depth-first'
dts: { enabled: true, outDir: '.styleframe' },
scanner: {
content: ['./src/**/*.{html,jsx,tsx,vue}'], // Enable utility class auto-detection
},
});| Import | Context | Returns |
|---|---|---|
virtual:styleframe |
From *.styleframe.ts |
Global instance factory for extending |
virtual:styleframe |
From app code | Compiled recipe functions and selector constants |
virtual:styleframe.css |
Any | All compiled CSS styles |
// button.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useButtonRecipe } from '@styleframe/theme';
const s = styleframe();
const { selector } = s;
export const button = useButtonRecipe(s);
selector('.button-grid', {
display: 'flex',
gap: '@spacing.md',
});
export default s;import { button } from 'virtual:styleframe';
import 'virtual:styleframe.css';
const classes = button({ color: 'primary', size: 'md' });
// => "button _border-width:thin _cursor:pointer _background:primary ..."| Type | Pattern | Example |
|---|---|---|
| Variables | use<Context>Variables() |
useColorVariables(s) |
| Selectors | use<Context>Selectors() |
useButtonSelectors(s) |
| Utilities | use<Context>Utilities() |
useSpacingUtilities(s) |
| Recipes | use<Context>Recipe() |
useButtonRecipe(s) |
All composable variables MUST use { default: true }:
export function useColorVariables(s: Styleframe) {
const { variable, ref } = s;
const colorPrimary = variable('color.primary', '#006cff', { default: true });
return { colorPrimary };
}styleframe init [cwd] # Scaffold project, install deps, configure build tool
styleframe build [entry] # Compile config to CSS/TS/DTS output
styleframe figma export # Export variables to DTCG JSON for Figma
styleframe figma import -i in.json # Generate Styleframe code from DTCG JSON- NEVER hardcode colors, spacing, or sizes — Use variables and
ref() - NEVER use
ref()without destructuring it from the instance - NEVER forget
{ default: true }in composable variables - NEVER define utilities without calling the creator function —
utility()returns a function - NEVER use arbitrary CSS values without
csstemplate literal for complex expressions - NEVER forget to export the Styleframe instance as default
- NEVER use appearance-based names — Use semantic names (
color.primarynotcolor.blue) - NEVER use named exports in index files — Use
export *for all re-exports - NEVER import from
virtual:styleframein non-*.styleframe.tsfiles expecting the global instance — only extension files get the extension face - NEVER import from
@styleframe/*sub-packages — Import from'styleframe'barrel package
Prefer LSP over Grep/Glob/Read for code navigation:
goToDefinition/goToImplementationto jump to sourcefindReferencesto see all usages across the codebaseworkspaceSymbolto find where something is defineddocumentSymbolto list all symbols in a filehoverfor type info without reading the fileincomingCalls/outgoingCallsfor call hierarchy
Before renaming or changing a function signature, use findReferences to find all call sites first. Use Grep/Glob only for text/pattern searches where LSP does not help. After writing or editing code, check LSP diagnostics before moving on.
Each package has its own AGENTS.md with detailed API reference, types, and conventions:
| Package | AGENTS.md Path |
|---|---|
| Core engine | engine/core/AGENTS.md |
| Barrel package | engine/styleframe/AGENTS.md |
| Loader | engine/loader/AGENTS.md |
| Transpiler | engine/transpiler/AGENTS.md |
| Runtime | engine/runtime/AGENTS.md |
| Scanner | engine/scanner/AGENTS.md |
| Theme | theme/AGENTS.md |
| Build plugin | tooling/plugin/AGENTS.md |
| CLI | tooling/cli/AGENTS.md |
| Figma | tooling/figma/AGENTS.md |
| Integration tests | testing/integration/AGENTS.md |
| Storybook | apps/storybook/AGENTS.md |
| Documentation site | apps/docs/content/docs/AGENTS.md |
| Customer app | apps/app/AGENTS.md |
| Shared Nuxt layer | apps/shared/AGENTS.md |
| Build config | config/AGENTS.md |
.claude/styleframe-api.md— Complete API reference.claude/styleframe-patterns.md— Common patterns and examples.claude/styleframe-recipes.md— Recipe system guide.claude/styleframe-tokens.md— Design token composables