Logical Properties & Writing Modes
Understanding Physical vs Logical Properties
For decades, CSS has used physical properties like margin-left, padding-right, width, and height to control layout. These properties are tied to the physical dimensions of the screen: left, right, top, and bottom. They work perfectly well if your content always flows from left to right and from top to bottom, as in English. But the web is global. Arabic and Hebrew flow right-to-left. Traditional Chinese and Japanese can flow vertically. When you hardcode physical directions into your CSS, your layouts break the moment the text direction changes.
CSS Logical Properties solve this problem by replacing physical directions with logical ones based on the document's writing mode and text direction. Instead of thinking in terms of "left" and "right," you think in terms of "start" and "end." Instead of "top" and "bottom," you think in terms of "block start" and "block end." This abstraction means that a single set of CSS rules can automatically adapt to any writing direction -- left-to-right, right-to-left, or vertical -- without any additional code. Logical properties are not just a convenience; they are the foundation of truly internationalized web layouts.
Writing Modes: The Foundation
Before diving into logical properties, you need to understand writing modes. The writing-mode property determines the direction in which blocks of content are laid out and the direction in which inline content flows within a block. It fundamentally changes how the browser interprets "block" and "inline" directions.
The writing-mode Property
The writing-mode property accepts three main values, each defining a different flow for content:
CSS writing-mode Values
/* Default: horizontal text, top-to-bottom block flow */
.horizontal {
writing-mode: horizontal-tb;
/* Inline flows left-to-right (in LTR) or right-to-left (in RTL) */
/* Blocks stack from top to bottom */
}
/* Vertical text, flowing right-to-left */
.vertical-rl {
writing-mode: vertical-rl;
/* Inline flows top to bottom */
/* Blocks stack from right to left */
/* Traditional Chinese, Japanese, Korean layout */
}
/* Vertical text, flowing left-to-right */
.vertical-lr {
writing-mode: vertical-lr;
/* Inline flows top to bottom */
/* Blocks stack from left to right */
/* Mongolian traditional script layout */
}
In horizontal-tb (the default for English, Arabic, and most languages), text runs horizontally and blocks stack from top to bottom. In vertical-rl, text runs vertically and blocks stack from right to left -- this is how traditional Japanese and Chinese is often typeset. In vertical-lr, text also runs vertically but blocks stack from left to right.
Practical Writing Mode Examples
/* Japanese vertical text layout */
.japanese-vertical {
writing-mode: vertical-rl;
font-family: "Noto Sans JP", sans-serif;
line-height: 1.8;
/* Text reads top-to-bottom, columns flow right-to-left */
}
/* Sidebar with rotated vertical text */
.sidebar-label {
writing-mode: vertical-rl;
text-orientation: mixed;
padding: 1rem;
background: #2c3e50;
color: white;
font-size: 0.875rem;
letter-spacing: 0.1em;
text-transform: uppercase;
}
/* Creative vertical headline */
.vertical-headline {
writing-mode: vertical-lr;
transform: rotate(180deg);
/* This creates a right-to-left reading vertical text */
font-size: 3rem;
font-weight: 700;
}
writing-mode property is inherited by child elements. Setting it on a parent container affects all children unless they explicitly override it. This is important when creating mixed-direction layouts where some elements need to remain horizontal within a vertical container.The direction Property
The direction property controls the inline base direction of text: left-to-right (ltr) or right-to-left (rtl). While writing-mode determines whether text is horizontal or vertical, direction determines which end of the line is the "start."
Setting Text Direction
/* Left-to-right (English, French, etc.) */
.ltr-content {
direction: ltr;
/* Inline start = left, Inline end = right */
}
/* Right-to-left (Arabic, Hebrew, etc.) */
.rtl-content {
direction: rtl;
/* Inline start = right, Inline end = left */
}
/* Full Arabic page setup */
html[lang="ar"] {
direction: rtl;
}
/* Alternatively, use the HTML dir attribute (preferred) */
/* <html lang="ar" dir="rtl"> */
dir attribute on the <html> element rather than the CSS direction property. The dir attribute is semantic and affects how browsers handle bidirectional text algorithms, form inputs, and other non-CSS behaviors. Use CSS direction only for visual overrides when the HTML attribute is not appropriate.Block vs Inline Axis
The core concept behind logical properties is the distinction between the block axis and the inline axis. These axes change meaning depending on the writing mode:
In horizontal-tb (default): the inline axis is horizontal (left to right or right to left), and the block axis is vertical (top to bottom). In vertical-rl or vertical-lr: the inline axis is vertical (top to bottom), and the block axis is horizontal (right to left or left to right).
Block and Inline Axis Visualization
/*
horizontal-tb (English, Arabic):
┌──────────────────────────────┐
│ Inline axis ──────────► │ Block
│ (or ◄────── in RTL) │ axis
│ │ │
│ Text flows along inline │ │
│ Blocks stack along block │ ▼
└──────────────────────────────┘
vertical-rl (Traditional CJK):
┌──────────────────────────────┐
│ ◄── Block axis │
│ │
│ │ Text flows along │
│ │ the inline axis │
│ │ (top to bottom) │
│ ▼ │
└──────────────────────────────┘
The key insight: "block" and "inline" are RELATIVE
to the writing mode, not fixed to physical screen edges.
*/
Logical Margin Properties
Logical margin properties replace the physical margin-top, margin-right, margin-bottom, and margin-left with direction-agnostic equivalents. The naming convention uses block for the block axis and inline for the inline axis, combined with start and end.
Logical Margin Properties
/* Physical margins (avoid for i18n layouts) */
.physical {
margin-top: 1rem;
margin-right: 2rem;
margin-bottom: 1rem;
margin-left: 2rem;
}
/* Logical margin equivalents (preferred) */
.logical {
margin-block-start: 1rem; /* top in horizontal-tb */
margin-inline-end: 2rem; /* right in LTR, left in RTL */
margin-block-end: 1rem; /* bottom in horizontal-tb */
margin-inline-start: 2rem; /* left in LTR, right in RTL */
}
/* Shorthand for both start and end on the same axis */
.logical-shorthand {
margin-block: 1rem; /* block-start AND block-end */
margin-inline: 2rem; /* inline-start AND inline-end */
}
/* Two-value shorthand: start and end separately */
.logical-two-value {
margin-block: 1rem 2rem; /* block-start: 1rem, block-end: 2rem */
margin-inline: 0.5rem 1rem; /* inline-start: 0.5rem, inline-end: 1rem */
}
/* Practical example: spacing a navigation icon from text */
.nav-icon {
margin-inline-end: 0.5rem;
/* In LTR: icon has right margin (icon then text) */
/* In RTL: icon has left margin (text then icon) */
}
The mapping between physical and logical properties in a horizontal-tb, LTR context is: margin-block-start equals margin-top, margin-block-end equals margin-bottom, margin-inline-start equals margin-left, and margin-inline-end equals margin-right. In an RTL context, margin-inline-start becomes margin-right and margin-inline-end becomes margin-left.
Logical Padding Properties
Logical padding properties follow exactly the same naming pattern as margins. They replace padding-top, padding-right, padding-bottom, and padding-left with flow-relative equivalents.
Logical Padding Properties
/* Physical padding */
.card-physical {
padding-top: 1.5rem;
padding-right: 2rem;
padding-bottom: 1.5rem;
padding-left: 2rem;
}
/* Logical padding equivalents */
.card-logical {
padding-block-start: 1.5rem;
padding-inline-end: 2rem;
padding-block-end: 1.5rem;
padding-inline-start: 2rem;
}
/* Shorthand */
.card-shorthand {
padding-block: 1.5rem; /* top and bottom */
padding-inline: 2rem; /* left and right (in LTR) */
}
/* Two-value shorthand */
.card-asymmetric {
padding-block: 1rem 2rem; /* block-start: 1rem, block-end: 2rem */
padding-inline: 1.5rem 3rem; /* inline-start: 1.5rem, inline-end: 3rem */
}
/* Practical: list item with indentation */
.list-item {
padding-inline-start: 2rem;
/* In LTR: padding-left indents from left edge */
/* In RTL: padding-right indents from right edge */
}
/* Practical: sidebar navigation */
.nav-link {
padding-block: 0.75rem;
padding-inline: 1rem 1.5rem;
border-inline-start: 3px solid transparent;
}
.nav-link.active {
border-inline-start-color: #3498db;
background: rgba(52, 152, 219, 0.1);
}
Logical Border Properties
Logical border properties provide the same directional flexibility for borders. You can set individual border sides, or use the shorthand properties for the block and inline axes.
Logical Border Properties
/* Physical borders */
.physical-border {
border-left: 3px solid #3498db;
border-bottom: 1px solid #eee;
}
/* Logical border equivalents */
.logical-border {
border-inline-start: 3px solid #3498db;
border-block-end: 1px solid #eee;
}
/* Individual logical border properties */
.detailed-borders {
border-block-start-width: 2px;
border-block-start-style: solid;
border-block-start-color: #333;
border-inline-end-width: 1px;
border-inline-end-style: dashed;
border-inline-end-color: #ccc;
}
/* Shorthand for entire axis */
.axis-borders {
border-block: 1px solid #eee; /* top and bottom */
border-inline: 2px solid #333; /* left and right (LTR) */
}
/* Logical border-radius */
.logical-radius {
border-start-start-radius: 8px; /* top-left in LTR, top-right in RTL */
border-start-end-radius: 8px; /* top-right in LTR, top-left in RTL */
border-end-start-radius: 0; /* bottom-left in LTR, bottom-right in RTL */
border-end-end-radius: 0; /* bottom-right in LTR, bottom-left in RTL */
}
/* Practical: callout box with start-side accent */
.callout {
border-inline-start: 4px solid #e74c3c;
padding-block: 1rem;
padding-inline: 1.5rem;
background: #fef2f2;
border-start-start-radius: 4px;
border-end-start-radius: 4px;
}
border-{block-position}-{inline-position}-radius. So border-start-start-radius means "block-start, inline-start corner," which maps to top-left in a horizontal LTR context. The naming can feel confusing at first, but it follows a consistent pattern once you internalize the block/inline axis concept.Logical Sizing: inline-size and block-size
Physical width and height always refer to horizontal and vertical dimensions respectively. Logical sizing properties replace them with flow-relative equivalents: inline-size replaces width (in horizontal writing modes) and block-size replaces height.
Logical Sizing Properties
/* Physical sizing */
.physical-box {
width: 300px;
height: 200px;
min-width: 200px;
max-width: 600px;
min-height: 100px;
max-height: 400px;
}
/* Logical sizing equivalents */
.logical-box {
inline-size: 300px; /* width in horizontal-tb */
block-size: 200px; /* height in horizontal-tb */
min-inline-size: 200px; /* min-width in horizontal-tb */
max-inline-size: 600px; /* max-width in horizontal-tb */
min-block-size: 100px; /* min-height in horizontal-tb */
max-block-size: 400px; /* max-height in horizontal-tb */
}
/* Why this matters: vertical writing mode */
.vertical-container {
writing-mode: vertical-rl;
}
.vertical-container .box {
inline-size: 300px;
/* In vertical-rl, this sets the HEIGHT (not width) */
/* because the inline axis is now vertical */
block-size: 200px;
/* In vertical-rl, this sets the WIDTH (not height) */
/* because the block axis is now horizontal */
}
/* Responsive container with logical sizing */
.responsive-card {
inline-size: 100%;
max-inline-size: 40rem;
min-block-size: 15rem;
margin-inline: auto;
}
Logical Positioning with inset Properties
The inset properties are the logical equivalents of top, right, bottom, and left used with positioned elements. They allow absolutely or fixed positioned elements to respond to writing direction automatically.
Logical Inset Properties
/* Physical positioning */
.physical-positioned {
position: absolute;
top: 0;
right: 1rem;
bottom: auto;
left: auto;
}
/* Logical positioning equivalents */
.logical-positioned {
position: absolute;
inset-block-start: 0; /* top in horizontal-tb */
inset-inline-end: 1rem; /* right in LTR, left in RTL */
inset-block-end: auto; /* bottom in horizontal-tb */
inset-inline-start: auto; /* left in LTR, right in RTL */
}
/* Shorthand: inset-block and inset-inline */
.shorthand-positioned {
position: absolute;
inset-block: 0; /* top: 0; bottom: 0; */
inset-inline: 1rem 2rem; /* start: 1rem, end: 2rem */
}
/* The inset shorthand (sets all four) */
.inset-all {
position: absolute;
inset: 0;
/* Equivalent to top: 0; right: 0; bottom: 0; left: 0; */
}
.inset-values {
position: absolute;
inset: 1rem 2rem 3rem 4rem;
/* top: 1rem; right: 2rem; bottom: 3rem; left: 4rem; */
}
/* Practical: close button in top-end corner */
.close-button {
position: absolute;
inset-block-start: 0.5rem;
inset-inline-end: 0.5rem;
/* LTR: top-right corner */
/* RTL: top-left corner */
}
/* Practical: tooltip positioning */
.tooltip {
position: absolute;
inset-block-end: 100%;
inset-inline-start: 50%;
transform: translateX(-50%);
margin-block-end: 0.5rem;
}
Logical Text Alignment
Text alignment also has logical values. Instead of text-align: left and text-align: right, you can use text-align: start and text-align: end. These automatically adapt to the text direction.
Logical Text Alignment
/* Physical alignment (avoid for i18n) */
.physical-align {
text-align: left;
}
/* Logical alignment (preferred) */
.logical-align {
text-align: start;
/* left in LTR languages */
/* right in RTL languages */
}
.end-aligned {
text-align: end;
/* right in LTR languages */
/* left in RTL languages */
}
/* Practical usage */
.article-body {
text-align: start;
}
.article-date {
text-align: end;
color: #666;
font-size: 0.875rem;
}
/* Navigation items aligned to start */
.nav-item {
text-align: start;
padding-inline-start: 1rem;
}
/* Price aligned to end in a product card */
.product-price {
text-align: end;
font-weight: 700;
font-size: 1.25rem;
}
Logical Float, Clear, and Resize Values
Float and clear properties also have logical equivalents using inline-start and inline-end instead of left and right. The resize property similarly supports block and inline values.
Logical Float and Clear
/* Physical float */
.physical-float {
float: left;
}
/* Logical float */
.logical-float {
float: inline-start;
/* Floats to the left in LTR */
/* Floats to the right in RTL */
}
.float-end {
float: inline-end;
/* Floats to the right in LTR */
/* Floats to the left in RTL */
}
/* Logical clear */
.clear-start {
clear: inline-start;
}
.clear-end {
clear: inline-end;
}
/* Practical: image floating beside text */
.article-image {
float: inline-start;
margin-inline-end: 1.5rem;
margin-block-end: 1rem;
max-inline-size: 300px;
/* In LTR: floats left with right margin */
/* In RTL: floats right with left margin */
}
/* Logical resize */
.resizable-block {
resize: block;
/* Allows resizing along the block axis (vertically in horizontal-tb) */
overflow: auto;
}
.resizable-inline {
resize: inline;
/* Allows resizing along the inline axis (horizontally in horizontal-tb) */
overflow: auto;
}
Why Logical Properties Matter for Internationalization
The real power of logical properties becomes evident when you build layouts that need to support multiple writing directions. Consider a website that must work in both English (LTR) and Arabic (RTL). Without logical properties, you would need separate stylesheets or extensive overrides for RTL. With logical properties, a single stylesheet handles both directions automatically.
Before Logical Properties: RTL Override Approach
/* Base styles (LTR) */
.card {
padding-left: 1.5rem;
padding-right: 1rem;
border-left: 4px solid #3498db;
margin-right: 1rem;
text-align: left;
}
.card-icon {
float: left;
margin-right: 0.75rem;
}
/* RTL overrides -- you must flip EVERY directional property */
[dir="rtl"] .card {
padding-left: 1rem;
padding-right: 1.5rem;
border-left: none;
border-right: 4px solid #3498db;
margin-right: 0;
margin-left: 1rem;
text-align: right;
}
[dir="rtl"] .card-icon {
float: right;
margin-right: 0;
margin-left: 0.75rem;
}
/* That is 14 extra declarations just to flip one component! */
After Logical Properties: Zero RTL Overrides Needed
/* Single set of styles works for BOTH LTR and RTL */
.card {
padding-inline-start: 1.5rem;
padding-inline-end: 1rem;
border-inline-start: 4px solid #3498db;
margin-inline-end: 1rem;
text-align: start;
}
.card-icon {
float: inline-start;
margin-inline-end: 0.75rem;
}
/* No RTL overrides needed. Zero extra code. */
/* In LTR: padding-left, border-left, margin-right, float left */
/* In RTL: padding-right, border-right, margin-left, float right */
Building Bidirectional Layouts
Let us build a complete component that works flawlessly in both LTR and RTL contexts. This media card component demonstrates how logical properties eliminate the need for directional overrides entirely.
Complete Bidirectional Media Card
/* Media card: image on the start side, content on the end side */
.media-card {
display: flex;
gap: 1.5rem;
padding: 1.5rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
max-inline-size: 600px;
}
.media-card__image {
flex-shrink: 0;
inline-size: 120px;
block-size: 120px;
border-radius: 8px;
object-fit: cover;
}
.media-card__content {
flex: 1;
min-inline-size: 0;
}
.media-card__title {
font-size: 1.25rem;
font-weight: 600;
margin-block-end: 0.5rem;
text-align: start;
}
.media-card__description {
color: #64748b;
margin-block-end: 1rem;
text-align: start;
line-height: 1.6;
}
.media-card__meta {
display: flex;
justify-content: space-between;
align-items: center;
padding-block-start: 0.75rem;
border-block-start: 1px solid #e2e8f0;
}
.media-card__author {
display: flex;
align-items: center;
gap: 0.5rem;
}
.media-card__avatar {
inline-size: 32px;
block-size: 32px;
border-radius: 50%;
}
.media-card__date {
color: #94a3b8;
font-size: 0.875rem;
}
.media-card__badge {
padding-block: 0.25rem;
padding-inline: 0.75rem;
background: #eff6ff;
color: #3b82f6;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 500;
}
/* This entire component works perfectly in both LTR and RTL
without a single [dir="rtl"] override. The flex layout
naturally reverses in RTL, and all logical properties
automatically flip to the correct physical sides. */
Complete Mapping Reference
Here is a comprehensive reference table mapping physical properties to their logical equivalents. This reference assumes a horizontal-tb writing mode with ltr direction for the physical mappings shown.
Physical to Logical Property Mapping
/* ===== MARGINS ===== */
/* margin-top → margin-block-start */
/* margin-bottom → margin-block-end */
/* margin-left → margin-inline-start */
/* margin-right → margin-inline-end */
/* ===== PADDING ===== */
/* padding-top → padding-block-start */
/* padding-bottom → padding-block-end */
/* padding-left → padding-inline-start */
/* padding-right → padding-inline-end */
/* ===== BORDERS ===== */
/* border-top → border-block-start */
/* border-bottom → border-block-end */
/* border-left → border-inline-start */
/* border-right → border-inline-end */
/* ===== BORDER RADIUS ===== */
/* border-top-left-radius → border-start-start-radius */
/* border-top-right-radius → border-start-end-radius */
/* border-bottom-left-radius → border-end-start-radius */
/* border-bottom-right-radius → border-end-end-radius */
/* ===== SIZING ===== */
/* width → inline-size */
/* height → block-size */
/* min-width → min-inline-size */
/* max-width → max-inline-size */
/* min-height → min-block-size */
/* max-height → max-block-size */
/* ===== POSITIONING ===== */
/* top → inset-block-start */
/* bottom → inset-block-end */
/* left → inset-inline-start */
/* right → inset-inline-end */
/* ===== TEXT & FLOAT ===== */
/* text-align: left → text-align: start */
/* text-align: right → text-align: end */
/* float: left → float: inline-start */
/* float: right → float: inline-end */
/* clear: left → clear: inline-start */
/* clear: right → clear: inline-end */
/* ===== RESIZE ===== */
/* resize: horizontal → resize: inline */
/* resize: vertical → resize: block */
Browser Support and Fallback Strategies
CSS Logical Properties have excellent browser support in all modern browsers. Chrome, Firefox, Safari, and Edge all support the core logical properties for margins, padding, borders, sizing, and positioning. However, some newer logical values like float: inline-start have slightly less consistent support. Here are strategies for handling older browsers:
Fallback Strategies
/* Strategy 1: Progressive enhancement with physical fallbacks */
.card {
/* Physical fallback for older browsers */
padding-left: 1.5rem;
padding-right: 1rem;
/* Logical properties override in supporting browsers */
padding-inline-start: 1.5rem;
padding-inline-end: 1rem;
}
/* Strategy 2: @supports feature query */
.sidebar {
/* Default physical properties */
margin-left: 2rem;
border-left: 3px solid #3498db;
}
@supports (margin-inline-start: 1rem) {
.sidebar {
margin-left: unset;
border-left: unset;
margin-inline-start: 2rem;
border-inline-start: 3px solid #3498db;
}
}
/* Strategy 3: PostCSS plugin (build-time transformation)
The postcss-logical plugin can automatically generate
physical fallbacks from logical properties:
Input: margin-inline-start: 1rem;
Output: margin-left: 1rem;
margin-inline-start: 1rem;
This gives you the best of both worlds. */
unset keyword used to clear fallbacks resets a property to its inherited or initial value, which may have unintended effects if the property is inherited.Practical Real-World Examples
Let us look at several practical scenarios where logical properties dramatically simplify your CSS.
Example: Notification Component
/* Notification with icon on the start side */
.notification {
display: flex;
align-items: flex-start;
gap: 1rem;
padding-block: 1rem;
padding-inline: 1.25rem;
border-inline-start: 4px solid;
border-radius: 0.5rem;
margin-block-end: 1rem;
}
.notification--success {
border-inline-start-color: #22c55e;
background: #f0fdf4;
}
.notification--error {
border-inline-start-color: #ef4444;
background: #fef2f2;
}
.notification--warning {
border-inline-start-color: #f59e0b;
background: #fffbeb;
}
.notification__icon {
flex-shrink: 0;
inline-size: 24px;
block-size: 24px;
margin-block-start: 2px;
}
.notification__close {
margin-inline-start: auto;
padding: 0.25rem;
border: none;
background: none;
cursor: pointer;
}
Example: Form Layout
/* Form with labels and inputs */
.form-group {
margin-block-end: 1.5rem;
}
.form-label {
display: block;
margin-block-end: 0.5rem;
font-weight: 500;
text-align: start;
}
.form-input {
inline-size: 100%;
padding-block: 0.75rem;
padding-inline: 1rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 1rem;
text-align: start;
}
.form-input:focus {
border-color: #3b82f6;
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Input with icon inside */
.input-with-icon {
position: relative;
}
.input-with-icon .icon {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 1rem;
transform: translateY(-50%);
color: #9ca3af;
pointer-events: none;
}
.input-with-icon .form-input {
padding-inline-start: 2.75rem;
}
/* Form actions aligned to end */
.form-actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
padding-block-start: 1.5rem;
border-block-start: 1px solid #e5e7eb;
}
Example: Breadcrumb Navigation
/* Breadcrumb that automatically flips for RTL */
.breadcrumb {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
padding-block: 0.75rem;
padding-inline: 0;
list-style: none;
margin: 0;
}
.breadcrumb__item {
display: flex;
align-items: center;
gap: 0.5rem;
color: #64748b;
font-size: 0.875rem;
}
.breadcrumb__item::after {
content: "\203A"; /* Single right-pointing angle (›) */
color: #cbd5e1;
font-size: 1.25rem;
}
[dir="rtl"] .breadcrumb__item::after {
content: "\2039"; /* Single left-pointing angle (‹) for RTL */
}
.breadcrumb__item:last-child::after {
content: none;
}
.breadcrumb__item:last-child {
color: #1e293b;
font-weight: 500;
}
.breadcrumb__link {
color: inherit;
text-decoration: none;
padding-block: 0.125rem;
padding-inline: 0.25rem;
border-radius: 0.25rem;
}
.breadcrumb__link:hover {
background: #f1f5f9;
color: #3b82f6;
}
Exercise 1: Bidirectional Profile Card
Build a profile card component using only logical properties (zero physical directional properties). The card should contain a circular avatar on the inline-start side, user name and bio text in the center, and a follow button on the inline-end side. Add a colored accent border on the inline-start edge. Below the main row, add a stats bar showing followers, following, and posts counts separated by vertical dividers. Use margin-block, padding-inline, border-inline-start, text-align: start, and other logical properties throughout. Test your card by adding dir="rtl" to the parent element and verify that everything flips correctly without any additional CSS rules. Add a writing-mode: vertical-rl variant class that makes the entire card render vertically, and verify that all your logical properties still work correctly in the vertical writing mode.
Exercise 2: Responsive Dashboard Layout
Create a dashboard layout with a fixed sidebar on the inline-start side and a main content area. The sidebar should have navigation links with icons on the inline-start and text following after, an active indicator border on the inline-start side, and padding using logical properties. The main content area should contain a header with a page title aligned to start and action buttons aligned to end, followed by a grid of stat cards. Each stat card should have an icon, a metric value, and a label using only logical properties for all spacing and borders. Build the entire layout without a single use of left, right, width, or height in any directional context. Use only inline-size, block-size, inset-inline-start, margin-inline, padding-block, and other logical equivalents. Add a language switcher button that toggles the dir attribute between ltr and rtl on the HTML element, and verify the entire dashboard mirrors correctly.