cssfonts

Strange results with -webkit-text-stroke-width?


I'm attempting to achieve a particular visual style in html. My css for the span looks like the following:

h1 *:nth-child(2) {
    font-family: "ImpactURW";
    font-size: 50vw;
    letter-spacing: 1vw;
    line-height: 0.8;
    margin: -3vw 0 1vw 0;
    -webkit-text-stroke-width: 2.5px;
    -webkit-text-stroke-color: black;
    background-image: linear-gradient(to bottom, transparent 55%, #ccc 45%);
    -webkit-text-fill-color: transparent;
    -webkit-background-clip: text;
}

This seems to be more or less correct with pretty much any font I use except this particular font, and I do not know why. Occasionally it will render somewhat correctly, when the page is sized small enough (font-size being tied to vw units), but at larger sizes of the sort that are expected, it bugs out. Here it is at a smaller size:

enter image description here

And here it is at not much larger:

enter image description here

This is cross-browser, I'm having the same trouble in Chrome and Firefox, so it might be the font file itself. Is it damaged? Is my code just wrong but correctable? Adjusting the stroke-width at any size will cause the same effect (and the top image has the stroke-width a little too thick anyway). This works as expected with other fonts, of course, but those are just the wrong ones.


Solution

  • Faux bolds break text-stroke renderings

    The -webkit-text-stroke property applies strokes to the glyphs shape geometry. It has a similar effect like converting text to a <path> in a graphic application like inkscape and adding a stroke.
    This can introduce weird results for modern fonts using overlapping shapes – mostly variable fonts. See also "Text Stroke (-webkit-text-stroke) css Problem".

    @font-face {
      font-family: 'Roboto Flex';
      font-style: oblique 0deg 10deg;
      font-weight: 100 1000;
      font-stretch: 25% 151%;
      font-display: swap;
      src: url(https://fonts.gstatic.com/s/robotoflex/v26/NaPccZLOBv5T3oB7Cb4i0zu6RME.woff2) format('woff2');
      unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    }
    
    .grd {
      display: grid;
      gap: 1em;
      grid-template-columns: 1fr 1fr 1fr;
    }
    
    h1 {
      font-family: Anton, "ImpactURW";
      font-size: clamp(350px, 40vw, 50vw);
      line-height: 1em;
      margin: 0;
    }
    
    .noSynth {
      font-synthesis: none;
    }
    
    .stroke {
    
      -webkit-text-stroke-width: 0.01em;
      -webkit-text-stroke-color: black;
      background-image: linear-gradient(to bottom, transparent 55%, #ccc 45%);
      -webkit-text-fill-color: transparent;
      -webkit-background-clip: text;
    }
    
    
    .roboto {
      font-family: 'Roboto Flex';
      font-stretch: 50%;
    
    }
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Anton:wght@400&display=swap">
    <div class="grd">
      <div class="col">
        <h3>Faux bold</h3>
        <h1 class="stroke">A</h1>
        <h1 class="">A</h1>
      </div>
      <div class="col">
        <h3>font-synthesis disabled</h3>
        <h1 class="noSynth stroke">A</h1>
        <h1 class="noSynth">A</h1>
      </div>
      <div class="col">
        <h3>Variable font – overlapping shapes</h3>
        <h1 class="roboto noSynth stroke">A</h1>
        <h1 class="roboto noSynth">A</h1>
      </div>
    </div>

    Native Bold fonts like Impact

    Fonts like Impact are designed for headline purposes and usually only have a single font-weight. Despite the fact we perceive a bold font the native font weight is in fact regular or 400!

    When we apply the Impact to a heading like <h1> the already bold font design gets artificially emboldened.

    Combined with text-stroke the rendering goes haywire in most browsers (e.g Chromium) as the text-stroke is also applied to the new faux-bold structure.

    The rendering differs from browser to browser since all 3 major engines (Chromium, Firefox, Webkit) use completely different methods for faux-styles and weights.

    Size-dependant stroke-widths

    In your case you sometimes see a single thick stroke and sometimes (as in your first screenshot) the described nested strokes (second screenshot). That's simply because browsers usually apply some value rounding especially for font related properties to retain a crisper rendering. Otherise the rendering would quickly become blurry.

    Solution