Please, web font vendors, learn how to use CSS @font-face properly!

This has been bugging me for years and I can’t believe it’s still happening.

Being able to use custom fonts has been a huge boon to web design. And font hosting services like Typekit (sorry, I will never call it Adobe Fonts) and Google Fonts make using custom fonts easy.

But sometimes you still buy a font license that involves hosting the font files directly on your own server, and that’s where things get absolutely maddening because, for some reason, someone early on grossly misinterpreted how to use @font-face and that error has been perpetuated by countless unthinking others.

(Yes, I’m being harsh. But this is really not that complicated. And getting it right makes writing your CSS and HTML so much easier.)

Here’s an example of some font-specifying CSS you might receive from a font vendor:

@font-face {
    font-family: 'Font-Name-Regular';
    src: url('Font-Name/Font-Name-Regular.woff2') format('woff2'),
         url('Font-Name/Font-Name-Regular.woff') format('woff');
    font-weight: normal;
    }
.Font-Name-Regular {
    font-family: 'Font-Name-Regular';
    }

@font-face {
    font-family: 'Font-Name-Regular';
    src: url('Font-Name/Font-Name-Regular-Italic.woff2') format('woff2'),
         url('Font-Name/Font-Name-Regular-Italic.woff') format('woff');
    font-weight: normal;
    font-style: italic;
    }
.Font-Name-Regular-Italic {
    font-family: 'Font-Name-Regular';
    font-style: italic;
    }

@font-face {
    font-family: 'Font-Name-Bold';
    src: url('Font-Name/Font-Name-Bold.woff2') format('woff2'),
         url('Font-Name/Font-Name-Bold.woff') format('woff');
    font-weight: normal;
    }
.Font-Name-Bold {
    font-family: 'Font-Name-Bold';
    }

@font-face {
    font-family: 'Font-Name-Bold';
    src: url('Font-Name/Font-Name-Bold-Italic.woff2') format('woff2'),
         url('Font-Name/Font-Name-Bold-Italic.woff') format('woff');
    font-weight: normal;
    font-style: italic;
    }
.Font-Name-Bold-Italic {
    font-family: 'Font-Name-Bold';
    font-style: italic;
    }

This is, in fact, the exact code I just received yesterday from a font vendor when I purchased a license, with the actual font name removed to protect the guilty innocent.

What’s so bad about this, you might ask? Aside from the conventions I dislike of indenting the closing } and using 4 spaces instead of tabs, there are two glaring problems with this:

  1. Because the font-family name defined for each weight and style is different, when you go to use this font, you need to specify the font-family every time you want to use bold or italics in your HTML, or at least use the custom CSS classes defined here. No! No no no! You should not have to apply a class to get bold or italics to render properly. The <strong> and <em> tags should do that on their own!
  2. Don’t f***ing define a bold font with font-weight: normal;! If you don’t realize from this, alone, that something is wrong with your approach, stop coding right now.

So, how should this be done, you ask?

Well, it’s simple. Each @font-face declaration has four properties. One is src: which tells the browser where to find the correct font file(s) for this face. The other three properties work together to define the context in which this particular src should be used: any time this combination of font-family, font-weight and font-style come together.

You can use the same font-family in different @font-face declarations as long as font-weight and font-style are different. In fact, you’re supposed to! That’s the way it’s designed to work!!!

When you do this properly, you don’t need any custom CSS classes. Try this on for size:

@font-face {
    font-family: 'Font-Name';
    src: url('Font-Name/Font-Name-Regular.woff2') format('woff2'),
         url('Font-Name/Font-Name-Regular.woff') format('woff');
    font-weight: normal;
}

@font-face {
    font-family: 'Font-Name';
    src: url('Font-Name/Font-Name-Regular-Italic.woff2') format('woff2'),
         url('Font-Name/Font-Name-Regular-Italic.woff') format('woff');
    font-weight: normal;
    font-style: italic;
}

@font-face {
    font-family: 'Font-Name';
    src: url('Font-Name/Font-Name-Bold.woff2') format('woff2'),
         url('Font-Name/Font-Name-Bold.woff') format('woff');
    font-weight: bold;
}

@font-face {
    font-family: 'Font-Name';
    src: url('Font-Name/Font-Name-Bold-Italic.woff2') format('woff2'),
         url('Font-Name/Font-Name-Bold-Italic.woff') format('woff');
    font-weight: bold;
    font-style: italic;
}

Aside from the fact that this eliminates 1/3 of the lines of code, it also will make your HTML much cleaner and more properly separates content from styling.

Here’s an example of some HTML you might have to write using the first approach:

<p class="Font-Name-Regular">This is some regular text, which also
includes a bit of <em class="Font-Name-Regular-Italic">italics</em>
and even a dash of <strong class="Font-Name-Bold">bold</strong>.</p>

Now, granted, my version does require you to define the font-family for your <p> tags in your CSS file. But guess what… you’re supposed to do that! Put this in your CSS:

p { font-family: 'Font-Name'; }

With that in place, the proper HTML for the same appearance becomes this:

<p>This is some regular text, which also
includes a bit of <em>italics</em>
and even a dash of <strong>bold</strong>.</p>

So, again… when thinking about @font-face, just remember these two simple things:

  1. All @font-face declarations for the same font family should have the same font-family. (Seems kind of obvious when I put it that way, doesn’t it?)
  2. The value for font-weight should be the actual weight of the font. Only the “regular” weight should have font-weight: normal; or font-weight: 400;. If you’re using font-weight: normal; on a bold font, you’ve done something wrong.

This change makes for cleaner code, easier maintenance, and proper separation of content from design.

Addendum

Shortly after I posted this, I went back and looked at the unnamed font vendor’s sample page, because I knew it referenced “the @font-face standard since 2017”. I could not believe that this approach was actually a “standard,” so I tracked down the source, an article Bram Stein published on A List Apart in 2017 called Using Webfonts.

Guess what… Bram Stein’s examples do it the right way!

I do know one place where I’ve consistently seen this wrong way I’m railing against… it’s the code generated on FontSquirrel (no link, on principle) whenever you download a font. Other “web font generator” sites like FontSquirrel probably do it to. They’re all wrong… but Bram Stein isn’t. Don’t drag him down with this bad code!

Noted for future reference (by me): How to reset the MySQL root password in Ubuntu 18.04

As a bleeding-edge early adopter, here in June 2018 I am already using Ubuntu 18.04 LTS for new sites I’m setting up for clients. (How daring!)

I ran aground this afternoon with a new server setup, because I couldn’t log into phpMyAdmin as root (or, therefore, as anyone, since I hadn’t set up my own user yet).

All of the old familiar ways I had been trying, and the tutorials I had referred to for at least the past two years, were not working. So then I specifically searched for a solution for Ubuntu 18.04 and found this excellent tutorial.

First off, mysql_secure_installation wasn’t working. That was one of the “old familiar ways” I had already tried thrice. THRICE I TELL YOU!

The key, I think, was these two unexpected lines:

$ sudo mkdir -p /var/run/mysqld
$ sudo chown mysql:mysql /var/run/mysqld

Because that folder didn’t exist and/or didn’t have the proper permissions, some other steps were failing silently or giving error messages that failed to point me in the direction of the actual problem.

The other thing to note is that with the version of MySQL included in Ubuntu 18.04, these are the proper MySQL commands to run:

USE mysql;
UPDATE user SET authentication_string=PASSWORD("linuxconfig.org") WHERE User='root';
UPDATE user SET plugin="mysql_native_password" WHERE User='root';

And it is actually important to run both of those UPDATE commands, because in the tutorial the results displayed show that the first one updated a record for them, while the second didn’t. I had already run the first command (but not the second) in one of my failed updates. So when I ran these, the first one didn’t update any records and the second one did.

Yes, Virginia, there really is a difference between null and false

Fairly often, it’s necessary in PHP programming to write your code around the fact that, most of the time, PHP does not distinguish between null, false and 0, although there is, nonetheless, a distinction between all three.

Today I ran into one of the few instances where I was expecting PHP to treat them as equivalent, but it did not.

Often I am working with arrays, and I write conditionals that should only execute if there are elements in the array. Technically the proper check for the status of an array-type variable is the is_array() function, but most of the time I don’t use that. I may have initialized the array variable or not, but that’s irrelevant to me; what I care about is whether there’s anything in the array, so I just use count() instead.

These days I’m working on some object-oriented code, and I’ve been writing several “get” methods that return either an array of data or, as I had originally written them, a false value if no matching data exists.

Fine. But then I applied some of this new OO code to an existing page, and found that one of my count()-based conditionals was evaluating incorrectly. I checked the variable, whose value was set by one of the object methods, and as anticipated, its value was false. But strangely, the count() function was returning 1 rather than 0 when applied to the variable.

I resisted my initial temptation to switch from count() to is_array() because I don’t want to have to change every place where I use it. Then I tried changing the “failed” return value of the method from false to null and, what do you know, it worked!

So now I’ve gone through all of my various “get” methods and, on failure, they’re returning null instead of false.