Angular Material: Theming

Angular Material: Theming

Overview

Angular Material allows you to theme all components based on specified colors. For highly customized themes, reading through the source code can provide deeper insights. The article will walk through several common SASS functions with example code, and hope it helps build Angular material themes.

Prerequisite: SASS

It’s the basic before reading source code from Angular material, so if you never heard of SASS before, please read through the section before proceeding.

What is SASS?

SASS (Syntactically Awesome Style Sheets) is a CSS preprocessor that extends the capabilities of standard CSS. It provides features that don't exist in CSS yet, such as variables, nesting, mixins, and more, allowing you to write more maintainable and organized stylesheets.

Key Features of SASS

  1. Variables: SASS allows you to define variables to store colors, font stacks, or any CSS value you want to reuse throughout your stylesheets.
  2. Nesting: You can nest your CSS selectors in a way that follows the same visual hierarchy of your HTML, making your stylesheets more readable and organized.
  3. Partials: SASS lets you split your CSS into smaller, more manageable files called partials. These can be imported into other SASS files, promoting modularity.
  4. Mixins: These allow you to define reusable pieces of CSS code that you can include in other rules.
  5. Extend/Inheritance: This feature lets you share a set of CSS properties from one selector to another, reducing code repetition.

How SASS Works

SASS files must be preprocessed before they can be used on your website. The SASS preprocessor compiles your SASS code into standard CSS that browsers can understand. This process is called transpiling.

SASS Syntax

SASS has two syntaxes:

  1. SCSS (.scss): This is the most commonly used syntax. It's a superset of CSS, meaning all valid CSS is also valid SCSS.
  2. Indented Syntax (.sass): This uses indentation instead of brackets to nest statements and newlines instead of semicolons to separate them.

Example of SASS Code

Here's a simple example of SASS code using variables and nesting:

$font-stack: Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;

  nav {
    ul {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    li { display: inline-block; }

    a {
      display: block;
      padding: 6px 12px;
      text-decoration: none;
    }
  }
}

        

Benefits of Using SASS

  1. Improved Organization: SASS helps keep your stylesheets organized and easier to maintain.
  2. Time-Saving: Features like variables and mixins reduce repetition and save time.
  3. Easy to Learn: SASS is easy to learn, especially if you're already familiar with CSS..
  4. Compatibility: SASS is fully compatible with all versions of CSS.

To start using SASS, you'll need to set it up in your project and use a preprocessor to compile your SASS code into CSS.

Prerequisite: Angular version

As writing the article, I’m using Angular version 17, and some syntax are different in the version 18, which is the latest version at the moment. However, the conecpts are universal with some rules being changes by the Angular material 3 (m3). Please refer to the latest documentation for more details.

Source code

The source code link might be invalid afterward, but if you’re interested in how the code is structured, please follow the URL structure to locate the file.

// <https://github.com/angular/components/blob/7cf8c6c464732af4283c5e3c3c724dd90acc4136/src/material/core/m2/_theming.scss>
/// Creates a map of hues to colors for a theme. This is used to define a theme palette in terms
/// of the Material Design hues.
/// @param {Map} $base-palette Map of hue keys to color values for the basis for this palette.
/// @param {String | Number} $default Default hue for this palette.
/// @param {String | Number} $lighter "lighter" hue for this palette.
/// @param {String | Number} $darker "darker" hue for this palette.
/// @param {String | Number} $text "text" hue for this palette.
/// @returns {Map} A complete Angular Material theming palette.
@function define-palette($base-palette, $default: 500, $lighter: 100, $darker: 700,
  $text: $default) {
  $result: map.merge($base-palette, (
    default: _get-color-from-palette($base-palette, $default),
    lighter: _get-color-from-palette($base-palette, $lighter),
    darker: _get-color-from-palette($base-palette, $darker),
    text: _get-color-from-palette($base-palette, $text),
    default-contrast: get-contrast-color-from-palette($base-palette, $default),
    lighter-contrast: get-contrast-color-from-palette($base-palette, $lighter),
    darker-contrast: get-contrast-color-from-palette($base-palette, $darker)
  ));

  // For each hue in the palette, add a "-contrast" color to the map.
  @each $hue, $color in $base-palette {
    $result: map.merge($result, (
      '#{$hue}-contrast': get-contrast-color-from-palette($base-palette, $hue)
    ));
  }

  @return $result;
}
        

Custom theme example

Let’s define a custom theme from scratch using Angular Material's theming functions.

// variables.scss
@use '@angular/material' as mat;
@include mat.core();

// Define custom palettes
$primary-palette: mat.define-palette(
  (
    50: #e0f2f1,
    100: #b2dfdb,
    200: #80cbc4,
    300: #4db6ac,
    400: #26a69a,
    500: #009688,
    600: #00897b,
    700: #00796b,
    800: #00695c,
    900: #004d40,
    contrast: (
      50: rgba(black, 0.87),
      100: rgba(black, 0.87),
      200: rgba(black, 0.87),
      300: rgba(black, 0.87),
      400: white,
      500: white,
      600: white,
      700: white,
      800: white,
      900: white,
    ),
  )
);
// other palettes
// ...

// the 500 represents the default hue for the theme
$primary: mat.define-palette($primary-palette, 500);
$secondary: mat.define-palette($secondary-palette, 500);
$accent: mat.define-palette($accent-palette, 500);
$warn: mat.define-palette($warn-palette, 500);
$remark: mat.define-palette($remark-palette, 500);
        
@use '@angular/material' as mat;
@use 'variables' as var;
@include mat.core();

html,
body {
  height: 100%;
}

body {
  margin: 0;
}

$primary-theme: mat.define-light-theme(
  (
    color: (
      primary: var.$primary,
      accent: var.$accent,
      warn: var.$warn,
    ),
    typography: var.$typography,
    density: -2,
  )
);

// define custom theme mixin with theme
@mixin custom-button-toggle-theme($theme) {
  // custome css class
  .custom-mat-button-toggle {
    background-color: mat.get-color-from-palette(var.$primary, 500) !important;
    color: mat.get-color-from-palette(var.$primary, 500-contrast) !important;
  }
}

@include custom-button-toggle-theme($primary-theme);
@include mat.all-component-themes($primary-theme);

.secondary {
  background-color: mat.get-color-from-palette(var.$secondary, 500) !important;
  color: mat.get-color-from-palette(var.$secondary, 500-contrast) !important;
}

.remark {
  background-color: mat.get-color-from-palette(var.$remark, 500) !important;
  color: mat.get-color-from-palette(var.$remark, 500-contrast) !important;
}

.icon-contrast {
  color: mat.get-color-from-palette(var.$remark, 500-contrast) !important;
}        

The angular palette takes three colors: primary, accent, and warn. There are some points:

  1. We can use global styles with defined palettes if those components don’t take color attributes. For instance, the secondary and the remark classes.
  2. We can also use a custom mixin to group a set of CSS to be included.

Conclusion

We walked through the SASS basics of using theme customization and the source code (m2) to define palettes from scratch. Finally, there’s a given palette example starting from scratch. As Angular Material evolves to version 3, several changes have been made, but the core concepts in theming remain consistent.

To view or add a comment, sign in

More articles by Guan Xin Wang

Others also viewed

Explore content categories