Package Font Files on NPM for Angular Usage

November 24, 2020

1,047 words

Post contents

While working on my company's shared component system, I got a request from our design team. They wanted to keep our branding consistent with internal documents and other assets. As such, they requested we use a font called "Stirling Foundry".

While we're prepping our shared component system for an open-source release to the public, we quickly acknowledged that we couldn't possibly ship this font with the package we intend for public publishing due to it's licensing and cost.

However, we have multiple teams that rely on our shared component system, and we don't want to have to copy+paste the relevant @font-face definition or font files. What was our solution? Ship a second npm package (in our internal npm registry) that contained all of our private assets - including font files.

Let's walk through how we did that.

Setup Assets Package

As we're wanting to ship our packages separately, we opted for two Git repositories for the component system and private assets. In a new repository, I have the following for the package.json:

json
{
"name": "ecp-private-assets",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"release": "standard-version"
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-angular": "^11.0.0",
"husky": "^4.3.0",
"standard-version": "^9.0.0"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"commitlint": {
"extends": [
"@commitlint/config-angular"
]
}
}

While this package will not maintain code, I still believe it important to maintain a semver for the package. If a path of the package changes, the semver will communicate that with your package's consumers alongside the changeling. As such, this package.json utilizes Conventional Commit and commitlint to auto-generate changelogs and maintain history version.

Add Font Files

The "Foundry Stirling" font that I'm shipping is a combination of 7 .otf files. I start by creating a fonts directory. Inside that directory, I place the .otf files in the fonts directory.

Once done, your project repo should look something like this:

.
├── CHANGELOG.md
├── README.md
├── fonts
│ ├── foundry_sterling_bold.otf
│ ├── foundry_sterling_book.otf
│ ├── foundry_sterling_book_italic.otf
│ ├── foundry_sterling_demi.otf
│ ├── foundry_sterling_extra_bold.otf
│ ├── foundry_sterling_light.otf
│ └── foundry_sterling_medium.otf
├── index.js
├── package-lock.json
└── package.json

@font-face CSS Definition

Now that we have the fonts in their place, we need to create a common foundry_stirling.css file to access those fonts from CSS.

Because we're planning on using Angular CLI, we'll want to set the src property to be prefixed with /assets/, since that's where Angular sends it's assets.

css
/* foundry_stirling.css */
@font-face {
font-family: 'Foundry Sterling';
font-style: normal;
/* Light */
font-weight: 300;
src: local('Foundry Sterling Light'), local('FoundrySterling-light'), url("/assets/foundry_sterling_light.otf") format('opentype')
}
/* ... */
@font-face {
font-family: 'Foundry Sterling';
font-style: normal;
/* Extra-Bold */
font-weight: 800;
src: local('Foundry Sterling Extra Bold'), local('FoundrySterling-extra-bold'), url("/assets/foundry_sterling_extra_bold.otf") format('opentype')
}

While we're using CSS here, if you wanted to set the src to a different location for non-Angular projects, you could use a SCSS @mixin to define the @font-face declarations with a customizable $base_path.

scss
@mixin foundry_sterling($base_path) {
@font-face {
font-family: 'Foundry Sterling';
font-style: normal;
/* Extra-Bold */
font-weight: 800;
src: url("#{$base_path}/foundry_sterling_extra_bold.otf") format('opentype')
}
// ... Other @font-face declarations
}

Then, when consuming the package in your client-side app, you'll want to use something like this:

scss
@include foundry_sterling("/assets")
Font Name Value Mapping

Because our font had multiple files to declare the different CSS values weights, we had to declare the @font-face for each of the font files. This is the mapping we used:

ValueCommon weight nameRelated File
100Thin / HairlineN/A
200Extra-Light / Ultra-LightN/A
300Lightfoundry_sterling_light.otf
400Normal / Regularfoundry_sterling_book.otf
500Mediumfoundry_sterling_medium.otf
600Semi-Bold / Demi-Boldfoundry_sterling_demi.otf
700Boldfoundry_sterling_bold.otf
800Extra-Bold / Ultra-Boldfoundry_sterling_extra_bold.otf
900Black / HeavyN/A

Consume Assets Package in Angular CLI

Now that we have our npm package configured for usage, we'll start preparing for consuming that package by installing it into our app's package.json:

npm i ecp-private-assets

Remember, ecp-private-assets is the name of our internal package. You'll need to replace this npm i command with your own package name

angular.json modification

Once this is done, two steps are required. First, add the following to angular.json's assets property. This will copy the files from ecp-private-assets to /assets once you setup a build.

json
{
"glob": "**/*",
"input": "./node_modules/ecp-private-assets/fonts",
"output": "./assets/"
}

This way, when we use the CSS url('/assets/'), it will point to our newly appointed fonts files. Once this is added, your angular.json should look like this:

json
{
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
"outputPath": "www",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/ecp-private-assets/fonts",
"output": "./assets/"
}
],
"styles": [
"src/main.scss"
],
"scripts": []
}
}
}
}

Import CSS

Now that we have our assets in place, we need to import the CSS file into our app.

If your app utilizes postcss's import plugin or if you're using vanilla CSS, add the following line to your main.scss file:

css
@import "ecp-private-assets/fonts/foundry_sterling.css";

Remember to keep the @imports at the top of your file, as you will receive an error otherwise.

However, if you're not using postcss and have SCSS installed, you can use the following:

scss
@import '~ecp-private-assets/fonts/foundry_sterling.css';

Conclusion

Once you've added the file to your CSS imports and angular.json, you should see your font loading as-expected. Because you've setup your fonts to use npm to distribute them, you can now reuse your fonts across multiple apps.

If you'd like to learn more or have questions about this setup, feel free to leave a comment down below or join our Discord and ask questions there!

Subscribe to our newsletter!

Subscribe to our newsletter to get updates on new content we create, events we have coming up, and more! We'll make sure not to spam you and provide good insights to the content we have.