TECH TALKS

An approach to dynamic theme color generation in Angular

author
Stefan Peshikj
25 Nov,2020

Designing and theming is one of the most important parts of the Web Development process. In theory, a web application is always created to solve a specific set of problems for the end-users, and as long as the application fulfills its primary goal, it will always attract the users which require that solution.

A good thing to remember is that a beautiful and appealing website with a great user experience will always attract even more customers since the application will be easier to use. Along with all the UX improvements, this is also where the brand’s specific visual identity comes into play.

How do we as developers tackle these problems?

Most of the time our job while developing is to follow one certain visual identity pattern and create a lovely application from that.

But what happens when we want our application to have one visual identity for some users, and a different one for others?

What happens if our application needs to support a large range of different visual identities?

In this blog, we will try to provide some insight using a sample solution that showcases an example project which utilizes Angular in an attempt to generate a wide range of different theme variations using only a couple of colors.

NOTE: This approach is not directly tied to Angular, i.e. we could use plain JavaScript code and get the same result, which is always a bonus as we are not tied to any specific framework, though some framework capabilities will make some steps easier to implement and use (eg: APP_INITIALIZER in Angular which helped us in one of the steps to the solution).

What is the end result of our work?

The end result is a simple website where we could play with a lot of theme options and find the best look for our application. It consists only of one page where we showcase the capabilities that are easily applicable to multiple pages and a lot of components, all of which could be themeable.

The full code for this example can be found on Github.

What are our requirements?

In order to start solving an issue, we need to be presented with certain problems. In this example, we try to tackle the following problems:

  • We need to create different themes for our application, and they can be comprised of any random colors
  • We should be able to change colors for certain components on demand
    Even if two versions of our website will use the same theme colors, some components on the first website could be themed differently from the ones on the second website.
  • We should be able to theme anything we want
  • We should only define a couple of colors, and create a theme only from that
  • We should be able to theme the Angular Material design components using the colors that we’ve specified
  • We should get the theming information from an external source (a webserver fetching them from a database)
  • If no theme is found, we should fall back to some default theme

If some of the requirements mentioned above are something that you can relate to, then you could find some ideas about a potential solution to the problem.

What challenges are we facing?

Challenge #1: Fetch theming data from server

If we’ve analyzed the issues above, we surely noticed that we need to fetch the theming information from a database, so this means that we need to do an asynchronous request to a web server. But what does this mean for our application?

Well, one take at it is to probably load the application and draw it with some default theming data while making the HTTP request, and only after we get the result from the server, we should switch over to using a different theme.

While it’s an option, this clearly should not be part of our scope. The end-user does not need to know that there are multiple variations of the website, he just wants to see his own version. It’s quite a pretty bad user experience to load the page, show that version for 200ms, and then all of a sudden change all the colors on the page. This flickering between two different states can surely decrease your audience.

This is a good place where we could put Angular’s APP_INITIALIZER injection token to good use.

The provided functions are injected at application startup and executed during app initialization. If any of these functions returns a Promise, initialization does not complete until the Promise is resolved.

We could potentially try to return a Promise where we could load the theme from the service, set up all the theme variables, and then return resolve the Promise to allow Angular to continue with the initialization. Let’s try it out by providing the injection token in our AppModule .

providers: [
  {
    provide: APP_INITIALIZER,
    // Provide the APP_INITIALIZER, wait until the theming configuration is fetched and set up correctly
    useFactory: (themingService: ThemingService) =>
      () => themingService.initialize(),
    deps: [ThemingService],
    multi: true
  }
]

Our theming service is initialized by either sending an HTTP request to some web server so that it can return the theming information so that we can generate all the color palettes and component variables. For the sake of simplicity, the demo project uses an in-memory approach as we have a default theme that is ready for use.

initialize(): Promise<any> {
  this.subscribeToThemeChanges();

  // httpClient.get("some_url/some_theme_id").toPromise()
  // use some browser storage mechanisms and caching as well
  // will use a local object for simplicity
  return observableOf(DEFAULT_THEME as ThemeConfig)
    .pipe(
      // If some error happens, use some default theme
      catchError(() => observableOf(DEFAULT_THEME)),
      // This could even be a syncronous process,
      // but we will use a subject for this example
      tap((themeConfig) => this.currentTheme.next(themeConfig))
    )
    .toPromise();
}