DEV Community

Muhammad Afsar Khan
Muhammad Afsar Khan

Posted on

How I Built a Side-by-Side Font Comparison Tool (And Accidentally Learned Way Too Much About Browser APIs)

I wanted to let people compare system fonts with Google Fonts.

Sounds simple, right?

It was not simple.

But after a lot of trial, error, and yelling at my browser console, I got it working. Here's how it works — including the parts that almost made me give up.

The Idea

I built FontPreview because I was tired of guessing how fonts would look with real text. But the thing designers kept asking was: "Can I compare this Google Font with the font already on my computer?"

Turns out, that's harder than it sounds.

Browsers don't really want you poking around someone's system fonts. For good reason — imagine every website you visit getting a list of everything installed on your computer. That's a privacy nightmare.

But there's a newer API that lets you do this, if the user says it's okay.

The Local Font Access API

There's this thing called the Local Font Access API. It's relatively new, and not every browser supports it yet (looking at you, Safari). But in Chrome and Edge, you can do this: javascript

const fonts = await window.queryLocalFonts();
Enter fullscreen mode Exit fullscreen mode

That one line of code returns an array of every font installed on the user's system.

Except — and this is important — the browser will ask the user for permission first. A little popup shows up saying "This site wants to see your fonts." If the user says no, you get nothing.

This is good. We don't want random sites scraping font lists without permission.

The Permission Dance

Here's how I handle it: javascript

function checkSystemFontsPermission() {
  if (!window.queryLocalFonts) {
    showToast('Local Font Access API not supported in this browser');
    useFallbackFonts();
    return;
  }

  // Show the permission modal
  document.getElementById('permissionModal').style.display = 'flex';
}
Enter fullscreen mode Exit fullscreen mode

If the browser doesn't support the API, I fall back to a list of popular system fonts. Not perfect, but better than nothing.

If it does support it, I show a little modal explaining why I'm asking and what I'll do with the data. (Spoiler: nothing. I just show them in a dropdown.)

What Happens When They Say Yes

When the user clicks "Allow", this runs: javascript

async function requestFontPermission() {
  try {
    const fonts = await window.queryLocalFonts();

    // Clean up font names (remove "Regular", "Bold", etc.)
    const fontMap = new Map();

    fonts.forEach(f => {
      let name = f.family;
      const suffixes = [' Regular', ' Bold', ' Italic', ' Light', ' Medium'];

      suffixes.forEach(suffix => {
        if (name.endsWith(suffix)) {
          name = name.substring(0, name.length - suffix.length);
        }
      });

      if (!fontMap.has(name)) {
        fontMap.set(name, {
          family: name,
          fullName: f.family,
          style: f.style,
          weight: f.weight
        });
      }
    });

    // Sort and store
    allSystemFonts = Array.from(fontMap.values()).sort((a, b) => 
      a.family.localeCompare(b.family)
    );

    showToast(`Loaded ${allSystemFonts.length} system fonts`);

  } catch (error) {
    if (error.name === 'NotAllowedError') {
      showToast('Permission denied. Using fallback fonts.');
    } else {
      showToast('Error loading fonts. Using fallback.');
    }
    useFallbackFonts();
  }
}
Enter fullscreen mode Exit fullscreen mode

The Map thing is important. A lot of fonts come back with multiple entries for different styles — "Arial Regular", "Arial Bold", "Arial Italic". I just want "Arial" once. The Map deduplicates them.

The Big Problem I Didn't Expect

Once I had the font list, I needed to actually use those fonts in the preview.

In CSS, you can just do:

font-family: 'Arial', sans-serif;
Enter fullscreen mode Exit fullscreen mode

And if the user has Arial installed, it works. Great.

But here's the thing — I also wanted to let users compare system fonts with Google Fonts side by side. So if someone picks Arial on the left and Roboto on the right, I need to load Roboto from Google Fonts.

That means dynamically injecting a tag: javascript

function loadGoogleFontForPanel(fontName, panel) {
  const fontFamily = fontName.replace(/ /g, '+');

  const linkId = `panel-font-${panel}`;
  const oldLink = document.getElementById(linkId);
  if (oldLink) oldLink.remove();

  const link = document.createElement('link');
  link.id = linkId;
  link.rel = 'stylesheet';
  link.href = `https://fonts.googleapis.com/css2?family=${fontFamily}&display=swap`;

  document.head.appendChild(link);
}
Enter fullscreen mode Exit fullscreen mode

This way, each panel can load its own font independently. Left panel can load a system font (which doesn't need a stylesheet), right panel can load a Google Font (which does).

The Part That Still Bugs Me

When you load a Google Font dynamically, there's a tiny delay before it's available. During that moment, the text shows up in the default font, then swaps to the chosen font.

It's a flash of unstyled text. FOIT, if you want the fancy term.

I tried a bunch of fixes. Preloading, font-display: swap, even hiding the text until the font loads. Everything felt janky.

Eventually I just left it. The flash is brief, and most users don't notice. But I notice. Every time.

What I'd Do Differently

If I built this again from scratch:

  • Cache the permission status. Right now, the modal shows every time you click "System". That's annoying. I should store their choice in localStorage.
  • Better error handling. Sometimes the API just... fails. No error, no nothing. The code just stops. I need to catch that better.
  • Fallback for Safari. Safari doesn't support this API at all. I should build a real fallback instead of just a list of popular fonts.

The Code (If You Want It)

The whole thing is on FontPreview if you want to see it in action. View source — it's all client-side, no backend, no tracking, just HTML, CSS, and JavaScript.

I'm not a great developer. I learned most of this by breaking things and Googling errors. But it works, and people use it, and that's enough for me.

If you're building something with the Local Font Access API, hit me up. I've probably already hit the same bugs you're hitting.

Originally published on dev.to. Try the tool here: FontPreview

Tags: javascript, webdev, tutorial, showdev, api

Top comments (0)