مقدمه

ماژول Flexible Box Layout که به‌اختصار Flexbox نامیده می‌شود، یک ابزار قدرتمند برای طرح‌بندی صفحه (layout) با استفاده از CSS است که بسیاری از مسائل و مشکلات مربوط به layout را از بین می‌برد و کارهایی را که در گذشته انجامشان سخت بود، بسیار ساده کرده است. Flexbox یک ابزار layout تک‌بعدی است؛ یعنی می‌تواند چینش عناصر را یا به صورت افقی و یا به صورت عمودی (و نه در هر دو جهت) انجام دهد. عنصری که چیدمانش با استفاده از Flexbox انجام شود یک کانتینر flex و عناصر درون آن عنصر را آیتم‌های flex می‌گویند.

مفاهیم پایه در مورد Flexbox

قبل از هر چیز، باید با مفاهیم و اصطلاحات مربوط به ماژول Flexbox آشنا شویم.

جهت یا Direction

یک کانتینرِ flex دارای یک جهت است که با استفاده از پراپرتی flex-direction تعیین می‌شود. این پراپرتی می‌تواند یکی از چهار مقدار row، row-reverse، column و column-reverse را دریافت کند. row و column به‌ترتیب به معنای سطر و ستون هستند اما کارکرد این مقادیر به جهت‌گیری محتوای عنصر بستگی دارد که می‌تواند راست‌به‌چپ (RTL) یا چپ‌به‌راست (LTR) باشد.

  • برای عنصری که جهت‌گیری چپ‌به‌راست دارد، مقدار row به معنای چینش در راستای افقی از سمت چپ‌به‌راست است و row-reverse به معنای چینش در راستای افقی در جهت راست‌به‌چپ است. مقادیر column و column-reverse نیز به ترتیب، به معنای چینش در راستای عمودی از بالا به پایین و پایین به بالا هستند.
  • برای عنصری که جهت‌گیری راست‌به‌چپ دارد، مقدار row به معنای چینش در راستای افقی از سمت راست‌به‌چپ است و row-reverse به معنای چینش در راستای افقی در جهت چپ‌به‌راست است. مقادیر column و column-reverse نیز به ترتیب، به معنای چینش در راستای عمودی از بالا به پایین و پایین به بالا هستند.

برای تبدیل یک عنصر به یک کانتینر flex کافیست پراپرتی display را برای آن عنصر روی مقدار flex تنظیم کنیم. به این ترتیب، یک کانتینر flex از نوع block-level ایجاد می‌شود. در صورت تمایل، می‌توانیم با تخصیص مقدار inline-flex به پراپرتی display یک کانتینر flex از نوع inline ایجاد کنیم.

تصویر زیر تأثیر استفاده از هر یک از مقادیر row، row-reverse، column و column-reverse را برای یک کانتینر flex که دارای جهت‌گیری چپ‌به‌راست است، نشان می‌دهد.

پراپرتی flex-direction در css

در مورد یک کانتینر flex با جهت‌گیری راست‌به‌چپ، کافیست در تصویر بالا جای برچسب‌های row و row-reverse را عوض کنیم.

محورها یا Axes

یک مفهوم دیگر که به جهت(direction) کانتینر flex نیز مربوط است، مفهوم محورها (axes) است. محور اصلی (main axis) از روی جهت تعیین‌شده توسط پراپرتی flex-direction مشخص می‌شود و محور فرعی (cross axis) عمود بر محور اصلی در نظر گرفته می‌شود. برای مثال، اگر پراپرتی flex-direction دارای مقدار row باشد، محور اصلی در راستای افقی و از چپ‌به‌راست است و محور فرعی در راستای عمودی و از بالا به پایین است. تصویر زیر این دو محور را نشان می‌دهد.

محورهای flexbox در css

یک مثال ساده از Flexbox

همانطور که قبلاً هم اشاره شد، برای ایجاد یک کانتینر flex و تبدیل عناصر فرزند آن عنصر به آیتم‌های flex کافیست از مقدار flex برای پراپرتی display استفاده کنیم. به علاوه، گفتیم که جهت یک flexbox را نیز با استفاده از پراپرتی flex-direction تعیین می‌کنیم. با به‌کارگیری این موارد در کد زیر، یک مثال بسیار ساده از یک flexbox ایجاد شده است.

 Copy Icon CSS
.container{
  display: flex;
  flex-direction: row;
  border: 1px solid black;
}

در اینجا چند نکته قابل مشاهده است. اول اینکه ارتفاع کانتینر از روی محتوایش مشخص می‌شود. به علاوه، آیتم‌ها به صورت پشت سر هم قرار گرفته‌اند و هیچ فاصله‌ای بین آنها وجود ندارد. برای ایجاد فاصله بین آیتم‌ها می‌توانیم از یک پراپرتی با نام gap استفاده کنیم. و البته از همه مهم‌تر اینکه آیتم‌های flex فارغ از اینکه از نوع عناصر block باشند یا inline، به صورت پشت سر هم در راستای محور اصلی یا main axis چیده می‌شوند.

تعیین اندازه

اگر آیتم‌های flex در کانتینر flex جا نشوند، چه اتفاقی رخ می‌دهد؟ برای اینکه مرزهای کانتینر را بهتر ببینید، در مثال بعدی مقداری padding برای کانتینر در نظر گرفته‌ایم.

 Copy Icon CSS
.container{
  border: 1px solid black;
  padding: 0.5rem;
  width: 500px;
  display: flex;
  flex-direction: row;
}

در اینجا کانتینر ما دارای عرض 500px است و شامل سه آیتم است که هر یک 300px عرض دارند. قاعدتاً باید سرریز (overflow) رخ دهد اما همانطور که می‌بینید، چنین اتفاقی رخ نداده و در عوض، آیتم‌ها تغییر اندازه داده‌اند تا درون کانتینر جا شوند.

اما گاهی اوقات عناصر تا آنجا که ممکن است کوچک می‌شوند اما باز هم درون کانتیر جا نمی‌شوند و اینجاست که overflow رخ می‌دهد. مثال زیر این وضعیت را نشان می‌دهد.

 Copy Icon CSS
.container{
  border: 1px solid black;
  padding: 0.5rem;
  width: 120px;
  display: flex;
  flex-direction: row;
}

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

پراپرتی flex-wrap

یک راه‌حل ممکن برای مشکل قبلی، استفاده از پراپرتی flex-wrap است. وقتی مقدار این پراپرتی را برای کانتینر روی wrap تنظیم کنیم، آیتم‌های flex به‌جای سرریز، به خط بعدی منتقل می‌شوند. تأثیر استفاده از مقدار wrap برای پراپرتی flex-wrap را در کد زیر می‌بینید.

 Copy Icon CSS
.container{
  border: 1px solid black;
  padding: 0.5rem;
  width: 120px;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

پراپرتی flex-grow

در حالت پیش‌فرض، اگر آیتم‌ها به اندازه‌ی کافی بزرگ نباشند تا کانتینر را پر کنند، فضای خالی وجود خواهد داشت. مثالی از این وضعیت را در کد زیر می‌بینید.

 Copy Icon CSS
.container{
  border: 1px solid black;
  padding: 0.5rem;
  width: 500px;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.item{
  width: 100px;
  text-align: center;
  color: white;
  font-size: 2rem;
}

اگر این کد را اجرا کنید، خواهید دید که آیتم‌ها بزرگ نمی‌شوند و مقداری فضای خالی در کانتینر دیده می‌شود. این رفتار را می‌توان با استفاده از یک پراپرتی با نام flex-grow کنترل کرد. این پراپرتی نه روی کانتینر بلکه روی آیتم‌های flex اعمال می‌شود. مقدار پیش‌فرض این پراپرتی صفر است که همانطور که گفته شد و دیدیم، باعث می‌شود که آیتم‌ها بزرگ نشوند. پراپرتی flex-grow دارای یک مقیاس نسبی است. یعنی اگر همه‌ی آیتم‌ها دارای مقدار برابری باشند، همگی به یک نسبت بزرگ می‌شوند تا کانتینر پر شود. مثال زیر را ببینید.

 Copy Icon CSS
.container{
  border: 1px solid black;
  padding: 0.5rem;
  width: 500px;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.item{
  width: 100px;
  text-align: center;
  color: white;
  font-size: 2rem;
  flex-grow: 1;
}

اگر این کد را اجرا کنید، خواهید دید که همه‌ی آیتم‌های flex به نسبت یکسانی بزرگ می‌شوند تا کانتینر پر شود.

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

 Copy Icon CSS
.item1{
  background: rebeccapurple;
  flex-grow: 1;
}

.item2{
  background: orangered;
  flex-grow: 2;
}

.item3{
  background: green;
  flex-grow: 1;
}

همانطور که در تصویر زیر می‌بینید، در نتیجه‌ی اجرای این کد، آیتم دوم با نسبت دو برابری نسبت به آیتم‌های اول و سوم بزرگ می‌شود.

توجه داشته باشید که این امر به این معنا نیست که اندازه‌ی آیتم دوم دو برابر آیتم اول یا سوم باشد، بلکه از فضای موجود، سهم آیتم دوم نسبت به سایر آیتم‌ها دو برابر است. در ابتدا هر سه آیتم دارای عرض 100px هستند. از 200px فضای باقیمانده، 100px سهم آیتم دوم است و آیتم‌های اول و سوم هر یک 50px بزرگ می‌شوند.

پراپرتی flex-shrink

قبلاً دیدیم که وقتی سایز آیتم‌های flex از کانتینر بیشتر باشد، مرورگر سعی می‌کند با کاهش سایز آیتم‌هنا آنها را در کانتینر جا دهد. این کاهش سایز در حالت پیش‌فرض، به طور یکسان روی آیتم‌ها اعمال می‌شود. اما همانطور که نسبت بزرگ‌شدن آیتم‌ها را با استفاده از پراپرتی flex-grow تعیین کردیم، می‌توانیم از یک پراپرتی مشابه با نام flex-shrink برای کنترل نسبت کوچک‌شدن آیتم‌ها استفاده کنیم.

مقدار پیش‌فرض پراپرتی flex-shrink برابر با 1 است که باعث می‌شود همه‌ی آیتم‌ها با نسبت یکسانی کوچک شوند. در کد زیر، مقدار این پراپرتی را برای آیتم دوم به 2 تغییر داده‌ایم.

 Copy Icon CSS
.item2{
  backgroun: orangered;
  flex-shrink: 2;
}

در این مثال، عرض هر آیتم 200px است و بنابراین، مجموعاً 600px فضا برای این سه آیتم لازم است اما کانتینر ما فقط 300px فضا دارد. در حالت پیش‌فرض، هر سه آیتم به اندازه‌ی یکسان (یعنی 100px) کوچک می‌شوند تا در کانتینر جا شوند. اما با توجه به این که مقدار پراپرتی flex-shrink را برای آیتم دوم روی 2 تنظیم کرده‌ایم، 150px از آیتم دوم و 75px از هر یک از دو آیتم دیگر کم می‌شود.

پراپرتی flex-basis

پراپرتی flex-basis سایز اولیه‌ی یک آیتم flex را در راستای محور اصلی (main axis) قبل از اعمال پراپرتی‌های flex-grow و flex-shrink تعیین می‌کند. در مثال‌های قبلی، ما از پراپرتی width برای تعیین سایز آیتم‌های flex استفاده کردیم اما بهتر است این کار را با استفاده از پراپرتی flex-basis انجام دهیم که با سایر پراپرتی‌های مربوط به Flexbox سازگاری بیشتری دارد.

 Copy Icon CSS
.item1{
  background: rebeccapurple;
  flex-basis: 100px;
}

.item2{
  background: orangered;
  flex-basis: 150px;
}

.item3{
  background: green;
  flex-basis: 100px;
}

تراز و فاصله‌بندی

تا اینجا دیدیم که چطور آیتم‌های flex را در یک کانتینر flex سایزدهی می‌کنیم. در این بخش قصد دریم در مورد تراز (alignment) آیتم‌ها صحبت کنیم و ببینیم چه اتفاقی رخ می‌دهد وقتی که همه‌ی آیتم‌ها در راستای محور فرعی (cross axis) هم‌اندازه نیستند. به علاوه، در مورد فاصله‌گذاری بین آیتم‌ها و نحوه‌ی توزیع فضای باقیمانده صحبت خواهیم کرد.

پراپرتی justify-content

پراپرتی justify-content روی کانتینر flex اعمال می‌شود و نحوه‌ی تراز آیتم‌های کانتینر را در راستای محور اصلی تعیین می‌کند. مقدار پیش‌فرض این پراپرتی flex-start است که باعث می‌شود آیتم‌ها با شروع از ابتدای محور اصلی به صورت پشت سر هم قرار بگیرند. حالا اینکه محور اصلی کدام است و ابتدا و انتهای ان کدام سمت، چیزی است که از روی writing mode تعیین می‌شود. جدول زیر، مقادیر ممکن برای پراپرتی justify-content و نتیجه‌ی هر مقدار را برای یک عنصر دارای یک زبان چپ‌به‌راست (مثل انگلیسی) و با پراپرتی flex-direction: row نشان می‌دهد.

مقدار توضیح مثال
flex-start آیتم‌ها با شروع از ابتدای محور اصلی، به صورت پشت سر هم قرار می‌گیرند. پراپرتی justify-content با مقدار start
flex-end آیتم‌ها یکی پس از دیگری در انتهای محور اصلی قرار می‌گیرند. پراپرتی justify-content با مقدار end
center آیتم‌ها به صورت پشت سر هم و در مرکز محور اصلی قرار می‌گیرند. پراپرتی justify-content با مقدار center
space-between فاصله‌ی بین آیتم‌ها ماکزیمم می‌شود. به این ترتیب که اولین آیتم در نقطه‌ی شروع محور اصلی و آخرین آیتم در نقطه‌ی پایان محور اصلی قرار می‌گیرند و سایر آیتم‌ها به صورت یکنواخت بین این دو آیتم توزیع می‌شوند. پراپرتی justify-content با مقدار space-between
space-around مشابه مقدار space-between است با این تفاوت که در ابتدا و انتها کمی فضا وجود دارد. پراپرتی justify-content با مقدار space-around
space-evenly مشابه مقدار space-around است با این تفاوت که فاصله در همه‌ی جهات همه‌ی آیتم‌ها یکسان است. پراپرتی justify-content با مقدار space-evenly

پراپرتی align-items

پراپرتی align-items هم روی کانتینر flex اعمال می‌شود و نحوه‌ی تراز آیتم‌ها را در راستای محور فرعی (cross axis) کنترل می‌کند. مقدار پیش‌فرض این پراپرتی stretch است که باعث می‌شود آیتم‌ها در راستای محور فرعی کشیده شوند تا فضای کانتینر را پر کنند؛ البته در حالی که به محدودیت‌های مربوط به عرض و ارتفاع پایبند هستند. مقادیر مختلف برای پراپرتی align-item در جدول زیر ارائه شده است.

مقدار توضیح مثال
stretch آیتم‌ها به اندازه‌ای کشیده می‌شوند که فضای موجود در راستای محور فرعی را پر کنند. یک آیتم بیشتر از میزان height یا max-height کشیده نمی‌شود. پراپرتی align-items با مقدار stretch
flex-start آیتم‌ها با شروع از محور فرعی تراز می‌شوند. پراپرتی align-items با مقدار start
flex-end آیتم‌ها با شروع از انتهای محور فرعی تراز می‌شوند. پراپرتی align-items با مقدار end
center آیتم‌ها نسبت به مرکز محور فرعی تراز می‌شوند. پراپرتی align-items با مقدار center
baseline آیتم‌ها نسبت به baseline مربوط به متنشان تراز می‌شوند. پراپرتی align-items با مقدار baseline

پراپرتی align-content

وقتی بیش از یک سطر داشته باشیم (یا بیش از یک ستون در یک flex ستونی) و مقداری فضای اضافی در راستای محور فرعی موجود باشد، با استفاده از پراپرتی align-content می‌توانیم نحوه‌ی توزیع این فضا را کنترل کنیم. مقدار پیش‌فرض این پراپرتی stretch است که باعث میشود آیتم‌ها در راستای محور فرعی کشیده شوند. مقادیر ممکن برای پراپرتی align-content در جدول زیر ارائه شده است.

مقدار توضیح مثال
stretch آیتم‌ها به اندازه‌ای کشیده می‌شوند که فضای موجود در راستای محور فرعی را پر کنند. یک آیتم بیشتر از میزان height یا max-height کشیده نمی‌شود. پراپرتی align-content با مقدار strech
flex-start سطرها را نسبت به ابتدای محور فرعی تراز می‌کند. پراپرتی align-content با مقدار flex-start
flex-end سطرها را نسبت به انتهای محور فرعی تراز می‌کند. پراپرتی align-content با مقدار flex-end
center سطرها را نسبت به مرکز محور فرعی تراز می‌کند. پراپرتی align-content با مقدار center
space-between فاصله‌ی بین سطرها را ماکزیمم می‌کند. پراپرتی align-content با مقدار space-between
space-around بین سطرها فاصل ایجاد می‌کند به نحوی که در ابتدا و انتها نصف این فاصله باشد. پراپرتی align-content با مقدار space-around
space-evenly بین همه‌ی سطرها فاصله‌ی مساوی قرار می‌دهد. پراپرتی align-content با مقدار space-evenly

پراپرتی align-self

دیدیم که پراپرتی align-items همه‌ی آیتم‌ها را تراز می‌کند. حالا اگر بخواهیم برای یکی ازآیتم‌ها مقداری را تنظیم کنیم که مقدار تنظیم‌شده توسط align-items را بازنویسی یا لغو (override) کند، باید از پراپرتی align-self برای آن آیتم استفاده کنیم.

تغییر ترتیب آیتم‌ها با پراپرتی order

در حالت پیش‌فرض، چیدمان آیتم‌ها بر اساس دو عامل انجام می‌شود: یکی ترتیب آیتم‌ها در HTML و دیگری مقدار پراپرتی flex-direction.

ترتیب حاصل از این دو عامل را می‌توان با استفاده از پراپرتی order برای آیتم‌ها تغییر داد. وقتی از این پراپرتی برای یک آیتم flex استفاده کنیم، ترتیب وقوع آن آیتم را تعیین می‌کند. این امکان هم وجود دارد که برای چند آیتم مقدار یکسانی را برای پراپرتی order تعیین کنیم که در این صورت، برای تعیین ترتیب وقوع این آیتم‌ها به ترتیب آنها در HTML رجوع می‌شود.

در مورد پراپرتی order یک نکته در ارتباط با Accessibility وجود دارد که باید آن را مد نظر قرار دهیم:

پراپرتی order فقط روی ترتیب عناصر در صفحه‌نمایش اثر می‌گذارد و مثلاً یک screen reader عناصر را به همان ترتیبی که در سورس آمده، می‌خواند. این امر باعث می‌شود که نمایش آیتم‌ها با ترتیب نمایش آنها در صفحه‌نمایش هماهنگ نباشد که می‌تواند گیج‌کننده باشد. بنابراین، بهتر است حتی المقدور از پراپرتی order استفاده نکنیم.

مثال‌هایی از Flexbox

با تکنولوژی Flexbox و پراپرتی‌های مربوط به آن آشنا شدیم و دیدیم که چطور امکان تعیین layout در یک بعد را برای ما ممکن می‌کند. در پایان این درس، دو مثال کاربردی از Flexbox ارائه می‌دهیم تا این تکنولوژی را بهتر درک کنید.

مرکزی‌سازی مطلق

مسئله‌ی مرکزی‌سازی مطلق (absolute centering) یعنی هم به صورت افقی و هم به صورت عمودی، با استفاده از Flexbox به سادگی حل می‌شود. کافیست مقدار پراپرتی‌های justify-content و align-items را روی center تنظیم کنیم.

 Copy Icon CSS
.container{
  background: skyblue;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 10rem;
  height: 10rem;
}

.item{
  background: orangered;
  width: 5rem;
  height: 5rem;
}

اگر کد بالا را اجرا کنید، خواهید دید که باکس درونی هم به صورت افقی و هم به صورت عمودی در مرکز باکس بیرونی قرار گرفته است.

طرح‌بندی صفحه

با تودرتو کردن باکس‌های flex می‌توانیم انواع طرح‌بندی‌ها (layouts) را ایجاد کنیم. در مثال زیر، یک طرح‌بندی کامل برای یک صفحه با استفاده از Flexbox ایجاد شده است.

 Copy Icon CSS
.container{
  display: flex;
  flex-direction: column;
  height: 100vh;
  border: 5px dashed black;
  box-sizing: border-box;
}