Why two fonts end up fighting over one character

Every @font-face rule can carry a unicode-range: the list of code points the font is allowed to be used for. It is how font services hand out subsets. The Latin file covers the Latin letters, the Greek file covers the Greek ones, and the browser picks per character rather than per family.

As soon as more than one family declares a range, the browser has to choose. It takes the last matching face in the cascade for that character, and it does it silently. There is no warning, no console message and no visible difference on most pages — the heading simply comes out in a typeface you did not expect for a handful of letters.

What the advisor checks

  • Conflicting ranges between families — two different families claim the same characters. One of them is never used for them, and which one wins depends on the order of the rules rather than on your intent.
  • Overlapping ranges inside one family — the same family at two weights covering the same characters. The later face wins, so the earlier one never applies. Switch on the strict option to see these too.
  • Duplicate declarations — two blocks with the same family, weight and style. The second silently replaces the first.
  • Missing font-display — without it the browser hides text while the file loads, which is the flash of invisible text on a slow connection.
  • Missing format() — modern browsers infer it, but older ones need it to know how to read the file.
  • local() before url() — the installed copy wins when it exists, so the same page can look different on two machines.
  • No unicode-range at all — the browser may then use any character the file contains, which rules out subsetting and makes the fallback unpredictable.

Reading the character map of a real font file

Drop a .ttf or .otf file into the panel and the tool reads its character map straight out of the binary tables — the format 4, 6 and 12 subtables — and reports the code points the font actually holds, the number of glyphs and the tables the file carries. Files are matched to the rules by name, ignoring the extension, so a stylesheet pointing at Inter-Regular.woff2 is matched against an Inter-Regular.ttf you drop in.

That comparison is what turns a guess into a fact. A face can promise a range the file does not honour: the range says the font covers U+00E9, the file has no glyph for it, and the browser draws a substituted character or a box. The tool lists those separately — characters no face covers, and characters a face claims but the file does not contain.

How to use it

  1. Paste the CSS that holds your @font-face rules into the top panel.
  2. Drop the font files into the side panel. The file names have to match the ones in the stylesheet, apart from the extension.
  3. Paste the text the page actually shows into the sample box. Every character in it is checked against the ranges, and then against the real files.
  4. Read the Font faces table for the parsed rules, the Findings list for the problems and the Character coverage list for the gaps.
  5. Decide what to change. Which rule stays and which extra character gets added is a design decision, so the tool stops at naming the conflict.
  6. Once the CSS is settled, the HTML, CSS & JavaScript Minifier strips the whitespace back out of it before it ships.

Good to know

  • WOFF and WOFF2 files wrap the same tables in a compressed stream, so their character maps cannot be read here. Drop the TTF or OTF with the same name instead; the tool says so on screen when you try.
  • Only the rules you paste are checked. A stylesheet assembled from several files has to be pasted as one, or the ranges living in the other files stay invisible.
  • Weights inside one family are allowed to share a range, so that case is only reported when the strict option is on.
  • Nothing is uploaded. The CSS is parsed in the page and the font files are read with the browser's own file API.
  • The sample text is what makes coverage meaningful. A page of plain ASCII looks fine against a font that is missing every accented character.