Search code examples
htmlcssfontstruetypeopentype

Using specific instance for @font-face in TTF file containing multiple instance


First of all I have no idea how TTF files are organized so I might have some of my terminology wrong here.

I have a stylesheet with a @font-face that references a single TTF file that has multiple faces in it. In the Windows font viewer it looks like this if I cycle through "next" (sorry, GIF got messy, picked bad encoding parameters, didn't feel like redoing it, but you get the drift):

enter image description here

The file has a .ttf extension although I'm not sure what the relationship is to OpenType (it says "OpenType" in that window).

Anyways, I reference it like this in the stylesheet:

@font-face {
  font-family: TestFont;
  src: url(...) format('truetype');
}

:root {
  font-family: TestFont;
}

So, the font name in the file is "Bahnschrift", and the face I want to use is "Bahnschrift Condensed". The above almost works except it uses the base "Bahnschrift" font.

My question is: How do I specify that I want to use the "condensed" variant instead?.

Here's a fiddle. I wanted to embed the font as a data URI just for this post, but it was too large to post here (about 500kB encoded) so it's here instead: https://jsfiddle.net/qugoeam5/


Solution

  • Ok, I mostly figured it out. I've got a fundamental solution, I just don't have the browser compatibility nailed down.

    So what I called "variants" are actually called "instances". Essentially, they're just named presets (stored in the file) that specify values for a collection of OpenType axes.

    The Windows 10 built in font inspector can be used to inspect the axis/value sets for a given instance, presuming the font is installed on the system and is a variable font:

    1. Open Font Settings

    2. Search for and select the font under "Available Fonts"

    3. In the next windows that opens, scroll all the way down and click "Variable Font Properties".

    4. Here you can select the instance then go through each axis to see its tag and value:

      enter image description here

    So for the "Condensed" instance, weight (wght) is 400 and width (wdth) is 75.

    As for the CSS, compatibility is currently spotty. CSS3 doesn't have a way to select a named instance. It also doesn't officially have support for setting axis values. CSS4, though, is standardizing support for both (font-named-instance and font-variation-settings).

    Now, I don't know the history here, but I think font-variation-settings (set individual axis values) was maybe intended for CSS3 at some point, then deferred to CSS4? Or something. In any case, it seems that:

    • There is support for it in Edge, Firefox, and Chrome, but lack of support in IE and Safari. Among others.
    • There seems to be more support for it when it's used outside of @ rules. I couldn't find any information about why, I just observed it.

    So the general solution here is to individually set the axis values that correspond to the named instance you want (at least until font-named-instance rolls around).

    The good news is, there are more widely supported high-level equivalents for certain axes; and in this case I got lucky: For this font only weight and width are set, which can be set by font-weight and font-stretch. Still, font-stretch support is also a bit inconsistent right now (depending on whether or not you use the named form vs. the % form).

    Anyways, I can't speak for compatibility, but there are a number of options here.

    • Compatibility aside, these are all equivalent for the weight:
      • [W1] font-weight: 400
      • [W2] font-weight: normal
      • [W3] font-variation-settings: "wght" 400;
    • And these are all equivalent for the width:
      • [S1] font-stretch: 75%
      • [S2] font-stretch: condensed
      • [S3] font-variation-settings: "wdth" 75;
    • Note: For font-variation-settings, if you set more than one they all have to be set at once:
      • [WS3] font-variation-settings: "wght" 400, "wdth" 75;
    • Again ignoring compatibility, those can be placed either in an @ rule (to define the default), or elsewhere (to override the default), so both of these are also equivalent:
      • Settings in the face definition:

        @font-face {
           font-family: "TestFont";
           src: ...;
           [ one of the width settings ];
           [ one of the stretch settings ];
        }
        :root {
           font-family: "TestFont";
        }
        
      • Or, outside of the @ rule:

        @font-face {
           font-family: "TestFont";
           src: ...;
        }
        :root {
           font-family: "TestFont";
           [ one of the width settings ];
           [ one of the stretch settings ];
        }
        

    So, putting all that together, an example of one option (compatibility notwithstanding) is:

    @font-face {
        font-family: 'TestFont';
        src: ...;
        font-weight: normal;
        font-stretch: condensed;
    }
    :root { 
        font-family: 'TestFont', sans-serif;
    }
    

    Now, I haven't really tested on lots of browsers yet, but here are the combinations of the above that at least appear to work on Chrome 90.0.4430.93 (Windows 10, 64-bit). YMMV:

    @font-face{} :root{} div{}
    [W1] font-weight:400 YES YES YES
    [W2] font-weight:normal YES YES YES
    [W3] font-variation-settings:"wght" 400 no YES YES
    [S1] font-stretch:75% YES no no
    [S2] font-stretch:condensed YES no no
    [S3] font-variation-settings:"wdth" 75 no YES YES
    [WS3] font-variation-settings:"wght" 400,"wdth" 75 no YES YES

    Which means that, at least for Chrome:

    • If you want to put both in @font-face, you have to use [W1/2] + [S1/2], e.g.:

      @font-face {
        font-family: "TestFont";
        src: ...;
        font-weight: normal; 
        font-stretch: condensed;
      }
      :root {
        font-family: "TestFont", sans-serif;
      }
      
    • But if you want to put both in :root or another element, you can use any form for the weight but you have to use font-variation-settings for the width (valid combos would be [W1+S3], [W2+S3], or [WS3]), e.g.:

      @font-face {
        font-family: "TestFont";
        src: ...;
      }
      :root {
        font-family: "TestFont", sans-serif;
        font-weight: normal; 
        font-variation-settings: 'wdth' 75;
      }
      

    I have no idea what the behavior is on other browsers; I haven't done any more tests, mostly because this post was kind of exhausting, and took like 100x longer to type than it did for me to actually figure all this out, lol.

    Hope that gets somebody pointed in the right direction. Once CSS4 hits the streets, this will all theoretically be a lot simpler.