Preset System
A powerful theming and styling system for global component configuration with hierarchical composition and reactive state support.
Overview
Define default styles, variants, and behaviors at any level of your application hierarchy.
The Preset System is a sophisticated theming mechanism that uses Svelte's context system combined with deep merging to enable powerful composition and extension patterns. Set base themes at app root, override at route level, or customize at component level with full type safety and reactive state support.
Key Features
Hierarchical Composition
Define base themes at app root, override at route level, customize at component level
Deep Merging
Presets merge across context layers, allowing progressive enhancement
Variant Support
Define variant systems with defaults that work globally
Reactive State
Access component bond state for dynamic styling
Compound Components
Configure nested components using dot notation
Performance Optimized
Memoized resolution with early exit optimizations
Global Preset Configuration
Define base theme at application root for consistent styling.
App Root Configuration
// +layout.svelte (App Root)
import { setPreset } from '@svelte-atoms/core/context';
import { Root } from '@svelte-atoms/core/components/root';
const theme = {
button: () => ({
class: 'rounded-lg px-4 py-2 font-semibold transition-colors',
variants: {
variant: {
primary: { class: 'bg-primary text-primary-foreground hover:bg-primary/90' },
secondary: { class: 'bg-secondary text-secondary-foreground hover:bg-secondary/80' },
destructive: { class: 'bg-destructive text-destructive-foreground hover:bg-destructive/90' }
},
size: {
sm: { class: 'h-8 px-3 text-sm' },
md: { class: 'h-10 px-4' },
lg: { class: 'h-12 px-6 text-lg' }
}
},
defaults: {
variant: 'primary',
size: 'md'
}
}),
card: () => ({
class: 'rounded-xl border border-border bg-card shadow-sm'
}),
'card.title': () => ({
class: 'text-xl font-bold text-card-foreground'
})
};
setPreset(theme);Route-Level Overrides
Extend or override presets for specific routes and sections.
Route Layout Override
// routes/dashboard/+layout.svelte
import { setPreset } from '@svelte-atoms/core/context';
// Extends and overrides the global preset
setPreset({
button: () => ({
class: 'text-sm', // Merges with global button classes
variants: {
variant: {
// Adds new variant, keeps primary, secondary, destructive
info: { class: 'bg-blue-500 text-white hover:bg-blue-600' }
}
}
}),
card: () => ({
class: 'bg-slate-50 border-slate-200' // Overrides global card styling
})
});
// All buttons in /dashboard/* now have the extended variantsComponent-Level Customization
Scope presets to specific component trees without affecting global theme.
Component Scoped Preset
// components/Settings.svelte
import { setPreset } from '@svelte-atoms/core/context';
import { Card } from '@svelte-atoms/core/card';
// Component-level preset for this subtree
setPreset({
'card.title': () => ({
class: 'text-purple-600' // Purple titles in settings only
})
});
<Card.Root>
<Card.Header>
<Card.Title>Settings</Card.Title> <!-- Purple! -->
</Card.Header>
</Card.Root>Compound Component Presets
Configure nested components using dot notation for precise control.
Use dot notation to style compound components and their children. This is especially powerful
for components like Alert, Card, Dialog, and Accordion. The pattern is 'parent.child' for precise styling control.
Dot Notation Example
Use dot notation to style compound components and their children. This is especially powerful for components like Alert, Card, Dialog, and Accordion. The pattern is 'parent.child' for precise styling control.
setPreset({
// Parent component
alert: () => ({
class: 'relative rounded-lg border p-4',
variants: {
variant: {
default: { class: 'bg-background text-foreground' },
destructive: { class: 'bg-destructive/10 text-destructive border-destructive/50' },
success: { class: 'bg-green-50 text-green-900 border-green-200' }
}
},
defaults: {
variant: 'default'
}
}),
// Child components (dot notation)
'alert.icon': () => ({
class: 'h-4 w-4'
}),
'alert.title': () => ({
class: 'mb-1 font-semibold leading-tight'
}),
'alert.description': () => ({
class: 'text-sm leading-relaxed opacity-90'
}),
'alert.actions': () => ({
class: 'mt-3 flex items-center gap-2'
})
});Reactive State-Based Styling
Access component state for dynamic, reactive preset styling.
Preset functions receive the component's bond as a parameter, giving you access to the component's reactive state. Use this to create dynamic styles that update automatically when component state changes. The function returns another function that returns the preset object, allowing for reactive class computation based on bond state.
Accordion Active State Example
Preset functions receive the component's bond as a parameter, giving you access to the component's reactive state. Use this to create dynamic styles that update automatically when component state changes. The function returns another function that returns the preset object, allowing for reactive class computation based on bond state.
'accordion.item.header': (bond) => {
return () => ({
class: ['', bond?.state?.isActive ? 'text-foreground/100' : 'text-foreground/50']
});
}The $preset Placeholder
Control exactly where preset classes are inserted in your class arrays.
Placeholder Usage
// In your component
import { HtmlAtom } from '@svelte-atoms/core';
let { class: klass = '' } = $props();
<HtmlAtom
preset="button"
class={[
'my-custom-class',
'$preset', // Replaced with preset classes
klass // User classes override everything
]}
/>
// Result: 'my-custom-class rounded-lg px-4 py-2 font-semibold user-class'Setting HTML Attributes
Presets can define any HTML attributes, not just classes.
Beyond styling with classes, presets can set any HTML attributes including data-*, aria-*, role, and
more.
Common Use Cases:
aria-*, role, tabindex for consistent a11ydata-testid or data-component attributesdata-analytics or tracking attributesdata-state or data-variant for CSS selectorsAttribute Configuration
// Presets can set any HTML attributes, not just classes
setPreset({
button: () => ({
class: 'rounded-lg px-4 py-2',
'data-component': 'button',
'data-version': '1.0',
role: 'button',
tabindex: 0
}),
'card.title': () => ({
class: 'text-xl font-bold',
'data-heading': 'true',
'aria-level': 2
}),
// Variant-specific attributes
alert: () => ({
variants: {
variant: {
destructive: {
class: 'bg-destructive/10 text-destructive',
'data-variant': 'destructive',
'aria-live': 'assertive',
role: 'alert'
},
info: {
class: 'bg-blue-50 text-blue-900',
'data-variant': 'info',
'aria-live': 'polite'
}
}
}
})
});
// Components will receive these attributes automatically
<Button>Click me</Button>
// Renders: <button class="rounded-lg px-4 py-2" data-component="button" data-version="1.0" role="button" tabindex="0">Extending & Composing Presets
Build upon existing preset definitions with deep merging.
The preset system uses deep merging to combine configurations across context layers. This allows you to extend base presets with new variants, sizes, or completely new preset keys without losing existing definitions.
Deep Merge Example
The preset system uses deep merging to combine configurations across context layers. This allows you to extend base presets with new variants, sizes, or completely new preset keys without losing existing definitions.
// Base theme (global)
setPreset({
button: () => ({
class: 'rounded-md font-medium transition-colors',
variants: {
variant: {
primary: { class: 'bg-blue-500 text-white' },
secondary: { class: 'bg-gray-200 text-gray-900' }
},
size: {
sm: { class: 'px-3 py-1 text-sm' },
md: { class: 'px-4 py-2' }
}
}
})
});
// Extended theme (route or component level)
setPreset({
button: () => ({
// Add new variants (merges with existing)
variants: {
variant: {
gradient: { class: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white' },
outline: { class: 'border border-border/50 border-primary bg-transparent text-primary' }
},
size: {
xl: { class: 'px-8 py-4 text-xl' } // Add new size
}
}
})
});
// Result: All variants available (primary, secondary, gradient, outline)
// All sizes available (sm, md, xl)Best Practices
Guidelines for effective preset usage and patterns.
Best Practices - Do's and Don'ts
// ✅ DO: Set global presets at app root
// +layout.svelte
setPreset({ /* base theme */ });
// ✅ DO: Override at route level for sections
// routes/admin/+layout.svelte
setPreset({ /* admin theme */ });
// ✅ DO: Use dot notation for specificity
setPreset({
'card.title': () => ({ class: 'text-xl font-bold' })
});
// ✅ DO: Access bond state for reactivity
setPreset({
'accordion.item.header': (bond) => ({
class: bond?.state?.isOpen ? 'bg-accent' : 'bg-background'
})
});
// ❌ DON'T: Override presets for one-off styling
// Use component props instead
<Button class="custom-one-off-style">Click</Button>
// ❌ DON'T: Create overly complex preset functions
// Keep preset logic simple and focusedSet global presets at app root
Define your base theme in +layout.svelte for consistent styling
Override at route level (future)
Use route layouts to customize sections of your app without affecting global theme
Use component-level sparingly
Only when specific component trees need unique styling that can't be achieved at route level
Leverage dot notation for specificity
'card.title' is more specific than 'card'
Access bond state for reactivity
Use the bond parameter in preset functions for dynamic, state-driven styles
Merge, don't replace
Presets merge across contexts - build up configurations gradually instead of replacing
Keep presets simple
Presets are for common patterns; use component props for one-off customizations
Common Use Cases
Real-world scenarios where presets excel.
Multi-Tenant Applications
Different organizations can have their own branded themes by setting presets based on tenant configuration.
Dark Mode Implementation
Toggle between light and dark presets at the root level to implement theme switching.
Dashboard vs Marketing Site
Use route-level presets for compact dashboard styles and spacious marketing page layouts.
Component Library Theming
Provide default presets with your library that consumers can easily override or extend.
A/B Testing Styles
Conditionally apply different presets based on user segments or feature flags.
Responsive Styling
Adjust component variants based on viewport size or device capabilities.
API Reference
Core functions and types for the preset system.
setPreset(preset)
Sets or merges preset configuration in the current context.
setPreset(preset: Partial<Preset>): voidgetPreset(key?)
Retrieves preset configuration. If key is provided, returns specific preset entry; otherwise returns all presets.
getPreset<K>(key?: K): PresetEntry | Partial<Preset>Preset Entry Function
Preset entries are functions that receive the component bond and return configuration objects.
type PresetEntry = (bond: Bond | null) => PresetEntryRecordPresetEntryRecord
The object returned by preset functions. Supports any HTML attributes including class, data-*, aria-*, role, etc.
{
class?: ClassValue;
as?: string;
base?: Base;
variants?: {
[variantName: string]: {
[variantValue: string]: any; // Can include class, data-*, aria-*, etc.
};
};
compounds?: Array<Record<string, any>>;
defaults?: Record<string, any>;
// Any additional HTML attributes:
// 'data-*'?: string;
// 'aria-*'?: string;
// role?: string;
// tabindex?: number;
// etc.
}