Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Jesse Roach and Miguel Olivarez and Adam Presley
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Jesse Roach ( @jessewroach ) Miguel Olivarez Adam Presley ( @adampresley )

Using ColdFusion Custom Tags To Create An HTML Email DSL In Lucee CFML 5.3.7.47, Part XIII

By on
Tags:

As I've been continuing to flesh-out my ColdFusion custom tag DSL (Domain Specific Language) for HTML emails, I've come to realize that dealing with custom web fonts and font-weights is a pretty big challenge. Specifically because we can't be sure that our custom web font has loaded; and, a number of email clients have support for just two font-weights: 400 and 700. As such, I'm starting to think that it's a good idea to avoid "bold" weights below 600 regardless of the font-weight variations available in your custom web font.

View this code in my ColdFusion Custom Tag Emails project on GitHub.

We know that custom web fonts are only available in non-MSO (non-Outlook on Windows) clients. As such, the core cf_email ColdFusion custom tag in the DSL won't even apply importUrls in an MSO context. However, there are a variety of clients that don't support custom web fonts but also don't identify as an MSO client (ex, Outlook.com, IBM Notes, and AOL Mail). And finally, some email clients that support custom web fonts don't show the font-weight variations as well as other clients. For example, in GMail on desktop, 300 and 400 are essentially indistinguishable where as the GMail app for mobile shows a clear difference between these two font-weights.

All to say, font-weight is a tricky concept to deal with across such a large variety of email clients. To see this in action, I've put together a demo that uses a custom web font - Roboto - that has a huge variety in font-weight options. This demo uses all the "bold" variations so that we can see how our email renders on different clients:

<!--- Import custom tag libraries. --->
<cfimport prefix="core" taglib="./core/" />
<cfimport prefix="html" taglib="./core/html/" />

<!--- // ------------------------------------------------------------------------- // --->
<!--- // ------------------------------------------------------------------------- // --->

<core:Email
	subject="Custom fonts and weights"
	teaser="Falling back with managed expectations">

	<!---
		We're going to load a custom font for this email. Note that if we load this font,
		we have a large variety in FONT-WEIGHT options. However, if our font fails to
		load (or is not supported in a given email client), we will NOT have all of these
		font-weight options at our disposal.
		--
		CAUTION: ImportURLs are only applied in a NON-MSO context (login within the root
		cf_email ColdFusion custom tag).
	--->
	<cfset theme = getBaseTagData( "cf_email" ).theme />
	<cfset theme.importUrls.append( "https://fonts.googleapis.com/css?family=Poppins:500|Roboto:100,200,300,400,500,600,700" ) />

	<!--- Update the base fonts for the custom font-family import. --->
	<core:HtmlEntityTheme entity="h1, h2, h3, h4, h5, th">
		font-family: Poppins, BlinkMacSystemFont, helvetica, arial, sans-serif ;
		font-weight: 500 ;
	</core:HtmlEntityTheme>
	<core:HtmlEntityTheme entity="blockquote, img, li, p, td">
		font-family: Roboto, BlinkMacSystemFont, helvetica, arial, sans-serif ;
		font-weight: 300 ;
	</core:HtmlEntityTheme>

	<!--- Define some STRONG tag themes for our copy font variations. --->
	<core:HtmlEntityTheme entity="strong">
		font-weight: 700 ;
	</core:HtmlEntityTheme>
	<core:HtmlEntityTheme entity="strong" class="semi-bold">
		font-weight: 400 ;
	</core:HtmlEntityTheme>
	<core:HtmlEntityTheme entity="strong" class="bold">
		font-weight: 500 ;
	</core:HtmlEntityTheme>
	<core:HtmlEntityTheme entity="strong" class="extra-bold">
		font-weight: 600 ;
	</core:HtmlEntityTheme>
	<core:HtmlEntityTheme entity="strong" class="heavy">
		font-weight: 700 ;
	</core:HtmlEntityTheme>

	<!---
		Since MSO clients (Outlook on WINDOWS primarily) won't load remote fonts at all,
		we have to define a solid fallback font-family and font-weight.
		--
		CAUTION: The STRONG tag font-weight is hard to override.
	--->
	<core:HeaderContent>
		<core:IfMso>
			<style type="text/css">
				h1, h2, h3, h4, h5, th {
					font-family: helvetica, arial, sans-serif !important ;
					font-weight: 700 !important ;
				}
				blockquote, body, img, li, p, td {
					font-family: helvetica, arial, sans-serif !important ;
					font-weight: 400 !important ;
				}
				strong,
				strong.bold,
				strong.extra-bold,
				strong.heavy {
					font-weight: 700 !important ;
				}
				strong.semi-bold {
					font-weight: 400 !important ;
				}
			</style>
		</core:IfMso>
	</core:HeaderContent>

	<core:Body>
		<cfoutput>

			<html:h1>
				Custom fonts and weights
			</html:h1>

			<core:IfMso>
				<html:p style="color: red ;">
					<html:symbol>&##9888;</html:symbol>
					Email is being processed as an <html:strong>MSO client</html:strong>.
					<html:symbol>&##9888;</html:symbol>
				</html:p>
			</core:IfMso>

			<html:p>
				Let's try to use the class-name-based font-weights:
			</html:p>

			<html:ul>
				<html:li>
					<html:strong class="semi-bold">
						STRONG: semi-bold
					</html:strong>
				</html:li>
				<html:li>
					<html:strong class="bold">
						STRONG: bold
					</html:strong>
				</html:li>
				<html:li>
					<html:strong class="extra-bold">
						STRONG: extra-bold
					</html:strong>
				</html:li>
				<html:li>
					<html:strong class="heavy">
						STRONG: heavy
					</html:strong>
				</html:li>
				<html:li>
					<html:strong>
						STRONG: Default setting (no class-name)
					</html:strong>
				</html:li>
			</html:ul>

			<html:hr />

			<html:p>
				Let's look at font-weight applied to normal text:
			</html:p>

			<html:ul>
				<cfloop value="weight" list="100,200,300,400,500,600,700">
					<html:li>
						<html:span style="font-weight: #weight# ;">
							SPAN: Font weight #weight#
						</html:span>
					</html:li>
				</cfloop>
			</html:ul>

			<html:p>
				Let's look at font-weight applied to strong text:
			</html:p>

			<html:ul>
				<cfloop value="weight" list="100,200,300,400,500,600,700">
					<html:li>
						<html:strong style="font-weight: #weight# ;">
							STRONG: Font weight #weight#
						</html:strong>
					</html:li>
				</cfloop>
			</html:ul>

		</cfoutput>
	</core:Body>
</core:Email>

As you can see, in addition to trying out named font-weights, I'm also just trying to see how various font-weight inline styles affect both span and strong tags. And, when we run this ColdFusion code through Litmus, we get the following output:

Apple Mail 13 on MacOS

Apple Mail 13 on MacOS is basically "perfect" rendering of our custom web font weights. All the various have clear and crisp differentiation.

IBM Notes 10 on Windows 10

IBM Notes 10 is basically our worst case scenario: it does not identify as an MSO client, which means that we can't easily provide fallbacks; and, it clearly doesn't support custom web fonts. Because of this, two of our "bold" variations - 400 and 500 - show up as the "normal" font-weight because IBM Notes doesn't support a "bold" font-weight less than 600 with whatever font it is rendering (helvetica? arial?).

AOL Mail

AOL Mail works basically like IBM Notes - it doesn't announce itself as an MSO client and it doesn't support custom web fonts.

Outlook.com in Firefox

Outlook.com works basically like IBM Notes and AOL Mail - it doesn't announce itself as an MSO client and it doesn't support custom web fonts.

Outlook 2013 on Windows 10

Outlook 2013 on Windows at least identifies itself as an MSO client. Which means, our fallback <style> tag will be in place. This gives us the opportunity to add some !important overrides. That said, if you look back at my overrides, I have this:

strong.semi-bold {
	font-weight: 400 !important ;
}

However, if you look at the following rendering, you'll notice that every single <strong> tag is the same weight. There may be some Outlook-specific shenanigans we can do in our CSS (that I'm not doing); but, it might just be safer to assume that in Outlook all "strong" tags are gonna render as bold.

GMail on Desktop

According to the Litmus blog, GMail on desktop doesn't actually support custom web fonts. But, they do embed "Google Sans" and "Roboto" for their own user interface (UI). Which means, our attempt to use "Roboto" will actually work. However, when we render our demo, there appears to be no variation between font-weight 300 and 400, presumably because they aren't embedding both variations:

When I look at all these results, I don't know how to take the "best" rendering (in Apple Mail, for example) and make it look "good" in the "worst" rendering (in AOL Mail, for example). There might be some tricks I can use to target different email clients with some CSS shenanigans; but, that feels like a Pyrrhic victory. Adding "hacks" to make it work becomes a never-ending rabbit hole of person-hours. And, I think we'd all much rather apply those person-hours to feature development, not HTML email development.

So, my grand take-away from all of this is: continue to try and use custom web fonts where they are available; but, don't use <strong> weights lower than 600. This way, "semi-bold" treatments (using a <span> tag, perhaps) can fall-back to "normal" font-weights; and, "bold" treatments (using a <strong> tag) can fall-back to "bold" font-weights. Essentially this:

<!--- Emphasized bold. --->
<span style="font-weight: 400 ;"> Semi-bold falls-back to normal </span>
<span style="font-weight: 500 ;"> Bold falls-back to normal </span>

<!--- True bold. --->
<strong style="font-weight: 600 ;"> Extra-bold falls-back to bold </strong>
<strong style="font-weight: 700 ;"> Heavy falls-back to bold </strong>

NOTE: My use of the terms "bold", "semi-bold", etc is not meant to align with the standards outlined in MDN: font-weight. I'm just trying to use different names for purposes of differentiation in the demo. My names are anchored with the assumption that the "copy" weight is a "normal" weight, regardless of whatever it's numeric font-weight is.

HTML Emails are not about "pixel perfection". More than anything, they are about grace degradation and trying to keep things as simple as possible while still applying some degree of "design" to the rendering.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel