Alain Boudard
Posted on November 30, 2023
In a previous article, I was talking about some ways to include assets in an Agular library. Let’s go a little further, and see how we can handle scss files and compile in the application and not in the library.
The example of bootstrap
One example we can name is how we can integrate bootstrap style to our application. It goes like this :
- Install bootstrap
- Reference bootstrap scss in angular.json or global styles.scss
- Eventually override some variables of the library
Note: if you import such a scss file like bootstrap, you may override some variables, only if you import the stylesheet in your styles.scss (or another one) of your project. It won’t work from angular.json.
The goal of our application
The main goal here is to have an Angular application that doesn’t require the library scss to be previously built. We will see what options we have to let the application do the job. The library will not only expose scss files but also images and fonts.
This means that our library will not expose compiled css to the outer world, but sometimes, and it can be convenient, a library can expose both the scss and the css.
Of course that means that each time our application will build, this task will be running, but as we might already know, Angular build system does separate typescript and style build.
The Angular workspace
Let’s create a workspace with both a library and an application.
ng new ng-assets-wk --create-application false
cd ng-assets-wk
ng g library @abo/ng-lib
ng g application ng-app -routing --style scss
We can write some scripts to build both library and application :
"build:lib": "ng build --project @abo/ng-lib",
"watch:lib": "ng build --watch --configuration development --project @abo/ng-lib",
"build:app": "ng build --project ng-app",
"watch:app": "ng build --watch --configuration development --project ng-app",
Styles in the application
In the src/styles.scss
file, we can add some basic style and reference a simple asset with variables.
When we compile the application in watch
mode, we see that chunks are produced, both js
and css
files.
What can seem weird, is that when you modify your scss files (main file or variables), the message is pretty simple and doesn’t reflect the fact that styles.css
output has indeed been updated.
Let’s note that there is a difference in the build output when you alter any of the source code, like a component, wether it’s typescript
, html
template or scss
file, which are not processed like the stylesheets referenced in angular.json
configuration file in the styles section :
This makes sense because Angular uses a default configuration for style isolation which is encapsulation: ViewEncapsulation.Emulated that would prevent styles declared for a component to spread to other components.
Styles in the library
Ok, now we want to add scss files to our library, and expose them to our application. Let’s create an assets folder in the libray and put some files in it:
Note: if you want to be able to override your scss variables, you would need to use the keyword
!default
like here, in the first declaration of your variable.
Let’s add this folder, and all files in it, to the ng-package.json
file of the library. You can find more information here about why we should declare this assets
property.
"assets": [
"./assets/**/*.*"
]
During the build of the library, the folder should be exported in the dist folder. Following a previous article about Angular libraries, we will publish on a local Verdaccio server. Now we can add the scss file to the list of styles of our application in the angular.json
(the point is to be as close as a production ready use case).
"unpublish:lib": "npm unpublish @abo/ng-lib --registry http://localhost:4873/ --force",
"publish:lib": "ng build @abo/ng-lib && cd dist/abo/ng-lib && npm publish --registry http://localhost:4873/",
"install:lib": "npm install @abo/ng-lib --registry http://localhost:4873/",
Don’t forget to remove the reference to the library path in the
tsconfig.json
file.
Note: if you build your application in production mode, you might see something weird in the code inspector : the styles appear twice :
This is due to an implicit configuration in production
build mode called inlineCritical (turn this to false to disable if you want) that is meant to reduce the initial styled render of the page :
"optimization": {
"styles": {
"minify": true,
"inlineCritical": true
}
}
Anyway, if we run our application, the style of the library should be compiled with our application and be applied. Since we want to override the variables, let’s try to use this stylesheet in our main styles.scss
file.
We remove the reference in the angular.json
file. Then we add a reference in the styles.scss
file :
This is working, obviously, but it doesn’t look great DX wise, so we will use the export property of the Angular package format, in order to declare how we can access our scss resources. In this Angular documentation, it states that you need to declare this exports
property. As a result, we can use the entrypoint of the library in the scss import statement.
This import is way much cleaner right ?
Add other files to the scss package
Now, let’s add the most used files in combination with css: fonts and images.
In our library assets
folder, we can add an /img
folder with images, here we add two logos of Angular, and then we can simply reference these images in any CSS rule that we see fit.
Now we should be able to see our images in the page with some simple code :
<h1 class="logo next">Angular next</h1>
<h1 class="logo">Angular</h1>
Now let’s add a custom font of our choice. Let’s go with Segoe UI font. In our library assets
folder, we can add an /fonts
folder with the desired files. For the sake or readability, let’s create a dedicated _fonts.scss
file and reference it in our main library scss file.
In our main scss files, we import the fonts definition, and use the font in the body for a start, but anywhere you would want :
Now we can test in the browser if everything is ok, the font should be loaded in the devtools.
Use the library scss in application components
We saw that the components scss are not processed like the main styles declared from angular.json
section. But we still can use our declared variables (or any kind of tool, like mixin for example) in our components.
Without any modification we could use the @use
annotation and consume our variables like that :
@use "node_modules/@abo/ng-lib/assets/lib-variables" as lib;
But again, that’s not very nice, we can then add another entry in our library package.json and have a better import in our component’s scss :
With the same pattern, we can declare a mixins
file and use it through an export entry :
Conclusion
We saw that having a clean Angular library exposing scss resources is quite easy to configure. The main advantage of this method is to get rid of the css pre-build task in the library, and the maintainability of our custom variables and other uses of scss files. This is possible thanks to the scss capabilities of the Angular build, since we never had to launch a dedicated sass build.
The use of the Angular endpoint reference is a plus for any library publisher.
References
https://github.com/aboudard/ng-assets-wrk
https://github.com/ng-packagr/ng-packagr/blob/main/docs/copy-assets.md
Posted on November 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.