CSS & Front-End

Font Face Generator: The Licence and the Layout Shift

Two things go wrong with web fonts: the licence nobody opened, and the layout that jumps when the font finally arrives. Both answers are already inside the file, in tables you can read in your own browser before you serve a single byte.

Font Face Generator: The Licence and the Layout Shift

A designer sends you a folder: BrandSans-Regular.otf, BrandSans-Bold.otf, and a PDF that nobody opens. You copy the two files into the theme, write six lines of @font-face, and the site looks the way the mockup looked. The licence you just agreed to is sitting inside those two files, in the name table under name ID 13, and for commercial fonts it very often says the font may be installed on a workstation and may not be served from a web server.

The second problem shows up on every visit. The page paints in Arial, the font lands 800 milliseconds later, the browser reflows. The web font has a bigger x-height and wider advances, a three line paragraph becomes four, and everything below it drops 28 pixels. Google records that as Cumulative Layout Shift. Your reader records it as the sentence they were reading sliding out from under their eyes.

Both answers are in the file. A font carries its own licence text, its own vertical metrics, its own character coverage, and if it is variable, its own axis ranges. None of it needs guessing, and none of it needs a service.

Read the file before you serve it

Drop a TTF, OTF, TTC, WOFF or WOFF2 on the font face generator below and it walks the table directory and prints what is there: family and subfamily, full name, version, designer and designer URL, manufacturer, licence description and licence URL, trademark, vendor ID and the embedding permission in fsType. It counts coverage out of the cmap, generates the @font-face block, and derives the four descriptors that stop the page moving when the font lands. It runs entirely in your browser: the bytes are read with a DataView in the tab, nothing is uploaded and nothing is fetched.

Font face and fallback generator

Drop a font file and read what is actually inside it: the name records, the vertical metrics, the italic angle, the variable axes and every code point it covers. Then take the finished @font-face block, the preload line, and a matched local fallback whose overrides hold the layout still while the web font is on its way. The file is read here in the browser tab with a DataView, byte by byte, and is never uploaded.

Drop a font here
or press Enter to choose one.
TTF, OTF, WOFF and WOFF2.

Nothing loaded yet.

Specimen

Hamburgefonstiv

The quick brown fox jumps over the lazy dog

0123456789 & @ # ? ! « » € £ — •

Drawn with a FontFace made from the bytes on your disk.

Drop a font file above to read its tables.

Fallback matcher
The web font

Nothing loaded yet

height n/a
Before: plain fallback

Type is measured, not guessed. This paragraph is set in the fallback stack exactly as a browser would show it while the web font is still loading.

height n/a
After: adjusted fallback

Type is measured, not guessed. This paragraph is set in the fallback stack exactly as a browser would show it while the web font is still loading.

height n/a

The dashed line marks the height of the web font block. The closer the other two blocks sit to it, the smaller the jump when the real font arrives.

The CSS

Preload only the one file the first screen really needs, and only when it is served from your own origin. A preload that is never used is a wasted request, and crossorigin is required even for a same origin font because fonts are always fetched in CORS mode.

Making the WOFF2

This tool cannot convert anything to WOFF2. WOFF2 is Brotli compressed, and no browser exposes a Brotli compressor, so the conversion has to happen on your machine. Run one of these, both from Google's woff2 and fonttools:

For the same reason a dropped WOFF2 can only be read down to its table list, never into the tables themselves. Drop the TTF or OTF it was built from to see every value on this page.

Nothing loaded.
WunderPaint
The Dynamic Design and Automation Studio
WunderPaint is a layered image editor for your WordPress media library. WunderPaint Studio is the same thing in any browser, free and without an account.

What is actually in a font file

An OpenType file is not a compressed blob. It opens with twelve bytes of header, then one fixed record per table, then the tables at the offsets those records name. That is the container format, and it has not changed in thirty years.

sfnt header, 12 bytes
   0  uint32   sfntVersion     0x00010000 (TrueType) or 'OTTO' (CFF)
   4  uint16   numTables
   6  uint16   searchRange
   8  uint16   entrySelector
  10  uint16   rangeShift

table record, 16 bytes, numTables of them
   0  char[4]  tag             'head', 'cmap', 'OS/2', 'fvar', ...
   4  uint32   checkSum
   8  uint32   offset          from the start of the file
  12  uint32   length

A collection starts with ttcf and holds offsets to several fonts sharing glyph data. A WOFF starts with wOFF and a twenty byte record per table, each table stored raw or zlib compressed. A WOFF2 starts with wOF2 and everything after the header is Brotli, which matters later. Past the wrapper, these are the tables a web developer needs.

  • head: unitsPerEm, the grid every other number is expressed in. 1000 for most CFF fonts, 2048 for most TrueType ones. Also the font revision, the global bounding box, macStyle, and the created and modified timestamps, counted in seconds from 1 January 1904.
  • hhea: ascender, descender, lineGap and the maximum advance width. One of three places the vertical metrics live.
  • OS/2: the other two places, sTypoAscender / sTypoDescender / sTypoLineGap and usWinAscent / usWinDescent. Also sCapHeight and sxHeight from version 2, usWeightClass, usWidthClass, fsSelection, ten PANOSE bytes and fsType.
  • name: every human readable string, keyed by ID. Family is 1, subfamily 2, full name 4, version 5, trademark 7, manufacturer 8, designer 9, licence description 13, licence URL 14.
  • post: italicAngle, underline position and thickness, and the fixed pitch flag that says whether it is genuinely monospaced.
  • maxp: numGlyphs. A useful sanity check against file size before you decide subsetting is worth the trouble.
  • cmap: the map from code point to glyph. Format 4 covers the Basic Multilingual Plane in segments, format 12 everything above it, formats 0 and 6 are small legacy tables. Merge the segments and you have the exact set of characters the font can render.
  • fvar: present only in variable fonts. One record per axis with a four character tag (wght, wdth, slnt, opsz, ital or a custom one), a minimum, a default and a maximum, plus the named instances.

Every claim you make in CSS can be checked against those numbers. Write font-weight: 100 900 for a variable face whose wght axis runs 300 to 800 and the browser clamps silently, so your light weight never appears. The generator reads the ranges out of fvar into the rule, with a slider per axis so you can see each end before you commit.

The licence is name ID 13, and nobody reads it

Type licensing is split by use. A desktop licence covers installing the font on a number of workstations so Illustrator and Word can see it. A webfont licence is a separate product, usually priced per monthly pageview, and it is the one that permits serving the file from your domain. The two arrive in the same ZIP often enough that nobody notices there was a distinction.

The file states which one you have. Name ID 13 is the licence description, 14 a URL to the full text, 0 the copyright and 9 the designer. Open source fonts name the SIL Open Font License in one sentence. Commercial fonts write a sentence that begins something like “This font may not be modified, embedded in a web page, or redistributed” and point at a URL. That is a complete answer, and it takes five seconds to read once something has parsed it out.

fsType in OS/2 is the machine readable version, a bit field: 0 means installable embedding, bit 1 restricted licence embedding, bit 2 preview and print only, bit 3 editable embedding, bit 8 no subsetting, bit 9 bitmap embedding only. Be honest about what it governs. It describes embedding a font into a document such as a PDF, no browser enforces it, and a CSS @font-face rule is outside its scope. It is still the foundry’s stated intent in a form you cannot claim you missed, and bit 8 set to 1 is about as clear a signal as you will get regarding running a subsetter over the file.

The risk is not abstract. Converting an OTF to WOFF2 produces a modified copy of the software, which plenty of licence agreements forbid outright, and a font served from your origin is discoverable by anyone who opens the network tab. Foundries do audit, and a retroactive webfont licence for a busy site costs more than the licence would have.

font-display and what each value costs

A web font is a dependency the browser discovers late. It parses the HTML, fetches the CSS, matches an @font-face rule to an element that has text in it, and only then requests the file. Something has to happen during the wait, and font-display decides what.

The specification defines two windows rather than five behaviours. During the block period the text is laid out with the fallback but painted invisibly, the classic flash of invisible text. During the swap period the fallback is painted and the web font is still allowed to replace it. After both periods end, whatever is on screen stays. The five keywords are just five pairs of durations, and the browser picks the actual numbers.

Table of the five font-display values with Chrome's block and swap periods and the consequence of each: auto and block block for 3 seconds, swap paints the fallback at once and always reflows, fallback stops swapping after 3 seconds, optional keeps the fallback for the whole page load.

swap is the value everyone recommends, and it is worth being clear about what it buys: it removes the invisible text, and it guarantees the reflow. The font is applied however late it arrives, so the page always moves. optional is the only value that trades the font for a stable first paint. If the file is not cached within roughly 100 milliseconds the fallback stays for the whole page load, and the font downloads quietly for the next navigation. On a site whose readers arrive from search, read one article and leave, that means most of them never see your typeface. It is a design decision, not a performance setting. Fonts also sit fairly low in the diagnostic order that actually finds a slow site’s cause, well below the database and the hosting.

unicode-range only pays if you cut the file

unicode-range is the one descriptor that changes what gets downloaded rather than how it is displayed. The browser builds the set of code points it needs to render, compares it against each face in the family, and requests only the files whose declared range intersects that set. Declare seven faces for one family and an English page fetches one of them.

The trap is that the descriptor subsets nothing. It is a promise about the file, not an instruction to it. Writing unicode-range: U+0000-00FF on a face whose file holds 3,400 glyphs downloads all 3,400 and renders 200. The win comes from cutting real files with a subsetter and describing each one accurately, which is why the range and the subsetting command belong together. Google Fonts ships seven subsets for Latin script families, latin, latin-ext, greek, greek-ext, cyrillic, cyrillic-ext and vietnamese, and the tool counts your coverage against those seven plus twenty further named blocks, each with a count and a bar.

A subset is only claimed when its key characters are all present, so a font carrying a few shared accents is not passed off as covering Vietnamese. Code points outside every named block are counted into a CSS comment rather than quietly dropped, because a unicode-range that omits characters your content uses is a font that stops applying halfway down a page.

@font-face {
  font-family: "Brand Sans";
  src: url("https://cdn.wp-image-editor.com/fonts/brand-sans-latin.woff2") format("woff2"),
       url("https://cdn.wp-image-editor.com/fonts/brand-sans-latin.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  /* abbreviated; the generator emits the full list */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
                 U+2000-206F, U+2074, U+20AC, U+2122, U+FEFF, U+FFFD;
}

<link rel="preload" href="https://cdn.wp-image-editor.com/fonts/brand-sans-latin.woff2"
      as="font" type="font/woff2" crossorigin>

Two details there earn their place. The src list puts WOFF2 first because the browser takes the first entry whose format() hint it understands, so leading with the fat file hands every modern browser the wrong one. And crossorigin is required on the preload even for a font on your own origin, because font requests are made in anonymous CORS mode; leave it off and the file downloads twice. Preload the one subset that renders above the fold and nothing else.

Why the swap moves the page

A line box is not the font size. It is ascent plus descent plus line gap, scaled from unitsPerEm to the used font size, and the width of a text run is the sum of its glyph advances. Swap one font for another at the same font-size and both change: the line gets taller or shorter, and the text wraps at a different word. A paragraph that gains a line pushes everything after it down to the footer.

Worse, the vertical metrics are not one answer. They are three, and which one the browser uses depends on the platform and on a single bit.

Table of the three vertical metric sets in an OpenType file, OS/2 typographic, OS/2 win and hhea, with the platform that uses each depending on fsSelection bit 7, followed by the formulas for size-adjust, ascent-override, descent-override and line-gap-override with a worked example.

That bit is number 7 of fsSelection, USE_TYPO_METRICS. Set, the typographic values win everywhere. Clear, Windows browsers take the win values and macOS takes hhea, so a font whose three sets disagree renders at a visibly different line height on the two platforms. “It looks fine on my Mac” is not a test, and this is why.

CSS now lets the fallback lie convincingly. Four descriptors, all valid inside @font-face: size-adjust multiplies every glyph outline and advance by a percentage, and ascent-override, descent-override and line-gap-override replace the font’s vertical metrics with percentages of the used font size. Put them on a second @font-face rule pointing at a local system font and the fallback occupies exactly the space the web font will.

@font-face {
  font-family: "Brand Sans Fallback";
  src: local("Arial");
  size-adjust: 94%;
  ascent-override: 101.1%;
  descent-override: 26.6%;
  line-gap-override: 0%;
}

body {
  font-family: "Brand Sans", "Brand Sans Fallback", sans-serif;
}

The adjusted family goes second in the stack, after the real font and before the generic. The numbers are not found by nudging. Render the same string in both faces at the same pixel size, measure the advance width of each, and divide the web font’s width by the fallback’s: that ratio is size-adjust. Each vertical override is then the web font’s metric divided by unitsPerEm, divided again by that same ratio, because size-adjust has already rescaled the em the percentages resolve against. Forget the second division and every override is out by the size adjustment.

The tool measures rather than reads, and that is deliberate. It loads your file through the FontFace constructor, draws the measuring string on a canvas and takes fontBoundingBoxAscent and fontBoundingBoxDescent from the returned metrics: the values the browser actually chose on this platform, bit 7 already resolved, for your font and for the system stack you picked from the eleven in the select. The measuring string is an editable field for a reason, because a column of numerals has a different average advance from a paragraph of prose. The four numbers appear when the FontFace resolves and stay blank until it does, which is the tool refusing to guess. Before and after are drawn side by side with the web font’s height marked across both as a dashed line and the remaining shift printed in pixels.

Once the fallback and the web font agree on vertical space, a fluid type scale built with clamp() behaves the same at every viewport width instead of crossing a wrap threshold on one screen size and not another. Metric work is unglamorous, and it removes a whole class of the small typographic errors that give an amateur site away.

What the tool will not do, and the command that does

WOFF2 compresses the whole transformed font with Brotli. DecompressionStream, the only decompressor a browser exposes to script, supports gzip, deflate and deflate-raw, and there is no Brotli option in any engine. So a WOFF2 dropped on the tool is read as far as its header and its table inventory, and then it stops and asks for the TTF or OTF. For exactly the same reason it cannot produce a WOFF2 for you. It prints the commands instead.

# TTF or OTF to WOFF2, Google's woff2 tools
woff2_compress BrandSans-Regular.ttf

# subset and compress in one pass, fonttools
pip install "fonttools[woff]" brotli

pyftsubset BrandSans-Regular.ttf 
  --unicodes="U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+20AC" 
  --layout-features="kern,liga,clig,calt" 
  --flavor=woff2 
  --output-file=brand-sans-latin.woff2

The other limits are stated in the interface rather than hidden behind a spinner. A WOFF whose tables are zlib compressed needs DecompressionStream; where that is missing you get the table list and the reason. Only the first font of a collection is read, only the first 64 tables and 64 named instances are listed, and files over 32 MB are refused. If the browser declines the file, every table is still shown and the tool says the specimen and the width measurement will stay empty. Strings from your file are treated as data throughout, set with textContent, escaped for CSS, with a comment terminator inside a family name broken up so it cannot close a comment early.

Where the rules go in WordPress

The @font-face rules and the adjusted fallback belong in your own stylesheet, which means a child theme if the parent is one you did not write, so an update does not take the fonts with it. The preload cannot live in a stylesheet at all, because its whole point is starting the request before the CSS is parsed. It goes into the head, early, from functions.php.

add_action( 'wp_head', function () {
    printf(
        '<link rel="preload" href="%s" as="font" type="font/woff2" crossorigin>' . "n",
        esc_url( get_stylesheet_directory_uri() . 'https://cdn.wp-image-editor.com/fonts/brand-sans-latin.woff2' )
    );
}, 1 );

The two failures at the top of this article are one failure wearing different clothes. Nobody opened the file. The licence was in it, the metrics were in it, the coverage was in it, and all of it was readable in the time it takes to drag a font onto a page. It goes unread because reading a binary by hand is tedious, not because the information is hard.

A working order falls out of that. Read the licence first, because if the answer is no then nothing else matters. Check the coverage against the languages you publish in. Subset and compress on your own machine with the commands above. Then generate the rule, choose font-display deliberately rather than by habit, and measure the fallback against the real font so the two occupy the same space.

Done in that order, a web font arriving stops being an event on the page. The shapes change, the letterforms sharpen into the ones the designer chose, and not a single line moves. That is the whole target, and four descriptors and ten minutes will reach it.

Font Face Generator: The Licence and the Layout Shift

Table of Contents

Learn it by building something

Every week one thing you can make the same afternoon, from dynamic templates to 3D type. Written down step by step.

One mail a week, and then it ends.
Unsubscribe in one click.

Photo Editing

Content Aware Resize: Changing a Photo’s Shape Without Squashing Anyone

One photograph has to be a wide header, a square thumbnail and a tall story card. Cropping loses the edges, stretching lies about proportions, letterboxing buys space it never uses. Seam carving spends the quiet parts of the frame instead: what a seam is, why the search needs a table, and exactly where the method falls apart.

AI Images

AI Product Photos That Do Not Look Fake: Light, Shadow, Perspective

A real product photo on a generated background gives itself away on exactly three axes: light direction, shadow quality and perspective. Here is the physics in plain words, a browser tool that walks you through all three checks on your own composite, and the prompt lines that stop the mismatch happening in the first place.

Design Fundamentals

Text on a Photograph That Stays Readable, Whatever the Picture

A headline that sits beautifully over a dark corner becomes unreadable the moment somebody swaps the picture. That is a solved problem.

SEO & Structured Data

Redirect Rule Generator: The Rules That Never Fire

A redirect list rarely breaks. It accumulates: a broad prefix rule that kills every rule below it, a target missing a trailing slash that doubles your hop count, a 301 where a 308 was needed. Generate the Apache, nginx and CSV versions of your rules, then walk a URL through them and watch where it actually goes.

Troubleshooting

WordPress Maximum Upload Size: Where the Limit Really Lives

The media library says 8 MB, the file is 12 MB, and nothing you edit in WordPress moves the number. It is not a WordPress setting at all: core reads two PHP directives and prints the smaller one. Which limit is really stopping the upload, why post_max_size is usually the culprit, how to raise the limits in the order that works, and why a photograph almost never needs a bigger ceiling.

WordPress Images

Responsive Hero Images: The Focal Point Decides Everything

Your hero becomes a 3.2:1 strip on desktop and a portrait crop on phones, and object-fit: cover decides what survives. The formula behind the crop, the CSS that steers it, and a simulator that shows all four breakpoints before you publish.

Download the free WunderPaint Plugin for WordPress

The WunderPaint workspace with the layers panel, adjustment sliders, text style presets and the asset library along the bottom

The Image Editor & Design Studio

Everything described here can be done in the browser, on your own site. The live demo runs the full editor with nothing to install.

Free

Chaos Art

Autonomous painters make one-of-a-kind abstract art in 3D space - gestures, art movements, painterly media, and embeds that paint a new original for every visitor.

Pro

Particle Strokes

Paint with swarms of light: twenty-two movements, a stamp you draw yourself, and curves that give a stroke a shape - the swarm keeps painting for a few seconds after you let go.

Pro

City Diorama

Any place on earth as a miniature you could hold: real streets, water and building footprints raised into a 3D diorama - or wrapped around a sphere as your own tiny planet.

Free

Papercut Art

Layered paper pictures with real depth - a photo sliced along its actual depth into up to twenty layers, parametric landscapes, animals and clouds you can shape, letters with real counters, and a look that runs on three dials.

Pro

3D Earth Studio

A hyperrealistic globe - day and night with city lights, live clouds, atmosphere halo, country borders and highlights, click-to-place markers with flight-route arcs, satellite orbits, seamless rotation video and a live website embed.

Free

Mystic Studio

Turn a birth date into wall art - a real natal chart with houses and aspects, the moon of that night, zodiac and Chinese zodiac posters, numerology cards and a synastry wheel for two, in eight artful themes.

Free

Marble Bath

Marble paper on a virtual water bath - drop, rake and comb real Ebru patterns with gestures, flowers and classic recipes, razor-sharp at any size and re-editable as a layer.

Free

Day Ring

Turn a day into a beautiful circular schedule - colour-coded time blocks as arcs around a 24-hour clock, with concentric rings for overlaps, emoji, templates and a legend.

Free

Code Shot

Turn code into a gorgeous, share-ready image - syntax highlighting, editor themes, window frames and diff highlighting - then drop it into your design as a re-editable layer.

Pro

3D Solar System Studio

Build a date-accurate 3D solar system - real planet positions for any date, photoreal textures and one slider from artistic to true scale - then drop it into your design as an editable layer.

Pro

3D Molecule Studio

Build a real 3D molecule - from a curated library, the periodic table or a SMILES string - then style it, measure it and drop it into your design as an editable layer.

Pro

3D Textile Studio

Drop your design onto cloth that behaves like the material you pick: silk falls soft, felt holds its shape, flag fabric snaps in the wind. Hang it, blow it and drape it, then lay the finished drape back into your document as a picture.

Pro

3D Particle Studio

Point the engine at any layer and it becomes a cloud of particles that keeps its colours, flowing through a sphere, a galaxy or your own outline. Keep the frame you like as a still, or embed the running engine so it keeps moving on your page.

Free

Origami

Put your own picture on the paper and watch that very sheet fold itself into a crane or a box. Every step is a station you can stop at and turn around in 3D, which is exactly where printed diagrams leave you alone.

Pro

3D Flip Studio

A hardcover you can leaf through, a limp magazine, a strewn pile of sheets, a sticker peeling off its backing. The curl is real geometry, so the print never slides across the paper.

Free

Handwriting Fonts

Draw the alphabet here or fill in a printed sheet and photograph it. What comes out is a genuine font family, installed into your site and available in every picker.

Pro

Step Guides

Turn any picture into an instruction. Every mark is pinned to a place in the image, so arrows still point at the right thing after the callout has been dragged somewhere else.

Free

Text Art

One studio, sixteen art types: ASCII and emoji art, brick, dice, cube, sticky-note, LED, ceramic and keycap mosaics, word portraits, text flows, silhouettes, element tiles and more.

Free

Photo Mosaic

The classic photomosaic, computed in your browser: your main image emerges from many media-library photos via true structure matching - never a cheap overlay.