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

فصل ۷ از ۱۱

پیشرفت ترم
۰٪

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

layout کامل صفحه با grid

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

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

آخر این فصل یک صفحهٔ کامل داری — سرصفحه، منو، محتوای اصلی، یک ستونِ کناری و پاصفحه — که همه با grid سرِ جای درستشان نشسته‌اند. و داخلِ محتوای اصلی، یک گالریِ کارت که خودش با کم‌وزیادشدنِ عرضِ صفحه، تعدادِ ستون‌هایش را عوض می‌کند، بدون اینکه تو حتی یک عدد را دستی تغییر بدهی. این همان کاری است که هر سایتِ واقعی می‌کند.

طرح یک صفحهٔ کامل با ناحیه‌های نام‌دار: نوارِ سرصفحه و منو در بالا، یک ستونِ کناریِ باریک در سمت راست، محتوای اصلیِ پهن در چپ، و پاصفحه در پایین

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

  • با repeat() ستون‌های تکراری را کوتاه و تمیز بنویسی
  • با minmax() برای هر ستون یک کف و یک سقفِ اندازه تعیین کنی
  • با auto-fit یک گالری بسازی که خودش تعدادِ ستون‌ها را با عرضِ صفحه تنظیم کند
  • با grid-template-areas نقشهٔ کلِ یک صفحه را با اسم بکشی

قبل از شروع#

  • از فصل ۵ و ۶ (همین ترم): display: grid، grid-template-columns/rows، fr و gap — همه‌شان اینجا کنارِ هم به کار می‌روند.
  • از ترم ۱: <header>، <nav>، <main>، <aside> و <footer> — ناحیه‌های معناداری که این‌بار با grid می‌چینیم‌شان.

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

۱. repeat(): تکرار بدون تکرار#

اول محتوای اصلیِ صفحه را می‌سازیم: یک گالریِ کارت داخلِ <main>. فرض کن سه ستونِ برابر می‌خواهی. نوشتنِ 1fr 1fr 1fr که بد نیست، ولی برای شش ستون می‌شود 1fr 1fr 1fr 1fr 1fr 1fr — خسته‌کننده و خطاخیز. grid یک کوتاه‌نویسی دارد: repeat(). عددِ اول می‌گوید چند بار، دومی می‌گوید چه چیز را تکرار کن. اول HTML:

<main class="content">
  <article class="card">پست ۱</article>
  <article class="card">پست ۲</article>
  <article class="card">پست ۳</article>
  <article class="card">پست ۴</article>
</main>

و سه ستونِ برابر، این‌بار با repeat:

.content {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.card {
  background-color: #bc3e1c;
  color: #ffffff;
  padding: 16px;
  border-radius: 8px;
  text-align: center;
}

repeat(3, 1fr) دقیقاً یعنی 1fr 1fr 1fr — سه ستونِ یک‌سهمی. اگر شش ستون می‌خواستی، فقط عددِ ۳ را ۶ می‌کردی. یک جا عوض می‌کنی، نه شش جا.

چک کن: چهار کارتِ قرمز را می‌بینی که سه‌تا در سطرِ اول و یکی در سطرِ دوم چیده شده‌اند، در سه ستونِ برابر. عددِ ۳ را به ۲ و بعد ۴ عوض کن و ببین چطور کلِ گالری با یک تغییرِ کوچک بازچیده می‌شود.

۲. minmax(): نه خیلی باریک، نه خیلی پهن#

با 1fr ستون‌ها هر چه لازم شد کش می‌آیند و جمع می‌شوند — ولی هیچ حد و مرزی ندارند. روی صفحهٔ خیلی باریک، یک ستونِ 1fr می‌تواند آن‌قدر باریک شود که محتوا لهش کند. minmax() این را حل می‌کند: به هر ستون یک کمینه و یک بیشینه می‌دهی. شکلش minmax(کمینه, بیشینه) است:

.content {
  display: grid;
  grid-template-columns: repeat(3, minmax(120px, 1fr));
  gap: 12px;
}

minmax(120px, 1fr) یعنی «هر ستون دستِ‌کم ۱۲۰ پیکسل، و اگر جا بود تا یک سهمِ کامل کش بیا». یعنی ستون هیچ‌وقت از ۱۲۰ باریک‌تر نمی‌شود، ولی روی صفحهٔ پهن آزادانه بزرگ می‌شود.

چک کن: ظاهرِ گالری روی صفحهٔ پهن مثل قبل است. حالا عرضِ آزمایشگاه را کم کن تا خیلی باریک شود؛ می‌بینی ستون‌ها زیرِ ۱۲۰ پیکسل نمی‌روند و به‌جای له‌شدن، از لبه بیرون می‌زنند. این کف، محتوای تو را از خفه‌شدن نجات می‌دهد. در قدمِ بعد کاری می‌کنیم که به‌جای بیرون‌زدن، به سطرِ بعد بروند.

۳. auto-fit: بگذار خودش تعدادِ ستون‌ها را تصمیم بگیرد#

تا اینجا تعدادِ ستون‌ها را خودت گفتی (repeat(3, ...)). ولی چه می‌شد اگر می‌گفتی «هر چند ستونِ ۱۲۰پیکسلی که جا شد بگذار»؟ این کارِ auto-fit است. عددِ تکرار را با کلمهٔ auto-fit عوض کن:

.content {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}

حالا grid خودش حساب می‌کند: «روی این عرض، چند ستونِ دستِ‌کم‌۱۲۰پیکسلی جا می‌شود؟» — و همان‌قدر ستون می‌سازد؛ بقیه به سطرِ بعد می‌روند. minmax هم مطمئن می‌شود ستون‌ها فضای باقی‌مانده را پُر کنند و لاغر نمانند.

چک کن: عرضِ آزمایشگاه را آرام‌آرام باریک کن. می‌بینی گالری خودش از چهار ستون به سه، بعد دو، بعد یک ستون می‌رود — بی‌آنکه تو دست به کد بزنی. این اولین بارِ توست که یک چیدمانِ responsive می‌سازی: چیدمانی که با اندازهٔ صفحه ور می‌رود. ابزارِ کاملِ responsive بودن (@media) را ترم بعد یاد می‌گیری؛ ولی auto-fit یک تکهٔ رایگانش را همین حالا به تو داد.

💡 نکته: یک خواهرِ دوقلو هم هست: auto-fill. فرقش فقط وقتی دیده می‌شود که آیتم‌ها کم باشند و جا زیاد: auto-fit ستون‌های خالیِ اضافه را جمع می‌کند و آیتم‌ها را کش می‌آورد تا فضا را پُر کنند؛ auto-fill ستون‌های خالی را نگه می‌دارد و آیتم‌ها را باریک می‌گذارد. برای گالری‌ای که باید فضا را پُر کند، auto-fit معمولاً همانی است که می‌خواهی.

۴. نقشهٔ صفحه با grid-template-areas#

محتوای اصلی آماده است؛ حالا یک صفحهٔ کامل دورش می‌سازیم. یک صفحهٔ واقعی چند ناحیه دارد: سرصفحه، منو، محتوای اصلی، ستونِ کناری، پاصفحه. می‌شد هرکدام را با شمارهٔ خط جا گذاشت (فصل قبل)، ولی grid یک راهِ خیلی خواناتر دارد: نقشه را بکش. با grid-template-areas صفحه را مثل یک نقاشیِ متنی رسم می‌کنی و به هر ناحیه یک اسم می‌دهی.

اول HTML را با tag های معنادار بساز؛ همه مستقیم داخلِ <body> می‌روند، بدونِ هیچ جعبهٔ اضافه:

<header class="header">وبلاگ من</header>
<nav class="nav">خانه · درباره · تماس</nav>
<aside class="aside">لینک‌های مفید</aside>
<main class="content">محتوای اصلی صفحه</main>
<footer class="footer">ساختهٔ من — ۱۴۰۵</footer>

حالا خودِ body را grid می‌کنیم و نقشه را رویش می‌کشیم. هر خطِ داخلِ grid-template-areas یک سطر است، و هر کلمه یک خانه:

body {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
    "header header"
    "nav    nav"
    "aside  content"
    "footer footer";
  gap: 12px;
  min-height: 480px;
}

این نقشه را مثل یک نقاشی بخوان: سطرِ اول تماماً header، سطرِ دوم تماماً nav، سطرِ سوم دو خانه دارد — aside و content، و سطرِ آخر تماماً footer. تکرارِ یک اسم (مثل header header) یعنی آن ناحیه هر دو ستون را می‌گیرد.

یک نکتهٔ RTL که باید ببینی: چون صفحه راست‌به‌چپ است، اولین کلمهٔ هر سطر به ستونِ راست می‌خورد. پس در سطرِ "aside content"، ناحیهٔ aside سمتِ راست می‌نشیند و content سمتِ چپ — دقیقاً همان جایی که یک ستونِ کناریِ فارسی باید باشد. grid-template-columns: 200px 1fr هم یعنی ستونِ راست (aside) ۲۰۰ پیکسلِ ثابت، و ستونِ چپ (content) هرچه فضا ماند.

تا اینجا فقط نقشه را کشیدی؛ حالا باید به هر عنصر بگویی «تو کدام ناحیه‌ای». این کار با grid-area است:

.header  { grid-area: header; }
.nav     { grid-area: nav; }
.aside   { grid-area: aside; }
.content { grid-area: content; }
.footer  { grid-area: footer; }

چک کن: پنج ناحیه پریدند سرِ جای درستشان: سرصفحه و منو به‌صورت دو نوارِ کاملِ بالا، بعد ستونِ کناری در راست و محتوای اصلیِ پهن در چپ، و پاصفحه در پایین. یک صفحهٔ کامل، فقط با یک نقشهٔ متنی و پنج خطِ grid-area.

۵. یک حرفِ جاافتاده و یک سوراخ در صفحه#

grid-template-areas یک قانونِ سختگیر دارد: اسمی که در نقشه می‌نویسی باید مو به مو با اسمی که در grid-area می‌دهی یکی باشد. یک حرفِ اشتباه و ناحیه گم می‌شود. بیا ببینیم. فرض کن موقعِ نوشتنِ grid-areaِ محتوای اصلی، انگشتت بلغزد:

.content { grid-area: contnet; }

حالا نگاه کن: جایی که باید محتوای اصلی باشد یک سوراخِ خالی مانده، و خودِ محتوا از نقشه افتاده بیرون و یک‌جای عجیب، پایینِ صفحه، ظاهر شده.

🔧 اگر کار نکرد: این عمدی بود. در نقشه نوشتی content ولی در grid-area تایپ کردی contnet. grid دنبالِ ناحیه‌ای به اسمِ contnet می‌گردد، پیدا نمی‌کند، پس عنصر را در ناحیهٔ contentِ نقشه نمی‌گذارد — آن خانه خالی می‌ماند (سوراخ) و عنصر به یک سطرِ خودساختهٔ بیرونِ نقشه پرت می‌شود. رفعش یک حرف است: contnet را content کن. اسم‌ها باید دقیقاً یکی باشند — حتی دو حرفِ جابه‌جا کافی است که کار خراب شود.

چک کن: تا اسم را درست کردی، محتوا از پایین پرید سرِ جای اصلی‌اش و سوراخ پُر شد. هر وقت یک ناحیه سرِ جایش نبود یا یک خانه بی‌دلیل خالی ماند، اول از همه اسم‌ها را با نقشه بسنج.

۶. کد کاملِ صفحه#

حالا همه‌چیز را سرِ هم می‌کنیم: صفحهٔ نام‌دار، و محتوای اصلی که همان گالریِ auto-fit است. اول index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <title>چیدمان کامل صفحه</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <header class="header">وبلاگ من</header>
    <nav class="nav">خانه · درباره · تماس</nav>
    <aside class="aside">لینک‌های مفید</aside>
    <main class="content">
      <article class="card">پست ۱</article>
      <article class="card">پست ۲</article>
      <article class="card">پست ۳</article>
      <article class="card">پست ۴</article>
    </main>
    <footer class="footer">ساختهٔ من — ۱۴۰۵</footer>
  </body>
</html>

و styles.css:

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

body {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
    "header header"
    "nav    nav"
    "aside  content"
    "footer footer";
  gap: 12px;
  margin: 0;
  padding: 16px;
  min-height: 480px;
  background-color: #f6edda;
}

.header  { grid-area: header; }
.nav     { grid-area: nav; }
.aside   { grid-area: aside; }
.content { grid-area: content; }
.footer  { grid-area: footer; }

.header,
.nav,
.aside,
.content,
.footer {
  background-color: #2a6b93;
  color: #ffffff;
  padding: 16px;
  border-radius: 8px;
}

.content {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}

.card {
  background-color: #bc3e1c;
  color: #ffffff;
  padding: 16px;
  border-radius: 8px;
  text-align: center;
}

چک کن: یک صفحهٔ کامل و مرتب می‌بینی: نوارهای سرصفحه و منو در بالا، ستونِ کناری در راست، محتوای اصلی در چپ با چهار کارتِ قرمز تویش، و پاصفحه در پایین. عرض را باریک کن و دو چیز را با هم ببین: کارت‌های داخلِ محتوا کم‌ستون می‌شوند، و کلِ صفحه هم دستِ‌نخورده سرِ پا می‌ماند.

🤖 با AI: نقشهٔ grid-template-areas ات را به دستیار AI ات بده و بخواه یک چیدمانِ جایگزین پیشنهاد بدهد (مثلاً ستونِ کناری در چپ به‌جای راست). ولی یادت باشد AI اغلب فراموش می‌کند صفحهٔ ما RTL است و ترتیبِ ستون‌ها را برعکس فرض می‌کند. پیشنهادش را در آزمایشگاه امتحان کن و با چشمت ببین کدام سمت نشست — اول خودت، بعد AI.

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

کلمه تلفظ به حروف فارسی یعنی چه
repeat() ری‌پیت ستون یا سطرِ تکراری را کوتاه می‌نویسد، مثل repeat(3, 1fr)
minmax() مین‌مکس برای یک ستون یک کمینه و یک بیشینهٔ اندازه تعیین می‌کند
auto-fit اتو-فیت خودش هر چند ستون که جا شد می‌سازد و خالی‌ها را جمع می‌کند
auto-fill اتو-فیل مثل auto-fit ولی ستون‌های خالی را نگه می‌دارد
grid-template-areas گِرید تمپلیت اریاز نقشهٔ صفحه را با نامِ ناحیه‌ها می‌کشد
grid-area گِرید اریا می‌گوید یک عنصر کدام ناحیهٔ نقشه را پُر کند

تمرین‌ها

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

در فصل بعد#

الان می‌توانی کلِ اسکلتِ یک صفحه را با grid بچینی. تا اینجا هر چیز سرِ خانهٔ خودش در جریانِ چیدمان نشسته. ولی گاهی می‌خواهی یک چیز را از جریان بیرون بکشی و دقیقاً یک‌جای مشخص بچسبانی‌اش — یک برچسبِ «جدید» روی گوشهٔ کارت، یک دکمه که همیشه گوشهٔ صفحه بماند، یک سرصفحه که موقعِ اسکرول بچسبد بالا. فصل بعد سراغ position می‌رویم و این کارها را یاد می‌گیری — و یک تلهٔ معروف به اسمِ containing block را می‌بینی که چرا یک عنصر گاهی به جای اشتباه می‌پرد.

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

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