Search code examples
cssbrowserfontscross-browser

Google Fonts - variable fonts not working in Windows browsers


I have a simple Next.js app that I am developing on macOS (chrome) and have only noticed something is wrong when testing on Windows (chrome, and others too).

I use the font Inter from Google Fonts, nextjs + tailwind takes care of injecting needed css in <head>:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet" />

which after build translates to:

<style data-href="https://fonts.googleapis.com/css2?family=Inter&display=swap">
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZthjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZNhjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZxhjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+0370-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZBhjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZFhjp-Ek-_EeAmM.woff) format('woff');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v7/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hjp-Ek-_EeA.woff) format('woff');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
</style>

and tailwind config:

theme: {
    extend: {
        fontFamily: {
            sans: ['Inter var', ...defaultTheme.fontFamily.sans],
        },
    },
},

which then after build, in browsers sets following declaration to <html> tag:

font-family:
Inter var,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji

The "Inter var" works perfectly on macOS browsers, doesn't work on Windows - it falls back to next option. When I change it to "Inter" only, it works on Windows too, but obviously, the variable font stuff is gone and everything is too thin. Rather than changing all CSS to reflect to this, why does the variable font not work on Windows? I already spend about a whole day on this and other SO posts didn't seem to work for me. Am I doing something wrong?


Solution

  • Update: google font API might return static font files

    Due to user-agent detection, you might get static rules in some browsers – even though they support variable fonts flawlessly (e.g. chromium/blink based Opera browser). Firefox or Chrome should work perfectly.

    Use the '..' range delimiter and open the css URL in firefox

    https://fonts.googleapis.com/css2?family=Inter:wght@100..900
    

    The desired variable @font-face rules should look like this:

    @font-face {
      font-family: 'Inter';
      font-style: normal;
      font-weight: 100 900;
      src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2');
    }
    

    Note the font-weight: 100 900 property using two values to specify a weight range – Bingo! we got the correct variable font css.

    Example1: retrieve @font-face via API css URL

    let fontWeight = document.querySelector('#fontWeight');
    let fontVariation = document.querySelector('#fontVariation');
    let fontSamples = document.querySelectorAll('.fontSample');
    
    fontWeight.addEventListener('change', function(e){
      let value = e.target.value;
      fontSamples.forEach(function(item, i){
          fontSamples[i].setAttribute('style', 'font-weight:'+value);
      } )
    } );
    
    
    fontVariation.addEventListener('change', function(e){
      let value = e.target.value;
      fontSamples.forEach(function(item, i){
          fontSamples[i].setAttribute('style', 'font-variation-settings: \'wght\' '+value);
      } )
      
    } )
    body{
    font-family: georgia
    }
    
    
    @font-face {
        src: url('https://mdn.github.io/css-examples/variable-fonts/fonts/AmstelvarAlpha-VF.woff2') format('woff2-variations');
        font-family:'Amstelvar';
        font-style: normal;
    }
    
    .amstelvar{
         font-family: 'Amstelvar', serif;
    
    }
    
    .inter{
         font-family: 'Inter', 'Open Sans', sans-serif;
    }
      
    
    h1{
      font-weight: 500;
      transition: 0.3s;
    }
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
    
    
    <h1 class="fontSample inter">Hamburglefonstiv (Inter)</h1>
    <h1 class="fontSample amstelvar">Hamburglefonstiv (Amstelvar)</h1>
    
    <p>
    <label>Font weight</label>
      <input id="fontWeight" type="range" min="100" max="900" step="1">
    </p>
    
    <p>
    <label>font-variation-settings</label>
      <input id="fontVariation" type="range" min="100" max="900" step="1">
    </p>

    As mentioned before, this won't work in Opera (and maybe other browsers). So the above css URL

    https://fonts.googleapis.com/css2?family=Inter:wght@100..900

    will return the same result as:

    https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900

    containing rules for each font-weight.

    Example 2: workaround for Opera @font-face

    We can copy the correct variable font-face rule to our css.

    let fontWeight = document.querySelector('#fontWeight');
    let fontVariation = document.querySelector('#fontVariation');
    let fontSamples = document.querySelectorAll('.fontSample');
    
    fontWeight.addEventListener('change', function(e) {
      let value = e.target.value;
      fontSamples.forEach(function(item, i) {
        fontSamples[i].setAttribute('style', 'font-weight:' + value);
      })
    });
    
    
    fontVariation.addEventListener('change', function(e) {
      let value = e.target.value;
      fontSamples.forEach(function(item, i) {
        fontSamples[i].setAttribute('style', 'font-variation-settings: \'wght\' ' + value);
      })
    
    })
    body {
      font-family: georgia
    }
    
    @font-face {
      font-family: 'Inter';
      font-style: normal;
      font-weight: 100 900;
      src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2');
    }
    
    @font-face {
      src: url('https://mdn.github.io/css-examples/variable-fonts/fonts/AmstelvarAlpha-VF.woff2') format('woff2-variations');
      font-family: 'Amstelvar';
      font-style: normal;
    }
    
    .amstelvar {
      font-family: 'Amstelvar', serif;
    }
    
    .inter {
      font-family: 'Inter', 'Open Sans', sans-serif;
    }
    
    h1 {
      font-weight: 500;
      transition: 0.3s;
    }
    <h1 class="fontSample inter">Hamburglefonstiv (Inter)</h1>
    <h1 class="fontSample amstelvar">Hamburglefonstiv (Amstelvar)</h1>
    
    <p>
      <label>Font weight</label>
      <input id="fontWeight" type="range" min="100" max="900" step="1">
    </p>
    
    <p>
      <label>font-variation-settings</label>
      <input id="fontVariation" type="range" min="100" max="900" step="1">
    </p>

    Since the font-family name is defined just as "Inter" (without "var") in the retrieved @font-face declaration you should also refer to it using this name:

    theme: {
        extend: {
            fontFamily: {
                sans: ['Inter', ...defaultTheme.fontFamily.sans],
            },
        },
    },
    

    google font UI

    Update as of 2024 the UI allows you to generate a variable font query. However, undestanding the creation of tuples may still be interesting if you intend to create something like a custom font selector.

    Theres is currently no intuitive way to retrieve the correct parameters since the the UI is more focused on static file output.

    Concatenate the URL query parameters manually:

    https://fonts.googleapis.com/css2?family= +
    Inter +
    : + slnt + , + wght (design axis names)+
    @ + -10..0 + , + 100..900 (design axis range values)

    Example 3: Inter slant+weight

    let fontWeight = document.querySelector('#fontWeight');
    let fontVariation = document.querySelector('#fontVariation');
    let fontSamples = document.querySelectorAll('.fontSample');
    
    fontWeight.addEventListener('change', function(e) {
      let value = e.target.value;
      fontSamples.forEach(function(item, i) {
        fontSamples[i].setAttribute('style', 'font-weight:' + value);
      })
    });
    
    
    fontVariation.addEventListener('change', function(e) {
      let value = e.target.value;
      fontSamples.forEach(function(item, i) {
        fontSamples[i].setAttribute('style', 'font-variation-settings: \'slnt\' ' + value);
      })
    
    })
    /* latin */
    
    @font-face {
      font-family: "Inter";
      font-style: oblique 0deg 10deg;
      font-weight: 100 900;
      src: url(https://fonts.gstatic.com/s/inter/v12/UcCo3FwrK3iLTcviYwY.woff2) format("woff2");
    }
    
    body {
      font-family: Inter;
    }
    
    .inter {
      font-family: "Inter", "Open Sans", sans-serif;
    }
    
    h1 {
      font-weight: 500;
      transition: 0.3s;
    }
    <h1 class="fontSample inter">Hamburglefonstiv (Inter)</h1>
    
    <p>
      <label>Font weight</label>
      <input id="fontWeight" type="range" min="100" max="900" step="1">
    </p>
    
    <p>
      <label>font-slant</label>
      <input id="fontVariation" type="range" min="-10" max="0" step="1" value="0">
    </p>

    Worth noting: You can also style your text via font-variation-settings property.
    To change the boldness we would use:

    .black{
        font-variation-settings: wght 900
    }
    

    See also css-tricks.com: Getting the Most Out of Variable Fonts on Google Fonts