- Published on
CSS Writing Mode: The Ultimate Guide to Vertical Text and Layouts
- Authors
- Name
- Md Nasim Sheikh
- @nasimStg
'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
- 'CSS Writing Mode: The Ultimate Guide to Vertical Text and Layouts'
- What is writing-mode? The Core Concepts
- The writing-mode Property Values
- horizontal-tb (The Default)
- vertical-rl
- vertical-lr
- The "Logical" Revolution: Thinking Beyond Width and Height
- Practical Example: The Power of Logical Properties
- Practical Use Cases and Creative Examples
- 1. Vertical Headers and Sidetitles
- 2. Authentic CJK Typography
- 3. Integration with Flexbox and Grid
- Gotchas, Best Practices, and Accessibility
- Gotchas
- Best Practices
- Accessibility (A11y)
- Conclusion
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.
writing-mode
? The Core Concepts
What is 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:
- 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. - 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.
writing-mode
Property Values
The 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. Invertical-rl
, it's the horizontal axis. - Inline Axis: The direction text flows in. In
horizontal-tb
, this is the horizontal axis. Invertical-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 Property | Logical Property | Description |
---|---|---|
width | inline-size | The size in the inline dimension |
height | block-size | The size in the block dimension |
min-width / max-width | min-inline-size / max-inline-size | Min/max size in the inline dimension |
min-height / max-height | min-block-size / max-block-size | Min/max size in the block dimension |
margin-top | margin-block-start | Margin at the start of the block axis |
margin-bottom | margin-block-end | Margin at the end of the block axis |
margin-left | margin-inline-start | Margin at the start of the inline axis |
margin-right | margin-inline-end | Margin at the end of the inline axis |
padding-top | padding-block-start | Padding at the start of the block axis |
border-left | border-inline-start | Border at the start of the inline axis |
top / left | inset-block-start / inset-inline-start | Position 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
text-align
: Just like with margins,text-align: left
ortext-align: right
can be misleading. In avertical-rl
mode,left
refers to the physical left, which is now the end of the block flow. It's better to use the logical valuesstart
andend
.text-align: start
will align text to the top of the line, andend
will align it to the bottom.- Overflow and Scrolling: When content overflows its container in a vertical writing mode, the scrollbar will appear at the
block-end
position. Forvertical-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. - 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 usetransform: 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. Usewriting-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.