HackSoft logo
  • Approach
  • Case Studies
  • Team
  • Company
  • Services
      Custom Software Development ConsultingAI Integrations
  • Solutions
  • Open Source
  • Blog

Using multiple font files the right way

Kamen Kotsev
Aug 27, 2020
Categories:CSSSoftware Development

Having multiple font files for a single font can lead to a messy css if you don't know how to use them all together as a single font-family, using the full potential of the @font-face css declaration.

When implementing the design of a website we often have to use different files for different font weights (light, bold) and font styles (italic, oblique). Some custom-made fonts don't contain all the variations of all the characters and the reasons for this are a topic for another article.

If you are like me, you're just a developer implementing the design that someone else has already created. The designer wants you to use several files with different font characteristics of a single font. For example:

  • Roboto-Regular.woff
  • Roboto-Light.woff
  • Roboto-Semibold.woff
  • Roboto-Bold.woff
  • Roboto-Italic.woff
  • Roboto-BoldItalic.woff

When I first saw this, I was taunted by the complexity that this would bring into the css implementation. If I use only a single font (for example "Roboto-Regular") for the entire website and set the font-weight property to 600 (semibold) - the font looks different than it does in the design I'm given. That's because the browser tries to use the "Roboto-Regular" font with font-weight: 600; and it fails to produce a decent result.  That brings me to an implementation of this sort:

@font-face {
  font-family: 'Roboto-Regular';
  src: url(fonts/Roboto-Regular.woff) format('woff');
}
@font-face {
  font-family: 'Roboto-SemiBold';
  src: url(fonts/Roboto-SemiBold.woff) format('woff');
}
@font-face {
  font-family: 'Roboto-Bold';
  src: url(fonts/Roboo-Bold.woff) format('woff');
} 

body {
  font-family: 'Roboto-Regular';
}

And then whenever I want some text to have a font-weight of 600 (semibold) I would have to write:

p {
 font-family: 'Roboto-Semibold';
}

instead of what everyone would expect:

p {
 font-weight: 600;
}

This proves difficult to read, troublesome to use with libraries and a pain to implement. Overall it's a bad solution for this case.

The better solution is to use the full potential of the @font-face rule. It describes the styles that an element should have in order for this @font-face rule to be used. Let's call those characteristics in the @font-face"guards". The @font-face itself has a lot of different guards that can be checked before a font is used for some purpose.

@font-face {
  /* Essential deinitions. Without one them the font-face is considered invalid */
  font-family: <name>;
  src: <local/url/guards>;
  
  /*
  "Guard" definitions.
  They define the cases when the current font-face should be used.
  They can all be omitted in a font-face definition
  */
  unicode-range: <range of characters for which the font should be used>;
  font-variant:
    normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ];
  font-feature-settings: normal | <feature-tag-value>;
  font-stretch:
    normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded;
  font-weight:
    normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
  font-style: normal | italic | oblique;
}

Using the font-face guards, we can improve our css from above to be:

@font-face {
  font-family: 'Roboto';
  src: url(fonts/Roboto-Regular.woff) format('woff');
  font-style: normal;
  font-weight: 400;
}
@font-face {
  font-family: 'Roboto';
  src: url(fonts/Roboto-SemiBold.woff) format('woff');
  font-style: normal;
  font-weight: 600;
}
@font-face {
  font-family: 'Roboto';
  src: url(fonts/Roboto-Italic.woff) format('woff');
  font-style: italic;
  font-weight: 400;
}
@font-face {
  font-family: 'Roboto';
  src: url(fonts/Roboto-Bold.woff) format('woff');
  font-style: normal;
  font-weight: 700;
}
@font-face {
  font-family: 'Roboto';
  src: url(fonts/Roboto-BoldItalic.woff) format('woff');
  font-style: italic;
  font-weight: 700;
}

body {
 font-family: Roboto;
}

Now, when we want a certain part of our website to use the "Roboto-SemiBold" font file - we can still use the "Roboto" font family (like defined in the body element above) and only change the font-weight of that element to 600. This will force the browser to use the definition we gave of the font-face with font-weight: 600. Thus our code becomes something like this:

p {
 font-weight: 600;
}

instead of

/* Not recommended */
p {
  font-family: 'Roboto-SemiBold';
}

Further reading

The same works for using different font-files for different font-stylefont-stretch and even using a font file for different ranges of characters. You can use some fonts only for certain characters or unicode character ranges (like use them only for cyrlic or only for latin). This is possible by using the unicode-range guard. More on that in a future article, or in this link.

Resources

HackSoft logo
Your development partner beyond code.