Using Font Attributes with D3.js

Most of the typographic visualization examples on this site are created in D3.js, using  SVG. There are tons of examples of D3 on the web used to create bars, dots, etc; but not many online examples where font attributes are manipulated using D3. So, to help people create data-driven font-attributes using D3.js – here’s a collection of examples. A running version is over on codepen which creates a simple little visualization like this (on Chrome):

Font_Attributes_D3js.PNG

And, here’s some pointers on how to make this work.

Color “fill”

The simplest example is to use color with text. It uses the same attribute as any other colored element in d3, i.e. the fill attribute. A trivial example is to set the color with a function, in this case, a separate list of colors:

 .attr("fill", function(d,i) {return clrs[i];})

Font Weight (aka Bold): “font-weight”

Font-weight is almost as simple as color. The font-weight tag takes a numeric value between 100 and 900.

.attr("font-weight",function(d,i) {return i*100+100;})

For default web-safe fonts, such as serif or sans-serif, there are usually only 2 weights available: plain and bold, which are typically set to the numeric values 400 and 700. If you want to use a font with many more weights, you first have to load the font including all the target weights. There are many commercial fonts with many weight. Google fonts has some free fonts in many weights such as Saira and Roboto. In the header section of the html, the font needs to be loaded, such as this loading of all 9 weights of the font Saira:

<link href="https://fonts.googleapis.com/css?family=Saira:100,200,300,400,500,600,700,800,900" rel="stylesheet">

Font Oblique Angle (aka Italic): “skewX()”

Font oblique angle is a mechanical skew of the font. It’s not the same as true italics. For data-driven purposes, the oblique angle of the font can be manipulated by using the transformation skewX() on a group per each element. This is a bit more effort than the simple color or weight. First the visual element has to at the group level. Then the transform attribute applied, wherein both the x,y location are set as well as skewX:

var obliquetext = svg.selectAll("obtext").data(data).enter().append("g")
  .attr("transform", function (d,i)
     {return " translate(" + (i*40+100) + ",55)" +
             " skewX(" + (i*10-40) + ")"; });

Then, with the group transform set, append a text element and the text will be appropriately skewed to represent oblique text:

obliquetext.append("text")
  .attr("font-family", "Saira")
  .text( function(d) {return d;});

One benefit of this approach is that any angle can be created, and fonts without italics or obliques can be created as needed. For example, a sloped blackletter can be created, although typographers might not be enthusiastic about this approach as legibility and readability will be impacted.

Case and Small Caps: toUpperCase(), toLowerCase and “font-variant”

Case can manipulated directly in Javascript using functions toUpperCase() and toLowerCase(). Small caps can be accessed by setting the SVG attribute “font-variant” to “small-caps” or “normal”. A simple example chooses between small-caps and normal lower-case letters:

.attr("font-variant", function(d,i) {return i<5 ? "small-caps" : "normal"; })

These can be combined with <tspan> so  parts of words can be set in upper-case, small-caps or lower-case, as shown in the codepen example and below:

var casetext = svg.selectAll("casetext").data(data).enter().append("text") 
  .attr("x",function(d,i) {return i*40;})
  .attr("y",75)
  .attr("font-family", "Saira");
casetext.append("tspan")
  .attr("font-variant","small-caps")
  .text( function(d,i) {return i<5 ? 
          d.substring(0,i).toLowerCase() : 
          d.substring(0,i%5).toUpperCase(); })
casetext.append("tspan")
  .attr("font-variant",function(d,i) {return i<5 ? "normal" : "small-caps";})
  .text( function(d,i) {return d.substring(i%5,10).toLowerCase();});

Typeface: “font-family”

Changing fonts is easy using the attribute “font-family”. For simple use cases, you can use websafe fonts, meaning there is no need to load the font into the browser, for example:

.attr("font-family", function(d,i) {return i<5 ? "serif" : "sans-serif"; })

For more variation in fonts, you can load lots of different fonts into your page and then access them in SVG. First step is to find and load fonts: see fonts.google.com/ for a large variety of free webfonts with easy cut-and-paste code to add to the <head> section your page. Here’s an example of a dozen fonts loaded into the page:

<link href="https://fonts.googleapis.com/css?family=Aldrich|Arima+Madurai|Arvo|Henny+Penny|Indie+Flower|Libre+Baskerville|Pirata+One|Poiret+One|Sancreek|Satisfy|Share+Tech+Mono|Smokum|Snowburst+One|Special+Elite" rel="stylesheet">

Then, you can access those fonts in your Javascript, e.g.

.attr("font-family", function(d,i) {return i<5 ? "Arvo" : "Sancreek"; })

Note that fonts with spaces in their names have a plus symbol in the link string, e.g. “Henny+Penny”, but when you specify that font as an attribute, you need to use the space, e.g. “Henny Penny”.

Underline: “text-decoration”

Underlines in SVG are disappointing: a single underline: no dashes, no wavy styles, no double lines. Blah. You could make your own underlines – it’s just a line added to text, but then you have to worry about interference with descenders, getting the right lengths and so on. Not for this post. For simple underlines, set the attribute “text-decoration” to “underline”. For this post, I tried to make underlines of different lengths by applying underlines to portions of text using <tspan> and surprised that different browsers gave different results. Use at own risk.

Font width: via “font-family”

Font width could be manipulated the same way that oblique text is created above – using scale() instead of skewX() – but scaling text is highly frowned upon by typographers (see Lupton’s type crimes, and more generally a really nice introduction to typography).

Instead, we can use a font that has been well-designed with lots of different widths.  Saira, at Google fonts, comes in 4 widths. So, you just need to load all the width variants, then access them using “font-family”. Here’s a trivial example:

.attr(“font-family”, function(d,i) {return i<5 ? “Saira” : “Saira Condensed”; })

Btw, thanks Omnibus Type for making a well-designed open-source font super-family in a variety of widths and weights freely available.

Spacing (aka Tracking): “letter-spacing”

The spacing can be adjusted between letters and this is often done on maps to indicate features that span across a large area such as  R o c k y  M o u n t a i n s. It is easily accessed in SVG via the attribute “letter-spacing”. Set the value in type coordinates using fractions of an em. 0em is the default spacing. Negative values such as -0.1em will pull the letters tighter together, positive values such as 0.5em will space them further apart. Note browser inconsistency: Chrome seems to do OK, Firefox and IE not.

.attr("letter-spacing", function(d,i) {return return i*.05-.1 + "em"; })

Outlines: “stroke” and “stroke-width”

I don’t like to use outlines on text, but you can use them. Generally, outlines don’t work well on thin or lightweight fonts, you need to start with a fairly heavyweight font without much detail (e.g. Saira Extra Bold, Arial Black, Source Code Pro Black, etc). Then it’s just a matter of setting the attribute “stroke” to a color such as black, “stroke-width” to some very small value and “fill” to none. Notice how it has a very limited effective range as shown in the example. The stroke is almost invisible on Abe and at the other end, the insides of the a in Ian and e in Gem just turn into blobs.

Baseline Shift: “dy”

You can shift letters up and down using the attribute dy. (if you want to put text on a path, see earlier post regarding microline text). You can provide a list of values to dy, then each successive character will take each successive value in dy. Here’s a simple example:

.attr("dy", "0 0.2 -0.1")

Outlines: “stroke” and “stroke-width”

I don’t like to use outlines on text, but you can use them. Generally, outlines don’t work well on thin or lightweight fonts, you need to start with a fairly heavyweight font without much detail (e.g. Saira Black, Arial Black, Source Code Pro Black, etc). Then it’s just a matter of setting the attribute “stroke” to a color such as black, “stroke-width” to some very small value and “fill” to none. Notice how it has a very limited effective range as shown in the example. The stroke is almost invisible on Abe and at the other end, the insides of the a in Ian and e in Gem just turn into blobs.

Combos:

All the  above can be used together in any combination. See the codepen examples.

Variable Fonts:

In theory, variable fonts can make other unique features of fonts available to D3. I haven’t figured out how to do this directly in a line of SVG (i.e. by setting an attribute tag) and presumably need to dig into style tag or CSS to connect these together. Maybe someone else will do and post examples?

 

 

 

Advertisements

About richardbrath

Richard is a long time visualization designer and researcher. Professionally, I am one of the partners of Uncharted Software Inc. I have recently completed a PhD in data visualization at LSBU. The opinions on this blog are related to my personal interests in data visualization, particularly around research interests related to my PhD work- this blog is about exploratory aspects of data visualization not proven principles.
This entry was posted in Data Visualization, Font Visualization, Text Visualization and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s