Css style

Setting CSS values ​​from fewer variables in Angular

Angular supports many of the most popular CSS preprocessors, including Scss, Sass, and Less, through the Angular CLI (Command Line Interface). Whenever web developers create a new application, the Angular CLI prompts you to choose between vanilla CSS and a preprocessor. If you choose to use a preprocessor, Angular will automatically compile to CSS as part of the build process.

However, if you use webpack or a similar application bundling tool, you will need to manually install the plugins and configure the necessary configuration to facilitate CSS compilation depending on the preprocessor you have chosen. This can make integrating a CSS preprocessor into your web application a real headache. Also, if you’re using variables that dynamically set CSS values ​​at runtime, it might make sense to compile the styles in the application itself. In this web development tutorial, we will modify this existing Angular application to read color variables from a Less file and assign compiled CSS attribute values ​​to certain class variables.

You can also check out our web programming tutorial on manipulating colors with JavaScript.

Theme color customization

Two good reasons to choose a CSS preprocessor like Less (short for “Leaner Style Sheets”) is its support for custom variables and many functions like alleviate() and darken(). These two characteristics allow us to define certain color properties like these in a .less case:

@BackgroundColor: rgb(82, 172, 240);
@HoverColor: lighten(@BackgroundColor, 20%);
@FocusBorderColor: gray;

We can then reference our variables from our CSS rules like this:

.background {
  background-color: @BackgroundColor;
}

.hover-background {
  background-color: @HoverColor;
}

.focus-border-background {
  background-color: @FocusBorderColor;
}

This provides a simple mechanism to change an application’s color theme by replacing one variables.less file with another.

The two code snippets will be combined into the following CSS rules when compiled:

.background {
  background-color: #52acf0;
}
.hover-background {
  background-color: #b0d9f8;
}
.focus-border-background {
  background-color: gray;
}

Our CSS tutorial, Working with CSS Variables, explains in more detail how to work with variables in CSS.

Reading asset files from an Angular application

By default, Angular has an asset directory, located under the /src folder at the root of your project, which is copied to the build directory by Angular CLI during the build process. This is where you put all of the app’s resources, whether it’s images, videos, and .json files, styles, or scripts. It is defined under theassets» section of the angular.json case:

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      "outputPath": "dist/angular-theme-changer",
      "index": "src/index.html",
      "main": "src/main.ts",
      "polyfills": "src/polyfills.ts",
      "tsConfig": "tsconfig.app.json",
      "assets": [
        "src/favicon.ico",
        "src/assets"
      ],
      "styles": [
        "src/styles.scss"
      ],
      "scripts": []
    },
    "configurations": {
    // ...

You can also add instructions for copying files from a common library in the node_modules folder if you need to:

"assets": [
  "src/favicon.ico",
  "src/assets",
  {
    "glob": "**/*",
    "input": "./node_modules/@acme/common-assets/css/",
    "output": "./assets/"
  }

We will store our three color variables in the variables.less file, which we will create in the src/assets folder of our project so that we can access them later:

 

There are a few ways to read files in TypeScript/JavaScript, but I would recommend Angular’s HttpClient class. It is specifically for communicating with a server over the HTTP protocol. To use it, we can import the HttpClient service class from @angular/common/http and inject it as a constructor argument. Its get() method takes two arguments: the endpoint URL from which to fetch, and an options object that is used to configure the request. If web developer do not specify any options, get() expects the response body to contain JSON data. Since that is not the case here, we need to specify a responseType of “text“.

The get() method returns an Observable that we can subscribe to and process the file contents:

import { HttpClient } from '@angular/common/http';

export class AppComponent {
  constructor(http: HttpClient) {
    http.get('./assets/variables.less', {responseType: 'text'})
    .pipe(first())
    .subscribe(
      variables => {
      // Process the variables here... 
    },
    err => {
      console.log(err);
    });
  }
}

Read: HTML, CSS, and JavaScript Tools and Libraries

Converting Less Variables to a JS Object

Rather than try to parse the contents of the variables.less file ourselves, we can utilize less-vars-to-js library to convert our variables to JavaScript (JS). Specifically designed for Less variables, it converts ours to the following JS object:

{
  @BackgroundColor: "rgb(82, 172, 240)"
  @FocusBorderColor: "gray"
  @HoverColor: "lighten(@BackgroundColor, 20%)"
}

It can be invoked directly, without a constructor, as it only does one thing. We just need to pass in the Less variables string within the http.get’s subscribe callback:

import lessToJs from 'less-vars-to-js';

// ...
variables => {
  let vars: Object;
  try {
    vars = lessToJs(variables);
  } catch(err) {
    console.log(err);
    return;
  } 

Setting the CSS Values from the Less Variables Object

If you look at the generated JS Object, you’ll notice that Less functions like lighten() are not compiled by the lessToJs() function. That’s because less-vars-to-js is just a Less variable parser and not a Less processor. To compile Less functions into actual color values, we need to use the official, stable version of the Less npm JS library. Its methods may be invoked from the command-line, or, as in our case, programmatically. The main entry point into less is the less.render() function, which takes the following formats:

less.render(lessInput[, options]) .then(function(output) { // output.css = string of css // output.map = string of sourcemap // output.imports = array of string filenames of the imports referenced }, function(error) { }) ;  // or... less.render(css, options, function(error, output) {})

We will use the first syntax.

Although the options are not mandatory, we will need to provide the editVars attribute to enable runtime modification of Less variables. It accepts a JS object like this:

{ '@buttonFace': '#5B83AD', '@buttonText': '#D9EEF2' }

The lessInput The argument must be in the format Less Rules, not Variable Declarations. The last thing we want to do is go through all of our stylesheets to find rules that use the color variables. In fact, that would be counterproductive, since we’re only interested in the compiled color values ​​and nothing else. For that, we can simply wrap them in some dummy rules:

let dummyCssRules="", i = 0;
for (const [key, value] of Object.entries(vars)) {
  dummyCssRules += 'rule' + ++i + ' { color: ' + key + '; }n';
}

We now have three valid CSS rules that incorporate Less color variables:

rule1 { color: @BackgroundColor; }
rule2 { color: @HoverColor; }
rule3 { color: @FocusBorderColor; }

The least to return() returns a Promise with an Object that contains, among other things, the compiled CSS string:

const options = { modifyVars: vars };
less.render(dummyCssRules, options)
  .then((cssObject) => {
	// set the theme colors...
  })
  .catch((reason) => console.error(reason));

By meeting @HoverColor’s value “lighten(@BackgroundColor, 20%)», defines the value less willingly to lighten the results:

rule1: {color: '#52acf0'}
rule2: {color: '#b0d9f8'}
rule3: {color: 'gray'}

Lily: Introduction to CSS in JS

Assigning CSS colors to component variables

Recall that the purpose of all this is to call the existing application component setColors() method with the three theme color values. To do this, we still have to do a conversion if we want to access the values ​​of the CSS rules. Again, we should rely on a library rather than parsing the CSS ourselves. A good CSS to JS converter is available from none other than American Express (as in credit card). They have a few repositories on GitHub, including one for the css to js library. It takes CSS rules in a string format and converts them to a JS object like this:

{
  rule1: {
    color: "#52acf0"
  }
  rule2: {
    color: "#b0d9f8"
  }
  rule3: {
    color: "gray"
  }
}

All that remains is to invoke setColors() with our object attributes:

import { convert } from '@americanexpress/css-to-js';

// In the Component constructor...
const compiledCssObject = convert(cssObject.css);
// Set default colors
this.setColors(
  compiledCssObject.rule1.color, 
  compiledCssObject.rule2.color, 
  compiledCssObject.rule3.color
);

You can take a look at the demo to see how all the above code works together.

Final Thoughts on CSS Variables

This web development tutorial introduced some good JS libraries for converting between Less, CSS, and JS, which we put to good use by setting custom theme colors. As we’ll see in the next article, we can retrieve entire Less stylesheets in the same way, allowing us to fully separate our application’s theme styles.

Read more CSS web development and programming tutorials.