Search code examples
javascriptcssfont-face

How to set font family but retain fallback stack?


Is it possible to easily set a custom local font using JavaScript, but retain some default font-family stack in case the user tries to set a font that doesn't exist?

E.g., if the stack is font-family: Courier, monospace;, how to add to the beginning of the stack without resorting to manipulating strings?


Solution

  • Method 1: CSS custom properties

    The original answer below (method 2) had some issues, but then I came up with a more straightforward solution using CSS custom properties (variables).

    p {
        font-family: var(--userfont), Courier, monospace;
    }
    
    document.documentElement.style.setProperty('--userfont', 'Arial');
    // Result is equivalent to font-family: Arial, Courier, monospace;
    
    document.documentElement.style.setProperty('--userfont', 'non existant');
    // Result is equivalent to font-family: 'non existant', Courier, monospace;
    // which falls back to Courier assuming 'non existant' isn't actually the name of
    // an installed font
    

    JSFiddle demo

    Support

    Check here

    Unlike method 2 below, this method is supported by Edge v15+. Also it avoids the bug in older versions of Chromium that method 2 suffers from.


    Method 2: FontFace

    You can use @font-face, or rather the JavaScript API FontFace.

    p {
      font-family: userfont, Courier, monospace;
    }
    
    function setUserFont(fontName) {
        const userFontFace = new FontFace('userfont', `local(${fontName})`);
        document.fonts.add(userFontFace);
    }
    
    setUserFont('Arial');
    

    JSFiddle demo

    Support

    Check here

    Old/non-Chromium Edge (up to v18) does not support the JavaScript API.

    It appears there is a bug in Chromium 73 whereby fonts such as "Segoe UI Light" will not be found, only the base "Segoe UI" will be found. This applies to @font-face as well. It seems to be fixed as of Chrome 80.