htmlcssgoogle-chromesafaritext-rendering

Text is rendered differently (shifted/scaled horizontally and vertically) on Safari vs. Chrome


Take a look at this codepen: https://codepen.io/Ardeshir81/pen/EaVYXJp

* {
  font-family: "Audiowide";
  font-weight: 900;
}

.root {
  transform-origin: top left;
  scale: 15
}

span {
  background: red;
  position: absolute;
  opacity: 0.3;

  &.horizontal {
    width: 50px;
    height: 1px;
  }

  &.vertical {
    width: 1px;
    height: 20px;
  }
}
<div class="root">
  <span class="horizontal" style="transform: translateY(2.8px);"></span>
  <span class="horizontal" style="transform: translateY(16.2px);"></span>
  <span class="vertical"   style="transform: translateX(0px);"></span>
  <span class="vertical"   style="transform: translateX(44.4px);"></span>
  Hello
  </span>

  <!-- Registering Font Below -->
  <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=Audiowide&display=swap" rel="stylesheet">

It renders a text, and draws 4 red lines that fit perfectly around it on my Chrome:

Text on Chrome

Now look at the exact same code on Safari:

Same text on Safari looks different

I tried on Chrome/Linux and Chrome/Mac, the red lines fit perfect. Then I tried Safari/Mac and it was off.

What I experienced is it does not happen with all fonts, and does not happen with all font weights. Most of the fonts/weights I tried looked pixel perfect on both browsers.

I initially thought it was related to font ascenders and descenders, but then I noticed they also differ horizontally. The line-height also rules out because of the same reason.

I'm looking for ways to make sure different browsers render my text/font pixel perfectly the same.


Solution

  • »Faux bolds« are unpredictable

    The font-family "Audiowide" currently only supports a regular/400 font weight.
    Since you applied a non-existing font weight 900 – the browser will synthetically "embolden" the available font geometry.

      *{ font-family: "Audiowide"; font-weight:900; }
    

    Simplified: you can think of it as a rendering filter or effect – sometimes similar to a stroke applied to the actual glyph/letter shapes.

    Unfortunately, all 3 major browser engines – Chromium/Blink, Firefox/Gecko and Webkit(probably most significant Apple Safari – use completely different methods to increase boldness.

    Safari/Webkit tends to apply are more subtle emboldening while also adding some extra letter-spacing – that's why the text exceeds your bounding box elements.

    That's why the boundaries or your rendering differ so significantly and the adjacent elements are not aligned correctly.

    Workaround?

    Pixel-perfect cross-browser rendering?

    Sorry to disappoint you again but you will always encounter subtle differences in rendering across different browser.
    Especially, if you're aiming at rendering pixel-based boundaries like in your example.
    However, these effects are usually negligible as long as the used font is rendered in a genuine font weight and style.
    Besides, these engine specific subpixel/antialiasing differences are more negligible on high-pixel-density displays such as 4K/+ or most mobile device displays.

    See also related post about synthetic font renderings: "Different behaviour Edge and Chrome with font-weight"

    If your ultimate goal is to render tight bounding boxes around text you may also check JS font-parsing libraries such as opentype.js or typr.js which provide SVG rendering options.

    However, none of these libraries provide a weight extrapolation algorithm – so if you need a more heavy/black font – you should search for alternative fonts.