native-font
v0.3.1
Published
Native font stacks for web wizards
Readme
native-font
Native font stacks for web wizards
About
This library intends to help you make use of native font stacks on the web.
It comes bundled with 76 pre-defined fonts. But don't worry! You only ship to the browser what you need. And these are native fonts, so there won't be any downloading of font files. It's just the definitions needed to use the fonts that are pre-installed on the end user machine.
Install
npm install --save-dev native-fontInclude
mixins
@use "native-font";
.some-rule {
@include native-font.variables();
}fonts
@use "native-font/arial";
@use "native-font/arial-black";
@use "native-font/courier-new";Background
What is a font stack?
In web development, we don't know beforehand about all the fonts that will be available on the user device. So we can define a stack of fonts; the first font will be used if available. If not, we proceed to the next font on the stack until we hit one that is available on the user device. That one will be used. The trick then becomes to find those fonts available on the platforms we want to target, that resemble each other enough so that one can be used as a fallback for the other. People have done that work and published their results on sites like cssfontstack.com and modernfontstacks.com.
Why this module?
This module acts both as a format for defining font stacks in a re-usable way and as a repository for fonts that can be used. Hopefully, we will be able to expand the repository of ready-made fonts with open source contributions.
We define font stacks in such a way that we can re-use one font stack in the definition of another and use the power of sass to automatically resolve the dependency tree and generate the CSS definitions.
The fonts will be made available through css variables. For example, this sass code:
@use "native-font";
@use "native-font/industrial";
:root, ::backdrop {
@include native-font.variables();
}
h1 {
font-family: var(--industrial);
}results in this CSS:
:root, ::backdrop {
--color-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--sans-serif: "Helvetica Neue", "Arial Nova", "Nimbus Sans", Helvetica, Arial, sans-serif, var(--color-emoji);
--industrial: Bahnschrift, "DIN Alternate", "Franklin Gothic Medium", "Nimbus Sans Narrow", sans-serif-condensed, var(--sans-serif);
}
h1 {
font-family: var(--industrial);
}You get a CSS variable --industrial that represents the font stack for the
font you selected. And in addition, you get CSS variable --sans-serif because
--industrial uses --sans-serif as fallback and you get --color-emoji
because sans-serif in turn has --color-emoji as fallback. All these
dependencies were resolved for you automatically.
Your font-family assignments become short and readable and the font stack
itself becomes explicit and managed in a structured way.
Use
Lets first look at how to apply a font to your text and cover how to define a set of fonts and output the styles later. Lastly we will discuss how you can add your own fonts to the set of available fonts.
Apply a font
You apply a font by assigning a CSS variable to the font-family
property of the element(s) you want to style. For example:
<p>Text in my font</p>p {
font-family: var(--arial);
}This would assign the font arial to the text in the p element. The CSS
variable name --arial can be easily derived from the name of the font. For
example, for Arial Black it would be --arial-black and for Segoe UI it would
be --segoe-ui.
Now lets look at how to define a set of fonts and output the styles for it.
Define a set of fonts
To define a set of fonts, you can use sass (recommended), or you can use a predefined set,
Use sass
With sass you can automatically build up the font set as you @use fonts in
your code:
// load the 'arial' font
@use "native-font/arial";
// load more fonts if you need
// a dependency tree is created automaticallyDependencies are automatically resolved because each font actually @uses the
fonts it depends on and whenever a font is used for the first time, it is added
to the set.
Font stacks list the most specific fonts first, then less specific fonts as fallbacks for when the most specific font is not available. For example, the font stack for Arial might look something like
Arial, Helvetica, sans-serif, which means so much as 'use Arial, but if that is not available, use Helvetica. If that too is not available, use any sans-serif font'. So it makes sense to think of fonts as 'deriving' from generic fonts. And that is exactly what this library does. That is also why the predefined sets start with the most generic fonts likeserif,sans-serifandmonospacefirst and we only add the more specific fonts later. Because those fonts actually build on top of the more generic fonts in the stack.
Use a predefined set
There are also some predefined sets available that you can load:
w3c: The family names defined by the World Wide Web Consortiumbase: The w3C fonts plus 3 extra generic namesmodern: A set of modern web fontscssfonts: The web-safe fonts we know and loveextra:cssfonts+ some extraall: The complete set of all of the above
You can load a set of fonts by @useing the set like this:
@use "native-font/sets/modern"You still have to output the styles for the set.
Or, you can load these predefined sets by importing the CSS stylesheet:
@import 'native-font/sets/modern.css';This already includes the styles so you don't have to output them yourself.
When you go the CSS route, you can also select the minified version:
@import 'native-font/sets/modern.min.css';Output the styles
Once we have defined the set of fonts we want to use, we need to output the styles for them. That is actually as simple as this:
// load native-font mixins
@use "native-font";
// load fonts
@use "native-font/arial";
// output the styles
:root, ::backdrop {
// render the CSS variables
@include native-font.variables();
}Outputs:
:root, ::backdrop {
--color-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--sans-serif: "Helvetica Neue", "Arial Nova", "Nimbus Sans", Helvetica, Arial, sans-serif, var(--color-emoji);
--arial: "Arial Nova", Arial, "Helvetica Neue", Helvetica, var(--sans-serif);
}As you can see, you actually get 3 variables. This font stack defines an extra
generic font name for color-emoji and makes it the fallback for the other
generic fonts, in order to improve emoji support across devices. The generic
fonts themselves are also defined as font stacks, which is why we get
sans-serif defined here as well. Because that is the fallback for arial,
the font we actually selected.
Add your own fonts
To add your own font, you create a folder named after your font and add an
index.scss file that defines the font. Inside that file, you @use the fonts
you will use as fallback.
As an example, lets imagine we want to define our own font 'comic-sans' based on this font stack:
--comic-sans: "Comic Sans MS", "Comic Sans", "Comic Neue", cursive;To turn this into a native-font, we would create this .scss file:
fonts/comic-sans/index.scss
@use "sass:meta";
@use "native-font";
@use "native-font/cursive";
$name: 'comic-sans';
$title: 'Comic Sans';
$ref: 'https://example.com/fonts/comic-sans';
$fallback: cursive.$name;
$font: native-font.add($name, meta.inspect((
'Comic Sans MS',
'Comic Sans',
'Comic Neue',
var(--#{$fallback}),
)));Now, you can use your comic-sans font like this:
@use "native-font";
@use "./fonts/comic-sans";
:root, ::backdrop {
@include native-font.variables();
}
h1 {
font-family: var(--comic-sans);
}Which generates this CSS:
:root,
::backdrop {
--color-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--cursive: cursive, var(--color-emoji);
--comic-sans: "Comic Sans MS", "Comic Sans", "Comic Neue", var(--cursive);
}
h1 {
font-family: var(--comic-sans);
}Note how
--comic-sansuses--cursiveas a fallback, which in turn uses--color-emojias a fallback. This increases emoji support for some fonts on some devices, by explicitly falling back to some popular fonts with known good emoji support for characters missing from the target font.
Available fonts (by set)
w3c
@use "native-font/sets/w3c";Contains 14 fonts (± 1.4kB, ~800 bytes gzipped):
color-emoji(core)cursive(generic)emoji(generic)fangsong(generic)fantasy(generic)math(generic)monospace(generic)sans-serif(generic)serif(generic)system-ui(ui)ui-sans-serif(ui)ui-serif(ui)ui-monospace(ui)ui-rounded(ui)
base
@use "native-font/sets/base";Contains 17 fonts (± 1.5kB, ~840 bytes gzipped), all 14 from w3c, plus these 3:
emojiconui-emojiui-emojicon
modern
scss
@use "native-font/sets/modern";Contains 28 fonts (± 2.6kB, ~1.2kB gzipped), all 17 from base, plus these 11:
antiqueclassical-humanistdidonegeometric-humanisthandwrittenindustrialmonospace-codemonospace-slab-serifrounded-sansslab-seriftransitional
cssfonts
scss
@use "native-font/sets/cssfonts";Contains 64 fonts (± 5.3kB, ~1.8kB gzipped), all 17 from base, plus these 47:
andale-monoarialarial-blackarial-narrowarial-rounded-mt-boldavant-gardebaskerville-old-facebaskervillebig-caslonbodoni-mtbook-antiquacalibricalisto-mtcambriacandaracentury-gothicconsolascopperplatecourier-newdidotfranklin-gothic-mediumfuturagaramondgenevageorgiagill-sansgoudy-old-stylehelveticahoefler-textimpactlucida-brightlucida-consolelucida-grandelucida-sans-typewritermonacooptimapalatinopapyrusperpetuarockwellrockwell-extra-boldsegoe-uitahomatimestimes-new-romantrebuchet-msverdana
extra
scss
@use "native-font/sets/extra";Contains 65 fonts (± 5.3kB, ~1.8kB gzipped), all 64 from cssfonts, plus this 1:
comic-sans
all
scss
@use "native-font/sets/all";Contains 76 fonts: (± 6.4kB, ~2.0kB gzipped)
About the extra fonts in base
The base font set adds three extra font names to the set defined
by w3c. Here is the reasoning behind these additions.
ui-emoji
Traditionally, the standard font names defined by w3c, such as sans-serif,
are used as fallbacks in font stacks to indicate the type of family of font
we would like to get when the main font is not available. The standard font
emoji fits in this category of fallback fonts. However, recently we have
seen a desire amongst web designers to communicate the intention of 'give us
the font that the host system uses for UI'. So instead of trying to specify
in generic terms which fallback font we want, we would like to say quitte
specifically actually that we want 'the ui font'. so we are not talking about
a fallback scenario here. And indeed we tend to specify fonts like system-ui
and ui-monospace at the start of the font stack, instead of at the end.
For this reason I find it strange that there is no ui-emoji, even though we
did get ui-serif, ui-sans-serif and ui-monospace. Just like with the other
ui-* fonts, ui-emoji would be intended to say 'the font the host system UI
uses for emoji', instead of the much more generic 'a font intended for emoji'.
So base adds ui-emoji for completeness.
emojicon
Emojis are somewhat of an oddity amongst the other characters and symbols, because
normally characters are not colored by themselves. Text is usually monochrome in
nature and we apply color via CSS rules like color: green;. However Emoji are
different. These characters often do get colors and usually don't respond to the
color CSS rule. Icon fonts on the other hand use the fact that characters usually
are monochrome and only get color through CSS to allow designers to display the same
icon in many different colors just like they can do with text.
Noto Emoji is a recent example of a font that specifically adresses this, by offering all the Unicode emoji as monochrome icons that can be colored by CSS rules.
I think it would be convenient to be able to express the idea of 'icons based on
emoji'. Its not just an emoji font. Its an emoji font specifically for
icons. Hence the contraction emojicon. i wrote
a blog post
about this idea and I am taking the liberty of introducing it in this library in the hope
that other developers will be able to build on it so we can work towards portable icons
on the web together.
ui-emojicon
The font the host OS uses for displaying emojicon.
Adding this font seems like the consistent choice to make. Don't expect too much in
terms of support but it is here and we can tune it in the future.
