Egret Admin Dashboard

Egret Documentation

Theming

Theming Overview

Egret Angular uses a powerful and flexible theming system that combines Angular Material's theming capabilities with CSS custom properties (variables) and Tailwind CSS. This approach allows for dynamic theme switching, customization, and consistent styling across the application.

Theming Architecture

The theming system in Egret Angular is built on three main components:

  • Angular Material Theming: Provides the foundation for component styling.
  • CSS Custom Properties: Enables dynamic theme switching without page reloads.
  • Tailwind CSS Integration: Offers utility classes that respect the current theme.
Available Themes

Egret Angular comes with two pre-configured themes:

  • Light Theme (egret-navy): The default theme with a light background and navy blue primary color.
  • Dark Theme (egret-navy-dark): A dark theme variant with indigo as the primary color.
Light Theme Colors
Primary: #0f174c
Accent: #ff8a48
Warn: #ff3d57
Success: #22C55E
Dark Theme Colors
Primary: #3F51B5
Accent: #ff8a48
Warn: #ff3d57
Success: #22C55E
Sidebar Themes

In addition to the main application themes, Egret Angular provides two sidebar themes that can be used independently:

  • sidebar-light: A light sidebar with dark text.
  • sidebar-dark: A dark sidebar with light text.

This allows you to mix and match, for example, using a light theme for the main application with a dark sidebar.

Switching Themes

Egret provides two ways to configure themes:

1. Build-time Theme Configuration

To set the default material and sidebar themes during build time, modify the setAppLayout method in layout.service.ts:

// In layout.service.ts
setAppLayout() {
  // ********** SET DEFAULT LAYOUT **********
  let defaultLayout: ILayoutConf = {
    ****** other options **********
    sidebarColor: 'sidebar-dark', // colors are defined in color-tokens.scss
    matTheme: 'egret-navy', // egret-navy, egret-navy-dark
  };
}
2. Runtime Theme Switching

For runtime theme changes, use the publishLayoutChange method from the LayoutService. This approach ensures that theme changes are properly applied and persisted to localStorage.

import { LayoutService } from 'app/shared/services/layout.service';

@Component({...})
export class YourComponent {
  constructor(private layoutService: LayoutService) {}
  
  // Switch to dark theme
  switchToDarkTheme() {
    this.layoutService.publishLayoutChange({
      matTheme: 'egret-navy-dark'
    });
  }
  
  // Switch to light theme
  switchToLightTheme() {
    this.layoutService.publishLayoutChange({
      matTheme: 'egret-navy'
    });
  }

  // Change sidebar theme
  setSidebarTheme(theme: string) {
    this.layoutService.publishLayoutChange({
      sidebarColor: theme // 'sidebar-dark' or 'sidebar-light'
    });
  }
}

The publishLayoutChange method automatically:

  • Updates the active theme using ThemeService
  • Updates the layout configuration
  • Persists the theme selection to localStorage using the key defined in config.themeLocalStorageKey
  • Notifies subscribers about the layout change
How Theming Works

The theming system works through several key files:

1. Color Tokens (_color-tokens.scss)

This file defines the base color palette and semantic color mappings for both light and dark themes.

// Base colors
$navy-blue: #0f174c;
$blue-default: #0081ff; 
$accent-orange: #ff8a48;
$warn-red: #ff3d57;

// Light theme semantic colors
$light-theme-colors: (
  color-primary: $navy-blue,
  color-accent: $accent-orange,
  color-warn: $warn-red,
  // ...other mappings
);

// Dark theme semantic colors
$dark-theme-colors: (
  color-primary: $indigo,
  color-accent: $accent-orange,
  color-warn: $warn-red,
  // ...other mappings
);
2. CSS Variables (_css-vars.scss)

This file converts the SCSS color tokens into CSS custom properties that can be used throughout the application.

// Light theme
.egret-navy {
  // Set semantic colors from light theme
  @each $key, $value in colors.$light-theme-colors {
    --#{$key}: #{get-rgb($value)};
  }
}

// Dark theme
.egret-navy-dark {
  // Dark theme semantic colors
  @each $key, $value in colors.$dark-theme-colors {
    --#{$key}: #{get-rgb($value)};
  }
}
3. Angular Material Theme Integration (_init.scss)

This file sets up Angular Material's theming system to work with our CSS variables and defines typography.

// Define typography
$egret-typography: mat.m2-define-typography-config(
  $font-family: $font-family-base,
  $headline-1: mat.m2-define-typography-level(1.875rem, 2.25rem, 800, $font-family-base),
  // ...other typography levels
);

// Generate static primary palette for Angular Material
$static-primary-light-palette: (
  50: color.adjust($static-primary-light, $lightness: 40%),
  // ...other palette entries
);
4. Tailwind Integration (tailwind.config.js)

This file extends Tailwind CSS to use our theme's CSS variables, ensuring consistency across utility classes.

module.exports = {
  // Support for dark mode via the .egret-navy-dark class
  darkMode: ['class', '.egret-navy-dark'],
  theme: {
    extend: {
      colors: {
        secondary: 'rgb(var(--fg-secondary))',
        // Background and foreground colors
        bg: {
          base: 'rgb(var(--bg-base))',
          card: 'rgb(var(--bg-card))',
          // ...other mappings
        },
        // ...other color extensions
      },
      // ...other theme extensions
    },
  },
};
Theme Persistence

Egret persists the theme selection to localStorage. This is handled by the ThemeService's setActiveTheme method:

// In theme.service.ts
private setActiveTheme(theme: ThemeConfig): void {
  // Remove all existing theme classes
  this.availableThemes.forEach(t => {
    this.renderer.removeClass(this.documentElement, t.id);
  });
  
  // Add active theme class
  this.renderer.addClass(this.documentElement, theme.id);
  
  // Add/remove dark theme class
  if (theme.mode === 'dark') {
    this.renderer.addClass(this.documentElement, 'egret-navy-dark');
    this.renderer.addClass(this.bodyElement, 'egret-navy-dark');
  } else {
    this.renderer.removeClass(this.documentElement, 'egret-navy-dark');
    this.renderer.removeClass(this.bodyElement, 'egret-navy-dark');
  }
  
  // Save to localStorage
  localStorage.setItem(config.themeLocalStorageKey, theme.id);
}

The theme selection is preserved between sessions, ensuring a consistent user experience.

To change themes during runtime, use the LayoutService:

// Change theme using LayoutService
layoutService.publishLayoutChange({
  matTheme: 'egret-navy-dark' // or 'egret-navy' for light theme
});
Customizing Themes

To customize the existing themes or create new ones, you can modify the following files:

  1. _color-tokens.scss: Modify the base colors or semantic color mappings.
    // Change the primary color for light theme
    $light-theme-colors: (
      color-primary: #1976d2, // New primary color
      // ...other mappings remain the same
    );
  2. _css-vars.scss: Add new theme classes following the pattern of existing themes.
    // Add a new theme
    .egret-purple {
      // Set semantic colors for the new theme
      @each $key, $value in $purple-theme-colors {
        --#{$key}: #{get-rgb($value)};
      }
    }
  3. tailwind.config.js: Update the darkMode configuration if adding new dark themes.
    // Add support for multiple dark themes
    darkMode: ['class', '.egret-navy-dark', '.egret-purple-dark'],

After making changes to these files, you'll need to rebuild the application for the changes to take effect.

Using Theme Colors in Components

There are several ways to use theme colors in your components:

1. Using Tailwind CSS Classes

The simplest way is to use Tailwind CSS utility classes that reference theme variables:

<div class="bg-card text-base p-4 rounded-lg shadow-card">
  <h2 class="text-primary font-medium">Card Title</h2>
  <p class="text-secondary">Text with muted color.</p>
</div>
2. Using CSS Variables Directly

You can use CSS variables directly in your component styles:

// In component SCSS
.custom-element {
  background-color: rgb(var(--bg-card));
  color: rgba(var(--fg-base), 0.87);
  border: 1px solid rgb(var(--color-primary));
}
3. Using Angular Material Components

Angular Material components automatically use the current theme:

<button mat-raised-button color="primary">Primary Button</button>
<button mat-raised-button color="accent">Accent Button</button>
<button mat-raised-button color="warn">Warn Button</button>
Best Practices
  • Use semantic color variables instead of hardcoded color values to ensure theme consistency.
  • Prefer Tailwind utility classes for styling when possible, as they automatically respect the current theme.
  • Test your UI in both light and dark themes to ensure good contrast and readability.
  • Use the ThemeService for programmatic theme switching rather than directly manipulating CSS classes.
  • When creating custom components, follow the pattern of existing components to ensure they work with theme switching.