Theming Guide
ng-magary uses pure CSS variables for theming, enabling complete customization without build-time configuration or preprocessor coupling. This guide covers everything from basic color changes to creating custom themes with glassmorphism effects.
Theme System Overview
Magary’s theming system is built on three core principles:
CSS Variables All styling uses CSS custom properties for runtime theme switching
Data Attributes Themes are applied via data-theme attribute on <body>
Theme Service Built-in service manages theme state and persistence
Built-in Themes
Magary includes 9 pre-designed themes:
Light
Dark
Purple
Green
Neo
Midnight
Cyberpunk
Cotton
Liquid
Default light theme with cyan primary colors and glassmorphism effects.themeService . setTheme ( 'light' );
Dark mode with inverted colors and enhanced contrast.themeService . setTheme ( 'dark' );
Purple gradient theme with vibrant purple primary colors.themeService . setTheme ( 'purple' );
Nature-inspired theme with green primary colors.themeService . setTheme ( 'green' );
Futuristic theme with neon accents and high contrast.themeService . setTheme ( 'neo' );
Deep dark theme with blue undertones.themeService . setTheme ( 'midnight' );
High-tech theme with pink and cyan accents.themeService . setTheme ( 'cyberpunk' );
Soft pastel theme with gentle colors.themeService . setTheme ( 'cotton' );
Fluid gradient theme with smooth transitions.themeService . setTheme ( 'liquid' );
Using the Theme Service
Basic Theme Switching
Inject the Theme Service
import { Component , inject } from '@angular/core' ;
import { MagaryThemeService } from 'ng-magary' ;
@ Component ({
selector: 'app-theme-switcher' ,
standalone: true ,
template: `
<button (click)="toggleTheme()">Toggle Theme</button>
<p>Current theme: {{ themeService.currentTheme() }}</p>
` ,
})
export class ThemeSwitcherComponent {
themeService = inject ( MagaryThemeService );
toggleTheme () {
this . themeService . toggleTheme ();
}
}
Set Specific Theme
// Set theme directly
this . themeService . setTheme ( 'dark' );
this . themeService . setTheme ( 'purple' );
this . themeService . setTheme ( 'cyberpunk' );
Read Current Theme
// Current theme is a signal
const currentTheme = this . themeService . currentTheme ();
console . log ( currentTheme ); // 'light', 'dark', etc.
// React to theme changes
effect (() => {
console . log ( 'Theme changed to:' , this . themeService . currentTheme ());
});
Theme Persistence
The theme service automatically persists the selected theme to localStorage and applies it on app initialization.
Storage Key : magary-theme
Behavior :
On first visit, respects system preference (prefers-color-scheme)
Saves user selection to localStorage
Restores saved theme on subsequent visits
Theme Switcher Component Example
theme-switcher.component.ts
import { Component , inject } from '@angular/core' ;
import { MagaryThemeService , MagaryButton , MagarySelect } from 'ng-magary' ;
import { CommonModule } from '@angular/common' ;
@ Component ({
selector: 'app-theme-switcher' ,
standalone: true ,
imports: [ CommonModule , MagaryButton , MagarySelect ],
template: `
<div class="theme-controls">
<h3>Theme Settings</h3>
<!-- Theme Dropdown -->
<magary-select
[options]="themes"
[ngModel]="themeService.currentTheme()"
(ngModelChange)="themeService.setTheme($event)"
placeholder="Select theme"
optionLabel="label"
optionValue="value"
/>
<!-- Quick Toggle -->
<magary-button
label="Next Theme"
icon="palette"
(onClick)="themeService.toggleTheme()"
/>
</div>
` ,
})
export class ThemeSwitcherComponent {
themeService = inject ( MagaryThemeService );
themes = [
{ label: 'Light' , value: 'light' },
{ label: 'Dark' , value: 'dark' },
{ label: 'Purple' , value: 'purple' },
{ label: 'Green' , value: 'green' },
{ label: 'Neo' , value: 'neo' },
{ label: 'Midnight' , value: 'midnight' },
{ label: 'Cyberpunk' , value: 'cyberpunk' },
{ label: 'Cotton' , value: 'cotton' },
{ label: 'Liquid' , value: 'liquid' },
];
}
CSS Variable Reference
Color Palette Variables
Magary uses a comprehensive color system based on CSS variables.
Primary Colors
:root {
--primary-50 : #ecfeff ;
--primary-100 : #cffafe ;
--primary-200 : #a5f3fc ;
--primary-300 : #67e8f9 ;
--primary-400 : #22d3ee ;
--primary-500 : #06b6d4 ; /* Main primary color */
--primary-600 : #0891b2 ;
--primary-700 : #0e7490 ;
--primary-800 : #155e75 ;
--primary-900 : #164e63 ;
}
Primary colors change based on the active theme. Use --primary-500 for main actions.
Accent Colors
:root {
--accent-50 : #fefce8 ;
--accent-100 : #fef9c3 ;
--accent-500 : #eab308 ; /* Main accent color */
--accent-900 : #713f12 ;
}
Surface Colors
:root {
--surface-0 : #ffffff ; /* Pure white background */
--surface-50 : #f8fafc ; /* Lightest gray */
--surface-100 : #f1f5f9 ;
--surface-200 : #e2e8f0 ;
--surface-300 : #cbd5e1 ;
--surface-500 : #64748b ;
--surface-700 : #334155 ;
--surface-800 : #1e293b ;
--surface-900 : #0f172a ; /* Darkest */
}
Text Colors
:root {
--text-primary : #0f172a ; /* Main text */
--text-secondary : #475569 ; /* Secondary text */
--text-tertiary : #94a3b8 ; /* Disabled/placeholder */
}
Semantic Colors
:root {
--success : #10b981 ; /* Green */
--warning : #f59e0b ; /* Orange */
--danger : #ef4444 ; /* Red */
--info : #3b82f6 ; /* Blue */
--help : #8b5cf6 ; /* Purple */
}
Layout Variables
:root {
/* Typography */
--font-family : "Inter" , system-ui , -apple-system , sans-serif ;
/* Border Radius */
--border-radius : 1.25 rem ;
--magary-field-radius : 1.25 rem ;
--magary-option-radius : 0.625 rem ;
--magary-overlay-radius : 1.25 rem ;
/* Spacing */
--magary-field-padding-x : 1 rem ;
--magary-overlay-padding : 0.375 rem ;
--magary-overlay-gap : 0.125 rem ;
/* Field Heights */
--magary-field-height-sm : 2.25 rem ;
--magary-field-height-md : 2.75 rem ;
--magary-field-height-lg : 3.125 rem ;
}
Shadow Variables
:root {
--shadow-sm : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.02 );
--shadow-md : 0 4 px 12 px rgba ( 0 , 0 , 0 , 0.05 );
--shadow-lg : 0 12 px 24 px -4 px rgba ( 0 , 0 , 0 , 0.08 );
--shadow-glow : 0 0 20 px rgba ( 6 , 182 , 212 , 0.4 );
--magary-overlay-shadow : 0 12 px 28 px rgba ( 2 , 6 , 23 , 0.24 );
}
Glassmorphism Variables
:root {
--glass-bg : rgba ( 255 , 255 , 255 , 0.85 );
--glass-border : 1 px solid rgba ( 219 , 217 , 217 , 0.85 );
--glass-shadow : 0 8 px 32 px 0 rgba ( 31 , 38 , 135 , 0.07 );
--backdrop-blur : blur ( 16 px );
}
Glassmorphism variables change in dark themes to maintain proper contrast and readability.
Gradient Variables
:root {
--gradient-primary : linear-gradient ( 135 deg , #06b6d4 0 % , #d946ef 100 % );
--gradient-hover : linear-gradient ( 135 deg , #22d3ee 0 % , #e879f9 100 % );
--gradient-text : linear-gradient ( to right , #0891b2 , #c026d3 );
--gradient-surface : linear-gradient ( 180 deg , #ffffff 0 % , #f8fafc 100 % );
}
Creating Custom Themes
Method 1: Using Data Attributes
Create a custom theme by defining CSS variables under a data-theme attribute:
// Custom "Ocean" theme
[ data-theme = ' ocean ' ] {
/* Primary Colors (Blue/Teal) */
--primary-50 : #e0f2fe ;
--primary-100 : #bae6fd ;
--primary-200 : #7dd3fc ;
--primary-300 : #38bdf8 ;
--primary-400 : #0ea5e9 ;
--primary-500 : #0284c7 ;
--primary-600 : #0369a1 ;
--primary-700 : #075985 ;
--primary-800 : #0c4a6e ;
--primary-900 : #082f49 ;
/* Accent Colors (Coral) */
--accent-500 : #fb7185 ;
/* Backgrounds */
--surface-0 : #f0f9ff ;
--text-primary : #082f49 ;
--text-secondary : #0c4a6e ;
/* Glassmorphism */
--glass-bg : rgba ( 240 , 249 , 255 , 0.9 );
--glass-border : 1 px solid rgba ( 14 , 165 , 233 , 0.2 );
--backdrop-blur : blur ( 20 px );
/* Gradients */
--gradient-primary : linear-gradient ( 135 deg , #0ea5e9 0 % , #fb7185 100 % );
}
Then use it with the theme service:
// Add to theme service types (in your app)
type CustomTheme = Theme | 'ocean' ;
// Set the theme
themeService . setTheme ( 'ocean' as any );
Method 2: Extending the Theme Service
Create a custom theme service that extends Magary’s:
import { Injectable } from '@angular/core' ;
import { MagaryThemeService , type Theme } from 'ng-magary' ;
type ExtendedTheme = Theme | 'ocean' | 'sunset' | 'forest' ;
@ Injectable ({
providedIn: 'root' ,
})
export class CustomThemeService extends MagaryThemeService {
override setTheme ( theme : ExtendedTheme ) {
super . setTheme ( theme as Theme );
}
// Add custom theme methods
setOceanTheme () {
this . setTheme ( 'ocean' as any );
}
}
Dark Mode Implementation
Automatic System Preference Detection
Magary automatically detects system dark mode preference on first load using prefers-color-scheme media query.
Manual Dark Mode Toggle
dark-mode-toggle.component.ts
import { Component , inject } from '@angular/core' ;
import { MagaryThemeService , MagaryButton } from 'ng-magary' ;
@ Component ({
selector: 'app-dark-mode-toggle' ,
standalone: true ,
imports: [ MagaryButton ],
template: `
<magary-button
[icon]="isDark() ? 'sun' : 'moon'"
[rounded]="true"
variant="text"
(onClick)="toggleDarkMode()"
[ariaLabel]="isDark() ? 'Switch to light mode' : 'Switch to dark mode'"
/>
` ,
})
export class DarkModeToggleComponent {
themeService = inject ( MagaryThemeService );
isDark = () => this . themeService . currentTheme () === 'dark' ;
toggleDarkMode () {
const newTheme = this . isDark () ? 'light' : 'dark' ;
this . themeService . setTheme ( newTheme );
}
}
Dark Theme CSS Variables
Example dark theme variable overrides:
[ data-theme = ' dark ' ] {
/* Inverted Surface Colors */
--surface-0 : #0f172a ;
--surface-50 : #1e293b ;
--surface-100 : #334155 ;
--surface-900 : #f8fafc ;
/* Inverted Text */
--text-primary : #f8fafc ;
--text-secondary : #cbd5e1 ;
--text-tertiary : #64748b ;
/* Glassmorphism for Dark */
--glass-bg : rgba ( 15 , 23 , 42 , 0.85 );
--glass-border : 1 px solid rgba ( 100 , 116 , 139 , 0.2 );
--glass-shadow : 0 8 px 32 px 0 rgba ( 0 , 0 , 0 , 0.3 );
/* Adjusted Shadows */
--shadow-sm : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.3 );
--shadow-md : 0 4 px 12 px rgba ( 0 , 0 , 0 , 0.4 );
--shadow-lg : 0 12 px 24 px -4 px rgba ( 0 , 0 , 0 , 0.5 );
}
Glassmorphism Styling
Understanding Glassmorphism
Glassmorphism creates depth through:
Semi-transparent backgrounds
Backdrop blur effects
Subtle borders
Soft shadows
Using Glass Effects in Components
< magary-card
[backgroundColor] = "'var(--glass-bg)'"
[border] = "'var(--glass-border)'"
[shadow] = "3"
[style.backdrop-filter] = "'var(--backdrop-blur)'"
>
< h2 > Glassmorphism Card </ h2 >
< p > Beautiful depth and transparency! </ p >
</ magary-card >
Custom Glass Component
glass-panel.component.scss
.glass-panel {
background : var ( --glass-bg );
border : var ( --glass-border );
border-radius : var ( --border-radius );
padding : 2 rem ;
backdrop-filter : var ( --backdrop-blur );
-webkit-backdrop-filter : var ( --backdrop-blur );
box-shadow : var ( --glass-shadow );
& :hover {
background : rgba ( 255 , 255 , 255 , 0.95 );
box-shadow : 0 12 px 48 px 0 rgba ( 31 , 38 , 135 , 0.12 );
}
}
Browser Support
Backdrop Filter Support : The backdrop-filter property is supported in modern browsers but may not work in older versions. Provide fallback backgrounds.
.glass-element {
/* Fallback for older browsers */
background : rgba ( 255 , 255 , 255 , 0.9 );
/* Modern browsers with backdrop-filter support */
@supports ( backdrop-filter : blur ( 10 px )) {
background : var ( --glass-bg );
backdrop-filter : var ( --backdrop-blur );
}
}
Advanced Theming Techniques
Per-Component Theme Overrides
< magary-card [style] = "{
'--surface-0': '#e0f2fe',
'--text-primary': '#0c4a6e'
}" >
< p > This card has custom colors! </ p >
</ magary-card >
Dynamic Theme Generation
import { Component , effect , inject } from '@angular/core' ;
import { MagaryThemeService } from 'ng-magary' ;
@ Component ({
selector: 'app-dynamic-theme' ,
template: `...` ,
})
export class DynamicThemeComponent {
themeService = inject ( MagaryThemeService );
constructor () {
effect (() => {
const theme = this . themeService . currentTheme ();
this . applyDynamicColors ( theme );
});
}
applyDynamicColors ( theme : string ) {
const root = document . documentElement ;
if ( theme === 'custom' ) {
root . style . setProperty ( '--primary-500' , '#ff6b6b' );
root . style . setProperty ( '--accent-500' , '#4ecdc4' );
}
}
}
Theme Transition Animations
// Smooth theme transitions
:root {
transition :
background-color 0.3 s ease ,
color 0.3 s ease ;
}
* {
transition :
background-color 0.3 s ease ,
border-color 0.3 s ease ,
color 0.3 s ease ;
}
Add transition-duration: 0s to rapidly changing elements to avoid performance issues.
Best Practices
Use CSS Variables Always reference CSS variables instead of hard-coded colors for theme consistency.
Test All Themes Verify your custom styles work across all built-in themes, especially dark mode.
Respect System Preferences Honor user’s system dark mode preference on first visit.
Provide Contrast Ensure sufficient color contrast ratios (WCAG AA: 4.5:1 for text).
Troubleshooting
Theme not persisting after reload
Issue : Theme resets to default on page refresh.Solution : Ensure localStorage is enabled and not blocked:// Check localStorage access
try {
localStorage . setItem ( 'test' , 'test' );
localStorage . removeItem ( 'test' );
console . log ( 'localStorage is available' );
} catch ( e ) {
console . error ( 'localStorage is blocked' );
}
Glassmorphism not working
Issue : Glass effects appear as solid backgrounds.Solution :
Check browser support for backdrop-filter
Ensure parent elements don’t have overflow: hidden
Verify backdrop element exists behind glass element
Custom theme not applying
Issue : Custom theme colors don’t appear.Solution :
Ensure data-theme attribute is set on <body>:
document . body . setAttribute ( 'data-theme' , 'ocean' );
Check CSS selector specificity:
// More specific
body [ data-theme = ' ocean ' ] {
--primary-500 : #0ea5e9 ;
}
Colors look wrong in dark mode
Issue : Some elements are hard to read in dark theme.Solution : Define proper dark theme variable overrides:[ data-theme = ' dark ' ] {
--text-primary : #f8fafc ; // Light text
--surface-0 : #0f172a ; // Dark background
// Invert surface colors
--surface-900 : var ( --surface-50 );
}
Next Steps
Component Library Explore all components and their theming options
Styling Guide Learn about Magary’s CSS organization and best practices
Accessibility Ensure your custom themes meet accessibility standards
Examples View theme examples in the live demo
Create beautiful, accessible themes that delight your users! 🎨