استخدم ملف CSS واحدًا لصفحات LTR وRTL عبر تعيين اتجاه HTML صراحةً، واستخدام الخصائص المنطقية للتباعد الاتجاهي، واستخدام gap بين عناصر التخطيط. ينشئ هذا الدليل مثالًا محليًا من صفحتين يمكنك مقارنتهما في متصفح على جهاز Mac.

المطلوب هو جعل المكوّن الصغير نفسه يحافظ على التباعد المقصود عند تغيير اتجاهه المضمّن. تحتاج إلى محرر نصوص عادي ومتصفح حديث لسطح المكتب. يستخدم المثال تسميات اصطناعية ودائرة زخرفية، من دون إطار عمل أو أصول خارجية أو JavaScript.

ما الذي ينبغي أن تتحكم فيه ورقة الأنماط المشتركة؟

اجعل HTML مسؤولًا عن الاتجاه، وورقة الأنماط مسؤولة عن التباعد. تحمّل كلتا الصفحتين مورد CSS نفسه وتستخدمان فئات المكوّنات نفسها؛ ويحدد جذر الصفحة إما dir="ltr" أو dir="rtl". يعرّف HTML rel="stylesheet" على أنه رابط مورد خارجي يساهم في تنسيق المستند. معيار HTML: روابط أوراق الأنماط.

استخدم HTML dir عندما يكون الاتجاه معروفًا. ترث العناصر المتفرعة الاتجاه عمومًا، أما lang فلا يحدده. فضّل الاتجاه الصريح على dir="auto" في هذه المقارنة المضبوطة: إذ يعتمد الاتجاه التلقائي على المحتوى، ما يجعل التسميات اللاتينية في العينة تختار LTR. MDN: السمة dir.

التمييز المفيد هنا هو بين حافة مادية وموضع في تدفق المحتوى. في نمط الكتابة horizontal-tb المستخدم في هذا المثال، تعني بداية الاتجاه المضمّن اليسار في LTR واليمين في RTL. أما بداية الاتجاه الكتلي فتبقى في الأعلى. تعبّر الخصائص المنطقية عن هذه العلاقات مباشرةً. MDN: الهوامش والحدود والحشوات المنطقية.

التباعد المقصود خيار CSS المشترك النتيجة عند تغيير الاتجاه
مساحة داخل الحافة المتقدمة للمكوّن padding-inline-start تنتقل إلى الجانب المادي الآخر
مساحة داخل كلتا الحافتين المضمّنتين padding-inline تُطبّق قيمتا البداية والنهاية في الاتجاه الجديد
مساحة بين عناصر Flexbox gap تحافظ على الفجوة المحددة بين العناصر
مساحة أعلى وأسفل هذا التخطيط الأفقي padding-block تبقى أعلى وأسفل
هامش قبل عنصر في التدفق المضمّن margin-inline-start يُطابق جانب بداية الاتجاه المضمّن الجديد

تتبع مطابقة الهوامش والحشوات نمط الكتابة والاتجاه الخاصين بالعنصر. أما gap فينشئ فواصل في حاويات التخطيط المدعومة. لكل منهما وظيفة مستقلة: استخدم الحشوة حول المحتوى، والفجوة بين العناصر الشقيقة. MDN: التباعد المنطقي، MDN: gap.

أنشئ صفحتين محليتين تستخدمان ملف CSS واحدًا

أنشئ مجلدًا باسم spacing-demo على جهاز Mac. احفظ الملفات التالية كنص عادي، مستخدمًا الامتدادات المحددة تمامًا. تجنب المحررات التي تضيف .txt بعد .html أو .css.

spacing-demo/
  ltr.html
  rtl.html
  components.css

احفظ مستند HTML الكامل التالي باسم ltr.html. ثم احفظ نسخة ثانية باسم rtl.html، مع تغيير سمة الجذر فقط من dir="ltr" إلى dir="rtl". أبقِ مرجع ورقة الأنماط وترميز المكوّن متطابقين. تظل التسميات بالإنجليزية لأن هذه مقارنة تخطيط اصطناعية.

<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Component spacing sample</title>
  <link rel="stylesheet" href="components.css">
</head>
<body>
  <main class="demo">
    <h1>Spacing sample</h1>
    <article class="sample-card">
      <span class="sample-card__shape" aria-hidden="true"></span>
      <div class="sample-card__copy">
        <h2 class="sample-card__title">Module A</h2>
        <p class="sample-card__description">A compact sample tile</p>
      </div>
      <span class="sample-card__tag">Set B</span>
    </article>
  </main>
</body>
</html>

والآن احفظ ورقة الأنماط الكاملة التالية باسم components.css. تجعل الحشوة المضمّنة غير المتساوية عمدًا تغييرات الاتجاه سهلة الملاحظة: ينبغي أن تتبع الإزاحة الأكبر بداية الاتجاه المضمّن.

:root {
  --space-small: 8px;
  --space-medium: 16px;
  --space-large: 24px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  color: #243447;
  background: #f2f5f8;
}

.demo {
  writing-mode: horizontal-tb;
  max-width: 640px;
  margin-inline: auto;
}

.sample-card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-medium);
  padding-block: var(--space-medium);
  padding-inline: var(--space-large) var(--space-small);
  border: 1px solid #8594a5;
  border-radius: 12px;
  background: #ffffff;
}

.sample-card__shape {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #5477a3;
}

.sample-card__copy {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.sample-card__title,
.sample-card__description {
  margin: 0;
}

.sample-card__title {
  font-size: 18px;
}

.sample-card__description {
  margin-block-start: var(--space-small);
}

.sample-card__tag {
  flex: 0 0 auto;
  padding-block: 4px;
  padding-inline: var(--space-small);
  border: 1px solid #8594a5;
  border-radius: 6px;
}

هنا، تعني padding-inline: 24px 8px حشوة البداية تليها حشوة النهاية. ولا تعني اليسار تليه اليمين. تطبّق القيمة الواحدة على الحافتين المضمّنتين معًا. MDN: padding-inline.

أبقِ flex-direction: row في كلتا الصفحتين. يتبع صف Flexbox اتجاه الحاوية بالفعل: من اليسار إلى اليمين في LTR، ومن اليمين إلى اليسار في RTL. إن إضافة row-reverse إلى نسخة RTL ستعكس هذا الاتجاه مرة أخرى. MDN: flex-direction.

لا تحمل الدائرة المحايدة أي معنى اتجاهي، لذلك يظل رسمها كما هو بينما يتبع موضعها اتجاه الصف. وتحافظ التسميات اللاتينية أيضًا على ترتيب أحرفها؛ فهذا المثال يفحص تخطيط المكوّن لا النص المترجم.

كيف تتحقق من أن التباعد مشترك فعلًا؟

افتح ملفي HTML في متصفح جهاز Mac وقارنهما عند عرض النافذة نفسه. القيم التالية متوقعة بناءً على التصريحات، وليست قياسات أبلغ عنها المتصفح. حد البطاقة منفصل عن حشوتها.

الفحص صفحة LTR صفحة RTL
موضع الدائرة العنصر الأول على اليسار العنصر الأول على اليمين
الحشوة اليسرى للبطاقة 24px 8px
الحشوة اليمنى للبطاقة 8px 24px
الفجوة بين عنصري Flex متجاورين 16px 16px
الحشوة أعلى البطاقة وأسفلها 16px 16px
المساحة قبل الوصف 8px أعلاه 8px أعلاه

اختبر مشاركة المورد بمعزل عن الاتجاه. غيّر --space-medium في components.css من 16px إلى 20px، واحفظ الملف، ثم أعد تحميل الصفحتين. ينبغي أن تزيد الفجوة بين العناصر والحشوة الكتلية في كلتيهما لأن هذه التصريحات تشير إلى المتغير نفسه. أعد القيمة إلى 16px بعد المقارنة.

ثم ضيّق نافذة المتصفح واستبدل الوصف بتسمية اصطناعية أطول، مثل Module A extended sample description. تحقق من التفاف النص من دون أن يغطي الدائرة أو الوسم. يضيف هذا اختبارًا ثانيًا مفيدًا: قد ينعكس المكوّن بشكل صحيح عند عرض واحد، ثم يصبح مزدحمًا عند عرض آخر.

لا تحكم على كل مسافة ظاهرة بالاعتماد على gap وحده. فقد تجعل هوامش العناصر والحشوة الداخلية والمحاذاة الموزعة المسافة المرئية أكبر من الفاصل المعلن. تعيد العينة ضبط هوامش النص وتتجنب المحاذاة الموزعة لتظل المقارنة واضحة. MDN: وصف gap.

كيف تنقل مكوّنًا موجودًا من دون تعارضات في التباعد؟

ابدأ بفهم معنى كل تصريح موجود. عادةً ما تنتمي إزاحة النص من جهة التقدم إلى بداية الاتجاه المضمّن؛ وقد تظل الخاصية المادية مناسبة لحافة مادية ثابتة عمدًا. حوّل وفقًا للمقصد التصميمي بدلًا من استبدال كل ظهور لـ left آليًا.

لمكوّن مثل هذه العينة، اتبع التسلسل التالي:

  1. حدّد العنصر السلف الذي يحدد الاتجاه.
  2. استبدل الحشوة الداخلية الاتجاهية بحشوة منطقية.
  3. استبدل الهوامش الخارجية الاتجاهية بهوامش منطقية عند الحاجة.
  4. استخدم gap الخاص بالعنصر الأب للمسافات المعتادة بين عناصر Flex، وأزل الهوامش التي كانت توفر المساحة نفسها.
  5. كرر مقارنات الحشوة غير المتساوية والنافذة الضيقة في كلا الاتجاهين.

تحقق أثناء النقل من وجود تصريحات مادية قديمة واختصارات لاحقة. تشارك الخصائص المنطقية والمادية التي تطابق الجانب نفسه في التتالي؛ ولا تتغلب الخاصية المنطقية تلقائيًا لمجرد أنها منطقية. يمكن لتصريح padding لاحق أن يعيد ضبط الحشوة التي عُيّنت سابقًا عبر خصائص منطقية إذا تساوت عوامل التتالي الأخرى. خصائص CSS وقيمها المنطقية، المستوى 1: خصائص نموذج الصندوق.

ضع أيضًا حدود الاتجاه في الحسبان. ينبغي لمكوّن متداخل له dir="ltr" خاص به استخدام مطابقات LTR حتى داخل صفحة RTL. وعلى العكس، فإن margin-inline نفسه لا يُورث، لذا عيّن لكل مكوّن تصريحات التباعد التي يحتاج إليها بدلًا من توقع انتقال هامش أحد الأسلاف إليه. MDN: توريث الاتجاه، MDN: التعريف الرسمي لـ margin-inline.

ما قيود المتصفحات التي تؤثر في هذا النهج؟

تشير MDN إلى أن padding-inline وmargin-inline متاحتان على نطاق واسع عبر المتصفحات منذ أبريل 2021. يدعم ذلك استخدامهما في متصفحات سطح المكتب الحالية، لكنه لا يضمن عملهما في كل متصفح قديم أو مع كل صياغة أحدث معروضة في صفحات المراجع تلك. يستخدم هذا المثال أطوالًا عادية وauto، من دون دوال التحجيم الأحدث. MDN: padding-inline، MDN: margin-inline.

لدعم فجوة Flexbox سجل مستقل. أضافها Safari في الإصدار 14.1؛ بينما كان دعم فجوة Grid متاحًا منذ Safari 12. وتشير WebKit تحديدًا إلى أن استعلامًا عن ميزة خاصية لم يكن قادرًا على التمييز بين هذين السياقين، لذا فإن @supports (gap: 16px) وحده لا يكفي لاكتشاف دعم فجوة Flexbox في إصدارات Safari القديمة. WebKit: ميزات WebKit الجديدة في Safari 14.1.

إذا كان متصفحك المستهدف يدعم الهوامش المنطقية لكنه يفتقر إلى فجوة Flexbox، فألحِق مقطع الاستبدال هذا بعد قواعد ورقة الأنماط الرئيسية. فهو يوفر تباعدًا لهذا المكوّن ذي الصف الواحد تحديدًا. استخدمه بدلًا من تباعد الفجوة النشط كما هو موضح؛ فهو ليس حلًا عامًا للصفوف الملتفة.

/* Optional single-row replacement for Flexbox gap. */
.sample-card {
  gap: 0;
}

.sample-card > * + * {
  margin-inline-start: var(--space-medium);
}

أما المتصفحات التي تفتقر إلى الخصائص المنطقية أيضًا، فهذا البديل غير كافٍ لها. استخدم متصفحًا حديثًا للمهمة المحلية، أو اكتب واختبر قواعد مادية صريحة لاتجاهي LTR وRTL للمتصفح القديم المستهدف. أبقِ هذا القرار الخاص بالمتصفح منفصلًا عن التصميم الأساسي لورقة الأنماط المشتركة.

ينبغي أن تُظهر المقارنة المكتملة عقدًا متسقًا واحدًا: يوفر HTML الاتجاه، وتوفر CSS المشتركة التباعد، وتتبع الإزاحة المتقدمة للمكوّن بداية الاتجاه المضمّن. احتفظ بالصفحتين المحليتين كاختبار انحدار موجز كلما غيّرت تباعد المكوّن أو تخطيطه.

المصادر