The @font-face
rule is used
to automatically download Web Fonts on demand,
and use them for rendering
without the user having to install them.
The fonts are discarded after use,
and do not become installed fonts.
The src
descriptor contains the url of the font to be downloaded,
plus additional metadata so that unsuitable or
unusable fonts are not wastefully downloaded.
Other descriptors include the font family name, the range of font weights supported, the range of Unicode characters supported by the font, and so on. These are used for matching styling requests (properties) to available Web Fonts.
For example,
@font-face {
font-family: "Foo";
src: url(https://example.org/Foo.baz);
}
h1 {
font-family: "Foo", serif;
}
Originally introduced in 1997 and then merged into CSS2, @font-face was implemented as far back as Internet Explorer 3 (with the EOT font format).
Although consensus was reached on the CSS syntax, there was no consensus on the font format to use. Embedded OpenType (from Microsoft and AGFA), Truedoc Portable Font Resource (from Bitstream and Netscape), and SVG Fonts (W3C) were all used by early implementations. It was therefore necessary for the src descriptor to indicate the font format, in addition to the url.
At the time there were no MIME types for fonts, and significant resistance at IETF and IANA to adding a font top-level type.
I initially planned to use MIME types there, but given significant resistance to the idea of a font/* top level type we went with the 'format' string as a temporary workaround. Temporary meaning two decades or so. source
Relying on filename extensions would also have been fragile, so the original proposal used string-based format hints.
@font-face {
font-family: "Foo";
src: url(https://example.org/Foo.pfr) format("truedoc-pfr");
}
Lack of a common format crippled interoperability and resulted in minimal uptake of this feature. When development of CSS 2.1 began in 2002, @font-face (and its associated descriptors) were dropped from the specification.
In 2010, the FPWD of WOFF 1.0 heralded convergence on a Web font format, and @font-face was re-introduced with CSS Fonts 3 in 2011.
By 2018, when CSS Fonts 3 became a Recommendation, support for @font-face with a src descriptor and a defined set of format strings was universal in Web browsers and in CSS to PDF converters.
In parallel, WOFF 1.0 was being smoothly replaced by WOFF 2.0, fallback being provided by multiple urls and format hints in the src descriptor:
/* load WOFF 2.0 font if possible, otherwise use WOFF 1.0 font */
@font-face {
font-family: something;
src: url(example.woff2) format("woff2"),
url(example.woff) format("woff");
}
Usage of WebFonts has taken off, from near-zero in 2011 to 80% of all websites in 2020.
This success has enabled the use of fonts on the web to be further refined and improved, but also poses a significant long-tail Web-compatibility issue if any changes are made to this src descriptor.
Further historical background may be found in the introductory sections of the Progressive Font Enrichment Evaluation Report
Early fonts contained raster bitmaps at various sizes. These were soon improved by adding vector outlines of the glyphs, although sadly with multiple, competing, non-interoperable technologies. PostScript™ Type 1 outlines, from Adobe, were soon joined by TrueType™ outlines, from Apple (to say nothing of less successful vector outlines such as Speedo or Intellifont).
The typographic and layout capabilities of fonts were also improved, with TrueType GX and AApple Advanced Typography from Apple, OpenType from Microsoft, and Graphite from SIL. At first, these were used in print and in native applications, but the Web lagged behind in typographic richness.
CSS Fonts 3 opened up these font features to CSS, primarily focussing on OpenType. That specification sadly notes the limitation of the format hint in ensuring a downloaded font has the desired features:
Given the overlap in common usage between TrueType and OpenType [OPENTYPE], the format hints "truetype" and "opentype" must be considered as synonymous; a format hint of "opentype" does not imply that the font contains Postscript CFF style glyph data or that it contains OpenType layout information
Font formats continued to evolve; OpenType 1.7 added color font support although sadly, as implementation largely preceded standardization, there were four different ways each largely tied to a single vendor, OS, or browser. OpenType 1.8, with rather more coordination, added font variations which was rapidly adopted at both the OS and browser level.
The FPWD of CSS Fonts 4 in 2017 continued to refine font features and added support for variable fonts and color fonts. It was becoming clear, however, that the format hint was insufficient by itself and that combinatorial explosion would result (formats such as "opentype-variation" were briefly specified, but "opentype-variation-SVG-Graphite" would become unwieldy and error-prone).
With the upcoming COLRv1 color font format, selection of the right font resource and detectability are once again important use cases which have been requested by early adopters of COLRv1, similar to the need to detect availability and prefer such font resources at the time when variable fonts were introduced.
- I want to use color fonts, but browser A supports only color font format X, while browser B supports color font format Y in older versions and both X and Y in newer versions. However, color fonts are large, so putting both X and Y in one font increases download size unacceptably. All fonts are in "opentype" format, so the font format by itself cannot be used to make the choice.
-
A minority language is best supported by Graphite features, but Graphite is only implemented in Firefox. For Chrome and Safari, I want to fall back to OpenType features. Both fonts are in "opentype" format, so again the font format by itself cannot be used to make the choice.
-
I want to know programmatically in my script code what level of font support is available.
Where 3. is in line in line with the TAG design principles, which recommend detectability of a feature.
This proposal is not intended as a server-side content negotiation solution. In many cases, third-party font providers currently choose based on User Agent which resources they deliver to clients at the time of the request to the included CSS. This is a different content negotiation mechanism from what is discussed in this proposal.
The largest constraint is that older browsers must continue to successfully parse the src descriptor, and to end up ignoring links to fonts containing unsupported features, while following the desired link to the fallback.
This was discussed in 2016, in Issue 663 @font-family src: should accept a specifier which lists font requirements and again in 2018, in Issue 2540 Content authors want to modify style based on the presence of color fonts
There were various proposals, such as an additional "features" function using OpenType table names:
@font-face {
font-family: heading-font;
src: url(fancy-font.woff2) format("woff2") features("CPAL,FVAR"),
url(fallback-font.woff2) format("woff2"),
url(fallback-fallback-font.woff) format("woff"),
url(how-old-is-your-browser-font.ttf) format("ttf");
}
but that was unacceptable because of backwards compatibility (older browsers would drop the entire src descriptor, not just the first element in the comma-separated list), and also a desire to avoid microsyntaxes which:
wreak havoc on object models source
Thus, it was resolved in 2018 to put the extra "supports" requirements inside the parentheses of the format hint, but without string concatenation.
There was also Issue 6340 Drop bracket matching step from @font-face src: line parsing, resolved in June 2021, about whether to do parenthesis matching before splitting the value of the src descriptor on commas.
The original format hints were strings, although limited to a defined set. In CSS Fonts 4, either strings or keywords may be used for the format. The new font technology specifiers are keywords. There is an open Issue 6328 @font-face src: url() format() keywords vs. strings ambiguous in spec regarding how to serialize this.
An alternative solution to putting supports inside format is proposed in Issue 6520 Nesting of @supports inside @font-face.
Detecting font capabilities of the UA programmatically proves difficult due to
no direct mapping of font technologies to CSS.supports()
.
Testing of font capabilities is possible through probing for rendered pixels on a 2D canvas and testing for RGB color values, as done in @RoelN's Chromacheck tool. However, this is an extremely wasteful approach, requiring canvas resources for something that can be returned by the UA and detected more efficiently.
See 4.3.1 Parsing the src descriptor of the CSS Fonts Level 4 spec
With the introduction of the tech()
function part, the use cases 1 and 2 are then solved using the current
syntax as follows:
/* 1. prefer COLRv1, then SVG-in-OpenType, then COLRv0 */
@font-face {
font-family: jewel;
src: url(jewel-v1.woff2) format("woff2") tech(color-COLRv1),
url(jewel-svg.woff2) format("woff2") tech(color-SVG),
url(jewel-v0.woff2) format("woff2") tech(color-COLRv0),
url(boring.ttf) format("woff2");
}
/* 2. prefer Graphite over OpenType layout, if supported */
@font-face {
font-family: rare;
src: url(rare-graphite.otf) format("opentype") tech(features-graphite),
url(rare.otf) format("opentype") tech(features-opentype),
url(fallback.ttf) format("truetype");
}
Two solution exist for detectability.
One is the addition of font-tech()
function to CSS Conditional 5.
If this is not available,
the parsing result of the tech()
function in the src line
can be used for feature detection.
The wording of the specification reads:
If a component value is parsed correctly and is of a format and font technology that the UA supports, add it to the list of supported sources. If parsing a component value results in a parsing error or its format or technology are unsupported, do not add it to the list of supported sources.
Using this rule, availability of a particular font technology
can be programmatically tested for
by evaluating a @font-face
rule and
accessing its result src:
descriptor value.
Without downloading any font resources,
knowledge about technology support can be retrieved.
<style>
@font-face {
font-family: a;
src: url(/FEATURETESTVAR) format(woff2) tech(variations);
}
</style>
<script>
window.onload = () => {
var variations_supported = document.styleSheets[0].cssRules[0].cssText.includes("FEATURETESTVAR")
? "Variations supported." : "Variations NOT supported.")
console.log("Variations supported: " + variations);
}
</script>