Sass – short for Syntactically Awesome Style Sheets – is a CSS compiler that simplifies the writing CSS. It’s a popular choice among Angular developers because, besides greatly reducing the amount of redundancy in CSS rules, Sass is also natively supported by the Angular CLI. In fact, it’s one of your choices whenever you create a new project:
In the Binding CSS Styles to Events in Angular Applications tutorial, we learned how to set vanilla CSS variables using the setProperty() method. You can think of today’s article as a sort of companion piece in that it will cover how to read variables and use them in your Angular applications. To do that, we’ll modify the demo app that we built in that tutorial to fetch theme colors that are defined in a Sass .scss file.
Why Use Sass Variables?
Sass provides many built-in modules that provide useful functions. For instance, the colors module contains many useful functions for both analyzing and modifying colors. These include the lighten() and darken() functions, which take a color and alter it by a percentage. These are especially useful for adjusting a wide range of theme colors in a consistent way. Here are some Sass variables that store the results of the lighten() and darken() functions:
$backgroundColor: rgb(82, 172, 240); $lightBackgroundCcolor: lighten($backgroundColor, 16%); $hoverColor: blue; $darkHoverColor: darken($hoverColor, 20%); $focusBorderColor: darkgray; $darkFocusBorderColor: darken($focusBorderColor, 40%);
These may be applied directly to page elements directly via CSS rules. However, there are times where that is not possible, such as when an Angular component accepts colors as @Input parameters, as employed in the Guide to CSS Variable Scoping in Angular Demo :
<feed-component [background-color]="backgroundColorInput" [hover-background-color]=" hoverColorInput" [focus-border-color]=" focusBorderColorInput" > </feed-component>
You could try to approximate the effect of the Sass functions in TypeScript, but your results will vary in consistency and almost surely won’t match those of the Sass functions. Hence, the ideal solution is to read in the Sass colors and supply those to components that need them.
Read: Introduction to TypeScript and Its Features
Reading CSS Variables From the Service
The first step in reading the Sass variables that we declared above is to store them in standard CSS variables. To do that, property values must be written within interpolation:
.color-demo-app { --background-color: #{$backgroundColor}; --light-background-color: #{$lightBackgroundCcolor}; --hover-color: #{$hoverColor}; --dark-hover-color: #{$darkHoverColor}; --focus-border-color: #{$focusBorderColor}; --dark-focus-border-color: #{$darkFocusBorderColor}; }
It’s also a good idea to limit the variable scope to an outer tag of your applications so as to not spill over into surrounding content, should your application be part of a larger page:
<div class='wrapper color-demo-app'> application content </div>
Our ColorService will have expose a public method named loadColors(). It will accept an array of colors to load, along with a Map in which to associate the loaded colors with their names. The document’s getElementsByClassName() method will get the ‘color-demo-app’ element. It then gets passed on to window.getComputedStyle() to retrieve all element styles. It returns a CSSStyleDeclaration that provides the getPropertyValue() method. Using it to fetch each color variable’s value, we can then add both color property and value to the colorMap:
import { Injectable } from '@angular/core'; @Injectable() export class ColorService { public loadColors(colors: string[], colorMap: Map<string, string>) { // Read the custom property of body section with given name: const appElement = document.getElementsByClassName('color- demo-app'); if (appElement && appElement.length > 0) { const appStyles = window.getComputedStyle( appElement[0]); colors.forEach((el) => { colorMap.set( el, appStyles.getPropertyValue(el) .replace(' ', '') ); }); } } }
Using the ColorService
In the AppComponent, all we need to do now is call our service to get the goods! I came up with the idea to divide color names into the colors and function effects in order to enforce naming conventions:
const CSS_PREFIX = "--"; const CSS_SUFFIX = "color"; enum PropertyNames { background = 'background', hover = 'hover', focusborder = 'focus-border' } enum ColorOptions { standard = '', light = 'light', dark = 'dark' } const COLOR_LIST = [ `${CSS_PREFIX}${PropertyNames.background}-${CSS_SUFFIX}`, `${CSS_PREFIX}${ColorOptions. light}-${PropertyNames. background}-${CSS_SUFFIX}`, `${CSS_PREFIX}${PropertyNames. hover}-${CSS_SUFFIX}`, `${CSS_PREFIX}${ColorOptions. dark}-${PropertyNames.hover}-$ {CSS_SUFFIX}`, `${CSS_PREFIX}${PropertyNames. focusborder}-${CSS_SUFFIX}`, `${CSS_PREFIX}${ColorOptions. dark}-${PropertyNames. focusborder}-${CSS_SUFFIX}` ]; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], providers: [ColorService] }) export class AppComponent implements OnInit { // Expose enums to the template public PropertyNames = PropertyNames; public ColorOptions = ColorOptions; private _colorMap: Map<string, string> constructor(private colorService: ColorService) { } }
A couple of things to note about the above code:
- The ColorService is declared in the providers array.
- It is then injected into the component as a constructor argument. That will allows us to reference it later.
In OnInit, we’ll instantiate the _colorMap variable that we created above and pass it to the colorService’s loadColors() method, along with the COLOR_LIST.
ngOnInit(): void { this._colorMap = new Map(); this.colorService.loadColors(COLOR_LIST, colorMap); }
Nothing is returned from loadColors() because, as an object, the colorMap is passed by reference. Hence, it retains its data even after the function has terminated. If we were do do a console.log() on the colorMap after invoking loadColors(), we would see that our six colors have all been read:
Conclusion Angular Services and Reading Sass Variables
There are other ways of reading in Sass variables from Angular applications, but this is the method we use in my day job. There’s a demo with today’s code on stackblitz. In fact, you’ll find that it contains a lot more code than we covered here today. We’ll talk about it in the next article.