Back to the homepage
Angular

Angular Styles Masterclass

As front-end developers, we are responsible for various types of web application functionalities. In my opinion, the second most important thing (after business logic) is style management. The right design system can be crucial in encouraging users to engage with our final product.

In this article, we will look at the styling features offered to us by Angular and SCSS. You’ll learn about style encapsulation , which SCSS selectors can be used in Angular, and, most importantly, what a design system is.

What are global styles?

First, let’s start with something simple — global styles. These are stylesheets that are applied to elements throughout the application, regardless of the location of the HTML file.

For example: if we declare the .red-highlight class in the default, global styles.scss file, we will be able to refer to it in every component within the application. The only exception are components that use ShadowDOM encapsulation, but we don’t need to worry about that now.

styles.scss

app.component.html

The above snippet will, unsurprisingly, render the text “Example Box” with a red background. As you can see, using global styles is very simple.

Global styles should be used to:

  • declare all kinds of utility classes (typography, margins, padding)
  • apply a CSS reset to eliminate rendering inconsistencies in different browsers. 
  • initialize CSS/SCSS variables
  • initialize an UI library theme, such as Angular Material
  • override or modify theme styles

Global styles configuration

Now that we know what global styles are, we can proceed with their configuration. This will vary slightly depending on whether we use the @nrwl/nx library in the project. Global styles can be configured in:

  • angular.json (If we don’t use Nx)
  • project.json (If we do)

In the case of the angular.json file, we will declare the path to the global style sheet like so:

projects > (project name) > architect > (configuration) > options > styles

angular.json

In the case of the project.json file, we will declare the paths under the 

targets > (configuration) > options > styles key.

project.json

By default, each Angular application imports only one global stylesheet — styles.scss, located in the src folder. The style list is also used to import external UI libraries, like Angular Material or Bootstrap.

Adding a global style (Bootstrap)

I will use the Bootstrap library as an example of global style configuration. It provides us with utility classes and ready-made components that significantly speed up the process of building UIs. We can add it to our project using the command:

After installing the library, it needs to be registered in the angular.json or project.json file, depending on whether we use Nx.

Any changes to the global style configuration require us to restart the ng serve script. Once that’s done, we should be able to use classes and components from Bootstrap. Remember to also import its .js file into the project!

As you can clearly see, configuring global style is very simple!! Let’s move on to something a little more complicated — importing styles into components.

Component Styling

In most cases, each component in Angular has a separate, “local” stylesheet. This lets applications remain modular and have a transparent structure.

Importing styles within components

To import stylesheets, we can use the following properties withinthe @Component decorator:

  • styleUrls — accepts a list of relative paths to style files.
  • styleUrl — accepts the file path as a string. It is worth noting that this property is only available from Angular 17.0.0-next.4 onwards.

In cases where we want to provide the CSS directly in the component file, we can use the styles property inside the @Component decorator. It accepts a list of styles. Additionally, it’s worth adding that fromAngular 17.0.0-next.4 onwards, its value can also be a string.

In the Angular world, however, writing styles directly in the component file is a rare practice. Thus, this option should only be pursued when the component’s styles are extremely short.

Different types of style encapsulation

There are different ways to encapsulate styles. Each method works differently — it is worth knowing the differences between them to avoid unpleasant surprises in the future.

Currently, we can use the following encapsulations:

  • emulated encapsulation (default)
  • ShadowDOM
  • None

Emulated encapsulation (local styles)

This encapsulation means that the imported styles only affect the elements inside of our component’s template. Thanks to this behavior, we can be sure that the styles of one component will not overwrite the styles of another component. As a result, applications using this method of encapsulation are reliable — as we add further features to the application, we completely avoid visual errors caused by class naming conflicts.

We declare emulated encapsulation in the @Component decorator’s encapsulation property . Declaring it, however, is optional — Angular uses it by default for every new component.

ShadowDOM encapsulation

ShadowDOM encapsulation utilizes a special Shadow Root for its components. As a result, they are isolated from the main DOM and WILL NOT be able to use global styles.

As an important aside, ShadowDOM is not available in some legacy browsers.

Shadow roots in the DOM tree are wrapped in the #shadow-root element.

The screenshot above shows the DOM after enabling ShadowDOM encapsulation in the app-root component.

No Encapsulation

You can also choose not to use any encapsulation whatsoever, which makes all styles in the component global.

Using no encapsulation is very risky, because as we add new features, we may experience visual regression — an unexpected change in the component’s appearance. The most common cause of this is declaring two classes with the same name. Because of this risk, it’s best to avoid using no encapsulation whenever possible.

When  we decide not to use encapsulation, it is worth wrapping the styles in a unique class whose name will not be repeated elsewhere in the application. Naming the class after its component is a good idea.

File: color-picker.component.ts

File: styles.scss

Component selectors

We can use special style selectors to target specific elements.

:host

This selector allows us to style our component tag.

The above snippet, when imported into a component using the app-card selector, will compile into the following code:

By default, every Angular component is inline; using :host to display the element as a block is relatively popular.

It is worth noting that the :host selector has a limitation: it only works when our component uses Emulated or ShadowDOM encapsulation. It requires a slightly different approach when using no encapsulation:

:host-context

This selector allows us to conditionally style the component’s hosting element based on its parent’s class.

For example, the style below will be applied to the my-button class inside our component only if one of the ancestors (in our case, the body element) has the dark-theme class:

::ng-deep

This selector allows us to style our component’s children. It is worth noting that  ::ng-deep is marked as deprecated. As such, opinions are divided on whether it should be used at all.

Creating a design system 

A very good practice in creating web applications is to establish a design system — a set of common values ​​for the application. Such values ​​may include distance units, colors, or typography.

To demonstrate the concept, we will implement a simple design system using SCSS variables.

We must properly configure the application in angular.json (or project.json) to use global variables and mixins. After the section where we added global styles we added 2 new entries: stylePreprocessorOptions and includePaths. From now on, after restarting the application, Angular will allow us to import variables and utilities in local styles from the src directory.

Let’s start by creating a file structure:

  1. Create a styles folder and a utils folder nested within it
  2. Inside the utils folder, create 3 SCSS partials — _breakpoints.scss, _colors.scss and _spacing.scss (mind the underscore!)
  3. Add an index.scss file inside the utils folder

Our structure should look like this:

Once we create the files, we can move on to writing our design system. Let’s start by defining our color palette in _colors.scss. To make our life easier, we can use an online palette generator or use a readymade palette.

If you want to create your own palette from scratch, however, a good approach is to compose it as such:

  • A primary color
  • An accent color
  • A warning color (yellow/orange)
  • A color for marking errors (red)

When creating UI elements, follow the 60/30/10 rule: select your colors so that the UI consists of:

  • 60% of the basic color (most often, it is a natural color, i.e. white/gray/black)
  • 30% of the conductive color
  • 10% of the accent color

By applying this principle, our UI will be very transparent, and all interactive elements (e.g. buttons) with a color to accept options will be immediately visible.

An example _colors.scss file might look like this:

File: _colors.scss 

In addition to a uniform color system, our application should have uniform spacing that will appropriately separate UI elements. The general rule is to always use 8/12px spacing within a UI component and larger spacing to separate components from each other.

An example file with spacing:

File: _spacing.scss

The last thing we need to declare in our design system are breakpoints. When writing applications, we should focus on three viewports:

  • smartphones
  • tablets and laptops with a small screen
  • computer monitors and TVs

We’ll create variables storing each of these breakpoints.. We’ll also use mixins to make the job of creating a responsive UI easier.

An example file with breakpoints might look like this:

File: _breakpoints.scss

That’s it for our little design system.

It’s time to export the variables from all of our files. We can do this by using @forward in the index.scss file. We will also need to use @forward in the global style file.

File: index.scss 

To use our design system inside the component, we will use, well, @use, in which we declare the path to the folder containing the index.scss file.

File: app.component.scss

Unfortunately, creating your own design system requires writing a lot of boilerplate code. There are already tools available, such as Tailwind CSS, which implement their own design system and allow us to adapt it to our needs.

Directives

Directives are an Angular structure through which we can write reusable code responsible for manipulating an element’s behavior and appearance.

In this part of the article, we will create directives that will apply a rainbow background to our element. In the process, we’ll examine three different ways to manipulate element styles:

  • the ElementRef – HTML element wrapper
  • the @HostBinding decorator
  • the component or directive’s host property 

We’ll start by creating three directives: 

We should have the following file structure afterwards:

Before we move on to implementing the directive logic, we need to set up a little bit of code. Let’s start by declaring a global style that gives our element a rainbow background in the styles.scss file.

Then we import our directives in app.component.ts

Finally, let’s add three divs in the main view of our application, so that we can see how the directives affect the HTML elements.

File: app.component.html

File: app.component.scss

Okay, that’s it for the setup.. Let’s move on to implementing directives — we’ll start with the one using ElementRef.

ElementRef and native element

We can divide this component’s logic into three steps:

Initialization:

We begin by injecting ElementRef and assigning it to the _elementRef variable

Style logic

We create the duration input, which will set the animation’s duration

We create the hideBackground input, which, depending on its value, will add or remove the rainbow-background class

Default Values

We add the OnInit interface to the class and implement the ngOnInit method, which provides the default input values

After running the code, we should see that our element has a rainbow background!

In my opinion, using ElementRef is very clumsy — you have to do a lot of things manually. It might be useful when we want to write the logic for more advanced features, such as Badge Element. Fortunately, in most cases we can style elements using simpler Angular features, such as the @HostBinding decorator.

The @HostBinding decorator

A slightly better way to style elements using directives is to use the @HostBinding decorator. With its help, Angular will automatically assign attributes to our element according to the value of the variable or getter.

Let’s look at the example above:

Initialization:

We declare two inputs — duration and hideBackground.

Style logic

We bind the animationDuration style to the element using the @HostBinding decorator. The element’s animation-duration style will have the same value as the animationDuration getter (“5s” by default).

We bind the rainbow-background class, which will appear on the element only when @Input hideBackground of our directive is false.

As you can plainly see, the @HostBinding decorator allows us to significantly shorten the styling logic. The question is, can we do something even better?

The Host property

The host property inside our directive’s decorator allows us to easily bind attributes to our component. In the example above:

Initialization

We declare two inputs — duration and hideBackground.

Style logic

We bind the rainbow-background class to the element when hideBackground is false

We bind the animationDuration style to the value of the duration variable and add the string “s”.

Summary

From now on, styles in Angular should no longer hide secrets from you. Angular provides us with a lot of features in the context of CSS; thanks to selectors, scoped styles, and global SCSS variables, we are able to build consistent user interfaces.

 

About the author

Dawid Kostka

Angular Developer at House of Angular. For me software development is something more than just writing code. I love exploring and playing around with finest code. I’m passionate about adrenaline and positive attitude. In spare time I skate on aggressive skates.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *