Css style

A guide to CSS variable scoping in Angular

In the article Binding CSS Styles to Events in Angular Applications, we learned some techniques for dynamically styling elements in Angular, including the use of CSS variables. The advantage of CSS variables is that they can be applied to pseudo-classes like hover and focus. Another feature of CSS variables that we haven’t covered is that they can be applied at the document level or also on individual elements. In this tutorial, we’ll explore some of the pros and cons of scoping global variables versus elements, and learn how to apply styles to specific elements at runtime.

Document-level variables vs. item

Similar to compiled CSS extensions like Sass and Less, CSS now supports its own “pure” variables. These are defined by prefixing a double dash (–) before the variable name. We would then pass the CSS variable to the var() function to access its value. You declare a CSS variable at the top of your CSS file, as in the first snippet below, or at the rule level, as shown in the second snippet:

1: Document level CSS variable declaration

--main-bg-color: brown;

.background {
  background-color: var(--main-bg-color);
}

2: Rule-level CSS variable declaration

.wrapper:focus {
  background-color: --focus-color;
}

When you apply the CSSStyleDeclaration’s setProperty() at the document level in TypeScript or JavaScript, the variable is added to the inline style attribute of the HTML tag:

document.documentElement.style.setProperty('--focus-color', this.textcolor);

This is equivalent to declaring a custom property on the :root pseudo-class in your stylesheet:

:root {
  --main-bg-color: brown;
}

Nothing wrong with that, but you should know that all items that refer to the —main-bg-color variable will be affected.

Best programming practices dictate that you should always try to declare your variables with the most limited scope possible. This advice stems from the old adage that “global variables are bad”. Whether or not you subscribe to this maxim, there can be many valid reasons for limiting the scope of your custom properties.

Here’s how we would apply the –focus-color to a specific DIV element, using TypeScript code:

@ViewChild("svgImage", { static: true }) 
private svgImageRef: ElementRef;

//later in the code
svgImageRef.nativeElement.style.setProperty('--focus-color', 'gray');

We now see that the CSS variable has been added to the inline style of this element:

CSS local focus color

An end-to-end example

Here is some CSS that defines two color variables: one for the hover background color, the other for the focus border color:

.news-image {
  $hoverColor: var(--hover-color);
  $focusColor: var(--focus-color);

  :hover {
    background-color: $hoverColor;
  }

  &:focus {
    outline: 2px solid $focusColor;
  }
}

Because variables are declared in a rule, the scope of the variable is limited to items that match the rule selector.

Recall that in the Binding CSS styles to events in Angular app demo, we used global CSS variables to define the three color preview squares. Now, let’s apply the same technique to the Feed component, but using localized variable declarations, like the one above.

In order to reference the news-image DIV in the TypeScript code, we will need to add the #svgImage template reference to DIV tag:

...

At the top of the Flow component class, you will see the same @To input variable declarations as before, as well as the reference to the news image DIV, courtesy of the @ViewChild entrance decorator:

export class FeedComponent implements AfterContentInit, OnChanges {
  @Input('background-color') 
  backgroundColor: string = 'blue';
  @Input('hover-background-color') 
  hoverBackgroundColor="cyan";
  @Input('focus-border-color') 
  focusBorderColor="#CCCCCC";

  @ViewChild('svgImage', { static: true })
  private svgImageRef: ElementRef
  //...
  
}

Maybe you noticed that the Flow component now implements the AfterContentInit and AboutChanges lifecycle hooks. Let’s review these now.

Variable initialization

Although you can access @To input variable in the ngOnInit event, you cannot reference DOM elements until ngAfterContentInit. The svgImageRef’s CSS style declaration It is stored in a private class member variable for later use. Then the hover and focus colors are set on the svgImageRef element:

private svgStyle: CSSStyleDeclaration;

ngAfterContentInit(): void {
  this.svgStyle = this.svgImageRef.nativeElement.style;
  this.svgStyle.setProperty(
    '--hover-color', this.hoverBackgroundColor);
  this.svgStyle.setProperty(
    '--focus-color', this.focusBorderColor);
}

Updating Variable Values

While the ngAfterContentInit The lifecycle hook sets CSS variables on the first app load, we need to implement another lifecycle hook for input variable updates, i.e. whenever the user enters data into the input fields and clicks the APPLY button. It’s there that ngOnChanges Between. He is called when a data-bound property of a directive changesin other words, whenever the values ​​of the component’s @Input variable change.

The first time ngOnChanges lights is very early in the application life cycle, in fact before ngOnInit(). Therefore, there is no point in updating CSS colors on the first go-around, because the DOM would not be ready. We can check whether or not this is the first run through the firstChange property. This is true the first time ngOnChanges is called. Beyond that, we also need to check the variable itself, as each bound variable is returned with its corresponding Simple changes example. Here is what we get for each of our @Input variables the first time ngOnChanges execute:

How to make CSS

The following code updates the hover and focus colors each time a value changes after the first run:

ngOnChanges(changes: SimpleChanges): void {
  if (changes.hoverBackgroundColor 
    &&& !changes.hoverBackgroundColor.firstChange) {
    this.svgStyle.setProperty(
      '--hover-color', changes.hoverBackgroundColor.currentValue);
  }
  if (changes.focusBorderColor
    &&& !changes.focusBorderColor.firstChange) {
    this.svgStyle.setProperty(
      '--Focus-color', changes.focusBorderColor.currentValue);
  }
}

You can find the new demo at stackblitz.com:

CSS Variable Scope

Conclusion

In this tutorial, we learned about the scope of CSS variables as well as how to apply them to specific elements using TypeScript/JavaScript. Ultimately, the decision of whether or not to declare your CSS variables in the root or in rule blocks is up to you. Personally, I try to set all variables as tightly as possible, because you can always increase the scope later.