Angular: Styles API using CSS

Nicholas Favero
5 min readJan 22, 2021

The Question:

How do you design a good style API for Angular Components using CSS and leave our Component Classes clean and free from styling logic?

(This article will cover some preliminary information, if you feel like skipping right to the bones, head down to the Solution, or you can check out the full code here: https://stackblitz.com/edit/angular-ivy-spi8xt?file=src/app/app.component.html)

The Definition:

Let us start by defining what a style API is. When creating UI components for a library or project. Having a consistent way to communicate with your components in order to style them or control their formatting is key. A lot of times this will involve setting a color, toggling on/off certain looks of a component or just controlling certain display aspects.

Examples on how other solve it:

Let us take a look at how some groups solve this.

If you have ever used Angular Material (a great library) then you are probably familiar with their declarative approach of using inputs to style the components. You define your theme for Material at the app level and then set things like color, and appearance on each component you use. Let us take a look at some sample code

// Example code used from https://material.angular.io/components/checkbox/examples
<mat-checkbox [(ngModel)]=”checked”
[labelPosition]=”labelPosition”
[color]="color"
[disabled]=”disabled”>I’m a checkbox</mat-checkbox>

Above is code from the material website. you will see that labelPosition and color are passed in as input properties to mat-checkbox. Using this you can tell the checkbox to use your primary/secondary color and have the labelPosition be before or after the box.

Other examples I have seen in projects include passing in a styles input that is an object of all the display options for your component.

styles={color: ‘primary’, labelPosition: ‘after’ }
<some-component [styles]=”styles”></some-component>

And other iterations such as these. But most if not all seem to rely on javascript properties to control the behavior of our styles. Which is not inherently bad, but does tend to bloat your component code with display logic which always seemed a bit unintuitive to me. Especially since we already have a web standard for styling which is of course everybody’s favorite language, the one that has definitely 0 issues at all with it CSS. (Yes that was sarcasm, but it is our styling language for the web, and can be used in a powerful way)

The Solution:

After experimenting with different approaches, I finally came up with one that I think provides a fantastic Style API that removes logic from our Javascript code and puts back into our templating code where it belongs.

To do this we are going to use CSS classes as our styling API.

In order to achieve the desired API we must have and understanding a little bit about Angular components (if you already know the following I apologize for the refresher) Angular components themselves are DOM elements that are rendered with their tags. I.E. If your component selector is my-button, you will see <my-button> rendered in the actual DOM tree on the browser. This is important to know, because it means that you can actual style the element itself and use it as part of your styling. Let me show you what I mean.

Let us take a button element and use this new idea. What we ideally would want is to be able to have <my-button color=”primary”></my-button> to <my-button class=”primary”></my-button>

To see the code I have attached a sample StackBlitz https://stackblitz.com/edit/angular-ivy-spi8xt?file=src/app/app.component.html

But let’s go ahead and walk through it a little bit. We created a button component with the template code:

<button class=”btn”><ng-content></ng-content></button>// https://stackblitz.com/edit/angular-ivy-spi8xt?file=src/app/button/button.component.html

I have truncated the CSS code to the important parts but it can be seen here:

$color-palette: primary, secondary;
:host {
@each $key in $color-palette {
&.#{$key} .btn {
color: var(--#{$key});
}
&.#{$key}.raised .btn {
background-color: var(--#{$key});
color: white;
}
}
}
...
// https://stackblitz.com/edit/angular-ivy-spi8xt?file=src/app/button/button.component.scss

And this is all we need now to create our Style API. So our button template code is clean (it has no input properties at the moment), and we can style our button from the template itself using classes.

For those who have not seen scss before, or not sure what the syntax is, the above translates to in regular css:

:host.primary .btn { color: var( — primary); }
:host.secondary .btn { color: var( — secondary); }
:host.primary.raised .btn { background-color: var( — primary); color: white;}
:host.secondary.raised .btn { background-color: var( — secondary); color: white; }

The important thing to note here is the :host selector towards the top. For those who have not seen this before, it represents the component that this style sheet is applied to. So in this case any style that is applied to :host is applied to <my-button> By utilizing this fact we can use purely CSS to change our children based on what styles are applied to our host container. The above code means that if my-button has the class primary, use our primary color on a template class .btn

<my-button class=”primary”>Primary</my-button><my-button class=”secondary”>Secondary</my-button><my-button class=”raised primary”>Primary Raised</my-button><my-button class=”raised secondary”>Secondary Raised</my-button>

As a follow on example I showed that you can actual change the color of your primary and secondary colors by doing this in your wrapping component

:host {
--primary: #f44336;
--secondary: #9c27b0;
}
https://stackblitz.com/edit/angular-ivy-spi8xt?file=src/app/example/alt-button-example.component.scss

So now when we render that template using our alt button component we actually get encapsulated changes to our colors for just that section.

So what about if you need conditional styling such as the time when the button is loading or something like that. Luckily this is just as easy to do with our styling API.

First we would define a new :host.loading state in our scss file and create whatever animations/styling that our button has during that state. Then it as simple as:

<my-button class=”primary” [class.loading]=”isLoading”></my-button>

This allows us to use our style API based on our Application logic and not have to add more @Input() decorators to our component.

I hope this helps and gives a new useful way to create a style API for your components!

--

--