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

فصل ۴ از ۱۱

پیشرفت ترم
۰٪

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

انعطاف واقعی

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

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

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

طرح‌وارهٔ flex-grow: یک ردیف که فضای خالیِ آخرش به‌صورتِ سهم‌های برابر بینِ کارت‌ها پخش می‌شود و کارت‌ها کش می‌آیند تا کلِ عرض پُر شود

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

  • با flex-basis اندازهٔ شروعِ هر آیتم را روی main axis تعیین کنی
  • با flex-grow آیتم‌ها را وادار کنی فضای خالی را پُر کنند و سهمشان را کنترل کنی
  • بگویی flex-shrink کِی و چطور آیتم‌ها را جمع می‌کند
  • هر سه را با shorthandِ flex در یک خط بنویسی
  • با flex-wrap کاری کنی آیتم‌ها به‌جای بیرون‌زدن از جعبه، به خطِ بعد بروند

قبل از شروع#

  • از فصل ۲: display: flex، main axis و gap — انعطاف در امتدادِ همان main axis اتفاق می‌افتد.
  • از فصل ۳: justify-content فضای خالی را جابه‌جا می‌کرد؛ اینجا با flex-grow فضای خالی را پُر می‌کنیم — دو کارِ متفاوت.
  • از ترم ۲ فصل ۴: box-sizing: border-box تا اندازه‌ها بی‌سورپرایز بمانند.

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

۱. flex-basis: اندازهٔ شروع#

هر flex item یک اندازهٔ شروع روی main axis دارد، قبل از اینکه کش بیاید یا جمع شود. اسمِ این اندازه flex-basis است. بیا سه کارت بسازیم و به هرکدام یک basisِ ۲۰۰ پیکسلی بدهیم:

<main class="cards">
  <div class="card">۱</div>
  <div class="card">۲</div>
  <div class="card">۳</div>
</main>
.cards {
  display: flex;
  gap: 16px;
  padding: 16px;
}

.card {
  flex-basis: 200px;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

چک کن: سه کارت می‌بینی که هرکدام ۲۰۰ پیکسل پهن‌اند و کنارِ هم نشسته‌اند، ولی سمتِ چپِ ردیف یک فضای خالیِ بزرگ مانده. چرا پُرش نمی‌کنند؟ چون flex-basis فقط اندازهٔ شروع است؛ به کارت‌ها نگفتیم که رشد کنند. آن کار، دستورِ بعدی است.

۲. flex-grow: فضای خالی را پُر کن#

flex-grow به یک آیتم می‌گوید «از فضای خالیِ باقی‌مانده سهم بردار و کش بیا». مقدارش یک عدد است: 0 یعنی «رشد نکن» (پیش‌فرض)، و هر عددِ بزرگ‌ترْ یعنی «سهم بردار». به همهٔ کارت‌ها flex-grow: 1 بده:

.card {
  flex-basis: 200px;
  flex-grow: 1;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

چک کن: حالا کارت‌ها کش آمدند و کلِ عرضِ ردیف را پُر کردند، بدونِ فضای خالی. چون هر سه flex-grow: 1 گرفتند، فضای اضافه به سهمِ برابر بینشان پخش شد و هر سه هم‌عرض شدند.

حالا بیا یکی را پهن‌تر کنیم. به کارتِ وسط یک کلاسِ دوم بده که سهمش دو برابر باشد:

<main class="cards">
  <div class="card">۱</div>
  <div class="card wide">۲</div>
  <div class="card">۳</div>
</main>
.wide {
  flex-grow: 2;
}

چک کن: کارتِ وسط پهن‌تر از دوتای دیگر شد. flex-grow نسبت است، نه اندازهٔ ثابت: کارتِ وسط با 2 از فضای خالی دو برابرِ هر کارتِ 1 سهم برد. عددها را عوض کن — 3، بعد 5 — و ببین هرچه بزرگ‌تر، سهمِ بیشتر. این تفاوتش با justify-content است: آنجا فضای خالی را جابه‌جا می‌کردیم؛ اینجا فضای خالی را بینِ آیتم‌ها پخش و پُر می‌کنیم.

قبل از ادامه، جواب بده: اگر دو کارت داشته باشی، یکی flex-grow: 1 و دیگری flex-grow: 3، فضای خالی چطور بینشان تقسیم می‌شود؟ (جواب: چهار سهم می‌شود؛ کارتِ اول یک سهم، کارتِ دوم سه سهم — یعنی دومی سه برابرِ اولی از فضای اضافه برمی‌دارد.)

۳. flex-shrink: وقتی جا کم می‌آید#

flex-grow برای فضای اضافه بود. حالت برعکس هم هست: وقتی مجموعِ اندازهٔ کارت‌ها از عرضِ جعبه بیشتر شود. اینجا flex-shrink تصمیم می‌گیرد چقدر جمع شوند. مقدارِ پیش‌فرضش 1 است، یعنی «اگر لازم شد جمع شو تا جا بشوی». برای دیدنش، flex-grow را موقتاً بردار و basis را بزرگ کن:

.card {
  flex-basis: 400px;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

چک کن: سه کارتِ ۴۰۰پیکسلی مجموعاً ۱۲۰۰ پیکسل می‌خواهند، ولی جعبه این‌قدر پهن نیست — با این حال کارت‌ها از جعبه بیرون نزدند، بلکه جمع شدند تا جا بشوند. این کارِ flex-shrink: 1ِ پیش‌فرض است: به آیتم‌ها اجازه می‌دهد کوچک‌تر از basisشان شوند تا در یک خط جا بگیرند. اگر flex-shrink: 0 بدهی، دیگر جمع نمی‌شوند — و آن وقت از جعبه بیرون می‌زنند، که دقیقاً مشکلِ بخشِ آخرِ همین فصل است.

۴. shorthandِ flex: هر سه در یک خط#

نوشتنِ سه دستورِ جدا خسته‌کننده است. یک shorthand هست که هر سه را یک‌جا می‌گیرد، به همین ترتیب: flex: <grow> <shrink> <basis>. پس این سه خط:

.card {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 200px;
}

دقیقاً برابر است با این یک خط:

.card {
  flex: 1 1 200px;
}

این پرکاربردترین شکلِ flex است و آن را زیاد خواهی دید: «رشد کن، جمع شو، و از ۲۰۰ پیکسل شروع کن». یک شکلِ کوتاه‌ترش هم هست: flex: 1 که برابرِ flex: 1 1 0 است — یعنی «همه با هم مساوی، بدونِ اهمیت به اندازهٔ محتوا».

💡 نکته: هر وقت خواستی چند کارت دقیقاً هم‌عرض شوند، flex: 1 روی همه‌شان کافی است. هر وقت خواستی هم‌عرض شوند ولی زیرِ یک اندازه کوچک نشوند، flex: 1 1 200px. همین دو الگو، نانِ شبِ بیشترِ layout هایت می‌شود.

۵. flex-wrap: وقتی دیگر جا نیست (خطای عمدی)#

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

<main class="cards">
  <div class="card">۱</div>
  <div class="card">۲</div>
  <div class="card">۳</div>
  <div class="card">۴</div>
  <div class="card">۵</div>
  <div class="card">۶</div>
</main>
.card {
  flex: 0 0 200px;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

flex: 0 0 200px یعنی «رشد نکن، جمع هم نشو، دقیقاً ۲۰۰ پیکسل بمان». شش کارتِ ۲۰۰پیکسلی مجموعاً ۱۲۰۰ پیکسل به‌علاوهٔ فاصله‌ها می‌خواهند — که از جعبه بزرگ‌تر است. نگاه کن چه شد.

🔧 اگر کار نکرد: بله، عمدی است. کارت‌ها از لبهٔ جعبه بیرون زده‌اند و بعضی‌شان نصفه بیرونِ کادرند یا اصلاً دیده نمی‌شوند. دلیلش دو چیز است که با هم دست‌به‌دستِ هم داده‌اند: اول، گفتیم flex-shrink: 0 پس کارت‌ها حاضر نیستند جمع شوند؛ دوم، و مهم‌تر، flexbox به‌طورِ پیش‌فرض همه‌چیز را به زور در یک خط نگه می‌دارد (به این حالت می‌گویند nowrap). وقتی جا کم بیاید و اجازهٔ جمع‌شدن هم نباشد، تنها کاری که از دستش برمی‌آید این است که بگذارد آیتم‌ها بیرون بزنند. راهِ حل این است که به آیتم‌ها اجازهٔ رفتن به خطِ بعد بدهی.

به جعبهٔ والد flex-wrap: wrap بده:

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
}

چک کن: حالا کارت‌هایی که جا نمی‌شدند، تمیز رفتند به خطِ دوم، به‌جای اینکه از جعبه بیرون بزنند. flex-wrap: wrap یعنی «اگر جا کم آمد، خطِ جدید بساز». این تفاوتِ بین یک layoutِ شکسته و یک layoutِ سالم است.

مقایسهٔ flex-wrap: در حالتِ nowrap کارت‌ها به زور در یک خط می‌مانند و از لبهٔ جعبه بیرون می‌زنند، در حالتِ wrap همان کارت‌ها به چند خط می‌ریزند و همه داخلِ جعبه می‌مانند

حالا آخرین قدم: به‌جای اندازهٔ ثابت، از الگوی انعطاف‌پذیر استفاده کن تا کارت‌ها هم به خطِ بعد بریزند و هم فضای هر خط را پُر کنند. flex را به 1 1 200px عوض کن:

.card {
  flex: 1 1 200px;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

چک کن: این همان شبکهٔ واقعی است. کارت‌ها هرکدام از ۲۰۰ پیکسل شروع می‌کنند، کش می‌آیند تا عرضِ هر خط پُر شود، و وقتی جا کم شد به خطِ بعد می‌ریزند. حالا پنجرهٔ پیش‌نمایش را باریک و پهن کن (یا در آزمایشگاه اندازهٔ پنجره را عوض کن) و ببین تعدادِ کارت‌های هر خط خودش کم‌وزیاد می‌شود. این اولین باری است که یک layout ساختی که با اندازهٔ صفحه ور می‌رود — و همهٔ این کار را flexbox تنهایی و بدونِ یک خط اضافه انجام داد.

🤖 با AI: از دستیارِ AI ات بپرس «تفاوتِ flex-basis با width چیست؟». جوابش را با چیزی که دیدی بسنج: flex-basis: 200px را با flex-grow: 1 ترکیب کن و ببین کارت از ۲۰۰ بزرگ‌تر می‌شود — کاری که width: 200pxِ سفت‌وسخت اجازه‌اش را نمی‌دهد. اگر AI این تفاوتِ «شروع در برابر قطعی» را نگفت، خودت دیدی‌اش.

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

این هم دو فایلِ کاملِ این فصل، یک‌جا. اول 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>
    <main class="cards">
      <div class="card">۱</div>
      <div class="card">۲</div>
      <div class="card">۳</div>
      <div class="card">۴</div>
      <div class="card">۵</div>
      <div class="card">۶</div>
    </main>
  </body>
</html>

و styles.css:

/* styles.css */
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
}

.card {
  flex: 1 1 200px;
  padding: 24px;
  background-color: #2a6b93;
  color: #ffffff;
  font-size: 20px;
}

چک کن: روی صفحه یک شبکهٔ شش‌کارتی می‌بینی که کارت‌ها فضا را پُر می‌کنند و وقتی پنجره تنگ شود به خطِ بعد می‌ریزند. flex: 1 1 200px روی آیتم‌ها و flex-wrap: wrap روی والد — همین دو خط، یک گالریِ خودتنظیم ساخت.

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

کلمه تلفظ به حروف فارسی یعنی چه
flex-basis فلکس بِیسیس اندازهٔ شروعِ آیتم روی main axis
flex-grow فلکس گرو چقدر آیتم از فضای خالی سهم بردارد و کش بیاید (پیش‌فرض ۰)
flex-shrink فلکس شرینک چقدر آیتم وقتی جا کم است جمع شود (پیش‌فرض ۱)
flex فلکس shorthandِ سه‌تایی: grow shrink basis
flex-wrap فلکس رَپ آیا آیتم‌ها به خطِ بعد بریزند (wrap) یا به زور یک خط بمانند (nowrap)
nowrap نو-رَپ پیش‌فرضِ flex-wrap: همه در یک خط، حتی اگر بیرون بزنند

تمرین‌ها

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

در فصل بعد#

الان کلِ flexbox را بلدی: از چیدن در یک محور تا وسط‌چین‌کردن تا انعطاف و wrap. با همین یک ابزار می‌توانی بیشترِ layout هایی را که هر روز روی وب می‌بینی بسازی. ولی flexbox یک محدودیت دارد: هربار روی یک محور کار می‌کند. برای چیدمانِ واقعیِ دومحوره — ستون‌ها و ردیف‌ها که هم‌زمان با هم تراز باشند، مثلِ یک صفحهٔ شطرنجیِ کامل — ابزارِ بعدی می‌آید: grid. فصل بعد شروعش می‌کنیم و می‌بینی grid و flexbox رقیب نیستند، دو ابزارِ مکمل‌اند و هرکدام جای خودش را دارد.

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

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