تاروپود — خودآموز HTML و CSS

فصل ۱۰ از ۱۱

پیشرفت ترم
۰٪

ترم ۳ · Layout: چیدمان

logical properties

فصل ۱۰پیش‌نمایش رایگان
۱۰ دقیقه مطالعه فصل ۱۰

در این فصل چه می‌سازی#

تا اینجا هر وقت فاصله یا جای چیزی را تعیین کردی، از کلمه‌های left، right، top و bottom استفاده کردی — چپ، راست، بالا، پایین. این کلمه‌ها به جهتِ فیزیکیِ صفحه بسته‌اند. ولی صفحهٔ ما فارسی و راست‌به‌چپ است، و همین‌جا یک مشکلِ ظریف کمین کرده: کدی که با left و right نوشته شود، وقتی جهتِ صفحه عوض شود از هم می‌پاشد. این فصل ابزاری را یاد می‌دهد که این مشکل را از ریشه حل می‌کند — property هایی که به‌جای «چپ و راست»، به «شروع و پایانِ» خط فکر می‌کنند و خودشان با جهتِ صفحه هماهنگ می‌شوند.

مقایسهٔ یک کارت در دو حالت: در حالتِ راست‌به‌چپ آیکون و تورفتگی سمتِ راست‌اند، و در حالتِ چپ‌به‌راست همان‌ها خودبه‌خود به سمتِ چپ رفته‌اند، بدونِ تغییرِ کد

آخر این فصل می‌توانی:

  • فرقِ property فیزیکی (left/right) و logical (inline-start/inline-end) را توضیح بدهی
  • با margin-inline و padding-block فاصله را جهت‌آگاه بدهی
  • بگویی چرا logical properties برای فارسی و RTL حیاتی‌اند
  • با inset جای یک عنصرِ positioned را جهت‌آگاه تعیین کنی

قبل از شروع#

  • از فصل ۷ (ترم ۲): direction و اینکه rtl یعنی صفحه از راست چیده می‌شود.
  • از فصل ۸ (ترم ۲): وسط‌چینِ افقی با margin: 0 auto.
  • از فصل ۸ این ترم: position و تعیینِ جای یک عنصر با top/right.

فقط یک مرورگر لازم داری، یا همین آزمایشگاه.

۱. دو محور: block و inline#

CSS صفحه را با دو محور می‌شناسد، نه با «چپ و راست و بالا و پایین»:

  • محورِ block: جهتی که بلوک‌ها پشتِ‌هم می‌چینند. در فارسی و انگلیسی این از بالا به پایین است — هر پاراگرافِ تازه می‌رود زیرِ قبلی.
  • محورِ inline: جهتی که متن در یک خط پیش می‌رود. در فارسی از راست به چپ، در انگلیسی از چپ به راست.

نکتهٔ کلیدی اینجاست: محورِ block همیشه بالا-پایین است و عوض نمی‌شود، ولی محورِ inline به directionِ صفحه بسته است. برای همین به‌جای «چپ» و «راست»، CSS از start و end حرف می‌زند: inline-start یعنی «جایی که خط از آن شروع می‌شود». در صفحهٔ راست‌به‌چپ، این سمتِ راست است؛ در صفحهٔ چپ‌به‌راست، سمتِ چپ. تو یک بار می‌گویی «شروع»، و مرورگر بسته به جهتِ صفحه خودش راست یا چپ را می‌فهمد.

اسمِ فیزیکی اسمِ logical در صفحهٔ rtl کجاست
top block-start بالا
bottom block-end پایین
right inline-start راست
left inline-end چپ

قبل از ادامه، جواب بده: در یک صفحهٔ چپ‌به‌راست (انگلیسی)، inline-start کدام سمت است؟


جواب: چپ. چون خط از چپ شروع می‌شود. همین inline-start در صفحهٔ فارسی سمتِ راست بود — و این دقیقاً همان قدرتی است که این فصل به تو می‌دهد: یک کلمه که با جهت هماهنگ می‌شود.

۲. چرا این مهم است: یک خطای واقعی#

بیا مشکل را با چشمِ خودمان ببینیم. در آزمایشگاه یک نظر داریم و زیرش یک پاسخِ تودرتو (.reply) که باید از سمتِ شروعِ خط کمی تورفتگی داشته باشد تا معلوم شود جوابِ آن نظر است. یک برنامه‌نویس که در ذهنش انگلیسی فکر می‌کند، طبیعتاً می‌نویسد «از چپ فاصله بگیر»:

.reply {
  margin-left: 32px;
}

اگر صفحه چپ‌به‌راست بود، این عالی به‌نظر می‌رسید: پاسخ از سمتِ چپ — که همان شروعِ خط است — تو می‌رفت. ولی صفحهٔ ما راست‌به‌چپ است. نگاه کن: تورفتگی افتاده سمتِ چپ، یعنی سمتِ پایانِ خط، و پاسخ به لبهٔ راست چسبیده. کاملاً برعکسِ چیزی که می‌خواستیم. کد «درست به‌نظر می‌رسد» ولی در این صفحه غلط است.

🔧 اگر کار نکرد: تورفتگی سمتِ اشتباه افتاد؟ چون margin-left همیشه به سمتِ فیزیکیِ چپ فاصله می‌دهد، بی‌اعتنا به جهتِ صفحه. چیزی که می‌خواهی «فاصله از سمتِ شروع» است، نه «فاصله از چپ». جوابش property ِ logical است: margin-inline-start. این یعنی «از سمتِ شروعِ خط فاصله بگیر» و خودش می‌داند در صفحهٔ راست‌به‌چپ، شروع یعنی راست.

margin-left را با margin-inline-start عوض کن:

.reply {
  margin-inline-start: 32px;
}

چک کن: حالا تورفتگی افتاد سمتِ راست — سمتِ شروعِ خط — و پاسخ درست زیرِ نظر و کمی تورفته دیده می‌شود. حالا آزمونِ واقعی: در index.html روی تگِ <html>، مقدارِ dir="rtl" را موقتاً به dir="ltr" عوض کن و به صفحه نگاه کن. کلِ صفحه چپ‌به‌راست می‌شود و تورفتگیِ پاسخ خودبه‌خود می‌رود سمتِ چپ — بدونِ اینکه یک حرف از CSS را عوض کنی. اگر با margin-left نوشته بودی، حالا در حالتِ ltr درست می‌شد ولی در rtl خراب می‌ماند. با margin-inline-start، هر دو حالت درست است. جهت را به rtl برگردان.

این کلِ حرفِ فصل است: یک کد بنویس که در هر دو جهت درست کار کند. برای سایتی که فارسی است — یا ممکن است روزی چند‌زبانه شود — این نه یک تجمل، که یک ضرورت است.

۳. جفتِ کامل: block و inline با هم#

margin-inline-start فقط یک طرف را می‌گیرد. اگر هر دو طرفِ محورِ inline را با هم بخواهی، margin-inline هست؛ و برای بالا و پایین (محورِ block)، margin-block. همین برای padding هم هست: padding-inline و padding-block. به بدنه و کارت‌هایمان لایی و فاصلهٔ جهت‌آگاه می‌دهیم:

body {
  padding-block: 24px;
  padding-inline: 16px;
}

.comment {
  padding-block: 12px;
  padding-inline: 16px;
  margin-block-end: 20px;
}

padding-block: 24px یعنی بالا و پایین هر دو 24px، و padding-inline: 16px یعنی دو طرفِ خط هر دو 16px. یک خط، دو طرف. و margin-block-end یعنی فاصله از سمتِ پایین (پایانِ محورِ block) — همان margin-bottomِ آشنا، ولی با نامِ logical.

حالا یک هدیهٔ قشنگ: یادت هست وسط‌چینِ افقی را با margin: 0 auto می‌کردیم؟ معادلِ logical و خواناترش margin-inline: auto است — «دو طرفِ خط را auto کن» — که همان کارِ وسط‌چینی را می‌کند. به کانتینرِ صفحه‌مان می‌دهیم:

.page {
  max-width: 520px;
  margin-inline: auto;
}

چک کن: کلِ محتوا در یک ستونِ باریکِ وسطِ صفحه جمع شد، دقیقاً مثلِ margin: 0 auto. فرقش این است که margin-inline: auto فقط دو طرفِ خط را دست می‌زند و به بالا و پایین کاری ندارد — پس اگر margin-block جدا داده باشی، خرابش نمی‌کند.

💡 نکته: یک راهِ ساده برای یادسپاری: block = بالا/پایین، inline = دو طرفِ خط. هر جا در کدِ قدیمی top/bottom دیدی، معادلِ logical اش با block است؛ هر جا left/right دیدی، با inline. همین دو کلمه کلِ دستگاه را باز می‌کند.

۴. inset: جای عنصرِ positioned، جهت‌آگاه#

در فصل ۸ یک عنصرِ absolute را با top و right سرِ جایش گذاشتی. این‌ها هم فیزیکی‌اند و همان مشکل را دارند. معادلِ logical شان هم هست: inset-block-start (به‌جای topinset-inline-start (به‌جای right در rtl)، و همین‌طور -end ها. یک کلمهٔ کوتاه‌تر هم هست: inset، که میان‌بُری برای تعیینِ هر چهار طرفِ یک عنصرِ positioned است.

در آزمایشگاه یک جعبه داریم با یک corner (نشانِ گوشه) که باید همیشه به گوشهٔ شروعِ بالا بچسبد. با logical می‌نویسیمش:

.tag-box {
  position: relative;
}

.corner {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
}

چک کن: نشانِ قرمز به گوشهٔ بالا-راستِ جعبه چسبید — چون در صفحهٔ راست‌به‌چپ، inline-start یعنی راست و block-start یعنی بالا. حالا باز جهتِ صفحه را به ltr عوض کن: نشان خودش می‌رود گوشهٔ بالا-چپ. با top و right این اتفاق نمی‌افتاد و مجبور بودی برای هر جهت جدا کد بنویسی.

💡 نکته: خودِ inset به‌تنهایی میان‌بُرِ چهار طرف است. مثلاً inset: 0 یعنی «به هر چهار طرف بچسب» و عنصر را کاملاً روی والدش کش می‌دهد — یک راهِ سریع برای پوشاندنِ کاملِ یک ناحیه. امتحانش کن: به .corner به‌جای آن دو خط، فقط inset: 0 بده و ببین به‌جای یک نقطهٔ گوشه، کلِ جعبه را می‌پوشاند.

کد کاملِ این فصل#

این هم دو فایلِ کامل. اول index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <title>logical properties</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="page">
      <article class="comment">
        <p>یک نظرِ ساده روی این مطلب.</p>

        <article class="reply">
          <p>پاسخِ تودرتو که باید از سمتِ شروع تورفتگی داشته باشد.</p>
        </article>
      </article>

      <section class="tag-box">
        <span class="corner"></span>
        <p>این جعبه یک نشانِ گوشه دارد که همیشه به گوشهٔ شروعِ بالا می‌چسبد.</p>
      </section>
    </main>
  </body>
</html>

و styles.css — همه‌چیز با logical properties، پس در هر دو جهت درست است:

/* styles.css */
* {
  box-sizing: border-box;
}

body {
  font-family: Tahoma, sans-serif;
  background-color: #f6edda;
  color: #2c2114;
  padding-block: 24px;
  padding-inline: 16px;
}

.page {
  max-width: 520px;
  margin-inline: auto;
}

.comment {
  padding-block: 12px;
  padding-inline: 16px;
  margin-block-end: 20px;
  background-color: #ffffff;
  border: 1px solid #2c2114;
}

.reply {
  margin-block-start: 12px;
  margin-inline-start: 32px;
  padding-block: 10px;
  padding-inline: 14px;
  background-color: #cfe3f3;
}

.tag-box {
  position: relative;
  padding-block: 16px;
  padding-inline: 20px;
  background-color: #ffffff;
  border: 1px solid #2c2114;
}

.corner {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: 16px;
  height: 16px;
  background-color: #bc3e1c;
}

یک صفحه با دو محور مشخص‌شده: یک فلش عمودی از بالا به پایین برای محورِ block و یک فلش افقی در جهتِ متن برای محورِ inline که در حالتِ راست‌به‌چپ از راست شروع می‌شود

🤖 با AI: یک تکه CSS که با left/right/margin-left نوشته‌ای را به دستیار AI ات بده و بپرس «کدام‌های این‌ها در یک صفحهٔ راست‌به‌چپ مشکل می‌سازند، و معادلِ logical شان چیست؟». این یک کاربردِ خوبِ AI است — بازبینیِ کدِ خودت. ولی هر معادلی که داد، در آزمایشگاه جهت را عوض کن و با چشمِ خودت چک کن که واقعاً با هر دو جهت درست می‌شود؛ AI گاهی inline-start و inline-end را جابه‌جا می‌گوید.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
logical property لاجیکال پراپِرتی property ای که به‌جای چپ/راستِ فیزیکی، به شروع/پایانِ جهتِ نوشتار فکر می‌کند
block axis بلاک اکسیس محورِ چیدنِ بلوک‌ها؛ در فارسی و انگلیسی از بالا به پایین
inline axis این‌لاین اکسیس محورِ پیش‌رفتنِ متن در یک خط؛ در فارسی راست‌به‌چپ
inline-start این‌لاین-اِستارت سمتی که خط از آن شروع می‌شود؛ در rtl راست، در ltr چپ
margin-inline مارجین-این‌لاین margin دو طرفِ خط با هم؛ margin-inline: auto یعنی وسط‌چین
inset اینسِت میان‌بُرِ تعیینِ جای یک عنصرِ positioned؛ inset: 0 یعنی چسبیدن به هر چهار طرف

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

الان ابزارِ چیدمانت کامل است: از فصل‌های اولِ این ترم flexbox و grid را داری، از فصل ۸ position، از فصل ۹ لایه‌بندی با z-index، و حالا logical properties که همهٔ این‌ها را برای فارسی امن می‌کند. فصل بعد، آخرین فصلِ این ترم، مفهومِ تازه‌ای ندارد — همه‌شان را کنارِ هم می‌گذاریم و layout کاملِ یک سایتِ واقعی را از صفر می‌سازیم: یک سرصفحه با منو، یک بدنهٔ گرید با ستونِ کناری، یک عنصرِ چسبان، و یک پاصفحه. هرچه این ترم یاد گرفتی، آنجا در یک صفحهٔ واقعی جمع می‌شود.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.