/postcss-font-pack

PostCSS plugin to simplify font declarations by validating only configured font packs are used and adding fallbacks.

Primary LanguageTypeScriptMIT LicenseMIT

postcss-font-pack

NPM version npm license Travis Build Status codecov Dependency Status

npm

PostCSS plugin to simplify font declarations by validating only configured font packs are used, adding fallbacks and transpiling human-readable font declaration values into valid CSS.

Introduction

Dealing with fonts can be a pain, especially on teams where not everybody knows where to find the exact fonts they are allowed to use. As a result, mistakes are made, inconsistencies are introduced and maintenance becomes a nightmare. PostCSS Font Pack aims to solve this problem with configuration.

Configuration

Let's start with the following assumptions:

  • We're using "Times New Roman" because it's a commonly used web safe font. It also illustrates how to use fonts that need quotes in this plugin.
  • We've installed Roboto and already setup its @font-face.

These fonts can be defined in JSON format. You might call it font-packs.json:

{
	"times": {
		"family": ["'Times New Roman'", "Times", "serif"],
		"propGroups": [
			{},
			{
				"weight": ["bold", 700]
			}
		]
	},
	"roboto": {
		"family": ["Roboto", "Arial", "sans-serif"],
		"propGroups": [
			{
				"style": "italic",
				"weight": ["light", 300],
				"stretch": "condensed",
				"variant": "small-caps"
			}
		]
	}
}

With the above configuration, we can write our CSS using the font shorthand property:

.foo {
	font: bold 1rem/1.2 times;
}

.bar {
	font: light condensed italic 1rem/1.2 roboto;
}

This would transpile into the following:

.foo {
	font: 700 1rem/1.2 'Times New Roman', Times, serif;
}

.bar {
	font: 300 condensed italic 1rem/1.2 Roboto, Arial, sans-serif;
}

Notice the weight was changed from bold to 700 and from light to 300. This came from the configuration's declaration value aliases, which were defined as "weight": ["bold", 700] and "weight": ["light", 300], respectively. You can do this with any of the prop groups, but since style: italic, stretch: condensed and variant: small-caps are already understood by the browser, it only made sense to use an alias for the weight in this case. You could have just as well congired the weight as "weight": 300, but that's not as human-readable as light, which the browser doesn't understand.

Also, notice that fallback fonts were added to the font-family. This allows you to keep your syntax easy to read/write and let the plugin do the dirty work with configuration.

You don't have to use the font shorthand property. You can also write-out each declaration individually or you can use the postcss-nested-props plugin to enable a nested syntax. Just make sure you unwrap the nested with that plugin before you run this one.

Linting

This plugin also handles linting so you can sleep sound knowing that nobody is using fonts or combinations of font declarations that are not supported or otherwise go against the design of the site. The following rules would all throw the same error, "pack not found":

.foo {
	font-family: "Futura PT";
}

.bar {
	font-family: roboto, sans-serif;
}

.baz {
	font: light 1rem/1.2 roboto;
}

Even though the light weight is found in your configuration, there is no font pack that uses light without also using italic and condensed. You have to use all three of them together to form a pack and to pass linting.

As you can see, this plugin will stop unsupported font declarations dead in their tracks.

Ignoring sections

If you need to ignore a specific declaration, but don't want to ignore the entire stylesheet, you can do so by preceding the declaration with a special comment:

.foo {
	/* postcss-font-pack: ignore-next */
	font: "Comic Sans", cursive;
}

This will cause the linter to ignore only the very next selector.

You can also ignore ranges:

/* postcss-font-pack: start-ignore */
.foo {
	font: "Comic Sans", cursive;
	font-size: 38px;
}
/* postcss-font-pack: end-ignore */

Installation

$ npm install postcss-font-pack

Usage

JavaScript

postcss([
	require('postcss-font-pack')({
		packs: require('./font-packs.json')
	})
]);

TypeScript

import * as postcssFontPack from 'postcss-font-pack';

postcss([
	postcssFontPack({
		packs: require('./font-packs.json')
	})
]);

Options

ignoreDeclarations

Type: { [prop: string]: string; } Required: false Default: undefined

A collection of declarations that you would like to ignore. These could be CSS hacks or something else that you really don't want throwing validation errors. Example below:

{
	ignoreDeclarations: [
		{ font: '0/0 serif' }
	]
}

requireSize

Type: boolean Required: false Default: false

When true, an error will be thrown if you have a rule with one or more font declarations, but without a font size.

.foo {
	font-family: roboto;
	/* missing required font-size */
}

Regardless of this option, if you have a rule with only a font-size specified you will get an error:

.foo {
	font-size: 1rem;
	/* font-size missing required family */
}

packs

Type: Object Required: true

An object literal where the keys are slugified fonts and the values are font packs. Each font pack consists of a required family and an optional collection of property groups, named as propGroups.

pack.family

Type: string[] Required: true

If your font slug is times, this is where you would define the extended font name along with any fallbacks.

Note: If your font name requires quotes, you must add them yourself.

pack.propGroups

Type: PropGroup[] Required: false

Define the property combinations that can be used together to reference a font.

pack.propGroups[n]

Type: PropGroup

Each prop group may contain 0 or more of the following keys:

Each value can be a string or a string[] with two values. The first value is a slugified value that you can type in your CSS to reference the associated key. The second value is what the first value will be transpiled into, so make sure they are CSS-valid. The weight values can additionally be numbers.

If an empty object is provided, this indicates that you want to support this font family with default browser values for weight, style, variant and stretch.

Note: If you don't include an empty object you will be unable to reference a family without also referencing additional properties.

Testing

Run the following command:

$ npm test

This will build scripts, run tests and generate a code coverage report. Anything less than 100% coverage will throw an error.

Watching

For much faster development cycles, run the following commands in 2 separate processes:

$ npm run build:watch

Compiles TypeScript source into the ./dist folder and watches for changes.

$ npm run watch

Runs the tests in the ./dist folder and watches for changes.