Chapter 6: Working with Text

In this chapter, we’ll look at two features of CSS that relate to text: @font-face, and writing modes. These features both play a role in internationalization—the process of making websites that work with the range of humanity’s languages and writing forms.

This chapter won’t be a comprehensive look at every text-related CSS property. There are far too many properties for that. Instead, we’ll focus on some features that are related to internationalization and text display.

Fonts are an integral part of web design and how we display text on the Web, but they can also add bloat. In the first half of this chapter, we’ll look at how to go beyond system fonts like Arial, or generic families such as sans-serif, with @font-face. We’ll also discuss strategies for font optimization.

We’ll end the chapter with a look at writing modes. Writing modes and the writing-mode property affect the display of text, particularly for non-Latin scripts such as Arabic and Japanese. Newer CSS layout modules such as Flexbox, Grid, and Box Alignment are writing mode agnostic. Understanding the basics of how writing modes work lays a good foundation for what we’ll cover in the next chapter.

Better-looking Text with @font-face

In the early days of CSS, font choice was limited to whatever fonts users had installed on their system, and generic font values such as sans-serif and monospace. Towards the end of the “aughts”, however, CSS introduced web fonts and the @font-face CSS rule. Web design and typography changed forever.

With @font-face, we can use just about any font for our web pages, as long as the font is available in a browser-compatible format.

Check Your Licenses

Not all fonts are licensed for web use, even if it’s possible to convert them to a web-friendly format. Do the right thing, and don’t risk being on the losing end of a lawsuit. Ensure that you’re adhering to the licensing terms of any font you use on your site.

Setting an @font-face Rule

Here’s what a basic @font-face rule set looks like. This is the bare minimum you’ll need in order to use a web font:

@font-face {
    font-family: 'MyAwesomeFont';
    src: url('https://example.com/fonts/myawesomefont.woff2');
}

The @font-face at-keyword tells the browser that we want to use an external font file. The font-family line sets a descriptor, or nickname, for this font. Don’t confuse this with the font-family property. When used within an @font-face rule set, font-family sets the value that will be used for CSS font-name matching. The last line defines a font source with the src descriptor, which is the location of a font file.

To apply this font to your text, include the descriptor value in the font or font-family declaration:

body {
    font: 16px / 1.5 'MyAwesomeFont', sans-serif;
}

The browser will match instances of MyAwesomeFont to the source we’ve specified in our @font-face rule set. If MyAwesomeFont isn’t available, or the browser doesn’t support web fonts, it will fall back to the sans-serif generic.

Just because we’ve defined a font for use doesn’t mean the browser will load it. Our font also needs to be in a format the browser can parse. For current browsers, that means WOFF2. However, a surprising number of web users don’t or can’t update their devices. We can accommodate these users by defining multiple font sources.

Using Multiple Font Formats

While the @font-face example above takes care of the latest and greatest browsers, older browser versions lack support for the WOFF2 format. They do, however, support its predecessor, WOFF. Let’s update our @font-face rule to provide a WOFF alternative:

@font-face {
    font-family: 'MyAwesomeFont';
    src: url('http://example.com/fonts/myawesomefont.woff2') format('woff2'),
         url('http://example.com/fonts/myawesomefont.woff') format('woff');
}

The src descriptor takes the format <url> format(), where <url> is the location of a font resource, and format() is a format hint. We can provide multiple src options by separating them with a comma. Using format() helps the browser select a suitable format from the ones provided. Its argument should be one of woff, woff2, truetype, opentype, or embedded-opentype. In this example, browsers that don’t support WOFF2 will download the WOFF-formatted font file instead.

More on Font Formats

The CSS Fonts Module Level 4 specification includes a more complete list of formats and their corresponding font hint values.

You may see examples of @font-face rules that include EOT, SVG, TrueType, or OpenType font formats. You can safely exclude these formats. EOT font support is limited to ancient versions of Internet Explorer 9 and below. Most browsers have removed support for SVG fonts, or never implemented it to begin with. TrueType and OpenType enjoy wide browser support, but WOFF2 file sizes are much smaller. The only reason to use either format is if the font in question isn’t available as a WOFF2-formatted or WOFF-formatted file.

Fonts and Origins

Web fonts are subject to the same-origin policy. Under this policy, a browser loads a resource only if it shares the same “origin” as the requesting document. An origin is the combination of a document’s scheme or protocol, host name, and port number.

An origin is comprised of a protocol, a host or domain name and, optionally, a port number

In other words, if your web page is served from https://example.com and your fonts are served from https://cdn.example.com, they won’t load. To get around this restriction, you’ll need to enable “cross-origin resource sharing”.

Cross-origin resource sharing, or CORS, is a system of headers that tell the browser whether or not a document from a requesting origin has permission to use a requested asset from another. A full discussion of CORS is well beyond the scope of this book, but I’ll try my best to explain it.

More about CORS

If you’d like to learn more about CORS, MDN Web Docs has what may be the Web’s most thorough explanation of cross-origin resource sharing and its collection of response headers.

When an HTML or CSS document links to external assets, the browser first checks whether those assets share the same origin as the requesting script or file. If so, it loads the asset.

If the requesting document doesn’t share the same origin as the requested resource, the browser makes a “preflight request” for the resource. A preflight request asks the external server: “Does https://example.com have permission to load GreatGroteskWebFont.woff2?” If the server response includes the Access-Control-Allow-Origin response header and https://example.com as its value, the browser follows up with a GET request for the font file and loads it. If the response doesn’t include that, the browser won’t make the GET request and the font won’t be loaded.

To enable CORS, you’ll need to add an Access-Control-Allow-Origin response header to your font URLs. This header grants permission to the requesting document’s origin. Origins must not have a trailing slash. Here’s an example:

Access-Control-Allow-Origin: https://example.com

Adding headers requires access to your server or content delivery network configuration. If you don’t have such access, or don’t feel comfortable managing headers, you have two options:

Hosted services implement their own cross-origin headers so that you don’t have to worry about it.

Using Multiple Font Weights and Styles

A font is actually a collection of typefaces or faces. A face is a single weight, width, and style of a font. EB Garamond is a font. EB Garamond Regular and EB Garamond Bold Italic are faces. Most people use the terms interchangeably, but differentiating between the two is helpful here.

When incorporating a web font into your site’s design, you may also want to incorporate its stylistic variants for bolded or italicized text. We can do this using the font-weight and font-style descriptors. These descriptors tell the browser which face (and corresponding file) to match with a particular weight or style:

@font-face {
    font-family: 'EB Garamond Regular';
    src: url('EB-Garamond-Regular.woff2') format('woff2'),
         url('EB-Garamond-Regular.woff') format('woff');
    /*
     The next line is optional, since this is the initial value.
     It's the equivalent of font-weight: 400
    */
    font-weight: normal;
}
@font-face {
    font-family: 'EB Garamond Italic';
    src: url('EB-Garamond-Italic.woff2') format('woff2'),
         url('EB-Garamond-Italic.woff') format('woff');
    font-style: italic;
}
@font-face {
    font-family: 'EB Garamond Bold';
    src: url('EB-Garamond-Bold.woff2') format('woff2'),
         url('EB-Garamond-Bold.woff') format('woff');
    font-weight: bold; /* The equivalent of font-weight: 700 */
}
@font-face {
    font-family: 'EB Garamond Bold Italic';
    src: url('EB-Garamond-Bold-Italic.woff2') format('woff2'),
         url('EB-Garamond-Bold-Italic.woff') format('woff');
    font-weight: bold;
    font-style: italic;
}

In the example above, we’ve matched faces from the EB Garamond font family with an appropriate style and weight. Here, too, font-weight and font-style are descriptors that tell the browser to download an additional font file to display weight and style variants, should the page use bold and/or italic text.

Browsers synthesize bold or italic text from the primary font when an appropriate weight or style isn’t available. However, this may lead to less readable or less attractive text. Compare the synthetic italic text (using EB Garamond, top) to the italic version (EB Garamond Italic) of this font in the image below.

Synthetic italic text using EB Garamond (top) versus EB Garamond Italic

That said, pretty isn’t always fast. Using multiple faces increases the amount of data that must be sent to the browser. As with most aspects of web development, you’ll need to make trade-offs between style and performance.

Variable Fonts

Variable fonts—more accurately called “OpenType font variations”—are an extension of the OpenType specification. Variable fonts are single font files with support for additional features that can be managed using CSS. You can, for example, control the width of each glyph or the degree of tilt used for oblique text. If the font file supports it, you can even adjust the width of serifs, as with the Foreday font by DSType Foundry.

With variable fonts, a single font file behaves like multiple font faces. Variable fonts make the previous section of this chapter moot.

Below is pictured the letter A, from the open-source variable font Jost, in varying weights.

Jost letter A in various weights

OpenType

OpenType is a file format that enables cross-platform font support by combining support for TrueType and PostScript font data in a single file.

To use variable fonts in your project, you’ll first need a font file that supports variable font features. Axis-Praxis (pictured below) and v-fonts are two sources for discovering and experimenting with variable fonts.

The user interface of Axis-Praxis.org, a playground for experimenting with variable fonts

Both sites include specimens and controls to play with the variable features that each font supports. Most hosted font services have small but growing selections.

Although most major browsers have implemented support for variable fonts, the number of such fonts is still fairly small in comparison to the number of traditional fonts.

Incorporating Variable Fonts

To incorporate a variable font, we’ll need to add another source and format hint to our CSS:

@font-face {
    font-family: 'FontFamilyName';
    src: url('FontFamilyName-Variable.woff2') format('woff2-variations'),
         url('FontFamilyName.woff2') format('woff2'),
         url('FontFamilyName.woff') format('woff');
}

If the browser supports variable fonts, it will download FontFamilyName-Variable.woff2. If it doesn’t, it will download a file that it’s capable of parsing. This syntax above works today in every browser that supports variable fonts.

In April 2018, the CSS Working Group decided to change the syntax of format hints. As Richard Rutter explains in his article “Upcoming changes to the CSS you need for variable fonts”:

the list of potential format strings is growing fast and could in future contain other kinds of font features, such as colour fonts. With an eye on the future, the CSS Working Group recently resolved to change the syntax of the format() hint [to] separate out the font features from the file type.

Format hints will soon use a format('format_name', supports feature_name) syntax, which is shown below:

@font-face {
    font-family: 'FontFamilyName';
    /* New CSS syntax. Not yet widely implemented. */
    src: url('FontFamilyName-Variable.woff2') format('woff2' supports variations);
}

A future-proof @font-face declaration with support for variable fonts might look like this:

@font-face {
    font-family: 'FontFamilyName';
    src: url('FontFamilyName-Variable.woff2') format('woff2-variations'),
         url('FontFamilyName.woff2') format('woff2'),
         url('FontFamilyName.woff') format('woff');
    /* New CSS syntax. Not yet widely implemented. */
    src: url('FontFamilyName-Variable.woff2') format('woff2' supports variations);
}

Why two src declarations? Remember: browsers ignore CSS rules they can’t understand, and the last rule wins. Adding the format('woff2' supports variations) hint to our existing src declaration would cause browsers to ignore the entire rule. By using two src declarations, we guarantee that the browser will use one of them. The first src declaration will be used by browsers that don’t support the newer format hint syntax. Browsers that do support it will override the first declaration with the second.

Specifying Font Weight When Using Variable Fonts

As mentioned in the previous section, the font-weight descriptor lets us tell the browser which font-face file should be matched to a particular weight. Variable fonts, however, can support a range of font weights within a single file.

Instead of using a src declaration for each font-face weight, CSS4 has modified the behavior of the font-weight descriptor to accept a value range:

@font-face {
    font-family: 'FontFamilyName';
    src: url('FontFamilyName-Variable.woff2') format('woff2-variations'),
    src: url('FontFamilyName-Variable.woff2') format('woff2' supports variations);
    font-weight: 1 1000; /* Use this file for values within this font range. */
}

Adding a font-weight range instructs the browser to use the same file for every font-weight value that falls within the range. This includes font-weight: bold, which is the equivalent of font-weight: 700, and font-weight: normal, which is the equivalent of font-weight: 400.

Historically, font-weight accepted numeric weight values ranging from 100–900, in increments of 100. As of CSS4—and with the advent of variable fonts—we no longer have those restrictions. For example, font-weight: 227 is now a valid, supported font-weight value. Any number greater than or equal to 1 and less than or equal to 1000 is a valid font-weight value. Fractional weights, such as font-weight: 200.5 are also valid.

Lower-level Font Control with font-variation-settings

CSS4 has also introduced a font-variation-settings property for finer-grained control of font features. It lets us manipulate fonts along one of five axes, using one of the registered axis tags defined in the OpenType specification.

Axis tag Name Notes
ital italic Typically a float value between 0 and 1, although some fonts may exceed those bounds
opsz optical size Adjusts the shape of glyphs according to the target point size. For example, "opsz" 72 adjusts the shape of each glyph to match that of 72pt type, regardless of the value of font-size. Requires the font to support optical sizing
slnt slant The degree of slant for oblique text
wdth width Works similarly to the font-stretch property
wght weight Works similarly to the font-weight property

We could, for example, use wght and ital to set the weight and amount of italicization of an h1 selector:

h1 {
    font-variation-settings: "wght" 900, "ital" .9;
}

Keep in mind that not all variable fonts support all of these axis tags. Some fonts, such as Amstelvar, support additional settings such as YTSE, which controls serif height. On the left of the image below is the Latin letter A with the default serifs. On the right is the same letter with "YTSE" 48 as part of its font-variation-settings declaration.

Left shows the Latin letter A with default serifs, while the right shows the same letter with YTSE 48

Which values we can modify, and the boundaries of those values, depends on the font file itself. You’ll need to consult the documentation for each font, if available. Because of this hurdle, your best bet is to use the font-weight, font-style, font-optical-sizing and font-stretch properties.

Shaping Loading Behavior with font-display

Early browser implementations of @font-face differed pretty significantly in how they handled text while waiting for the web font to download. Firefox, for example, drew text using the fallback font face, then swapped the font face once downloaded—resulting in the dreaded “flash of unstyled text”, or FOUT.

Safari’s implementation, on the other hand, drew invisible text, then swapped it for the visible font face once it finished loading. Users would see large blocks of empty space until the font face was available to the browser. Developers dubbed this the “flash of invisible text”, or FOIT.

The font-display descriptor came about largely as a way to let developers choose which behavior they prefer. It has five possible values. Excluding auto, each of these values changes the duration of the block and swap periods of the font display timeline.

  • auto: initial value. The “Browser’s Choice” option. It uses whatever policy the browser prefers.
  • block: draws invisible text until the font loads, then swaps the font face. (Approximately a three-second block period, and an infinite swap period.)
  • swap: draws text using the fallback font until the font face loads, then immediately swaps the font face. (Roughly a 100ms block period, and an infinite swap period.)
  • fallback: draws text using the fallback font, while waiting for the font face to load. If the font face takes too much time to load, it continues to use the fallback. Otherwise, it swaps the font face once loading completes. (Approximately a 100ms block period, and about a three-second swap period.)
  • optional: uses the font only if it can be downloaded immediately. Otherwise, it will use the fallback text. The browser can decide whether or not to download the font and swap it, or continue to use the fallback text. Optional won’t cause layout shifts. The browser may never swap the font face.

When a browser begins downloading a font face, it enters the block period. During the block period, the browser draws text using an invisible fallback font. If the font face finishes downloading during the block period, the browser will use it.

Next comes the swap period. During the swap period, if the font face hasn’t loaded, the browser draws the text using the fallback font. It then swaps the font face when loading completes.

If the font hasn’t loaded by the end of the swap period, the browser enters the failure period. The browser uses the fallback font.

Understanding auto

When font-display is auto, you’re relying on the browser’s default font-face handling. For most recent browsers, that behavior resembles block. There’s a short period of about three seconds during which the user won’t see any text. The image below shows what this looks like in Firefox using a simulated 2G connection.

The view in Firefox before the web font loads

In most browsers, font-display: auto means the text will be invisible until the font face loads. Text that doesn’t use the web font is immediately drawn.

If the font face hasn’t loaded by the end of this timeout period, the browser draws text using the fallback font. When the font fully downloads, as shown below, the browser swaps the text.

Once the browser loads the web font, it swaps the invisible text for the font face

On very slow connections, font faces can take several seconds to download. Until then, your site visitors won’t be able to read any text that uses a font face.

Instead, use fallback or optional, particularly for larger blocks of text. They’re both particularly well suited to serving users with slow internet connections. For smaller blocks of text, such as headlines, swap also works well. All three values keep your text visible while the font face loads. Something is happening. As a result, the page loading time seems faster to users than seeing no text at all, as happens with block or auto.

Optimizing Fonts with Subsetting and unicode-range

Languages are written using scripts, or groups of symbols or characters used to express a language. English, Spanish, and Norwegian use Latin script. Farsi uses a variant of Arabic script. Hindi and Rajasthani use Devanagari.

Scripts are comprised of characters. In computing, each character in a script is represented by a hexadecimal numeric value, also known a character code. Mapping codes to characters is called character encoding.

There are multiple systems of character encoding available in computing. On the Web, however, you should use Unicode. Unicode is a system that maps characters from multiple scripts to unique hexadecimal numeric values. The Latin letter A, for example, is represented by the number 0041, while the Armenian character Ֆ is represented by the number 0556. Depending on the context, these numbers may be prefixed by U+ or a u when used with CSS.

More on Unicode

I’ve left out a lot of background about the history of character encodings and how Unicode came to be. This is, after all, a book about CSS, not character encoding. If you’d like to learn more about the whys and what-fors of Unicode, visit the Unicode Consortium’s website—unicode.org.

Stick with me here—I promise there’s a point to all of this background information. Fonts map character codes to “glyphs”. A glyph is the actual shape that represents a character. A lowercase letter “a”, for example, can be represented by glyphs from several different fonts, as shown below. From left to right are glyphs representing the letter “a” from the Bodoni 72 Bold, Juju Outline, Junction Bold, and Futura Bold fonts.

The letter A is a character that can be represented by different glyphs or shapes

Now, font files contain the entire character set or glyph set available for that font. That includes obscure punctuation, characters from other scripts, and symbols such as © and ™. There’s a very good chance you won’t use all of those characters on your site. But if your web font contains them, you’re still sending those bytes to your users.

The good news is that we can manage this using the unicode-range descriptor and a process known as “subsetting”. Subsetting is the process of breaking a font into multiple files, each containing a smaller collection—a subset—of glyphs.

Browsers that fully support unicode-range—and this includes most versions released since 2016—only download a font face when characters in the document fall within its corresponding unicode range.

Most web font services automatically manage subsetting and unicode ranges. For self-hosted fonts, there’s FontTools.

Subsetting Self-hosted Fonts with FontTools

Consider a multi-script font such as Gaegu (available with an SIL Open Font License), which includes characters from Latin and Hangul scripts. We might split this font into two files: gaegu-latin.woff2 and gaegu-hangul.woff2. We can then use the unicode-range descriptor to assign each file to a different Unicode range:

@font-face {
    font-family: 'Gaegu';
    src: url('https://example.com/fonts/gaegu-latin.woff2') format('woff2');
    unicode-range: U+000-5FF; /* Latin glyph range */
}
@font-face {
    font-family: 'Gaegu';
    src: url('https://example.com/fonts/gaegu-hangul.woff2') format('woff2');
    unicode-range: U+1100-11FF; /* Hangul glyph range (partial) */
}

Licensing Requirements

The SIL Open Font License (OFL) requires that variations of a font file be completely renamed. This may include file format conversions, such as TrueType to WOFF. It probably includes subsetting. For the sake of clarity, I’ve retained the Gaegu font name for both files. In a public-facing project, you may wish to use a different name.

For self-hosted fonts, we’ll need to create the subset version of the font ourselves using FontTools. FontTools is a Python library for manipulating fonts. While this does require us to have Python installed, we don’t need to know how to program with Python.

To install FontTools, we’ll need to use pip, the Python package manager. In a terminal window or at the Windows command-line prompt, type the following:

pip install fonttools[woff]

This installs fonttools and two additional libraries that we’ll need for creating WOFF and WOFF2 files: brotli and zopfli.

Mac Users

Although macOS comes with Python installed, it may not include pip. Your best bet for installing pip while creating the fewest headaches is to install the latest Python version using Homebrew: brew install python. Homebrew will install pip as part of the Python installation process. Use pip3 instead of pip to run commands.

This command installs a few different subpackages, including ones for font format conversion (ttx) and merging fonts (pyftmerge). We’re interested in pyftsubset, which can create subsets from OpenType, TrueType, and WOFF font files.

Let’s use pyftsubset to create a Latin-only version of the Gaegu font:

pyftsubset ~/Library/fonts/Gaegu-Regular.ttf --unicodes=U+000-5FF

At a minimum, pyftsubset needs an input file and one or more glyph identifiers or a Unicode range as arguments. In the example above, we’ve used the --unicodes flag to specify the range of characters to include. Again, both of these arguments are required.

To create a WOFF2 web font, we need to pass an additional --flavor flag:

pyftsubset Gaegu-Regular.ttf --unicodes=U+000-5FF --flavor="woff2"

For OFL-licensed fonts, we should also rename our font file and remove name information from the font tables. To do that, we need to pass two more flags: --output-file flag, and --name-IDs:

pyftsubset ~/Library/fonts/Gaegu-Regular.ttf --unicodes=U+000-5FF --flavor="woff2"  --output-file='myproject/subsetfont-latin.woff2' --name-IDs=''

Passing an empty string as the argument for --name-IDs strips all existing name information from the font file. Now we can use our subset OFL-licensed font in our project.

pyftsubset is more feature-rich than we’ve discussed here. We can, for example, exclude ligatures and vertical typesetting data. To see a full list of commands and how they work, use pyftsubset --help.

Writing Modes

Writing modes are one of the more esoteric areas of CSS. However, they’re important to understand for developers who work with languages that are written from right to left (such as Hebrew and Arabic), languages that can be written vertically (such as Mongolian), or languages that can be written using both (such as Japanese, Chinese, or Korean). In this section, we’ll discuss:

  • what writing modes are, and how browsers determine the writing mode of a document
  • CSS properties that affect the writing mode

Although the primary purpose of writing modes is internationalization, you can also use them in creative ways. For instance, you might use vertical or sideways headings to mark sections of text, as shown below.

Writing modes used to add sideways headers

Let’s dig in!

What Is a Writing Mode?

A document’s writing mode is the combination of its inline base direction and its block flow direction. The inline base direction, or inline direction, is the primary direction in which lines of text are ordered. Block flow refers to the direction in which block-level boxes stack.

Languages such as English, French and Hindi are typically written and read from left to right. Lines of text start at the left edge of the container and continue horizontally, ending at the right edge of the container. Blocks of text—such as headings and paragraphs—stack vertically from the top of the screen to the bottom. These languages use a horizontal writing mode.

Languages such as Chinese and Korean, on the other hand, can also use a vertical writing mode. In a vertical writing mode, lines of text begin at the top of the container and continue to the bottom. Blocks of text stack horizontally.

Technically, what we’re discussing here are scripts, or the groups of symbols used to express a language. Scripts can be used to write multiple languages. For example, Spanish, English, and Norwegian all use Latin script. The inverse is also true: some languages can be written using more than one script. As the World Wide Web Consortium explains, Azeri can be written using Latin, Cyrillic, or Arabic scripts. Scripts have a writing direction. Languages use the direction of the script in which they’re written. In other words, when written using Latin or Cyrillic scripts, Azeri is read and written from left to right. When written using Arabic, it’s read from right to left. For the sake of precision, we’ll use “script” instead of “language” for the rest of this chapter.

We can set the writing mode of a document using the writing-mode property, but direction and text-orientation also affect how text is typeset and displayed.

Setting the Direction of Text with the direction Property

With the direction property, we can specify the direction of text—either rtl (right to left) or ltr (left to right). Its initial value is ltr. When the value of direction is ltr, text lines start at the left edge of the container and end at the right edge, as illustrated below.

Text begins at the left edge of the container when set to direction: ltr

When the value is rtl—as appropriate for Arabic and Hebrew scripts—text lines start at the right edge and end at the left, as shown below.

Text begins at the right edge of the container when set to direction: rtl

Using the HTML dir Attribute Is Best

Because browsers can strip CSS from HTML documents—say, when using Reader mode—the Writing Modes specification advises web developers to avoid using the direction property with HTML. Instead, use the HTML dir attribute to set text direction, and the <bdo> or <bdi> elements to override the direction for smaller bits of inline content:

<!DOCTYPE html>
<html lang="ar" dir="rtl">
    <head>
        <title>باستخدام السمة dir</title>
    </head>
    <body>
        <p>قفز الثعلب البني السريع على الكلب الكسول.<bdo dir="ltr" lang="en">SitePoint.com</bdo>
        </p>
    </body>
</html>

Using markup ensures that user agents will properly display the document, even if its CSS has been stripped away. For markup languages that lack these features (such as SVG), the direction CSS property is appropriate.

Setting Block Flow Direction with the writing-mode Property

The writing-mode property determines how block-level boxes and table rows are ordered on the screen or page. It also determines whether lines of text within those boxes are arranged horizontally or vertically. Its initial value is horizontal-tb, which is a shorthand for “horizontal, top to bottom”.

If no other CSS is applied to a document, block boxes will flow from top to bottom. Lines of text within those boxes will be arranged horizontally, as was shown in the two images in the previous section. For languages that use Latin, Arabic, Hebrew, or Devanagari script, this is always appropriate.

Humanity, of course, is varied and complicated. A top-to-bottom block flow doesn’t work for every language. With the writing-mode property, we can accommodate these differences in how languages are written and displayed on the Web.

Writing Modes

For a more comprehensive look at writing modes and scripts, consult the “Language enablement” documentation of the World Wide Web Consortium’s Internationalization Activity group.

In addition to horizontal-tb, the writing-mode property accepts four other values:

  • vertical-rl
  • vertical-lr
  • sideways-rl
  • sideways-lr

When the value of writing-mode is vertical-rl, text is arranged vertically, and the block boxes are ordered from right to left.

An example of vertical-rl text

When the value of writing-mode is vertical-lr, text is arranged vertically, and blocks progress from left to right.

An example of vertical-lr text

The following image features an example of Japanese text with a vertical-rl writing mode.

Japanese characters, with non-Japanese characters rotated 90 degrees

The text begins from the right edge of the image. Our Japanese glyphs are translated and rendered vertically. However, non-Japanese glyphs such as numerals are rotated 90 degrees.

Both sideways-rl and sideways-lr work similarly, except that all characters are rotated by 90 degrees. With writing-mode: sideways-rl, text is displayed vertically, from top to bottom, and all glyphs are rotated clockwise by 90 degrees, as illustrated below.

Text set using a sideways-rl writing mode

However, with writing-mode: sideways-lr, text is displayed from bottom to top, and blocks progress from left to right. Glyphs are instead rotated 90 degrees counter-clockwise.

An example of writing-mode: sideways-lr

Support for sideways-rl and sideways-lr is currently limited to Firefox 43 and above. Consider these values to be experimental for the time being. Their behavior may change, or support may be dropped from browsers entirely.

Note that the orientation of <img> and <video> elements isn’t affected by writing-mode, as pictured below.

Images retain their intrinsic orientation regardless of writing mode

Managing Typesetting with text-orientation

Writing systems, and the fonts that use them, have one or more native orientations. Latin-, Arabic- and Devangari-based scripts are always written horizontally, and therefore have a horizontal native orientation. Mongolian script is always written vertically and has a vertical native orientation. Chinese, Japanese, and Korean can be written vertically or horizontally, which is known as bidirectional orientation. Native orientation helps determine how glyphs are displayed within a document.

Most contemporary fonts assign a horizontal orientation for every glyph that’s used when glyphs are presented horizontally. But as we’ve mentioned, some scripts can be written vertically. Glyphs within those scripts are transformed when text is presented vertically.

When presented vertically, glyphs may be translated, as shown on the left, or rotated, as shown on the right

Transformed glyphs may be translated, or shifted, so that they’re arranged vertically, as pictured above on the left. Or they may be rotated, so they’re typeset sideways, as illustrated above on the right. Some scripts have a native bidirectional orientation. Their font files usually contain vertical typesetting information that’s used when glyphs are presented vertically.

It’s not uncommon, however, to use characters from horizontally oriented scripts in a vertically oriented document. Think numerals such as 0, 2, or 4 within a paragraph of Japanese text. We can shape how these glyphs are typeset using the text-orientation property.

The text-orientation property accepts one of three values, each of which is described as follows:

  • mixed: glyphs from horizontally oriented scripts are rendered sideways, or rotated by 90 degrees, but vertically oriented glyphs will be rendered vertically (as pictured below, left).
  • upright: glyphs from horizontally oriented scripts are rendered in horizontal orientation. Glyphs from vertically oriented scripts are rendered in their intrinsic, vertical orientation (as pictured below, center).
  • sideways: all text is rendered sideways, as if in a horizontal writing mode, and rotated 90 degrees (as pictured below, right).

From left to right: the effect of text-orientation: mixed, text-orientation: upright, and text-orientation: sideways

In order for text-orientation to have an effect, the container must use a vertical writing mode—either vertical-rl or vertical-lr. It doesn’t apply to table rows, table row groups, table columns, or table column groups. You can, however, use it with tables, table cells, and table headers.

Writing Mode and Alignment

Text alignment and box alignment are also affected by writing mode. Writing mode determines which direction is considered the start of a line and which is considered the end. In the image below, for example, our table has a direction value of rtl (right to left). As a result, text-align: start aligns the text of each cell along its right edge.

The effect of text direction on text alignment

However, in the image below, the direction is ltr (left to right). In this case, text-align: start causes the text of each cell to be aligned with its left edge.

When the text direction is left to right, text-align: start aligns text to the left edge of the container

Similarly, justify-content: flex-start aligns items with the left edge of their container when the value of writing-mode is horizontal-tb, and the direction: ltr, as seen below.

Writing mode has an effect on how the children of flex containers are aligned

However, when the value of direction is rtl (or the dir attribute value is rtl), justify-content: flex-start aligns items with the right edge, as shown below.

When the direction of text is rtl or right to left, justify-content: start stacks items against the left edge of the container

Conclusion

In this chapter, we’ve discussed how text can be manipulated and enhanced with CSS. You should now have a sense of how to:

  • implement web fonts and optimize them for a better user experience
  • support sites that use non-Latin scripts and multiple languages

You should also have a sense of how writing modes work.

In the next chapter, we’ll look at how to use CSS to add a sense of fun and energy to our web pages with transitions and animations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.56.45