Published on

Mastering CSS Writing Mode: A Comprehensive Guide for Modern Web Layouts

Authors

'Mastering CSS Writing Mode: A Comprehensive Guide for Modern Web Layouts'

Go beyond horizontal layouts and discover the power of CSS writing-mode. This guide covers everything from vertical text to logical properties for creating truly global and creative web designs.

Table of Contents

Have you ever wanted to break free from the standard top-to-bottom, left-to-right flow of the web? Maybe you've needed to design a layout for a language that's written vertically, like Japanese, or you just wanted to add a splash of typographic creativity with vertical text on a poster or navigation bar. For years, achieving this was a clunky affair involving CSS transforms and positioning hacks. But not anymore.

Meet writing-mode, a powerful CSS property that fundamentally changes how text and layout flow on a page. It’s not just a text-rotation tool; it’s a gateway to building more flexible, accessible, and internationally-aware websites. In this guide, we'll take a deep dive into writing-mode, exploring everything from the basics to advanced interactions with Flexbox, Grid, and the revolutionary concept of logical properties.

Get ready to change your perspective—literally.

What Exactly is writing-mode?

At its core, the writing-mode property defines the direction in which text lines are laid out (the inline direction) and the direction in which new lines and blocks are stacked (the block direction).

To truly grasp this, we need to understand two fundamental concepts:

  1. Inline Base Direction: This is the direction words and letters flow in a line. For English, it's left-to-right. For Arabic, it's right-to-left.
  2. Block Flow Direction: This is the direction in which new block-level elements (like paragraphs <p> or divs <div>) are stacked. For a standard English webpage, the block flow direction is top-to-bottom.

Think of a book. In an English book, you read letters from left-to-right (inline direction), and when you finish a line, you move down the page (block direction). writing-mode gives you control over both of these axes.

The Core Values of writing-mode

The writing-mode property accepts three main values that dictate the layout flow. Let's break them down.

1. horizontal-tb (The Default)

This is the writing mode you've been using all along, whether you knew it or not. It's the default value for content on the web.

  • horizontal: The inline base direction is horizontal.
  • tb: The block flow direction is top-to-bottom.
.element {
  writing-mode: horizontal-tb;
}

All text flows horizontally, and block elements like paragraphs stack vertically from the top of the container to the bottom. Simple.

2. vertical-rl

This is where things get interesting. vertical-rl is essential for many East Asian languages like Chinese, Japanese, and Korean.

  • vertical: The inline base direction is vertical. Text flows from top to bottom.
  • rl: The block flow direction is right-to-left. New vertical lines of text (and other blocks) are added to the left of the previous one.

Imagine a traditional Japanese newspaper. You start reading at the top-right of the page, read down the column, and then move to the next column to the left.

Example:

Let's apply this to a simple div:

<div class="vertical-rl-example">
  <h3>Vertical Title</h3>
  <p>This is a paragraph of text that will flow vertically from top to bottom. New lines will stack from right to left, creating a unique layout.</p>
  <p>This is a second paragraph. It will appear to the left of the first one.</p>
</div>
.vertical-rl-example {
  writing-mode: vertical-rl;
  border: 2px solid #4a90e2;
  padding: 1rem;
  width: 400px; /* Note the effect on width/height */
  height: 250px;
}

.vertical-rl-example h3 {
  border-left: 3px solid #d0021b;
  padding-left: 0.5rem;
}

In a browser, this will render with the <h3> as the rightmost block, and the two paragraphs following it to its left. The text within each element will read from top to bottom.

3. vertical-lr

This is similar to vertical-rl but changes the block flow direction.

  • vertical: The inline base direction is still vertical (top-to-bottom).
  • lr: The block flow direction is left-to-right. New vertical lines and blocks are added to the right of the previous one.

This mode is used for languages like Mongolian.

Example:

.vertical-lr-example {
  writing-mode: vertical-lr;
  border: 2px solid #4a90e2;
  padding: 1rem;
  width: 400px;
  height: 250px;
}

Using the same HTML as before, the <h3> would now be the leftmost block, with the subsequent paragraphs appearing to its right.

A Note on Deprecated Values: You might see sideways-rl and sideways-lr in older articles. These have been deprecated. Their goal was to lay out text as if you'd physically rotated a horizontal-tb line of text. This functionality is now better handled by the text-orientation property, which we'll discuss later.

The Game Changer: Logical Properties vs. Physical Properties

This is the most critical concept to master when working with writing-mode. For years, we've used physical properties to define size and space. Properties like width, height, margin-top, and padding-left are tied to the physical dimensions of the screen (top, right, bottom, left).

When you change the writing-mode, these physical properties don't adapt. A width is always a horizontal measurement, and a height is always vertical. This can lead to chaos in a vertical layout.

Enter Logical Properties.

Logical properties are direction-agnostic. Instead of top or left, they use concepts like block-start and inline-end. Their physical meaning changes based on the element's writing-mode.

Here’s a quick mapping:

Physical PropertyLogical PropertyDescription in horizontal-tbDescription in vertical-rl
widthinline-sizeHorizontal sizeVertical size
heightblock-sizeVertical sizeHorizontal size
margin-topmargin-block-startMargin at the topMargin at the right
margin-bottommargin-block-endMargin at the bottomMargin at the left
margin-leftmargin-inline-startMargin at the leftMargin at the top
margin-rightmargin-inline-endMargin at the rightMargin at the bottom
padding-toppadding-block-startPadding at the topPadding at the right
padding-bottompadding-block-endPadding at the bottomPadding at the left
padding-leftpadding-inline-startPadding at the leftPadding at the top
padding-rightpadding-inline-endPadding at the rightPadding at the bottom
border-leftborder-inline-startBorder on the leftBorder on the top

There are also shorthand properties like margin-block (for margin-block-start and margin-block-end) and margin-inline (for margin-inline-start and margin-inline-end).

A Practical Comparison

Let's see why this matters. Here's a card styled with physical properties.

<div class="card physical-card">
  <h4>Physical Properties</h4>
  <p>This card uses width, height, and margin-top. It will break in vertical mode.</p>
</div>
/* The Problem: Physical Properties */
.physical-card {
  width: 300px;
  height: 150px;
  margin-top: 20px;
  padding-left: 15px;
  border-left: 5px solid red;
  background: #f0f0f0;
}

/* Now let's switch the writing mode on a container */
.container-vertical {
  writing-mode: vertical-rl;
}

If you place this card inside .container-vertical, the width will still try to control the horizontal dimension, and height the vertical one. The margin-top will still add space above it, and the border-left will remain on the physical left. The result is a squished, unusable component.

Now, let's refactor it with logical properties.

<div class="card logical-card">
  <h4>Logical Properties</h4>
  <p>This card uses inline-size, block-size, and margin-block-start. It adapts perfectly!</p>
</div>
/* The Solution: Logical Properties */
.logical-card {
  inline-size: 300px; /* Was width */
  block-size: 150px;  /* Was height */
  margin-block-start: 20px; /* Was margin-top */
  padding-inline-start: 15px; /* Was padding-left */
  border-inline-start: 5px solid green; /* Was border-left */
  background: #e6f7ff;
}

When you place this card inside the .container-vertical, magic happens:

  • inline-size: 300px now controls the vertical height (the new inline direction).
  • block-size: 150px now controls the horizontal width (the new block direction).
  • margin-block-start: 20px adds a margin to the right (the start of the block flow).
  • border-inline-start: 5px adds a border to the top (the start of the inline flow).

The component reorients itself perfectly without a single media query or JavaScript intervention. This is the future of resilient, multi-directional web design.

Practical Use Cases & Creative Layouts

Now for the fun part! Let's see where writing-mode shines.

1. Vertical Navigation Tabs

This is a classic UI pattern. Instead of using transforms, we can use writing-mode for a cleaner implementation.

<nav class="vertical-tabs">
  <a href="#" class="active">Dashboard</a>
  <a href="#">Profile</a>
  <a href="#">Messages</a>
  <a href="#">Settings</a>
</nav>
.vertical-tabs {
  display: flex;
  flex-direction: column; /* Stack tabs vertically */
  width: 150px;
}

.vertical-tabs a {
  writing-mode: vertical-rl;
  text-orientation: sideways; /* We'll cover this next! */
  background: #eee;
  padding: 1rem;
  border-bottom: 1px solid #ccc;
  text-align: center;
  transform: rotate(180deg); /* Re-orient the sideways text to read top-to-bottom */
}

.vertical-tabs a.active {
  background: #4a90e2;
  color: white;
}

Wait, what's with text-orientation and transform? We're getting a little ahead, but in short: writing-mode makes the line vertical, text-orientation: sideways turns the letters on their side, and transform: rotate(180deg) flips the whole block so the text reads downwards instead of upwards. We'll clarify this in the next section.

2. Space-Saving Table Headers

Got a table with many columns? Vertical headers can make it fit in a smaller space.

<table>
  <thead>
    <tr>
      <th class="vertical-header"><span>Item ID</span></th>
      <th class="vertical-header"><span>Customer Name</span></th>
      <th class="vertical-header"><span>Order Date</span></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>101</td>
      <td>John Doe</td>
      <td>2023-10-27</td>
    </tr>
  </tbody>
</table>
.vertical-header {
  writing-mode: vertical-rl;
  text-orientation: sideways;
  transform: rotate(180deg); 
  text-align: left; /* Aligns text to the 'bottom' of the cell */
  white-space: nowrap;
  padding: 10px 5px;
  vertical-align: bottom;
}

This technique dramatically reduces the width required for your table headers.

3. Creative Typographic Posters

writing-mode is a fantastic tool for graphic and web designers looking to create visually striking hero sections or digital posters.

<div class="poster">
  <h1 class="main-title">VERTICAL</h1>
  <p class="subtitle">A New Perspective</p>
</div>
.poster {
  writing-mode: vertical-rl;
  height: 500px;
  border: 5px solid black;
  padding: 2rem;
  text-align: center; /* Centers the text along the inline (vertical) axis */
}

.main-title {
  font-size: 8rem;
  letter-spacing: 1rem; /* Creates space between letters vertically */
  text-transform: uppercase;
}

.subtitle {
  writing-mode: horizontal-tb; /* You can reset it for inner elements! */
  display: inline-block; /* Make it a block for layout */
  margin-top: 1rem;
  background: black;
  color: white;
  padding: 0.5rem 1rem;
}

Fine-Tuning: Interactions with Other CSS Properties

writing-mode doesn't exist in a vacuum. It has profound effects on other CSS layout modules.

text-orientation

This property is the loyal companion to writing-mode: vertical-*. It controls how characters are oriented within a vertical line.

  • mixed (default): This is the standard for East Asian languages. Characters from horizontal-only scripts (like Latin, which we're using) are rotated 90° clockwise. CJK (Chinese, Japanese, Korean) characters remain upright.
  • upright: Forces all characters to be set upright, one below the other. This can be great for short acronyms or labels (e.g., 'USA').
  • sideways: Lays out all characters sideways, as if an entire line of horizontal text was rotated 90° clockwise. This is what we used in the vertical tabs example. Note that sideways is often more readable for long strings of Latin text than mixed.
.element-mixed {
  writing-mode: vertical-rl;
  text-orientation: mixed; /* Default, 'Hello' is rotated */
}

.element-upright {
  writing-mode: vertical-rl;
  text-orientation: upright; /* Each letter of 'Hello' is upright */
}

.element-sideways {
  writing-mode: vertical-rl;
  text-orientation: sideways; /* The word 'Hello' is rotated as a block */
}

Flexbox and Grid

This is where your brain might bend a little. writing-mode redefines the axes for both Flexbox and Grid.

Flexbox:

The main axis and cross axis of a flex container are defined by flex-direction. But writing-mode determines what row and column actually mean.

  • In horizontal-tb (default):
    • flex-direction: row = main axis is horizontal.
    • flex-direction: column = main axis is vertical.
  • In vertical-rl:
    • flex-direction: row = main axis is vertical.
    • flex-direction: column = main axis is horizontal.

That's right—row becomes vertical and column becomes horizontal. The axes follow the flow of the text. justify-content will always align items along the main axis, and align-items will always align them on the cross axis, whatever direction those axes may be pointing.

Grid:

Grid layout is similarly affected. The block and inline axes, which determine the flow of auto-placed grid items, are swapped. grid-template-rows will define tracks along the new block direction (horizontally in vertical-rl), and grid-template-columns will define tracks along the new inline direction (vertically in vertical-rl).

Always think in terms of block and inline, not physical directions, and your layouts will be robust.

Best Practices and Accessibility

  1. Embrace Logical Properties: This is the #1 rule. If you're using writing-mode, you should be using logical properties for sizing, spacing, and borders. It future-proofs your components.

  2. Use the lang Attribute: When marking up content in a different language, always use the lang attribute (e.g., <html lang="ja">). This is crucial for accessibility, as it tells screen readers how to correctly pronounce the content.

  3. Test Your Fonts: Not all fonts are created equal. Some may not include vertical metrics or may render certain glyphs awkwardly in vertical mode. Test with your chosen web fonts.

  4. Consider Readability: For scripts that aren't natively vertical (like Latin), be mindful of readability. text-orientation: sideways is often better for full sentences than upright or mixed. Keep vertical text short and sweet for decorative purposes.

  5. Browser Support: Browser support for writing-mode and logical properties is excellent across all modern browsers. You can use them with confidence.

Conclusion

CSS writing-mode is so much more than a party trick for rotating text. It's a fundamental property that unlocks a new dimension of web layout. By shifting our mindset from a fixed, physical coordinate system to a flexible, logical one, we can build components and websites that are more resilient, more creative, and more inclusive of global audiences.

So, the next time you're building a UI, ask yourself: does this have to be horizontal-tb? You might be surprised by the elegant solutions that emerge when you change your writing mode and start thinking logically.