Merging
Overview
The merge() function allows you to combine multiple Styleframe instances into a single unified instance. This enables composition patterns where you can split your styling configuration into logical modules and merge them together.
Merging is particularly useful for creating shared design system foundations, composing third-party style libraries, or organizing large-scale styling architectures into maintainable modules.
Why merge styleframes?
Merging styleframes helps you:
- Compose design systems: Build modular design systems by splitting configurations into logical units or themes and combining them.
- Share configurations: Create reusable style libraries that can be imported and merged into multiple projects.
- Override and extend: Start with a base configuration and extend it with project-specific customizations.
- Organize large codebases: Break down complex styling systems into maintainable, focused modules.
Basic Usage
Import the merge() function from Styleframe to combine multiple instances:
import { merge } from 'styleframe';
import base from './base';
import extension from './extension';
const s = merge(base, extension);
export default s;
import { styleframe } from 'styleframe';
const base = styleframe();
const { variable, selector } = base;
variable('color--primary', '#3b82f6');
selector('.button', {
padding: '0.5rem 1rem',
borderRadius: '0.25rem',
});
export default base;
import { styleframe } from 'styleframe';
const extension = styleframe();
const { variable, selector } = extension;
variable('color--secondary', '#64748b');
selector('.card', {
padding: '1rem',
borderRadius: '0.5rem',
});
export default extension;
:root {
--color--primary: #3b82f6;
--color--secondary: #64748b;
}
.button {
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
.card {
padding: 1rem;
border-radius: 0.5rem;
}
merge() function returns a new Styleframe instance without modifying the original instances. You can safely reuse the base and extension instances elsewhere.Merging Multiple Instances
You can merge multiple Styleframe instances at once by passing them as arguments:
import { merge } from 'styleframe';
import colors from './colors';
import typography from './typography';
import spacing from './spacing';
const s = merge(colors, typography, spacing);
export default s;
import { styleframe } from 'styleframe';
const colors = styleframe();
colors.variable('color--primary', '#3b82f6');
colors.variable('color--secondary', '#64748b');
export default colors;
import { styleframe } from 'styleframe';
const typography = styleframe();
typography.variable('font--sans', 'Inter, system-ui, sans-serif');
typography.variable('font--mono', 'Fira Code, monospace');
export default typography;
import { styleframe } from 'styleframe';
const spacing = styleframe();
spacing.variable('spacing--sm', '0.5rem');
spacing.variable('spacing--md', '1rem');
spacing.variable('spacing--lg', '2rem');
export default spacing;
:root {
--color--primary: #3b82f6;
--color--secondary: #64748b;
--font--sans: Inter, system-ui, sans-serif;
--font--mono: Fira Code, monospace;
--spacing--sm: 0.5rem;
--spacing--md: 1rem;
--spacing--lg: 2rem;
}
Merge Behavior
Variables and Declarations
When merging instances, later variable declarations override earlier ones:
import { merge } from 'styleframe';
import base from './base';
import override from './override';
const s = merge(base, override);
export default s;
import { styleframe } from 'styleframe';
const base = styleframe();
base.variable('color--primary', '#3b82f6');
base.variable('color--secondary', '#64748b');
export default base;
import { styleframe } from 'styleframe';
const override = styleframe();
// Overrides base color--primary
override.variable('color--primary', '#ef4444');
export default override;
:root {
--color--primary: #ef4444;
--color--secondary: #64748b;
}
Utilities, Modifiers, Recipes
Array-based properties like utilities, modifiers, recipes, variables, and children are concatenated:
import { merge } from 'styleframe';
import base from './base';
import extension from './extension';
const s = merge(base, extension);
export default s;
import { styleframe } from 'styleframe';
const base = styleframe();
base.utility('text', {
sm: { fontSize: '0.875rem' },
md: { fontSize: '1rem' },
});
export default base;
import { styleframe } from 'styleframe';
const extension = styleframe();
extension.utility('text', {
lg: { fontSize: '1.125rem' },
xl: { fontSize: '1.25rem' },
});
export default extension;
.text\:sm {
font-size: 0.875rem;
}
.text\:md {
font-size: 1rem;
}
.text\:lg {
font-size: 1.125rem;
}
.text\:xl {
font-size: 1.25rem;
}
Themes
Themes are merged by name. When two instances define themes with the same name, they are merged together rather than duplicated. This allows you to compose theme definitions across multiple modules.
- Array-based properties like children within themes are concatenated
- When multiple instances define the same variable within the same theme, later instances override earlier ones
import { merge } from 'styleframe';
import base from './base';
import extension from './extension';
const s = merge(base, extension);
export default s;
import { styleframe } from 'styleframe';
const base = styleframe();
base.variable('color--primary', '#3b82f6');
base.theme('dark', (ctx) => {
ctx.variable('color--primary', '#60a5fa');
});
export default base;
import { styleframe } from 'styleframe';
const extension = styleframe();
extension.variable('color--secondary', '#64748b');
// This dark theme will be merged with the base dark theme
extension.theme('dark', (ctx) => {
ctx.variable('color--secondary', '#94a3b8');
});
export default extension;
:root {
--color--primary: #3b82f6;
--color--secondary: #64748b;
}
[data-theme='dark'] {
--color--primary: #60a5fa;
--color--secondary: #94a3b8;
}
Examples
Shared Design System
Create a shared design system foundation that can be used across multiple projects:
import { styleframe } from 'styleframe';
export function createBase() {
const s = styleframe();
const { variable } = s;
// Color palette
variable('color--white', '#ffffff');
variable('color--black', '#000000');
variable('color--primary', '#3b82f6');
variable('color--secondary', '#64748b');
// Typography
variable('font--sans', 'Inter, system-ui, sans-serif');
variable('font--mono', 'Fira Code, monospace');
// Spacing scale
variable('spacing--1', '0.25rem');
variable('spacing--2', '0.5rem');
variable('spacing--3', '0.75rem');
variable('spacing--4', '1rem');
return s;
}
import { styleframe, merge } from 'styleframe';
import { createBase } from '../base';
const base = createBase();
const custom = styleframe();
// Add project-specific styles
custom.selector('.hero', {
padding: '4rem 2rem',
textAlign: 'center',
});
const s = merge(base, custom);
export default s;
import { styleframe, merge } from 'styleframe';
import { createBase } from '../base';
const base = createBase();
const custom = styleframe();
// Add different project-specific styles
custom.selector('.dashboard', {
display: 'grid',
gap: '1rem',
});
const s = merge(base, custom);
export default s;
Component Library Integration
Merge third-party component library styles with your custom configuration:
import { merge } from 'styleframe';
import { createUILibrary } from '@company/ui-library';
import custom from './custom';
const library = createUILibrary();
// Merge with component library
const s = merge(library, custom);
// Override library variables
const { variable } = s;
variable('ui--button-color', '#7c3aed');
export default s;
import { styleframe } from 'styleframe';
const custom = styleframe();
custom.variable('brand--primary', '#7c3aed');
custom.variable('brand--secondary', '#a855f7');
export default custom;
Composing Multiple Themes
You can merge instances that define different themes. Each theme is preserved independently:
import { styleframe, merge } from 'styleframe';
import darkTheme from './dark-theme';
import highContrastTheme from './high-contrast-theme';
const defaultTheme = styleframe();
defaultTheme.variable('color--text', '#ffffff');
export default merge(defaultTheme, darkTheme, highContrastTheme);
import { styleframe } from 'styleframe';
const darkTheme = styleframe();
darkTheme.variable('color--text', '#ffffff');
darkTheme.theme('dark', (ctx) => {
ctx.variable('color--text', '#e5e7eb');
ctx.variable('color--bg', '#1f2937');
});
export default darkTheme;
import { styleframe } from 'styleframe';
const highContrastTheme = styleframe();
highContrastTheme.theme('high-contrast', (ctx) => {
ctx.variable('color--text', '#000000');
ctx.variable('color--bg', '#ffffff');
});
export default highContrastTheme;
:root {
--color--text: #ffffff;
}
[data-theme='dark'] {
--color--text: #e5e7eb;
--color--bg: #1f2937;
}
[data-theme='high-contrast'] {
--color--text: #000000;
--color--bg: #ffffff;
}
Layer-Based Architecture
Organize your styles into architectural layers:
import { merge } from 'styleframe';
import tokens from './tokens';
import semantic from './semantic';
import components from './components';
const s = merge(tokens, semantic, components);
export default s;
import { styleframe } from 'styleframe';
// Layer 1: Design tokens
const tokens = styleframe();
tokens.variable('color--blue-500', '#3b82f6');
tokens.variable('spacing--4', '1rem');
export default tokens;
import { styleframe, getVariable } from 'styleframe';
import tokens from './tokens';
// Layer 2: Semantic tokens
const semantic = styleframe();
const { ref } = semantic;
const colorBlue500 = getVariable(tokens.root, 'color--blue-500');
const spacing4 = getVariable(tokens.root, 'spacing--4');
semantic.variable('color--primary', ref(colorBlue500));
semantic.variable('button--padding', ref(spacing4));
export default semantic;
import { styleframe, getVariable } from 'styleframe';
import semantic from './semantic';
// Layer 3: Components
const components = styleframe();
const { ref } = components;
const colorPrimary = getVariable(semantic.root, 'color--primary');
const buttonPadding = getVariable(semantic.root, 'button--padding');
components.selector('.button', {
padding: ref(buttonPadding),
background: ref(colorPrimary),
});
export default components;
Feature Flags
Enable optional styling features through merging:
import { merge } from 'styleframe';
import base from './base';
import animations from './animations';
import darkMode from './dark-mode';
// Conditionally merge features
const features = {
animations: process.env.ENABLE_ANIMATIONS === 'true',
darkMode: process.env.ENABLE_DARK_MODE === 'true',
};
const instancesToMerge = [base];
if (features.animations) instancesToMerge.push(animations);
if (features.darkMode) instancesToMerge.push(darkMode);
const s = merge(...instancesToMerge);
export default s;
import { styleframe } from 'styleframe';
const base = styleframe();
base.variable('bg--primary', '#ffffff');
base.selector('.app', {
fontFamily: 'system-ui, sans-serif',
});
export default base;
import { styleframe } from 'styleframe';
// Optional animation features
const animations = styleframe();
animations.selector('.fade-in', {
animation: 'fadeIn 0.3s ease-in',
});
export default animations;
import { styleframe } from 'styleframe';
import base from './base';
// Optional dark mode features
const darkMode = styleframe();
const bgPrimary = base.root.declarations['bg--primary'];
darkMode.theme('dark', (ctx) => {
ctx.variable(bgPrimary, '#1f2937');
});
export default darkMode;
Best Practices
- Keep modules focused: Each Styleframe instance should represent a logical unit (colors, typography, components) for better maintainability.
- Merge in order of specificity: Merge from general to specific so that later merges can override earlier ones when needed.
- Document merge patterns: Clearly document which instances are meant to be merged and in what order for team consistency.
- Avoid circular dependencies: Don't create circular references between merged instances as this can lead to unexpected behavior.
- Use factory functions: Wrap Styleframe instances in factory functions to enable parameterization and reuse.
Type Safety
The merge function maintains full TypeScript type safety:
import { styleframe } from 'styleframe';
const base = styleframe();
base.variable('color--primary', '#3b82f6');
export default base;
import { styleframe } from 'styleframe';
import base from './base';
const extension = styleframe();
const { ref } = extension;
const colorPrimary = base.root.declarations['color--primary'];
extension.selector('.button', {
color: ref(colorPrimary), // ✅ Type-safe reference across instances
});
export default extension;
import { merge } from 'styleframe';
import base from './base';
import extension from './extension';
const s = merge(base, extension);
export default s;
FAQ
merge() returns a new Styleframe instance without modifying the original instances. You can safely reuse base instances across multiple projects.merge(instance1, instance2, instance3). They will be merged in order from left to right.Composables
Styleframe composables are reusable functions that provide design system components like variables, selectors, and utilities. They enable modular, consistent styling patterns across your application.
Guides
Step-by-step tutorials and practical examples to help you get the most out of Styleframe