ข้ามไปเนื้อหาหลัก

Category: reference

CSS Logical Properties — Writing Mode Aware Layout

margin-inline, padding-block, inset-inline-start — เขียน CSS ที่รองรับ RTL และ vertical text โดยไม่ต้องเขียน rule ซ้ำ

· อ่านประมาณ 3 นาที

สารบัญ

ทำไมต้องใช้ Logical Properties

CSS เดิมใช้ physical directions: left, right, top, bottom
ซึ่งไม่ทำงานถูกต้องกับ RTL languages (Arabic, Hebrew) หรือ vertical writing modes (Japanese)

/* ❌ Physical — พัง RTL */
.card { margin-left: 1rem; padding-left: 1.5rem; }

/* ✓ Logical — ทำงานถูกต้องทุก writing mode */
.card { margin-inline-start: 1rem; padding-inline-start: 1.5rem; }

Mapping พื้นฐาน

Physical (เดิม)Logical (ใหม่)
margin-topmargin-block-start
margin-bottommargin-block-end
margin-leftmargin-inline-start
margin-rightmargin-inline-end
margin-left + margin-rightmargin-inline
margin-top + margin-bottommargin-block
leftinset-inline-start
rightinset-inline-end
topinset-block-start
widthinline-size
heightblock-size
min-widthmin-inline-size
max-heightmax-block-size

Inline vs Block Axis

LTR / RTL textVertical text
inline axisแนวนอน (←→)แนวตั้ง (↕)
block axisแนวตั้ง (↕)แนวนอน (←→)
  • inline-start = ซ้ายใน LTR, ขวาใน RTL, บนใน vertical
  • inline-end = ขวาใน LTR, ซ้ายใน RTL, ล่างใน vertical
  • block-start = บนใน horizontal, ซ้ายใน vertical LTR

Shorthand Syntax

/* Shorthand ใหม่ */
margin-inline: 1rem;            /* margin-left + margin-right */
margin-block: 0.5rem 1rem;     /* margin-top 0.5rem, margin-bottom 1rem */

padding-inline: 1.5rem;
padding-block: 1rem 1.5rem;

/* inset shorthand (แทน top/right/bottom/left) */
inset: 0;                  /* ทุกด้าน */
inset-block: 0;            /* top + bottom */
inset-inline: 0 1rem;     /* left 0, right 1rem */

Size Properties

/* ❌ Physical */
.button {
  width: 100%;
  min-height: 44px;
}

/* ✓ Logical */
.button {
  inline-size: 100%;
  min-block-size: 44px;
}

Border Radius

/* Physical corners */
border-top-left-radius: 8px;
border-top-right-radius: 8px;

/* Logical corners */
border-start-start-radius: 8px;   /* top-left in LTR */
border-start-end-radius: 8px;     /* top-right in LTR */
border-end-start-radius: 8px;     /* bottom-left in LTR */
border-end-end-radius: 8px;       /* bottom-right in LTR */

Float และ Text Align

/* ❌ Physical */
.pull-quote { float: left; text-align: left; }

/* ✓ Logical */
.pull-quote { float: inline-start; text-align: start; }

ตัวอย่างจริง: Card Component

/* ❌ Physical — ต้องเขียนซ้ำสำหรับ RTL */
.card {
  padding: 1.5rem;
  border-left: 4px solid #2563eb;
  border-radius: 8px 0 0 8px;
}

/* RTL override */
[dir='rtl'] .card {
  border-left: none;
  border-right: 4px solid #2563eb;
  border-radius: 0 8px 8px 0;
}
/* ✓ Logical — ทำงานถูกต้องทั้ง LTR และ RTL โดยไม่ต้อง override */
.card {
  padding: 1.5rem;
  border-inline-start: 4px solid #2563eb;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  border-start-end-radius: 8px;
  border-end-end-radius: 8px;
}

ตัวอย่างจริง: Positioned Element

/* ❌ Physical */
.tooltip {
  position: absolute;
  top: 100%;
  left: 0;
}

/* ✓ Logical */
.tooltip {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
}

เมื่อไหรใช้ Logical vs Physical

ใช้ Logical เมื่อ:

  • Layout ที่ต้องรองรับ RTL หรือหลายภาษา
  • Spacing ที่เกี่ยวกับ flow direction (margin ข้างๆ text)
  • Component library หรือ design system

ยังใช้ Physical ได้เมื่อ:

  • Visual decoration ที่ไม่เกี่ยวกับ text flow
  • เช่น: border-top: 1px solid (เส้นด้านบนเสมอ ไม่ว่า writing mode ไหน)
  • Shadow, gradient direction ที่ specific

Browser Support

รองรับ Chrome 87+, Firefox 66+, Safari 14.1+ ทั้งหมดรองรับแล้วใน 2024+

Shorthand เช่น margin-inline รองรับ Chrome 87+, Firefox 66+, Safari 14.1+

/* Fallback pattern ถ้าต้องรองรับ browser เก่ามาก */
.element {
  margin-left: 1rem;                 /* fallback */
  margin-inline-start: 1rem;        /* override สำหรับ browser ใหม่ */
}

Quick Reference

/* Spacing */
margin-inline: auto;                    /* center ใน flow */
padding-inline: 1rem;                   /* left+right padding */
padding-block: 0.5rem 1rem;            /* top+bottom padding */

/* Positioning */
inset-inline-start: 0;                 /* left ใน LTR */
inset-block-start: 50%;               /* top */

/* Sizing */
inline-size: 100%;                     /* width */
block-size: 200px;                     /* height */
min-inline-size: min-content;

/* Border */
border-inline-start: 2px solid;       /* left border ใน LTR */
border-block-end: 1px solid;          /* bottom border */

/* Text */
text-align: start;                     /* left ใน LTR */
text-align: end;                       /* right ใน LTR */