Published on

CSS Writing Mode: The Ultimate Guide to Vertical Text and Layouts

Authors

'CSS Writing Mode: The Ultimate Guide to Vertical Text and Layouts'

Master the CSS writing-mode property to create stunning vertical layouts, CJK typography, and innovative web designs. This comprehensive guide covers everything from basics to advanced use cases.

Table of Contents

For most of the web's history, we've lived in a horizontal world. Content flows from top to bottom, and text reads from left to right (or right to left). It’s the default, the bedrock of web layout. But what if we could break free from this horizontal constraint? What if we could control the very direction of our content flow?

Enter the CSS writing-mode property. It’s not just a tool for rotating text; it’s a fundamental shift in how we can think about and build layouts on the web. Originally conceived to support scripts that are written vertically, like traditional Chinese, Japanese, and Korean (CJK), its power extends far beyond typography, opening up a new dimension for creative and functional design.

In this guide, we'll take a deep dive into writing-mode. We'll start with the basics, explore how it revolutionizes our box model with logical properties, and then build practical, real-world examples. Get ready to turn your layout concepts on their side—literally.

What is writing-mode? The Core Concepts

At its heart, the writing-mode property defines the direction in which content blocks are laid out on a page. It controls two fundamental directions:

  1. Block Flow Direction: This is the direction in which block-level elements (like <p>, <div>, or <h1>) stack. In standard English documents, the block flow direction is top-to-bottom.
  2. Inline Base Direction: This is the direction that text flows within a line. For English, it's left-to-right. For Arabic or Hebrew, it's right-to-left.

When you change the writing-mode, you are fundamentally changing these directions. A horizontal layout becomes a vertical one, and concepts like 'top' and 'left' lose their absolute meaning. This is the first crucial concept to grasp: writing-mode reorients the entire coordinate system of your layout.

The writing-mode Property Values

The writing-mode property accepts three main values that dictate the layout orientation.

horizontal-tb (The Default)

This is the one you've been using all along, whether you knew it or not. It stands for horizontal, top-to-bottom.

  • Block Flow Direction: Top to bottom.
  • Inline (Text) Flow Direction: Left to right (for LTR languages) or right to left (for RTL languages).
<div class="container horizontal-tb">
  <h3>Heading 1</h3>
  <p>This is the default writing mode. Blocks stack vertically from top to bottom.</p>
  <h3>Heading 2</h3>
  <p>Text inside the blocks flows horizontally.</p>
</div>
.container {
  border: 2px solid #333;
  padding: 1em;
  font-family: sans-serif;
}

.horizontal-tb {
  writing-mode: horizontal-tb;
}

This will look exactly as you'd expect any normal web page to look.

vertical-rl

This is where things get interesting. vertical-rl stands for vertical, right-to-left.

  • Block Flow Direction: Right to left. The first block starts on the far right, and subsequent blocks stack to its left.
  • Inline (Text) Flow Direction: Top to bottom. Text within a block starts at the top and flows downwards.

This mode is common for traditional Japanese and Chinese typography.

<div class="container vertical-rl">
  <h3>見出し 1</h3>
  <p>これは縦書きのテキストです。ブロックは右から左に流れます。</p>
  <h3>見出し 2</h3>
  <p>ブロック内のテキストは上から下に流れます。</p>
</div>
.vertical-rl {
  writing-mode: vertical-rl;
  /* We might need to set a height to see the effect clearly */
  height: 300px; 
}

In a browser, you'll see the first heading (<h3>見出し 1</h3>) on the top-right, with its text running vertically downwards. The paragraph will be beside it, and the second heading (<h3>見出し 2</h3>) will appear to the left of the first block.

vertical-lr

This one stands for vertical, left-to-right.

  • Block Flow Direction: Left to right. The first block starts on the far left, and subsequent blocks stack to its right.
  • Inline (Text) Flow Direction: Top to bottom. Text still flows downwards within each block.

This mode is used for languages like Mongolian.

<div class="container vertical-lr">
  <h3>Header 1</h3>
  <p>This is vertical left-to-right. Blocks stack from left to right.</p>
  <h3>Header 2</h3>
  <p>Text inside the blocks flows from top to bottom.</p>
</div>
.vertical-lr {
  writing-mode: vertical-lr;
  height: 300px;
}

This is similar to vertical-rl, but the content begins on the left side of the container and expands towards the right.

The "Logical" Revolution: Thinking Beyond Width and Height

Here's the most critical part of mastering writing-mode. When you switch to a vertical writing mode, physical properties like width, height, margin-top, and padding-left become confusing and counter-intuitive. Does width control the horizontal measure or the measure along the text line? Does margin-top refer to the top of the viewport or the start of the content block?

To solve this ambiguity, CSS introduced Logical Properties and Values. These properties are relative to the flow of content, not the physical screen.

Let's break down the two main axes:

  • Block Axis: The direction blocks are laid out in. In horizontal-tb, this is the vertical axis. In vertical-rl, it's the horizontal axis.
  • Inline Axis: The direction text flows in. In horizontal-tb, this is the horizontal axis. In vertical-rl, it's the vertical axis.

Logical properties are named using block and inline instead of top/bottom and left/right.

Here’s a comparison table:

Physical PropertyLogical PropertyDescription
widthinline-sizeThe size in the inline dimension
heightblock-sizeThe size in the block dimension
min-width / max-widthmin-inline-size / max-inline-sizeMin/max size in the inline dimension
min-height / max-heightmin-block-size / max-block-sizeMin/max size in the block dimension
margin-topmargin-block-startMargin at the start of the block axis
margin-bottommargin-block-endMargin at the end of the block axis
margin-leftmargin-inline-startMargin at the start of the inline axis
margin-rightmargin-inline-endMargin at the end of the inline axis
padding-toppadding-block-startPadding at the start of the block axis
border-leftborder-inline-startBorder at the start of the inline axis
top / leftinset-block-start / inset-inline-startPosition offset from the start edges

Practical Example: The Power of Logical Properties

Let's create a simple card component and see how it behaves when we change its writing mode.

Attempt 1: Using Physical Properties (The Wrong Way)

<div class="card physical-card">
  <h4>Physical Properties</h4>
  <p>This card uses `width`, `height`, and `margin-bottom`.</p>
</div>
<div class="card physical-card vertical">
  <h4>Physical Properties</h4>
  <p>This card uses `width`, `height`, and `margin-bottom`.</p>
</div>
.physical-card {
  background: #eef; 
  border: 1px solid #ccd;
  width: 250px;
  height: 120px;
  padding: 15px;
  margin-bottom: 20px;
}

.physical-card.vertical {
  writing-mode: vertical-rl;
}

When you view this, the horizontal card looks fine. The vertical card, however, is a mess. Its width of 250px now constrains the vertical space (the inline direction), and its height of 120px constrains the horizontal space (the block direction). The margin-bottom is still applied to the physical bottom, which is no longer the end of the content flow.

Attempt 2: Using Logical Properties (The Right Way)

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-end`.</p>
</div>
<div class="card logical-card vertical">
  <h4>Logical Properties</h4>
  <p>This card uses `inline-size`, `block-size`, and `margin-block-end`.</p>
</div>
.logical-card {
  background: #efe;
  border: 1px solid #cdc;
  /* The size along the direction of text */
  inline-size: 250px;
  /* The size along the direction of block stacking */
  block-size: 120px;
  padding: 15px; /* `padding` is a shorthand that maps to logical properties */
  /* A margin at the end of the block, regardless of direction */
  margin-block-end: 20px;
}

.logical-card.vertical {
  writing-mode: vertical-rl;
}

Magic! Both cards look correct relative to their writing mode. The horizontal card has an inline (horizontal) size of 250px and a block (vertical) size of 120px. The vertical card has an inline (vertical) size of 250px and a block (horizontal) size of 120px. The margin-block-end correctly adds space after the first card (at the bottom) and after the second card (to the left).

Actionable Insight: Whenever you use writing-mode with a value other than horizontal-tb, always use logical properties. It future-proofs your components and makes your CSS truly orientation-agnostic.

Practical Use Cases and Creative Examples

Now that we understand the mechanics, let's explore some real-world applications.

1. Vertical Headers and Sidetitles

This is a classic design pattern, perfect for adding a stylistic flourish to sections or articles.

<article class="side-title-article">
  <h2 class="side-title">Chapter 1: The Journey Begins</h2>
  <div class="content">
    <p>The content of the chapter goes here. It flows normally, top-to-bottom. The vertical title beside it acts as a beautiful, space-saving label for the section.</p>
    <p>By using Flexbox, we can easily align the vertical title with the main content block.</p>
  </div>
</article>
.side-title-article {
  display: flex;
  gap: 2em;
}

.side-title {
  writing-mode: vertical-rl;
  /* This prevents the text from wrapping, keeping it in a single vertical line */
  white-space: nowrap;
  /* Optional: rotate the text for better readability if using Latin characters */
  text-orientation: mixed; /* The default, keeps glyphs upright */
  /* Or for fully rotated text: */
  /* transform: rotate(180deg); */ 
  /* Center the text along its now-horizontal axis */
  text-align: center;
  background: #333;
  color: white;
  padding: 1em 0.5em;
  border-radius: 5px;
}

.content {
  flex: 1;
  max-width: 600px;
}

Note on text-orientation: This property controls the orientation of characters within a vertical line. The default, mixed, keeps Latin characters and numbers upright. upright forces all characters to be upright. sideways (not yet fully supported everywhere) lays all characters on their side.

2. Authentic CJK Typography

As mentioned, writing-mode is essential for typesetting Chinese, Japanese, and Korean text in its traditional vertical, right-to-left format. This is not just a stylistic choice but a matter of cultural and historical authenticity.

<div class="tategaki-container">
  <h1>日本の詩</h1>
  <p>
    古池や<br>
    蛙飛び込む<br>
    水の音
  </p>
  <p class="author">— 松尾芭蕉</p>
</div>
.tategaki-container {
  writing-mode: vertical-rl;
  text-orientation: mixed; /* Keeps punctuation and numbers oriented correctly */
  font-family: 'Noto Serif JP', serif;
  height: 250px; /* Or use block-size */
  border-left: 2px solid #900; /* This is the 'end' of the block flow */
  padding-left: 1em;
  font-size: 1.2rem;
}

.tategaki-container h1 {
  font-size: 2rem;
  /* Add spacing to the 'left' which is the block-end direction */
  margin-inline-end: 1em; 
}

.author {
  /* Align text to the 'bottom' of the line, which is 'end' */
  text-align: end;
}

3. Integration with Flexbox and Grid

This is where your brain might start to melt a little, in the best way possible. writing-mode redefines the axes for Flexbox and Grid.

Flexbox:

  • In horizontal-tb, the main axis is horizontal by default (flex-direction: row).
  • In vertical-rl, the main axis is vertical by default (flex-direction: row).

Wait, what? Yes. flex-direction: row always follows the inline direction of the writing mode. flex-direction: column always follows the block direction.

Let's see it in action:

<p>Flexbox with <code>horizontal-tb</code> (default)</p>
<div class="flex-container horizontal">
  <div>1</div><div>2</div><div>3</div>
</div>

<p>Flexbox with <code>vertical-rl</code></p>
<div class="flex-container vertical">
  <div>1</div><div>2</div><div>3</div>
</div>
.flex-container {
  display: flex;
  /* 'row' follows the inline direction */
  flex-direction: row;
  gap: 10px;
  border: 2px dashed blue;
  padding: 10px;
}

.flex-container div {
  background: lightblue; padding: 20px;
}

.flex-container.horizontal {
  writing-mode: horizontal-tb;
}

.flex-container.vertical {
  writing-mode: vertical-rl;
  height: 200px; /* To constrain the vertical space */
}

In the first example, the items lay out in a horizontal row. In the second example, with writing-mode: vertical-rl, the exact same flex-direction: row causes the items to lay out in a vertical column because the inline direction is now top-to-bottom.

This is incredibly powerful. You can create a component that completely reflows its children from horizontal to vertical just by changing the writing-mode, without ever touching its display or flex-direction properties.

Gotchas, Best Practices, and Accessibility

While writing-mode is a fantastic tool, there are a few things to keep in mind.

Gotchas

  1. text-align: Just like with margins, text-align: left or text-align: right can be misleading. In a vertical-rl mode, left refers to the physical left, which is now the end of the block flow. It's better to use the logical values start and end. text-align: start will align text to the top of the line, and end will align it to the bottom.
  2. Overflow and Scrolling: When content overflows its container in a vertical writing mode, the scrollbar will appear at the block-end position. For vertical-rl, this means a horizontal scrollbar at the bottom of the container that scrolls left-to-right. This can be unintuitive for users accustomed to vertical scrollbars.
  3. Images and Replaced Elements: writing-mode does not automatically rotate images, iframes, or videos. They will remain upright, which might be what you want. If you need them to rotate with the text flow, you'll need to use transform: rotate(90deg).

Best Practices

  • Embrace Logical Properties: I can't say it enough. Use inline-size, block-size, margin-block-start, etc., for robust, flow-relative layouts.
  • Use for Layout, Not Rotation: If you just want to rotate a single word or icon, transform: rotate() is a simpler and more performant choice. Use writing-mode when you need to change the actual content flow direction.
  • Progressive Enhancement: Since writing-mode has excellent browser support, you can use it confidently. However, always ensure your layout is still usable in the rare case it's not supported. A horizontal layout is a perfectly acceptable fallback.

Accessibility (A11y)

Changing the visual presentation of content so drastically requires careful consideration for accessibility.

  • DOM Order: The most important rule is to ensure your source order (the order of elements in the HTML) remains logical. Screen readers and keyboard navigation follow the DOM, not your CSS. A writing-mode layout should be a visual representation of a logical DOM structure.
  • Reading Order: For CJK languages, vertical-rl is the correct and expected reading order. For Latin-based languages, a vertical layout can be disorienting. Use it judiciously for things like headers or decorative text, and ensure the main body of content remains in a highly readable format.
  • Focus Order: Test your vertical layouts with keyboard navigation. The focus should move in a predictable pattern. Thanks to logical properties, the browser's default focus ring and tabbing order usually adapt correctly, but it's always worth a manual check.

Conclusion

CSS writing-mode is far more than a niche property for specific languages. It's a key that unlocks a new dimension of web layout. By shifting our mindset from a physical, screen-based coordinate system to a logical, flow-relative one, we can build components and layouts that are more resilient, flexible, and creative.

We've seen how it changes the fundamental block and inline directions, how it pairs with logical properties to create robust designs, and how it interacts with modern layout systems like Flexbox in powerful ways.

So the next time you're designing a layout, ask yourself: does this have to be horizontal? You might find that by turning your design on its side, you discover a whole new world of possibilities. Go ahead and experiment—the web is your canvas, and now you have a whole new axis to paint on.