Files
unicorn-utterances/content/blog/angular-npm-font-usage/index.md
2022-02-09 22:59:16 -08:00

224 lines
8.2 KiB
Markdown

---
{
title: "Package Font Files on NPM for Angular Usage",
description: "Do you use custom fonts that you want to share with multiple apps? Learn how to distribute those fonts on NPM and consume them in Angular!",
published: '2020-11-24T22:12:03.284Z',
authors: ['crutchcorn'],
tags: ['angular', 'javascript', 'npm'],
attached: [],
license: 'cc-by-4'
}
---
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](https://www.fonts.com/font/the-foundry/foundry-sterling).
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 {#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](/posts/setup-standard-version/).
## Add Font Files {#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 {#css-declare}
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 {#font-val-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:
| Value | Common weight name | Related File |
| ----- | ------------------------- | --------------------------------- |
| 100 | Thin / Hairline | N/A |
| 200 | Extra-Light / Ultra-Light | N/A |
| 300 | Light | `foundry_sterling_light.otf` |
| 400 | Normal / Regular | `foundry_sterling_book.otf` |
| 500 | Medium | `foundry_sterling_medium.otf` |
| 600 | Semi-Bold / Demi-Bold | `foundry_sterling_demi.otf` |
| 700 | Bold | `foundry_sterling_bold.otf` |
| 800 | Extra-Bold / Ultra-Bold | `foundry_sterling_extra_bold.otf` |
| 900 | Black / Heavy | N/A |
# Consume Assets Package in Angular CLI {#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 {#angular-json}
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 {#css-import}
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 `@import`s 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](https://discord.gg/FMcvc6T) and ask questions there!