AngularT2 Dark_1200x303

Learn how to create dynamic styles with CSS custom properties and Angular’s style binding.

An exciting feature that came in Angular 9 is the ability to bind to CSS custom properties using style binding. Style binding lets us declare updates to an element’s style property in the component template, freeing the component class from implementing code for style manipulations.

The style property is an object that represents an element’s inline CSS declaration block. Most importantly, an inline declaration block can have a CSS property and CSS custom property declarations. We can update both the CSS property and the CSS custom property declarations at runtime to dynamically style elements.

So, what are CSS custom properties? And why would we use them when we can already dynamically style elements by updating CSS properties?

CSS custom properties are a relatively new feature of CSS that allows application developers to define their own CSS properties. CSS custom properties are used to hold values that can then be used in property declarations to style elements.

CSS custom properties offer exciting possibilities. To name a few, CSS custom properties:

  • Allow us to define a value once and use it in multiple property declarations, making the code DRYer and easier to maintain
  • Make the code easier to read by giving meaning to arbitrary values
  • Can be used wherever a value is expected, including in CSS functions, such as hsla() and calc(), and in shorthand properties
  • Are native, so like CSS properties they cascade and are subject to inheritance
  • Can be queried and manipulated at runtime with TypeScript/JavaScript code to create dynamic styles

You can read about the relationship between the HTML style attribute and DOM style property as well as the similarities and differences between CSS properties and CSS custom properties in my article on Understanding the DOM Style Property to Create Dynamic Styles.

In this article we will learn how to:

  • Use style binding to dynamically set and update CSS custom properties
  • Use the var() function to access CSS custom property values in style rules
  • Determine the scope for CSS custom properties
  • Declare CSS custom properties on the host element using @HostBinding
  • Assign CSS custom properties to other CSS custom properties
  • Use CSS custom properties in CSS functions like hsla() and calc()

Demo Application

Let’s build a demo application that allows users to select the hue, saturation, lightness and opacity color values. We will create CSS custom properties to store these input values, then use them to style the application.

To create CSS custom properties, we need to declare them in a CSS declaration block. We will use style binding to declare the CSS custom properties inline and bind them to the input FormControl values.

Here is an example of the application in StackBlitz.

Style Binding Syntax

As mentioned earlier, from Angular V9, we can create and update inline CSS custom property declarations using style binding.

The syntax for binding to CSS custom properties is the same as for binding to CSS properties. There are two options:

  1. We can bind to individual properties:
<div [style.--css-custom-property-name]="template-expression"></div>
  1. Alternatively, from Angular 9, we can set and update the entire inline CSS declaration block by binding to the style property. In this case, we do not specify a CSS property or custom CSS property to bind to:
<div [style]="<template-expression>"></div>

The template expression should evaluate to a string containing the inline CSS declaration block.

Define CSS Custom Properties with Style Binding

Great! Let’s get started on our demo application by creating CSS custom property declarations to hold the input values for hue, saturation, lightness and opacity.

We could declare the CSS custom properties in individual bindings like so:

<div [style.--hue]="hue.value"
  [style.--saturation.%]="saturation.value"
  [style.--lightness.%]="lightness.value"
  [style.--opacity]="opacity.value">
</div>

Or, alternatively, we could declare all the CSS custom properties in a single style binding using the newer syntax, as shown below:

@Component({
  template: `
  <div [style]="getStyles()"><div>`
})

export class HslaComponent {
  // FormControl declarations go here
  getStyles() {
  return `--hue: ${this.hue.value},
    --saturation: ${this.saturation.value},
    --lightness: ${this.lightness.value},
    --opacity: ${this.opacity.value}`;
  }
}

The getStyles() method returns the CSS declaration block with the CSS custom property declarations.

I am going to use individual property bindings, like in the first option, in this article.

Naming CSS Custom Properties

The two dashes (--) in front of the property names denote CSS custom properties.

We can make our code easy to read and maintain by giving the CSS custom properties descriptive names.

The style bindings above will do two things. Let’s look at these next.

Declares CSS Custom Properties

The style bindings will create inline declarations for --hue, --saturation, --lightness and --opacity custom properties, setting their values to the respective FormControl values.

Remember that a CSS declaration is a property and value pair.

The end result will be parallel to this:

<div style="--hue: 320;
  --saturation: 100%;
  --lightness: 50%;
  --opacity: 1;">
</div>

if the FormControl values were initialized as such:

class DemoComponent {
  hue = new FormControl(320);
  saturation = new FormControl(100);
  lightness = new FormControl(50);
  opacity = new FormControl(1);
}

Remember that the style property represents an element’s inline CSS declarations, so our CSS custom properties are declared inline on the div element.

Automatically Updates CSS Custom Properties

Secondly, the style binding will automatically update the CSS custom property values whenever the FormControl values change. That is, whenever the user changes the input value.

Specify Unit as Part of Style Binding

The saturation and lightness values in the hsla() function need to be specified as percentages.

Instead of adding the percentage (%) in our template expression, like so:

<div [style.--saturation]="saturation.value + '%'"
  [style.--lightness]="lightness.value + '%'">
</div>

or adding it with the calc() function:

.color-demo {
  background-color: hsla(
    var(--hue),
    calc(var(--saturation) * 1%),
    calc(var(--lightness) * 1%),
    var(--opacity));
}

We can simply specify the unit as part of the style binding:

<div [style.--saturation.%]="saturation.value"
  [style.--lightness.%]="lightness.value">
</div>

Style binding makes it super easy for us.

All right! Now that we have created CSS custom properties to hold the dynamic color input values, all we have to do is use these values in style rules.

Let’s see how to do that next.

Access CSS Custom Property Values in Style Rules

var() Function

We use the var() function to access CSS custom property values in CSS property declarations.

Let’s style background-color of our div element to display the color selected by the user. It is still CSS properties that are used to style elements.

We have a couple of options for where to define the declaration:

  1. Inline using the style attribute:
<div style="background-color: hsla(
  var(--hue),
  var(--saturation),
  var(--lightness),
  var(--opacity);"
  class="demo">
</div>
  1. Or, better still, in the stylesheet together with the other property declarations:
.demo {
  width: 1em;
  height: 1em;
  border-radius: 50%;
  background-color: hsla(
    var(--hue),
    var(--saturation),
    var(--lightness),
    var(--opacity)
  );
}

Computed Value

The browser will substitute the var(<custom-property-name>) functions to a computed value. The computed value is the CSS custom property value.

Resulting in something like this:

.demo { background-color: hsla(320, 100%, 50%, 1);}

Whenever the user input changes, the corresponding CSS custom property values will be updated through style binding. The var() function will be substituted to the new value, setting the background-color property accordingly.

For example, if the user changed hue to 0 and saturation to 80%:

.demo { background-color: hsla(0, 80%, 50%, 1);}

We can pass a fallback value to the var() function as the second argument.

.demo {
  background-color: var(--primary-color, mintcream)
}

If the CSS custom property value is invalid or the CSS custom property is not in scope, the fallback value will be used.

CSS Custom Property Scope

The scope of a CSS custom property is the DOM element that it is declared on.

The CSS custom properties declared inline are scoped to that element. The CSS custom properties declared in stylesheets are scoped to DOM elements identified by the CSS selector in the style rule.

Just like CSS properties, CSS custom properties are inherited, so the children of the matching DOM elements also inherit the CSS custom properties.

We created the --hue, --saturation, --lightness and --opacity CSS custom property declarations inline on a div element using style binding.

However, we may want to use these CSS custom properties to style other elements.

For example, it would be nice to display the selected hsla values in text. Let us put the color div and text in a container and style it with the user-selected color:

<div class="demo-container">
  <div [style.--hue]="hue.value"
    [style.--saturation.%]="saturation.value"
    [style.--lightness.%]="lightness.value"
    [style.--opacity]="opacity.value">
  </div>
  <p>
    hsla({{hue.value}},
    {{saturation.value}},
    {{lightness.value}},
    {{opacity.value}})
  </p>
</div>
.demo-container {
  border: 2px solid hsla(
    var(--hue),
    var(--saturation),
    var(--lightness),
    var(--opacity));
}

However, the parent div does not have access to the --hue, --saturation, --lightness and --opacity CSS custom properties.

As we mentioned earlier, the scope of the CSS custom properties is the element that they are declared on. They are only available to that element and its children through inheritance.

We can increase the scope of our CSS custom properties to the main element so all the elements in our component can access them:

<main [style.--hue]="hue.value"
  [style.--saturation.%]="saturation.value"
  [style.--lightness.%]="lightness.value"
  [style.--opacity]="opacity.value">
  <!--Input elements -->
  <!--Color demo -->
</main>

As a side note, CSS custom properties are available to all elements, including semantic HTML elements like <main>. So, we don’t need to create a <div> wrapper in order to declare CSS custom properties.

Assign CSS Custom Properties to Other CSS Custom Properties

Instead of composing the hsla function each time, let’s create a CSS custom property declaration to hold the hsla values. We can give it a descriptive name, like --user-selected-color or --primary-color:

main {
  --primary-color: hsla(
    var(--hue),
    var(--saturation),
    var(--lightness),
    var(--opacity));
}

And use it each time we need the color:

.demo-container {
  border: 2px solid var(--primary-color);
}
.demo-color {
  background-color: var(--primary-color);
}
.demo-text {
  color: var(--primary-color);
}

Note that we can still access the individual --hue, --saturation, --lightness and --opacity values if we wanted to. You’d recall that we defined them using style binding earlier.

Bind to CSS Custom Properties Using HostBinding

What if we wanted to style our host element using the user’s selected color?

We can use @HostBinding() to create and update CSS custom property declarations on our host component:

export class HslaComponent {
  hue = new FormControl(1);
  saturation = new FormControl(50);
  lightness = new FormControl(50);
  opacity = new FormControl(1);
@HostBinding('style.--primary-color')
  public get hslaColor() {
    return `hsla( ${this.hue.value},
    ${this.saturation.value}%,
    ${this.lightness.value}%,
    ${this.opacity.value})`;
  }
}

We can then use --primary-color in the style rules for the host:

:host {
  display: block;
  background-color: var(--primary-color);
}

As we mentioned earlier, the child elements inherit the CSS custom properties.

Use CSS Custom Properties in Calculations

CSS custom properties can be used in the calc() function.

To demonstrate the potential use, let’s create an accent color that is the complement of the user-selected color.

The complementary hue value is calculated by adding 180 degrees to the hue color value. Note, this calculation takes advantage of the fact that when the hue is larger than 360 degrees the calculated value is the number of degrees past 360.

  • Complement of 60 is 240:
    60 + 180 = 240

  • Complement of 240 is 60:
    240 + 180 = 420, and the computed hue is 420 - 360 = 60

Let us give the CSS custom property a descriptive name, --accent-color, and declare it on the main element so it is available to all the elements in the component:

main {
  --accent-color: hsla(
    calc(var(--hue) + 180),
    var(--saturation),
    var(--lightness),
    var(--opacity) );
}
select {
  background-color: var(--accent-color);
}

The value assigned to --accent-color has few parts to it:

  • We already have --hue, --saturation, --lightness and --opacity CSS custom properties that hold the user-selected hsla color values.
  • We pass these CSS custom properties to the hsla() function.
  • And use the calc() function to calculate the complement of the --hue value.

Although the explanation is long, the code is quite neat and compact!

Through the magic of style binding and CSS custom properties, each time the user changes the hsla values, our select element gets styled with its complementary color.

Use CSS Custom Property Values in Multiple Property Declarations

Similar to CSS preprocessor variables, CSS custom properties can be defined in one place and used in multiple places.

Let’s use the --primary-color to style the select element’s border and add a box-shadow on hover:

select {
  border: 1px solid var(--primary-color);
}

select:hover {
  box-shadow: 0 0 3px var(--primary-color);
}

We can assign --accent-color or --primary-color to any CSS properties that expect a color value.

Unlike CSS preprocessor variables, CSS custom properties have the added advantage of being dynamic.

Summary

Let us recap what we learned in this article.

CSS custom properties are an exciting feature of CSS that allow us to define our own CSS properties to hold style values. The var() function lets us access these values in style rules.

We can dynamically style applications by manipulating CSS custom properties at runtime. However, this can also be achieved by manipulating the built-in CSS properties. So why would we be interested in CSS custom properties?

In addition to manipulating CSS custom properties to dynamically style our applications, we can use them in multiple CSS property declarations, assign them to other CSS custom properties and use them in CSS functions like hsla() and calc().

Instead of implementing the code in our component classes to get references to view children and update their CSS custom properties, we can now use style binding and keep our component class lean.

From Angular 9, style binding includes binding to CSS custom properties as well as CSS properties making it really easy to use CSS custom properties for dynamic styling.

Below you can find an interactive StackBlitz example of an Angular clock with CSS custom properties.

Resources

https://www.w3.org/TR/css-variables

https://twitter.com/wnodom/status/1191137920570286080

https://stackblitz.com/edit/css-custom-prop-color-values?file=style.css

https://coryrylan.com/blog/theming-angular-apps-with-css-custom-properties

https://www.sitepoint.com/practical-guide-css-variables-custom-properties

https://www.smashingmagazine.com/2018/05/css-custom-properties-strategy-guide

https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties

https://www.smashingmagazine.com/2019/07/css-custom-properties-cascade

https://una.im/css-color-theming/

https://codepen.io/MadeByMike/pen/mLNzdW

https://codepen.io/MadeByMike/pen/YLQWeb

https://codepen.io/mirisuzanne


ashnita-bali
About the Author

Ashnita Bali

Ashnita is a frontend web developer who loves JavaScript and Angular. She is an organizer at GDGReading, a WomenTechmakers Ambassador and a mentor at freeCodeCampReading. Ashnita is passionate about learning and thinks that writing and sharing ideas are great ways of learning. Besides coding, she loves the outdoors and nature.

Related Posts

Comments

Comments are disabled in preview mode.