Skip to main content

Harshal V. LADHE

CSS Logical Properties: Writing Modes, RTL, and Direction-Aware Layouts

Design once, adapt everywhere.
Published at:
Last updated:
Estimated reading time:15 min read

Introduction

In modern web design, layouts must seamlessly adapt to different languages and writing modes — CSS Logical Properties offer a future-proof way to style elements that respect writing direction, text flow, and orientation.

Whether it's right-to-left (RTL) languages like Arabic and Hebrew, or vertical writing in Japanese, logical properties let you create one stylesheet that works everywhere.

Instead of tying layout to fixed physical directions (left, right, top, bottom), they use flow-relative directions: start, end, inline, and block.

With physical properties:

/* Physical properties */
margin-left: 1rem; /* Always on the left side */

In an RTL layout, margin-left still applies to the left side — which may no longer be the "start" of the text.

With logical properties:

/* Logical equivalent */
margin-inline-start: 1rem; /* Adapts to start side based on writing mode */

If the document switches to RTL, margin-inline-start automatically applies to the right — no extra styles needed.

This ensures your layout is writing mode aware.

👉 Try this hands-on in the interactive playground below — flip Direction to rtl and watch the Physical card's margin stay on the left while the Logical card's follows the text.

Benefits of Logical Properties

  1. Internationalization Ready – Works for RTL and vertical writing without needing separate stylesheets.
  2. Consistent Layouts – Automatically adjusts for writing mode changes, keeping design predictable.
  3. Cleaner Code – Reduces redundancy and removes the need for duplicated directional styles.
  4. Accessibility Friendly – Plays nicely with screen readers in RTL and vertical text contexts.
  5. Performance Friendly – Fewer overrides and media queries improve maintainability.
  6. Future Proof – Ready for expanded language and writing system support as browsers evolve.

When to Use Logical vs. Physical

Logical properties shine when your layout needs to adapt across different writing modes, such as left-to-right (English), right-to-left (Arabic/Hebrew), or vertical scripts (Japanese). They future-proof your styles so you don't have to rewrite CSS when switching directions or supporting multilingual content.

That said, physical properties aren't "wrong." If your project is a small, LTR-only site (like a personal blog or an internal tool with no internationalization needs), using margin-left or padding-top may be perfectly fine and even easier for quick prototyping.

Think of it this way:

  • Use logical properties when accessibility, scalability, and localization are priorities.
  • Use physical properties when the content direction is fixed and simplicity matters.

Understanding Inline vs Block Axes

Inline axis — the direction in which text flows.

Block axis — the direction perpendicular to the inline axis, where blocks (paragraphs, divs) stack.

Key idea: Logical properties use inline / block combined with start / end:

  • inline-start – leading edge of the inline axis (left in LTR, right in RTL, top in some vertical modes).
  • inline-end – trailing edge of the inline axis.
  • block-start – leading edge of the block axis (top in horizontal writing).
  • block-end – trailing edge of the block axis.

horizontal-tb (LTR – e.g., English)

block-start(top)block-end(bottom)inline-start(left)inline-end(right)inline axisblock axis

horizontal-tb (RTL – e.g., Arabic, Hebrew)

block-start(top)block-end(bottom)inline-end(left)inline-start(right)inline axisblock axis

vertical-rl (e.g., Traditional Mongolian)

inline-start(top)inline-end(bottom)block-end(left)block-start(right)inline axisblock axis

vertical-lr (e.g., some East Asian scripts)

inline-start(top)inline-end(bottom)block-start(left)block-end(right)inline axisblock axis

Quick Reference: Logical vs Physical Mappings

Writing modeInline axisBlock axisinline-startinline-endblock-startblock-end
horizontal-tb LTRleft → righttop → bottomleftrighttopbottom
horizontal-tb RTLright → lefttop → bottomrightlefttopbottom
vertical-rltop → bottomright → lefttopbottomrightleft
vertical-lrtop → bottomleft → righttopbottomleftright

Inline vs. Block in Action

Example 1 — Inline / Block sizing

.box {
  inline-size: 40ch;    /* horizontal-tb: width; vertical-rl/lr: height */
  block-size: 10rem;    /* horizontal-tb: height; vertical-rl/lr: width */
  padding-inline: 1rem; /* horizontal-tb: left & right; vertical: top & bottom */
  padding-block: 0.5rem;/* horizontal-tb: top & bottom; vertical: right & left */
}

Example 2 — Direction-aware margins

.card {
  margin-inline-start: 1rem; /* horizontal LTR: left; horizontal RTL: right; vertical: top */
  margin-block-start: 2rem;  /* horizontal: top; vertical-rl: right; vertical-lr: left */
}

Example 3 — Switching writing modes

/* Default: horizontal LTR */
.article {
  writing-mode: horizontal-tb;
  direction: ltr;
}

/* Vertical (e.g., Japanese-style) */
.vertical {
  writing-mode: vertical-rl;
  /* Set `direction` only if your content is RTL; otherwise omit. */
}

👉 Try this hands-on in the interactive playground below — the Writing mode control cycles through exactly these values.

Categories of Logical Properties

Here's a categorized list of logical properties with their physical equivalents — use this as your quick lookup table. Most of these reached Baseline years ago and are safe to use everywhere; a few are newer or unevenly implemented, and those call that out inline.

Dimension

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1inline-sizeLogical width in the inline axiswidthwidth
2block-sizeLogical height in the block axisheightheight
3min-inline-sizeMinimum width in the inline axismin-widthmin-width
4max-inline-sizeMaximum width in the inline axismax-widthmax-width
5min-block-sizeMinimum height in the block axismin-heightmin-height
6max-block-sizeMaximum height in the block axismax-heightmax-height

Margin

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1margin-inline-startMargin at the start of the inline axismargin-leftmargin-right
2margin-inline-endMargin at the end of the inline axismargin-rightmargin-left
3margin-inlineShorthand for both margin-inline-start and margin-inline-endmargin-left + margin-rightmargin-right + margin-left
4margin-block-startMargin at the start of the block axismargin-topmargin-top
5margin-block-endMargin at the end of the block axismargin-bottommargin-bottom
6margin-blockShorthand for both margin-block-start and margin-block-endmargin-top + margin-bottommargin-top + margin-bottom

Padding

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1padding-inline-startPadding at the start of the inline axispadding-leftpadding-right
2padding-inline-endPadding at the end of the inline axispadding-rightpadding-left
3padding-inlineShorthand for both padding-inline-start and padding-inline-endpadding-left + padding-rightpadding-right + padding-left
4padding-block-startPadding at the start of the block axispadding-toppadding-top
5padding-block-endPadding at the end of the block axispadding-bottompadding-bottom
6padding-blockShorthand for both padding-block-start and padding-block-endpadding-top + padding-bottompadding-top + padding-bottom

Borders

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1border-inline-startBorder at the start of the inline axisborder-leftborder-right
2border-inline-endBorder at the end of the inline axisborder-rightborder-left
3border-inlineShorthand for both border-inline-start and border-inline-endborder-left + border-rightborder-right + border-left
4border-block-startBorder at the start of the block axisborder-topborder-top
5border-block-endBorder at the end of the block axisborder-bottomborder-bottom
6border-blockShorthand for both border-block-start and border-block-endborder-top + border-bottomborder-top + border-bottom
7border-inline-start-widthWidth of the border at inline-startborder-left-widthborder-right-width
8border-inline-start-colorColor of the border at inline-startborder-left-colorborder-right-color
9border-inline-start-styleStyle of the border at inline-startborder-left-styleborder-right-style
10border-inline-end-widthWidth of the border at inline-endborder-right-widthborder-left-width
11border-inline-end-colorColor of the border at inline-endborder-right-colorborder-left-color
12border-inline-end-styleStyle of the border at inline-endborder-right-styleborder-left-style
13border-block-start-widthWidth of the border at block-startborder-top-widthborder-top-width
14border-block-start-colorColor of the border at block-startborder-top-colorborder-top-color
15border-block-start-styleStyle of the border at block-startborder-top-styleborder-top-style
16border-block-end-widthWidth of the border at block-endborder-bottom-widthborder-bottom-width
17border-block-end-colorColor of the border at block-endborder-bottom-colorborder-bottom-color
18border-block-end-styleStyle of the border at block-endborder-bottom-styleborder-bottom-style
19border-inline-widthShorthand for width of borders on both inline sidesborder-left-width + border-right-widthborder-right-width + border-left-width
20border-inline-styleShorthand for style of borders on both inline sidesborder-left-style + border-right-styleborder-right-style + border-left-style
21border-inline-colorShorthand for color of borders on both inline sidesborder-left-color + border-right-colorborder-right-color + border-left-color
22border-block-widthShorthand for width of borders on both block sidesborder-top-width + border-bottom-widthborder-top-width + border-bottom-width
23border-block-styleShorthand for style of borders on both block sidesborder-top-style + border-bottom-styleborder-top-style + border-bottom-style
24border-block-colorShorthand for color of borders on both block sidesborder-top-color + border-bottom-colorborder-top-color + border-bottom-color

Border Radius

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1border-start-start-radiusRadius for the corner where block-start and inline-start meetborder-top-left-radiusborder-top-right-radius
2border-start-end-radiusRadius for the corner where block-start and inline-end meetborder-top-right-radiusborder-top-left-radius
3border-end-start-radiusRadius for the corner where block-end and inline-start meetborder-bottom-left-radiusborder-bottom-right-radius
4border-end-end-radiusRadius for the corner where block-end and inline-end meetborder-bottom-right-radiusborder-bottom-left-radius

Positioning / Offsets

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1inset-inline-startOffset from the inline-start edgeleftright
2inset-inline-endOffset from the inline-end edgerightleft
3inset-inlineShorthand for both inset-inline-start and inset-inline-endleft + rightright + left
4inset-block-startOffset from the block-start edgetoptop
5inset-block-endOffset from the block-end edgebottombottom
6inset-blockShorthand for both inset-block-start and inset-block-endtop + bottomtop + bottom
7insetShorthand for all four logical inset propertiestop + right + bottom + lefttop + right + bottom + left

Resizing

#Logical Property / ValueDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1resize: blockAllows resize in block axis only.resize: verticalresize: vertical
2resize: inlineAllows resize in inline axis only.resize: horizontalresize: horizontal

Containment

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1contain-intrinsic-block-sizeSpecifies the element's intrinsic size in the block (vertical) axis when its contents are subject to size containment.contain-intrinsic-heightcontain-intrinsic-height
2contain-intrinsic-inline-sizeSpecifies the element's intrinsic size in the inline (horizontal) axis when its contents are subject to size containment.contain-intrinsic-widthcontain-intrinsic-width

Floats & Clears

#Logical Property / ValueDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1float: inline-startFloats an element to the start side of the inline axis (like float: left in LTR).float: leftfloat: right
2float: inline-endFloats an element to the end side of the inline axis.float: rightfloat: left
3clear: inline-startPrevents floating elements on the start side.clear: leftclear: right
4clear: inline-endPrevents floating elements on the end side.clear: rightclear: left

Text Alignment

#Logical Property / ValueDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1text-align: startAligns text to the start side of the inline axis.text-align: lefttext-align: right
2text-align: endAligns text to the end side of the inline axis.text-align: righttext-align: left

Tables

#Logical Property / ValueDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1caption-side: topPlaces the caption on the block-start side of the table (the default).caption-side: topcaption-side: top
2caption-side: bottomPlaces the caption on the block-end side of the table.caption-side: bottomcaption-side: bottom
3caption-side: inline-startPlaces caption at inline start of table.caption-side: leftcaption-side: right
4caption-side: inline-endPlaces caption at inline end of table.caption-side: rightcaption-side: left

Scroll Margin

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1scroll-margin-inline-startScroll margin at inline start edgescroll-margin-leftscroll-margin-right
2scroll-margin-inline-endScroll margin at inline end edgescroll-margin-rightscroll-margin-left
3scroll-margin-inlineShorthand for both inline-start and inline-endscroll-margin-left + scroll-margin-rightscroll-margin-right + scroll-margin-left
4scroll-margin-block-startScroll margin at block start edgescroll-margin-topscroll-margin-top
5scroll-margin-block-endScroll margin at block end edgescroll-margin-bottomscroll-margin-bottom
6scroll-margin-blockShorthand for both block-start and block-endscroll-margin-top + scroll-margin-bottomscroll-margin-top + scroll-margin-bottom

Scroll Padding

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1scroll-padding-inline-startScroll padding at inline start edgescroll-padding-leftscroll-padding-right
2scroll-padding-inline-endScroll padding at inline end edgescroll-padding-rightscroll-padding-left
3scroll-padding-inlineShorthand for both inline-start and inline-endscroll-padding-left + scroll-padding-rightscroll-padding-right + scroll-padding-left
4scroll-padding-block-startScroll padding at block start edgescroll-padding-topscroll-padding-top
5scroll-padding-block-endScroll padding at block end edgescroll-padding-bottomscroll-padding-bottom
6scroll-padding-blockShorthand for both block-start and block-endscroll-padding-top + scroll-padding-bottomscroll-padding-top + scroll-padding-bottom

Overflow

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1overflow-blockControls overflow in the block direction (top → bottom in LTR/RTL).overflow-yoverflow-y
2overflow-inlineControls overflow in the inline direction (left → right in LTR, right → left in RTL).overflow-xoverflow-x

Overscroll Behavior

#Logical PropertyDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1overscroll-behavior-inlineOverscroll behavior in inline axisoverscroll-behavior-xoverscroll-behavior-x
2overscroll-behavior-blockOverscroll behavior in block axisoverscroll-behavior-yoverscroll-behavior-y

Scroll Snap

#Logical Property / ValueDescriptionPhysical LTR EquivalentPhysical RTL Equivalent
1scroll-snap-type: blockDefines scroll snapping along the block axis (vertical in LTR/RTL).scroll-snap-type: yscroll-snap-type: y
2scroll-snap-type: inlineDefines scroll snapping along the inline axis (horizontal in LTR/RTL).scroll-snap-type: xscroll-snap-type: x
3scroll-snap-align: startSnap aligns the element's start edge with the scroll container's snap position.Top / LeftTop / Right
4scroll-snap-align: endSnap aligns the element's end edge with the scroll container's snap position.Bottom / RightBottom / Left

Interactive Example

Reading the mappings above is one thing — watching them hold up under a direction change is another. The playground below renders two cards side by side: one styled entirely with physical properties (margin-left, padding-left, border-left, text-align: left), the other with their logical equivalents (margin-inline-start, padding-inline-start, border-inline-start, text-align: start).

Flip Direction between ltr and rtl, then cycle Writing mode through horizontal-tb, vertical-rl, and vertical-lr. The physical card stays nailed to the left no matter what you choose — the logical card tracks the start edge and repositions itself to match.

Logical vs Physical Properties

Flip direction and writing mode to see a logical-property card adapt while its physical-property twin stays put.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Logical Properties Playground</title>
  </head>
  <body>
    <main class="playground">
      <header class="playground-header">
        <h1>Logical vs Physical Properties</h1>
        <p>Flip the direction and writing mode — watch which card holds up.</p>
      </header>

      <section class="demo-area" aria-label="Logical vs physical property preview">
        <div id="stage" class="stage">
          <div class="card card--physical">
            <span class="card-badge card-badge--physical">Physical</span>
            <code class="card-code">
              margin-left · padding-left · border-left · text-align: left
            </code>
            <p class="card-text">Nailed to the left, no matter what you pick below.</p>
          </div>

          <div class="card card--logical">
            <span class="card-badge card-badge--logical">Logical</span>
            <code class="card-code">
              margin-inline-start · padding-inline-start · border-inline-start · text-align: start
            </code>
            <p class="card-text">Follows the start edge — flips with direction and writing mode.</p>
          </div>
        </div>
      </section>

      <output id="props-output" class="readout" aria-live="polite"></output>

      <fieldset class="control-group">
        <legend>Configuration</legend>
        <div class="fields-grid">
          <div class="field field--full">
            <span class="field-label" id="direction-label">Direction</span>
            <div class="segmented-control" role="radiogroup" aria-labelledby="direction-label">
              <label class="segment">
                <input type="radio" name="direction" value="ltr" checked>
                <span>LTR</span>
              </label>
              <label class="segment">
                <input type="radio" name="direction" value="rtl">
                <span>RTL</span>
              </label>
            </div>
          </div>

          <div class="field field--full">
            <span class="field-label" id="writing-mode-label">Writing mode</span>
            <div class="segmented-control" role="radiogroup" aria-labelledby="writing-mode-label">
              <label class="segment">
                <input type="radio" name="writing-mode" value="horizontal-tb" checked>
                <span>horizontal-tb</span>
              </label>
              <label class="segment">
                <input type="radio" name="writing-mode" value="vertical-rl">
                <span>vertical-rl</span>
              </label>
              <label class="segment">
                <input type="radio" name="writing-mode" value="vertical-lr">
                <span>vertical-lr</span>
              </label>
            </div>
          </div>
        </div>
      </fieldset>
    </main>

    <script src="./index.js"></script>
  </body>
</html>

Ln , Col HTML3.0 KBUTF-8

Starting sandbox…

No console output yet.

No original version of /index.html to compare against.

Best Practices

  • Start with fallbacks — Use physical as fallbacks, then layer logical — don't mix both unintentionally
  • Test different flows — Validate layouts in both horizontal-tb and vertical-rl writing modes to catch axis-mapping issues.
  • Prefer explicit properties — Use individual logical properties (margin-inline-start) instead of shorthands for broader browser support.
  • Handle RTL layouts — Apply direction: rtl for languages like Arabic and Hebrew to switch inline flow automatically.
  • Control writing flow — Use writing-mode for vertical text layouts, keeping in mind that block and inline axes swap roles.
  • Use alignment shortcuts — Rely on text-align: start / end to adapt automatically in LTR and RTL contexts.
  • Adopt logical positioning — Replace physical offsets (top, right, bottom, left) with inset-block / inset-inline for flexibility.
  • Check browser support — Always confirm on MDN's compatibility tables before using less common logical properties.

Fallbacks for Older Browsers

If you need to support older browsers that don't recognize logical properties, you can define physical property fallbacks first, followed by your logical properties. This ensures that browsers without logical property support (e.g., Internet Explorer) still display layouts correctly.

Without Logical Properties (LTR & RTL handled separately):

/* LTR */
.card {
  margin-left: 1rem;
  padding-left: 1rem;
  text-align: left;
}

/* RTL */
html[dir="rtl"] .card {
  margin-right: 1rem;
  padding-right: 1rem;
  text-align: right;
}

With Logical Properties (Single stylesheet):

.card {
  margin-inline-start: 1rem;
  padding-inline-start: 1rem;
  text-align: start;
}

Hybrid Approach (Fallback + Logical):

.card {
  /* Physical property fallbacks */
  margin-left: 1rem;
  padding-left: 1rem;
  text-align: left;

  /* Logical properties override */
  margin-inline-start: 1rem;
  padding-inline-start: 1rem;
  text-align: start;
}

@supports-Based Approach (Clean separation)

/* Physical property fallbacks */
.card {
  margin-left: 1rem;
  padding-left: 1rem;
  text-align: left;
}

/* Apply logical properties only if supported */
@supports (margin-inline-start: 1rem) {
  .card {
    margin-inline-start: 1rem;
    padding-inline-start: 1rem;
    text-align: start;
  }
}

👉 The interactive playground above renders this exact margin / padding / text-align set side by side — Physical vs. Logical — so you can see live what these fallbacks are working around.

Conclusion

CSS Logical Properties aren't just about cleaner code — they represent the future of responsive, internationalized web design. By thinking in terms of flow-relative directions instead of fixed sides, you create layouts that are naturally direction-aware, resilient, and easier to maintain across languages and scripts.

Start small: replace margin-left with margin-inline-start, or width with inline-size. Over time, your CSS will become not only simpler, but also future-proof and truly international-ready.

This one leaned harder on visuals than any post before it — four custom SVG diagrams built to keep the inline and block axes crossing the right way in every writing mode, plus the direction/writing-mode toggle above to prove it live. Chasing all sixty-some properties across sixteen categories down to their physical equivalents turned up a few surprises along the way — a couple of values that looked valid but aren't implemented anywhere, one that isn't even real syntax. I hope watching those arrows and toggles do their thing builds more intuition than the mapping tables alone ever could. Thanks for tracing every axis with me — go replace a `margin-left` with something that actually adapts. 🧭