Capsize makes the sizing and layout of text as predictable as every other element on the screen.
Using font metadata, text can now be sized according to the height of its capital letters while trimming the space above capital letters and below the baseline.
npm install @capsizecss/core
Usage
createStyleObject
Returns a CSS-in-JS style object.
- Import
createStyleObject
passing the relevant options.
import { createStyleObject } from '@capsizecss/core';
const capsizeStyles = createStyleObject({
fontSize: 16,
leading: 24,
fontMetrics: {
capHeight: 700,
ascent: 1058,
descent: -291,
lineGap: 0,
unitsPerEm: 1000,
},
});
Note: It is recommended that you install the @capsizecss/metrics package and import the metrics from there:
import { createStyleObject } from '@capsizecss/core';
import arialMetrics from '@capsizecss/metrics/arial';
const capsizeStyles = createStyleObject({
fontSize: 16,
leading: 24,
fontMetrics: arialMetrics,
});
See the fontMetrics option documented below for more ways to obtain these metrics.
- Apply styles to the text element, for example via the
css
prop.
<div
css={{
// fontFamily: '...' etc,
...capsizeStyles,
}}
>
My capsized text 🛶
</div>
⚠️ Note: It is not recommended to apply further layout-related styles to the same element, as this will risk interfering with the styles used for the trim. Instead consider using a nested element.
createStyleString
Returns a CSS string that can be inserted into a style
tag or appended to a stylesheet.
- Import
createStyleString
passing the relevant options.
import { createStyleString } from '@capsizecss/core';
import arialMetrics from '@capsizecss/metrics/arial';
const capsizedStyleRule = createStyleString('capsizedText', {
fontSize: 16,
leading: 24,
fontMetrics: arialMetrics,
});
- Add the styles into a stylesheet or
style
element and apply the specified class name.
document.write(`
<style type="text/css">
${capsizedStyleRule}
</style>
<div class="capsizedText">
My capsized text 🛶
</div>
`);
⚠️ Note: It is not recommended to apply further layout-related styles to the same element, as this will risk interfering with the styles used for the trim. Instead consider using a nested element.
Options
Text size
Capsize supports two methods of defining the size of text, capHeight
and fontSize
.
NOTE: You should only ever pass one or the other, not both.
capHeight: <number>
Sets the height of the capital letters to the defined value. Defining typography in this way makes aligning to a grid or with other elements, e.g. icons, a breeze.
fontSize: <number>
Setting the font size allows you to get all the benefits of the white space trimming, while still specifying an explicit font-size
for your text. This can be useful when needed to match a concrete design spec or fitting into an existing product.
Line height
Capsize supports two mental models for specifying line height, lineGap
and leading
. If you pass neither the text will follow the default spacing of the specified font, e.g. line-height: normal
.
NOTE: You should only ever pass one or the other, not both.
lineGap: <number>
Sets the number of pixels between lines, as measured between the baseline and cap height of the next line.
leading: <number>
Sets the line height to the provided value as measured from the baseline of the text. This aligns the web with how typography is treated in design tools.
Font Metrics
This metadata is extracted from the metrics tables inside the font itself. There are a number of ways to find this information:
-
If using a Google Font or system font, install the @capsizecss/metrics package and import the metrics by name. For example:
import arialMetrics from '@capsizecss/metrics/arial';
-
If using a font from a file, install the @capsizecss/unpack package and extract the metrics from the font file directly. For example:
import { fromFile } from '@capsizecss/unpack'; const metrics = await fromFile(filePath);
-
Or, use the Capsize website to find these by selecting a font and referencing
Metrics
tab in step 3.
Core
The core package also provides a few other metrics-based features for improving typography on the web:
createFontStack
Creates metrics-based @font-face
declarations to improve the alignment of font family fallbacks, which can dramatically improve the Cumulative Layout Shift metric for sites that depend on a web font.
Usage
Consider the following example, where the desired web font is Lobster, falling back to Helvetica Neue
and then Arial
, e.g. font-family: Lobster, 'Helvetica Neue', Arial
.
- Import
createFontStack
from the core package:
import { createFontStack } from '@capsizecss/core';
- Import the font metrics for each of the desired fonts (see Font Metrics above):
import lobster from '@capsizecss/metrics/lobster';
import helveticaNeue from '@capsizecss/metrics/helveticaNeue';
import arial from '@capsizecss/metrics/arial';
- Create your font stack passing the metrics as an array, using the same order as you would via the
font-family
CSS property.
const { fontFamily, fontFaces } = createFontStack([
lobster,
helveticaNeue,
arial,
]);
The returned value contains the generated font face declarations as well as the computed fontFamily
with the appropriately ordered font aliases.
Usage in CSS stylesheet or a style tag
The returned values can be templated into a stylesheet or a style
block. Here is an example handlebars template:
<style type="text/css">
.heading {
font-family: {{ fontFamily }}
}
{{ fontFaces }}
</style>
This will produce the following CSS:
.heading {
font-family: Lobster, 'Lobster Fallback: Helvetica Neue',
'Lobster Fallback: Arial', 'Helvetica Neue', Arial;
}
@font-face {
font-family: 'Lobster Fallback: Helvetica Neue';
src: local('Helvetica Neue');
ascent-override: 115.1741%;
descent-override: 28.7935%;
size-adjust: 86.8251%;
}
@font-face {
font-family: 'Lobster Fallback: Arial';
src: local('Arial');
ascent-override: 113.5679%;
descent-override: 28.392%;
size-adjust: 88.053%;
}
Usage with CSS-in-JS frameworks
If working with a CSS-in-JS library, the returned fontFaces
can be provided as a JavaScript style object by providing styleObject
as a fontFaceFormat
option.
Here is an example using Emotion:
import { Global } from '@emotion/core';
const { fontFaces, fontFamily } = createFontStack(
[lobster, helveticaNeue, arial],
{
fontFaceFormat: 'styleObject',
},
);
export const App = () => (
<>
<Global styles={fontFaces} />
<p css={{ fontFamily }}>...</p>
</>
);
Also useful as a source for further manipulation given it is a data structure that can be iterated over or extended.
Providing additional font-face
properties
Additional properties can be added to the generated @font-face
declarations via the fontFaceProperties
option:
const { fontFamily, fontFaces } = createFontStack(
[lobster, helveticaNeue, arial],
{
fontFaceProperties: {
fontDisplay: 'swap',
},
},
);
This will result in the following additions to the declarations:
@font-face {
font-family: 'Lobster Fallback: Helvetica Neue';
src: local('Helvetica Neue');
ascent-override: 115.1741%;
descent-override: 28.7935%;
size-adjust: 86.8251%;
+ font-display: swap;
}
@font-face {
font-family: 'Lobster Fallback: Arial';
src: local('Arial');
ascent-override: 113.5679%;
descent-override: 28.392%;
size-adjust: 88.053%;
+ font-display: swap;
}
Note
Passing any of the metric override CSS properties will be ignored as they are calculated by Capsize.
However, the size-adjust
property is accepted to support fine-tuning the override for particular use cases.
This can be used to finesse the adjustment for specific text, or to disable the adjustment by setting it to 100%
.
Scaling for different character subsets
For languages that use different unicode subsets, e.g. Thai, the fallbacks need to be scaled accordingly, as the scaling is based on character frequency in written language.
A fallback font stack can be generated for a supported subset by specifying subset
as an option:
const { fontFamily, fontFaces } = createFontStack([lobster, arial], {
subset: 'thai',
});
Tip
Need support for a different unicode subset?
Either create an issue or follow the steps outlined in the generate-weightings
script and open a PR.
precomputeValues
Returns all the information required to create leading trim styles for a specific font size given the provided font metrics. This is useful for integrations with different styling solutions.
Accepts the same options as createStyleObject and createStyleString.
import { precomputeValues } from '@capsizecss/core';
import arialMetrics from '@capsizecss/metrics/arial';
const capsizeValues = precomputeValues({
fontSize: 16,
leading: 24,
fontMetrics: arialMetrics,
});
// => {
// fontSize: string,
// lineHeight: string,
// capHeightTrim: string,
// baselineTrim: string,
//}
getCapHeight
Return the rendered cap height for a specific font size given the provided font metrics.
import { getCapHeight } from '@capsizecss/core';
import arialMetrics from '@capsizecss/metrics/arial';
const actualCapHeight = getCapHeight({
fontSize: 24,
fontMetrics: arialMetrics,
});
// => number
Metrics
To make the retrieval of font metrics easy, Capsize provides the @capsizecss/metrics
package containing all the required data for both system and Google fonts.
npm install @capsizecss/metrics
See the package for documentation.
Unpack
If you are using a custom font or one not included in the @capsizecss/metrics
package, Capsize provides the @capsizecss/unpack
package to extract the required data either via a URL or from a local file.
npm install @capsizecss/unpack
See the package for documentation.
Integrations
- vanilla-extract integration via @capsizecss/vanilla-extract
Thanks
- Vincent De Oliveira for writing Deep dive CSS: font metrics, line-height and vertical-align, which provided the research needed to build all this.
- Devon Govett for creating Fontkit, which does all the heavy lifting of extracting the font metrics under the covers.
- SEEK for giving us the space to do interesting work.
License
MIT.