head image CSS variables in Angular. Forget about SCSS variables: Part 1

CSS variables in Angular: Part 1. Forget about SCSS variables

How to make styling of all elements easy and maintainable

Maksim Dolgikh
5 min readDec 25, 2023

--

SCSS variables

In my career, I’ve managed to work on a lot of projects that used styling via SCSS, LESS, or Stylus. One of the main advantages of favoring them over CSS, besides “cascading”, was “variables

They enabled:

  • To respect the DRY principle
  • To centralize shared variables for reuse via @import or @use
  • To create more abstract styles without hardcoding
  • To synchronize variable names with Figma tokens
  • To perform simple arithmetic and complex operations on variables
  • Variables did not go directly into the build, but were compiled into actual CSS values

Example of implementation

Let’s realize a basic example of SCSS variables application

Variables

$primary: #d75894;
$secondary: #9b3dca;
$background: #fafafa;

Styling of elements

@use 'variables';


body {
background: variables.$background;


h1 {
color: variables.$primary;
}


p {
color: variables.$secondary;
}
}
Styles applied to elements
Styles applied to elements

It’s simple and intuitive 👍.

New theme

But let’s say we need to add support for a dark theme for the current style scheme due to business requirements. How do we implement it? 🧐

Experienced developers 🤓 will tell you that you should use mapping to substitute the correct value into the selector from each theme. In this way, we will provide extensibility for new themes.

Let’s change our simple variable list to a color theme map

$themes: (
"default": (
primary: #d75894,
secondary: #9b3dca,
background: #fafafa,
),
"dark": (
primary: #5e84ff,
secondary: #0dd0ff,
background: #1b1918,
)
);

Themes applying

@use "variables";
@use "sass:map";


@mixin setTheme($theme-map) {
background: map.get($theme-map, "background");


h1 {
color: map.get($theme-map, "primary")
}


p {
color: map.get($theme-map, "secondary")
}
}


body {
@each $theme-key in map.keys(variables.$themes) {
&[data-theme="#{$theme-key}"]{
@include setTheme(map.get(variables.$themes, $theme-key))
}
}
}

Result

Applied the new dark theme
Applied the new dark theme

Yay, we’ve achieved the requested behavior.

But at what cost?

Problems

As you can see from this small example, the amount of code to support the new dark theme has increased, and the old scheme had to be refactored.

Another disadvantage is obtained is the increase in the resulting CSS file due to having multiple themes simultaneously.

And hence its size, which takes time 📉 to download and parse.

body[data-theme=default] {
background: #fafafa;
}
body[data-theme=default] h1 {
color: #d75894;
}
body[data-theme=default] p {
color: #9b3dca;
}
body[data-theme=dark] {
background: #1b1918;
}
body[data-theme=dark] h1 {
color: #5e84ff;
}
body[data-theme=dark] p {
color: #0dd0ff;
}

As a consequence, with the growth of the code base and the requirement for stylization, we will get:

  • Increasing complexity of style architecture in the development of individual components
  • Maintenance and development of new features will be harder every time
  • Constant refactoring and need for regression screen tests

These problems will undoubtedly affect the speed of development, the quality of the result, and the convenience of developers.

Unfortunately, many libraries developed using SCSS, such as @angular/material, encourage this approach. Many developers are unaware of the recommended ways of styling via mixins, which leads to anti-pattern styling as well as the use of ::ng-deep

Those who have ever upgraded Angular in conjunction with @angular/material will understand UI problems in regression tests after upgrade if you don’t use the recommended way of component customization

So what’s the solution to this problem?

СSS-variables

Main advantages and features:

  • Just like SCSS variables allow us to declare one-time reusable values to comply with DRY
  • We don’t need to manually synchronize with Figma tokens, as they can be provided immediately in the final version from Figma
  • Have a global scope and do not require @import or @use to be used
  • They are native and supported by most browsers
  • They are not compiled and remain a part of the resulting CSS file. As a consequence, you can access the actual value of a particular variable in runtime
  • When publishing our components, customizing them is made noticeably easier by providing one new CSS variable file, instead of creating a lot of mixins as API-customization

Refactoring the old customization system

Convert SCSS variables to CSS variables and declare them in :root

:root {
[data-theme="default"] {
--primary: #d75894;
--secondary: #9b3dca;
--background: #fafafa;
}


[data-theme="dark"] {
--primary: #5e84ff;
--secondary: #0dd0ff;
--background: #1b1918;
}
}

Used as example. This option is not recommended and it is better to use a separate file for each theme with its own :root

New styling scheme for CSS variables

body {
background: var(--background);


h1 {
color: var(--primary);
}


p {
color: var(--secondary);
}
}

Result

Applying color themes via CSS-variables
Applying color themes via CSS variables

How does the resulting CSS file look like?

body {
background: var(--background);
}
body h1 {
color: var(--primary);
}
body p {
color: var(--secondary);
}

We got an end-to-end style file 🎉. You can have as many color themes as you want, and the file and its original size will not increase.

Conclusion

CSS variables allow us to create abstract component styles that don’t work with values directly.

We no longer need to create an alternate style file with all the selectors, but just provide a new list of CSS variables to rely on. Essentially, css variables are our style settings, and the more we rely on them, the more flexible we are in customizing the interface to fit individual themes and presets.

Even if you don’t need support for new themes now, this will be a useful option for the future.

SCSS variables are better used within the SCSS syntax for internal functions and mixins, selector generation

Resources

Other parts

Links

--

--