# Theming Guide Concept is designed with theming in mind, allowing you to create custom themes that match your brand or project requirements. This guide covers everything from simple color changes to complete theme systems. ## Theme Structure ### Theme Layers Concept's theming system works in layers: 1. **Bootstrap Core** - Base Bootstrap 5 variables 2. **Concept Defaults** - Template-specific customizations 3. **Custom Theme** - Your theme overrides 4. **Component Overrides** - Specific component tweaks ### Key Theme Files ``` src/scss/ ├── _variables.scss # Global variables ├── themes/ │ ├── _default.scss # Default theme │ ├── _dark.scss # Dark theme │ └── _custom.scss # Your custom theme ├── main.scss # Import orchestration └── custom/ └── _theme-overrides.scss # Runtime overrides ``` ## Creating a Custom Theme ### Step 1: Create Theme File Create a new theme file: ```scss // src/scss/themes/_corporate.scss // Theme name and metadata $theme-name: "corporate"; $theme-description: "Professional corporate theme"; // Color palette $theme-colors: ( "primary": #003366, "secondary": #0066cc, "success": #00a86b, "info": #17a2b8, "warning": #ffc107, "danger": #dc3545, "light": #f8f9fa, "dark": #001f3f ); // Extended colors $theme-extended-colors: ( "brand": #002244, "accent": #0088ff, "muted": #6c757d, "highlight": #ffeb3b ); // Typography $theme-fonts: ( "base": "'Inter', -apple-system, BlinkMacSystemFont, sans-serif", "heading": "'Poppins', sans-serif", "mono": "'JetBrains Mono', monospace" ); // Spacing $theme-spacing: ( "tight": 0.25rem, "normal": 1rem, "relaxed": 1.5rem, "loose": 2rem ); // Component specific $theme-components: ( "border-radius": 0.375rem, "border-radius-sm": 0.25rem, "border-radius-lg": 0.5rem, "box-shadow": 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075), "box-shadow-lg": 0 0.5rem 1rem rgba(0, 0, 0, 0.15) ); ``` ### Step 2: Apply Theme Variables Update your main variables file: ```scss // src/scss/_variables.scss // Import theme @import "themes/corporate"; // Apply theme colors to Bootstrap $primary: map-get($theme-colors, "primary"); $secondary: map-get($theme-colors, "secondary"); $success: map-get($theme-colors, "success"); $info: map-get($theme-colors, "info"); $warning: map-get($theme-colors, "warning"); $danger: map-get($theme-colors, "danger"); $light: map-get($theme-colors, "light"); $dark: map-get($theme-colors, "dark"); // Apply theme fonts $font-family-base: map-get($theme-fonts, "base"); $headings-font-family: map-get($theme-fonts, "heading"); $font-family-monospace: map-get($theme-fonts, "mono"); // Apply component settings $border-radius: map-get($theme-components, "border-radius"); $border-radius-sm: map-get($theme-components, "border-radius-sm"); $border-radius-lg: map-get($theme-components, "border-radius-lg"); $box-shadow: map-get($theme-components, "box-shadow"); $box-shadow-lg: map-get($theme-components, "box-shadow-lg"); ``` ### Step 3: Create Theme Utilities Add theme-specific utility classes: ```scss // src/scss/themes/_corporate-utilities.scss // Extended color utilities @each $name, $color in $theme-extended-colors { .text-#{$name} { color: $color !important; } .bg-#{$name} { background-color: $color !important; } .border-#{$name} { border-color: $color !important; } .btn-#{$name} { @include button-variant($color, $color); } } // Spacing utilities @each $name, $space in $theme-spacing { .spacing-#{$name} { padding: $space; margin: $space; } } ``` ## Dark Mode Implementation ### CSS Variables Approach ```scss // src/scss/themes/_dark-mode.scss :root { // Light mode defaults --bg-primary: #ffffff; --bg-secondary: #f8f9fa; --text-primary: #212529; --text-secondary: #6c757d; --border-color: #dee2e6; --shadow-color: rgba(0, 0, 0, 0.1); } [data-theme="dark"] { // Dark mode overrides --bg-primary: #1a1a1a; --bg-secondary: #2d2d2d; --text-primary: #ffffff; --text-secondary: #adb5bd; --border-color: #495057; --shadow-color: rgba(0, 0, 0, 0.3); // Component overrides --card-bg: var(--bg-secondary); --sidebar-bg: #161616; --header-bg: var(--bg-secondary); } // Apply variables body { background-color: var(--bg-primary); color: var(--text-primary); } .card { background-color: var(--card-bg); border-color: var(--border-color); } ``` ### JavaScript Theme Switcher ```javascript // src/js/theme-switcher.js export class ThemeSwitcher { constructor() { this.theme = this.loadTheme() || 'light'; this.init(); } init() { // Apply saved theme this.applyTheme(this.theme); // Setup toggle button const toggle = document.getElementById('theme-toggle'); if (toggle) { toggle.addEventListener('click', () => this.toggleTheme()); } // Watch for system preference changes if (window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)') .addEventListener('change', e => { if (this.theme === 'auto') { this.applySystemTheme(); } }); } } toggleTheme() { const themes = ['light', 'dark', 'auto']; const currentIndex = themes.indexOf(this.theme); this.theme = themes[(currentIndex + 1) % themes.length]; this.applyTheme(this.theme); this.saveTheme(this.theme); } applyTheme(theme) { if (theme === 'auto') { this.applySystemTheme(); } else { document.documentElement.setAttribute('data-theme', theme); } this.updateToggleButton(theme); } applySystemTheme() { const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light'); } updateToggleButton(theme) { const toggle = document.getElementById('theme-toggle'); if (toggle) { const icon = toggle.querySelector('i'); icon.className = theme === 'dark' ? 'fa-solid fa-sun' : theme === 'auto' ? 'fa-solid fa-circle-half-stroke' : 'fa-solid fa-moon'; } } saveTheme(theme) { localStorage.setItem('concept-theme', theme); } loadTheme() { return localStorage.getItem('concept-theme'); } } // Auto-initialize new ThemeSwitcher(); ``` ## Theme Presets ### Minimal Theme ```scss // src/scss/themes/_minimal.scss $theme-colors: ( "primary": #000000, "secondary": #666666, "success": #00c853, "danger": #ff1744, "light": #ffffff, "dark": #000000 ); $theme-components: ( "border-radius": 0, "box-shadow": none, "border-width": 1px ); // Minimal typography $font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; $headings-font-weight: 300; $btn-font-weight: 400; ``` ### Material Design Theme ```scss // src/scss/themes/_material.scss $theme-colors: ( "primary": #1976d2, "secondary": #dc004e, "success": #388e3c, "info": #0288d1, "warning": #f57c00, "danger": #d32f2f ); $theme-components: ( "border-radius": 4px, "box-shadow": 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12), "transition": all 0.3s cubic-bezier(0.4, 0, 0.2, 1) ); // Material typography $font-family-base: "Roboto", sans-serif; $headings-font-weight: 400; ``` ### Gradient Theme ```scss // src/scss/themes/_gradient.scss // Gradient backgrounds $gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); $gradient-success: linear-gradient(135deg, #00c853 0%, #00e676 100%); $gradient-danger: linear-gradient(135deg, #ff5252 0%, #ff1744 100%); // Apply to buttons .btn-primary { background: $gradient-primary; border: none; &:hover { background: linear-gradient(135deg, #5a67d8 0%, #6b46a1 100%); } } // Gradient cards .card-gradient { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); border: none; } // Gradient text .text-gradient { background: $gradient-primary; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } ``` ## Component Theming ### Sidebar Theming ```scss // src/scss/components/_sidebar-theme.scss .sidebar { // Use theme variables background-color: var(--sidebar-bg, $sidebar-bg); color: var(--sidebar-color, $sidebar-color); // Dark sidebar variant &.sidebar-dark { --sidebar-bg: #1e1e1e; --sidebar-color: #ffffff; --sidebar-link-color: rgba(255, 255, 255, 0.8); --sidebar-link-hover: #ffffff; --sidebar-border: rgba(255, 255, 255, 0.1); } // Light sidebar variant &.sidebar-light { --sidebar-bg: #ffffff; --sidebar-color: #495057; --sidebar-link-color: #495057; --sidebar-link-hover: #000000; --sidebar-border: rgba(0, 0, 0, 0.1); } // Gradient sidebar &.sidebar-gradient { background: linear-gradient(180deg, var(--gradient-start) 0%, var(--gradient-end) 100%); } } ``` ### Card Theming ```scss // src/scss/components/_card-theme.scss .card { // Theme variants &.card-minimal { border: none; box-shadow: none; background-color: transparent; } &.card-outlined { background-color: transparent; border-width: 2px; } &.card-elevated { border: none; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); &:hover { transform: translateY(-5px); box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15); } } &.card-glassmorphic { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); } } ``` ## Theme Configuration ### Theme Manager ```javascript // src/js/theme-manager.js export class ThemeManager { constructor() { this.themes = { default: () => import('./themes/default'), dark: () => import('./themes/dark'), corporate: () => import('./themes/corporate'), minimal: () => import('./themes/minimal') }; this.currentTheme = 'default'; this.init(); } async init() { const savedTheme = localStorage.getItem('selected-theme') || 'default'; await this.loadTheme(savedTheme); } async loadTheme(themeName) { if (!this.themes[themeName]) { console.error(`Theme "${themeName}" not found`); return; } try { // Remove previous theme this.removeTheme(this.currentTheme); // Load new theme const theme = await this.themes[themeName](); theme.apply(); this.currentTheme = themeName; localStorage.setItem('selected-theme', themeName); // Emit theme change event window.dispatchEvent(new CustomEvent('theme-changed', { detail: { theme: themeName } })); } catch (error) { console.error('Failed to load theme:', error); } } removeTheme(themeName) { document.documentElement.classList.remove(`theme-${themeName}`); } getAvailableThemes() { return Object.keys(this.themes); } getCurrentTheme() { return this.currentTheme; } } ``` ### Theme Settings UI ```html