Create a Design System in Under 15 Minutes
Building a design system from scratch can be daunting. Between defining color palettes, typography scales, spacing systems, and component styles, it often takes weeks to get everything right.
Introducing styleframe. In this guide, you'll build a complete, production-ready design system in under 15 minutes using styleframe's composable design tokens with fluid responsive typography.
Prerequisites
Before starting, make sure you have:
- Node.js 18+ installed
- A project with Styleframe and Styleframe Pro installed (Installation Guide)
- Basic understanding of CSS and TypeScript
Step 1: Foundation – Colors (2 minutes)
Every great design system starts with colors. Let's create a comprehensive color system with automatic variants using the OKLCH color space.
Define Your Base Colors
Open your styleframe.config.ts and define your brand colors:
import { styleframe } from 'styleframe';
import { useColor, useColorLightness, useColorShade, useColorTint } from '@styleframe/theme';
const s = styleframe();
// Define your brand colors
const { colorPrimary, colorSecondary, colorGray } = useColor(s, {
primary: '#0066ff',
secondary: '#7c3aed',
gray: '#64748b',
} as const);
// Define semantic colors
const { colorSuccess, colorWarning, colorDanger, colorInfo } = useColor(s, {
success: '#10b981',
warning: '#f59e0b',
danger: '#ef4444',
info: '#06b6d4',
} as const);
// Generate colors - Primary lightness variants
const {
colorPrimary50,
colorPrimary100,
colorPrimary200,
colorPrimary300,
colorPrimary400,
colorPrimary500,
colorPrimary600,
colorPrimary700,
colorPrimary800,
colorPrimary900,
colorPrimary950,
} = useColorLightness(s, colorPrimary);
// Generate colors - Gray lightness variants
const {
colorGray50,
colorGray100,
colorGray200,
colorGray300,
colorGray400,
colorGray500,
colorGray600,
colorGray700,
colorGray800,
colorGray900,
colorGray950,
} = useColorLightness(s, colorGray);
// Generate shades (darker variants) for hover/active states
const {
colorPrimaryShade50, // 5% darker
colorPrimaryShade100, // 10% darker
colorPrimaryShade150, // 15% darker
colorPrimaryShade200, // 20% darker
} = useColorShade(s, colorPrimary);
// Generate tints (lighter variants) for subtle backgrounds
const {
colorPrimaryTint50, // 5% lighter
colorPrimaryTint100, // 10% lighter
colorPrimaryTint150, // 15% lighter
colorPrimaryTint200, // 20% lighter
} = useColorTint(s, colorPrimary);
export default s;
That's it! You now have a complete color system with:
- 11 lightness variants per color (50-950)
- 4 shade variants (darker) for hover/active states
- 4 tint variants (lighter) for subtle backgrounds
All perceptually uniform thanks to OKLCH. No manual color picking required!
Use these for:
- Shades: Hover/active states on buttons and interactive elements
- Tints: Subtle backgrounds, badges, alerts, and highlighted areas
Step 2: Scales – The Secret Sauce (1 minute)
Modular scales are the foundation of harmonious design systems. They use mathematical ratios to create proportional relationships between sizes.
For fluid typography, we'll define separate scales for mobile and desktop to optimize readability at different viewport sizes:
import { useScale, useScalePowers, defaultScaleValues } from '@styleframe/theme';
// Use Minor Third (1.2) for mobile, Major Third (1.25) for desktop
const { scaleMin, scaleMax } = useScale(s, {
...defaultScaleValues,
min: '@minor-third', // 1.2 - subtle scaling for small screens
max: '@major-third', // 1.25 - stronger hierarchy for large screens
});
// Generate scale powers from -3 to 5
// These will be multipliers for creating proportional sizes
const scaleMinPowers = useScalePowers(s, scaleMin, [-3, -2, -1, 0, 1, 2, 3, 4, 5]);
const scaleMaxPowers = useScalePowers(s, scaleMax, [-3, -2, -1, 0, 1, 2, 3, 4, 5]);
These scale powers will be used to create mathematically harmonious typography and spacing systems that adapt to viewport size.
- Minor Third → Major Third (1.2 → 1.25): Balanced, works for most sites
- Minor Third → Perfect Fourth (1.2 → 1.333): More dramatic hierarchy for desktop
- Major Second → Minor Third (1.125 → 1.2): Subtle, great for dense content
- Major Third → Perfect Fourth (1.25 → 1.333): Bold, ideal for marketing sites
Step 3: Fluid Typography System (3 minutes)
With your scales defined, let's create a fluid typography system that scales smoothly across all viewport sizes — no breakpoints needed!
Setup Fluid Viewport
First, establish the viewport range for fluid calculations:
import { useFluidViewport } from '@styleframe/pro';
// Set up fluid viewport (320px mobile → 1440px desktop)
useFluidViewport(s);
This creates the foundation for all fluid design tokens. The composable automatically calculates a fluid breakpoint variable that represents the viewport's position between min and max widths.
Learn more about fluid viewports →
Fluid Font Sizes with Modular Scales
Create font sizes that scale smoothly from mobile to desktop using your modular scales:
import { useFluidFontSize } from '@styleframe/pro';
// Generate fluid font sizes that scale from 16px→18px at the base
const {
fontSize,
fontSizeXs, // Scale power -2: scales smoothly between mobile and desktop
fontSizeSm, // Scale power -1
fontSizeMd, // Scale power 0 (base size)
fontSizeLg, // Scale power 1
fontSizeXl, // Scale power 2
fontSize2xl, // Scale power 3
fontSize3xl, // Scale power 4
fontSize4xl, // Scale power 5
} = useFluidFontSize(
s,
{ min: 16, max: 18 }, // Base font size: 16px mobile → 18px desktop
{
xs: { min: scaleMinPowers[-2], max: scaleMaxPowers[-2] },
sm: { min: scaleMinPowers[-1], max: scaleMaxPowers[-1] },
md: { min: scaleMinPowers[0], max: scaleMaxPowers[0] },
lg: { min: scaleMinPowers[1], max: scaleMaxPowers[1] },
xl: { min: scaleMinPowers[2], max: scaleMaxPowers[2] },
'2xl': { min: scaleMinPowers[3], max: scaleMaxPowers[3] },
'3xl': { min: scaleMinPowers[4], max: scaleMaxPowers[4] },
'4xl': { min: scaleMinPowers[5], max: scaleMaxPowers[5] },
default: '@md',
}
);
The magic: Each font size automatically scales between mobile and desktop using complex CSS calc() functions. Your h1 might go from 38px on mobile to 90px on desktop—smoothly scaling at every viewport width in between!
Learn more about fluid typography →
Font Families
Define your font stacks:
import { useFontFamily, useFontWeight } from '@styleframe/theme';
const { fontFamily, fontFamilyMono, fontFamilyDisplay } = useFontFamily(s, {
default: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
mono: '"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace',
display: '"Inter Display", -apple-system, sans-serif',
} as const);
const {
fontWeightNormal,
fontWeightMedium,
fontWeightSemibold,
fontWeightBold
} = useFontWeight(s);
Line Heights and Letter Spacing
Complete your typography system with optimal readability:
import { useLineHeight, useLetterSpacing } from '@styleframe/theme';
const {
lineHeightTight, // 1.25 - for headlines
lineHeightNormal, // 1.5 - for body text
lineHeightRelaxed, // 1.75 - for long-form content
} = useLineHeight(s);
const {
letterSpacingTight, // -0.025em - for large text
letterSpacingNormal, // 0 - standard
letterSpacingWide, // 0.025em - for small text/labels
} = useLetterSpacing(s);
Step 4: Spacing System (1 minute)
Create consistent spacing using the same scale for visual harmony:
import { useSpacing, useMultiplier } from '@styleframe/theme';
// Define base spacing
const { spacing } = useSpacing(s, { default: '1rem' } as const);
// Generate spacing scale using the mobile scale for consistency
const {
spacing3xs, // ~0.579rem
spacing2xs, // ~0.694rem
spacingXs, // ~0.833rem
spacingSm, // ~0.833rem
spacingMd, // 1rem (base)
spacingLg, // ~1.2rem
spacingXl, // ~1.44rem
spacing2xl, // ~1.728rem
spacing3xl, // ~2.074rem
} = useMultiplier(s, spacing, {
'3xs': scaleMinPowers[-3],
'2xs': scaleMinPowers[-2],
xs: scaleMinPowers[-1],
sm: scaleMinPowers[-1],
md: scaleMinPowers[0],
lg: scaleMinPowers[1],
xl: scaleMinPowers[2],
'2xl': scaleMinPowers[3],
'3xl': scaleMinPowers[4],
});
Step 5: Visual Depth (2 minutes)
Add borders, shadows, and breakpoints to complete your system.
Borders
import { useBorderWidth, useBorderStyle, useBorderColor } from '@styleframe/theme';
const {
borderWidth,
borderWidthThin,
borderWidthMedium,
borderWidthThick,
} = useBorderWidth(s);
const { borderStyle } = useBorderStyle(s);
// Use your gray scale for border colors
const {
borderColor,
borderColorLight,
borderColorDark,
} = useBorderColor(s, {
default: s.ref(colorGray300),
light: s.ref(colorGray200),
dark: s.ref(colorGray400),
} as const);
Border Radiuses
Create consistent rounded corners using scale-based values:
import { useBorderRadius, useMultiplier } from '@styleframe/theme';
// Define base border radius
const {
borderRadius,
borderRadiusFull,
} = useBorderRadius(s, {
default: '0.25rem',
full: '9999px', // For pills/circles
});
// Generate border radius scale using your modular scale
const {
borderRadiusXs,
borderRadiusSm,
borderRadiusMd,
borderRadiusLg,
borderRadiusXl,
borderRadius2xl,
} = useMultiplier(s, borderRadius, {
xs: scaleMinPowers[-2], // Subtle rounding
sm: scaleMinPowers[-1], // Small rounding
md: scaleMinPowers[0], // Base rounding
lg: scaleMinPowers[1], // Large rounding
xl: scaleMinPowers[2], // Extra large rounding
'2xl': scaleMinPowers[3], // Maximum rounding
});
// Or create special purpose values
const { borderRadiusFull } = useBorderRadius(s, { full: '9999px' }); // For pills/circles
Learn more about border radiuses →
Box Shadows
Create an elevation system:
import { useBoxShadow } from '@styleframe/theme';
const {
boxShadowSm, // Subtle elevation
boxShadow, // Standard elevation
boxShadowMd, // Medium elevation
boxShadowLg, // High elevation
boxShadowXl, // Maximum elevation
} = useBoxShadow(s);
Learn more about box shadows →
Responsive Breakpoints
Even with fluid typography, breakpoints are useful for layout changes:
import { useBreakpoint } from '@styleframe/theme';
const {
breakpointXs, // 0px - mobile
breakpointSm, // 576px - tablets
breakpointMd, // 992px - laptops
breakpointLg, // 1200px - desktops
breakpointXl, // 1440px - large screens
} = useBreakpoint(s);
Learn more about breakpoints →
Step 6: Put It All Together
Here's your complete fluid design system in one place:
import { styleframe } from 'styleframe';
import {
useColor,
useColorLightness,
useColorShade,
useColorTint,
useScale,
useScalePowers,
useFontFamily,
useFontWeight,
useLineHeight,
useLetterSpacing,
useSpacing,
useBorderWidth,
useBorderStyle,
useBorderColor,
useBorderRadius,
useBoxShadow,
useBreakpoint,
useMultiplier,
defaultScaleValues,
} from '@styleframe/theme';
import { useFluidViewport, useFluidFontSize } from '@styleframe/pro';
const s = styleframe();
// 1. Colors
const { colorPrimary, colorSecondary, colorGray } = useColor(s, {
primary: '#0066ff',
secondary: '#7c3aed',
gray: '#64748b',
} as const);
const { colorSuccess, colorWarning, colorDanger, colorInfo } = useColor(s, {
success: '#10b981',
warning: '#f59e0b',
danger: '#ef4444',
info: '#06b6d4',
} as const);
// Generate color variants
// Generate lightness variants for primary color
const {
colorPrimary50,
colorPrimary100,
colorPrimary200,
colorPrimary300,
colorPrimary400,
colorPrimary500,
colorPrimary600,
colorPrimary700,
colorPrimary800,
colorPrimary900,
colorPrimary950,
} = useColorLightness(s, colorPrimary);
// Generate lightness variants for gray (for UI backgrounds, borders, text)
const {
colorGray50,
colorGray100,
colorGray200,
colorGray300,
colorGray400,
colorGray500,
colorGray600,
colorGray700,
colorGray800,
colorGray900,
colorGray950,
} = useColorLightness(s, colorGray);
// Generate shades and tints for interactive states
const {
colorPrimaryShade50,
colorPrimaryShade100,
colorPrimaryShade150,
colorPrimaryShade200,
} = useColorShade(s, colorPrimary);
const {
colorPrimaryTint50,
colorPrimaryTint100,
colorPrimaryTint150,
colorPrimaryTint200,
} = useColorTint(s, colorPrimary);
// 2. Scales for Fluid Typography
const { scaleMin, scaleMax } = useScale(s, {
...defaultScaleValues,
min: '@minor-third', // 1.2 for mobile
max: '@major-third', // 1.25 for desktop
});
const scaleMinPowers = useScalePowers(s, scaleMin, [-3, -2, -1, 0, 1, 2, 3, 4, 5]);
const scaleMaxPowers = useScalePowers(s, scaleMax, [-3, -2, -1, 0, 1, 2, 3, 4, 5]);
// 3. Fluid Typography
useFluidViewport(s);
const { fontFamily, fontFamilyMono, fontFamilyDisplay } = useFontFamily(s, {
default: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
mono: '"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace',
display: '"Inter Display", -apple-system, sans-serif',
} as const);
const fontSizes = useFluidFontSize(
s,
{ min: 16, max: 18 },
{
xs: { min: scaleMinPowers[-2], max: scaleMaxPowers[-2] },
sm: { min: scaleMinPowers[-1], max: scaleMaxPowers[-1] },
md: { min: scaleMinPowers[0], max: scaleMaxPowers[0] },
lg: { min: scaleMinPowers[1], max: scaleMaxPowers[1] },
xl: { min: scaleMinPowers[2], max: scaleMaxPowers[2] },
'2xl': { min: scaleMinPowers[3], max: scaleMaxPowers[3] },
'3xl': { min: scaleMinPowers[4], max: scaleMaxPowers[4] },
'4xl': { min: scaleMinPowers[5], max: scaleMaxPowers[5] },
default: '@md',
}
);
const { fontWeight, fontWeightNormal, fontWeightMedium, fontWeightSemibold, fontWeightBold } = useFontWeight(s);
const { lineHeight, lineHeightTight, lineHeightNormal, lineHeightRelaxed } = useLineHeight(s);
const { letterSpacing, letterSpacingTight, letterSpacingNormal, letterSpacingWide } = useLetterSpacing(s);
// 4. Spacing
const { spacing } = useSpacing(s, { default: '1rem' } as const);
const spacings = useMultiplier(s, spacing, {
'3xs': scaleMinPowers[-3],
'2xs': scaleMinPowers[-2],
xs: scaleMinPowers[-1],
sm: scaleMinPowers[-1],
md: scaleMinPowers[0],
lg: scaleMinPowers[1],
xl: scaleMinPowers[2],
'2xl': scaleMinPowers[3],
'3xl': scaleMinPowers[4],
});
// 5. Visual Depth
const { borderStyle } = useBorderStyle(s);
const { borderWidth, borderWidthThin, borderWidthMedium, borderWidthThick } = useBorderWidth(s);
const { borderColor, borderLight, borderColorDark } = useBorderColor(s, {
default: s.ref(colorGray300),
light: s.ref(colorGray200),
dark: s.ref(colorGray400),
} as const);
const {
borderRadius,
borderRadiusFull,
} = useBorderRadius(s, {
default: '0.25rem',
full: '9999px',
});
const { borderRadiusXs, borderRadiusSm, borderRadiusMd, borderRadiusLg, borderRadiusXl, borderRadius2xl } = useMultiplier(s, borderRadius, {
xs: scaleMinPowers[-2],
sm: scaleMinPowers[-1],
md: scaleMinPowers[0],
lg: scaleMinPowers[1],
xl: scaleMinPowers[2],
'2xl': scaleMinPowers[3],
});
const { boxShadow, boxShadowSm, boxShadowMd, boxShadowLg, boxShadowXl } = useBoxShadow(s);
const { breakpointSm, breakpointMd, breakpointLg, breakpointXl } = useBreakpoint(s);
export default s;
Next Steps: Components & Recipes
Your design system tokens are now complete! With these tokens in place, you're ready to build:
Components (Coming Soon)
Pre-built, customizable UI components that use your design tokens:
- Buttons (primary, secondary, outline, ghost variants)
- Forms (inputs, textareas, selects, checkboxes)
- Cards (with headers, footers, images)
- Modals & Dialogs
- Navigation (navbar, sidebar, breadcrumbs)
- Alerts & Notifications
Stay tuned for component documentation!
Utilities (Coming Soon)
Helper classes and functions to speed up development:
- Flexbox & Grid utilities
- Text alignment & decoration
- Margin & padding shortcuts
- Display & visibility helpers
- Positioning utilities (absolute, relative, fixed)
Recipes (Coming Soon)
Recipes are advanced component styling systems that combine utility declarations with configurable variants. They provide a powerful way to create flexible, reusable UI components with type-safe variant selection.
Recipes support:
- Default variants: Specify which variant applies when none is chosen
- Compound variants: Define special styling for specific variant combinations
- Full type safety: TypeScript support for variant names and values
- Runtime flexibility: Generate utility class strings on the fly
Examples
Using Your Design Tokens
Now that you've created your design system, let's use it to build some components!
Example: Button Component
const { ref, selector, css } = s;
selector('.btn', {
fontFamily: ref(fontFamily),
fontSize: ref(fontSize),
fontWeight: ref(fontWeight),
lineHeight: ref(lineHeight),
paddingTop: ref(spacingSm),
paddingBottom: ref(spacingSm),
paddingLeft: ref(spacingMd),
paddingRight: ref(spacingMd),
borderRadius: ref(borderRadius),
borderWidth: ref(borderWidth),
borderStyle: ref(borderStyle),
borderColor: ref(borderColor),
backgroundColor: ref(colorPrimary),
color: 'white',
cursor: 'pointer',
transition: 'all 0.2s ease',
});
selector('.btn:hover', {
backgroundColor: ref(colorPrimaryShade50),
boxShadow: ref(boxShadow),
});
selector('.btn:active', {
backgroundColor: ref(colorPrimaryShade100),
boxShadow: 'none',
});
// Subtle variant using tints
selector('.btn-subtle', {
fontFamily: ref(fontFamily),
fontSize: ref(fontSize),
fontWeight: ref(fontWeight),
lineHeight: ref(lineHeight),
paddingTop: ref(spacingSm),
paddingBottom: ref(spacingSm),
paddingLeft: ref(spacingMd),
paddingRight: ref(spacingMd),
borderRadius: ref(borderRadiusMd),
borderWidth: ref(borderWidth),
borderStyle: ref(borderStyle),
borderColor: ref(colorPrimary),
backgroundColor: ref(colorPrimaryTint150),
color: ref(colorPrimary),
cursor: 'pointer',
transition: 'all 0.2s ease',
});
selector('.btn-subtle:hover', {
backgroundColor: ref(colorPrimaryTint100),
});
What Makes This Different?
Traditional design systems require:
- ❌ Manually defining dozens of color shades
- ❌ Creating hover and active state colors by hand
- ❌ Creating multiple media queries for responsive typography
- ❌ Picking arbitrary font sizes at each breakpoint
- ❌ Inconsistent spacing values across your app
- ❌ Brittle, hard-coded CSS values
- ❌ Weeks of refinement to get proportions right
With Styleframe's fluid design system, you get:
- ✅ Automatic color variants using perceptually uniform OKLCH
- ✅ Automatic tints and shades for interactive states
- ✅ Smooth responsive scaling without breakpoints
- ✅ Mathematical harmony through modular scales
- ✅ Type-safe tokens with full autocomplete
- ✅ Theme-ready from day one
- ✅ Production-ready in under 15 minutes
Key Takeaways
- Start with colors: Define your brand colors and generate variants, tints, and shades automatically
- Choose mobile and desktop scales: Select modular scale ratios for each viewport size
- Enable fluid viewport: Set up the foundation for fluid design tokens
- Create fluid typography: Font sizes scale smoothly across all screen sizes
- Build systematic spacing: Use the same scale for visual harmony
- Add depth: Borders, shadows, and breakpoints complete the system
- Stay flexible: All tokens can be overridden for themes or special cases
Troubleshooting
useFluidFontSize(). The { min: 16, max: 18 } means your base text goes from 16px (mobile) to 18px (desktop). Try { min: 14, max: 16 } for smaller text, or { min: 16, max: 20 } for larger. All other sizes scale proportionally from this base!min: '@major-second' (1.125) and max: '@perfect-fourth' (1.333) for stronger differences. Or go bold with min: '@minor-third' (1.2) and max: '@perfect-fifth' (1.5). Each step up the scale makes the size differences more pronounced.useFluidFontSize() with regular useFontSize(). Use fluid sizes for headings and display text where scaling matters, and fixed sizes for UI elements like buttons and labels where consistency is more important.useColor() to define your exact brand colors, then let useColorLightness() generate the variants. The base color (500) will match your guidelines exactly, while variants provide flexibility for different UI states.useScalePowers(). For more sizes, use a wider range like [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]. For fewer, use [-1, 0, 1, 2, 3]. Then add or remove entries in the useFluidFontSize() size definitions. Remember: fewer choices often lead to better consistency.useFontSize() and useMultiplier() instead of useFluidFontSize(). You'll have a great design system—just with traditional responsive typography instead of fluid scaling.Additional Resources
- Design Tokens Overview - Complete design tokens reference
- Fluid Design Overview - Deep dive into fluid responsive design
- Composables API - All available composable functions
- Themes API - Creating and managing themes
Congratulations! 🎉 You've built a production-ready fluid design system in under 15 minutes. Your design tokens are type-safe, theme-ready, mathematically harmonious, and scale beautifully across all viewport sizes. Now go build something beautiful!