সরাসরি পর্বে যান
পর্ব ২৬ · ৫৯-এর মধ্যে · মডিউল ৪

Flexbox দিয়ে বাস্তব নকশা — navbar, কার্ড আর মাঝখানে বসানো

Practical Flexbox Patterns — Centering, Navbars, Equal-Height Cards, Media Objects, Sticky Footers & Common Bugs

Taught in Bangla, this lesson turns Flexbox theory into practical patterns: perfect centering, a navbar with logo and wrapping links, equal-height card rows with buttons pinned to the bottom, media objects and input groups, sticky footers and a holy-grail-lite page frame, tag chips and split toolbars, a project page, and fixes for common Flexbox bugs.

এই পর্বে যা শিখবেন

  • যেকোনো জিনিস আড়াআড়ি আর খাড়া দুই দিকে মাঝখানে বসানো — আর কোন পদ্ধতি নিরাপদ
  • রেসপন্সিভ navbar, সমান উচ্চতার কার্ড, media object, সার্চ বক্স, চিপের তালিকা আর দুই-ভাগের toolbar
  • sticky footer আর holy-grail-lite পাতার কাঠামো, media query ছাড়াই ভাঁজ হওয়া লেআউট
  • একটি বাংলাদেশি অনলাইন দোকানের পণ্য তালিকা — আর কোথায় Flexbox-এর বদলে Grid লাগে
  • প্রচলিত বাগ (min-width: 0, ছবি টেনে লম্বা হওয়া, flex-basis বনাম width, শতাংশের gap) আর অ্যাক্সেসিবিলিটির নিয়ম

১ · তত্ত্ব থেকে কাজে — চারটি কৌশল, যা প্রায় সব নকশায় লাগে

পর্ব ২৫-এ আপনি Flexbox-এর ভেতরের নিয়ম শিখেছেন — flex container আর তার flex item, main axis আর cross axis, কে কতটা বাড়বে বা কমবে। নিয়মগুলো এখানে আবার শেখানো হবে না; দরকার হলে পর্ব ২৫ খুলে পাশে রাখুন। এই পর্বের কাজ উল্টো দিক থেকে: আমরা একটি বাস্তব নকশা দেখব, তারপর খুঁজব কোন দুই-তিনটি লাইন সেটি বানায়।

পাড়ার মুদি দোকানের তাকের কথা ভাবুন। দোকানি জিনিস সাজানোর সময় কয়েকটি সিদ্ধান্তই বারবার নেন: কোন জিনিস বাঁ কোণে, কোনটা ডান কোণে; ফাঁকা জায়গা কোথায় থাকবে; তাক ভরে গেলে বাকি জিনিস নিচের তাকে যাবে কি না; আর কোন বাক্স কখনো চাপা দেওয়া যাবে না (ডিমের ট্রে!)। Flexbox-এর বাস্তব নকশাগুলোও ঠিক এই কয়েকটি সিদ্ধান্তের নানা রকম মিশ্রণ।

কৌশলযা লিখবেনযা ঘটেকোন নকশায়
ফাঁকা জায়গা খেয়ে ফেলাmargin-left: auto, margin-top: auto, margin: automain axis-এর সব বাড়তি জায়গা ওই margin নিয়ে নেয়, item ঠেলে সরে যায়navbar, কার্ডের বোতাম, মাঝখানে বসানো
কে বাড়বে, কে বাড়বে নাflex: 1, flex: none, flex: 1 1 200pxযে item বাড়তে পারে সে বাকি জায়গা ভরে; none নিজের মাপে স্থির থাকেসার্চ বক্স, media object, sticky footer
ভাঁজ আর ফাঁকflex-wrap: wrap, gap: 12pxজায়গা না হলে item পরের লাইনে যায়; item-এর মাঝে সমান ফাঁকচিপ, toolbar, পণ্যের তালিকা
চাপা খাওয়ার অনুমতিmin-width: 0লম্বা লেখা থাকলেও item নিজের content-এর চেয়ে সরু হতে পারেলম্বা নাম, লিংক, কোড, ইনপুট

শেষেরটির পেছনের কারণ একটু লুকানো, তাই আগেই বলে রাখি। Flex item-এর min-width-এর ডিফল্ট মান 0 নয়, auto — মানে “content যতটা সরু হতে পারে, তার চেয়ে সরু হবে না”। একে বলে automatic minimum size. তাই একটি লম্বা লিংক থাকলে item আর ছোট হয় না, পুরো সারি পর্দার বাইরে বেরিয়ে যায়। অংশ ৯-এ এটি নিজের চোখে মেপে দেখব।

মূল ধারণা

Flexbox একটি প্রশ্নের উত্তর দেয়: “এক সারিতে (বা এক কলামে) জায়গা বেশি বা কম হলে, সেই বাড়তি বা ঘাটতি কে নেবে?” প্রতিটি প্যাটার্ন আসলে এই free space ভাগ করার একটি সিদ্ধান্ত। নকশা দেখে আগে নিজেকে জিজ্ঞেস করুন — ফাঁকা জায়গাটা কোথায় যাচ্ছে? উত্তর পেলে কোডও পেয়ে যাবেন।

২ · নিখুঁতভাবে মাঝখানে — আড়াআড়ি আর খাড়া দুই দিকেই

“একটি div মাঝখানে কীভাবে বসাব?” — বহু বছর এটি ওয়েব ডেভেলপারদের সবচেয়ে বিখ্যাত ঠাট্টা ছিল। আড়াআড়ি মাঝখানে বসানো সহজ ছিল (margin: 0 auto), কিন্তু খাড়াভাবে মাঝখানে বসাতে লাগত টেবিল, negative margin বা transform-এর কৌশল (এর গল্প ভাবনার প্রশ্ন ১-এ)। Flexbox-এ দুটি উপায় আছে, আর দুটিই দুই-তিন লাইনের। লগইন বক্স, “লোড হচ্ছে” চিহ্ন, খালি তালিকার বার্তা, আইকনের ভেতরে অক্ষর — সব জায়গায় এটি লাগে।

.stage {
  height: 150px;
  margin-bottom: 12px;
  border: 2px dashed #789;
  background: #f1f5fb;
}
.badge {
  margin: 0;
  padding: 10px 16px;
  border-radius: 8px;
  background: #006a4e;
  color: white;
  font: bold 15px sans-serif;
}
/* উপায় ১: container দুই axis-এ সাজায় */
.flexbox {
  display: flex;
  justify-content: center;   /* main axis — আড়াআড়ি */
  align-items: center;       /* cross axis — খাড়া */
}
/* উপায় ২: item-এর চারদিকের auto margin */
.auto { display: flex; }
.auto .badge { margin: auto; }

দুটি ড্যাশ-দেওয়া বাক্সের ঠিক মাঝখানে একটি সবুজ ব্যাজ — চোখে কোনো পার্থক্য নেই। প্রথমটিতে container নিজে দুই axis-এ সাজাচ্ছে; দ্বিতীয়টিতে ব্যাজের চার দিকের auto margin সব ফাঁকা জায়গা সমান ভাগে নিয়ে নিয়েছে। চেষ্টা করুন: .stage-এর height-কে 30px করুন। এখন ব্যাজ বাক্সের চেয়ে উঁচু। প্রথম বাক্সে ব্যাজ ওপরে-নিচে সমান বেরিয়ে যায় — ওপরের অংশ বাক্সের বাইরে। দ্বিতীয়টিতে auto margin শূন্যে নেমে আসে, ব্যাজ ওপরে লেগে থাকে, বেরোয় শুধু নিচে। তারপর .flexbox-এ flex-direction: column; যোগ করুন — দুই axis জায়গা বদলায়, কিন্তু দুটিই center বলে ফল একই থাকে।

মাঝখানে বসানো content-এর ওপরের অংশ হারিয়ে যেতে পারে। align-items: center দেওয়া একটি পুরো-পর্দার বাক্সে লম্বা ফর্ম বা দীর্ঘ লেখা থাকলে, ছোট ফোনে content ওপর-নিচে সমান বেরিয়ে যায়। নিচের অংশে স্ক্রল করে যাওয়া যায়, কিন্তু ওপরে বেরোনো অংশে কখনো পৌঁছানো যায় না — ফর্মের প্রথম ঘর হয়তো আর দেখাই যায় না! দুটি সমাধান: item-এ margin: auto ব্যবহার করুন (বড় হলে নিজে থেকেই ওপরে লেগে থাকে), অথবা align-items: safe center লিখুন। safe শব্দটি সাম্প্রতিক সময়ে সব প্রধান ব্রাউজারে এসেছে; যেসব পুরোনো ব্রাউজার শব্দটি চেনে না, তারা পুরো ঘোষণাটি বাদ দেয়, তাই আগের লাইনে সাধারণ center রেখে দিন।

একটি বিকল্পও জেনে রাখুন। CSS Grid-এ display: grid; place-items: center; লিখলেও একই কাজ হয় — পর্ব ২৭-এ দেখবেন। আর ২০২৪ সালে এসেছে আরও সহজ একটি রূপ: সাধারণ block বাক্সেই align-content: center খাড়াভাবে content মাঝখানে আনে, কোনো flex বা grid ছাড়াই। এটি Baseline Newly available — নতুন ব্রাউজারে চলে, পুরোনোতে উপেক্ষিত হয়; তাই পুরোনো ব্রাউজারের ব্যবহারকারী বেশি হলে এখনো flex-ই নিরাপদ।

৩ · Navbar — লোগো বাঁয়ে, লিংক ডানে, ছোট পর্দায় ভাঁজ

প্রায় প্রতিটি সাইটের মাথায় একই নকশা: বাঁ দিকে লোগো, ডান দিকে কয়েকটি লিংক, কখনো তার পরে একটি কার্ট বা লগইন বোতাম। CSS লেখার আগে HTML ঠিক করুন — পর্ব ১০-এর semantic নিয়মে লিংকের দলটি <nav>-এর ভেতরে একটি <ul> তালিকা। এতে screen reader ব্যবহারকারী শোনেন “navigation, ৫টি আইটেমের তালিকা”, আর সরাসরি landmark ধরে মেনুতে লাফ দিতে পারেন। Flexbox এই HTML-এর গায়ে হাত না দিয়েই চেহারা বদলায়।

আসল প্রশ্ন: লোগো আর লিংকের মাঝের ফাঁকা জায়গাটা কে নেবে? অনেকে justify-content: space-between লেখেন — দুটি item থাকলে সেটা ঠিকঠাক চলে। কিন্তু তৃতীয় একটি item যোগ হলেই নকশা ভেঙে যায়। ধাপে ধাপে দেখুন:

শুধু display: flexসব বাঁয়ে জড়ো
দেশি হাটহোমশাড়িঅফারকার্ট ২
.site-head { display: flex; gap: 12px; }
space-betweenলিংক মাঝখানে চলে গেল
দেশি হাটহোমশাড়িঅফারকার্ট ২
.site-head { display: flex; justify-content: space-between; }
nav-এ margin-left: autoফাঁকা জায়গা একটাই জায়গায়
দেশি হাটহোমশাড়িঅফারকার্ট ২
.site-head { display: flex; gap: 12px; }
.site-head nav { margin-left: auto; }
দুই দিকে autoলিংক দুই পাশের মাঝখানে
দেশি হাটহোমশাড়িঅফারকার্ট ২
.site-head nav { margin-inline: auto; }  /* বাঁ আর ডান দুটোই auto */
  1. শুধু display: flex. তিনটি item — লোগো, লিংকের দল আর কার্ট — এক সারিতে বসেছে, gap দিয়ে সমান ফাঁক। কিন্তু সব বাঁ দিকে জড়ো; ডান দিকে বিশাল ফাঁকা জায়গা অব্যবহৃত পড়ে আছে। এই বাড়তি জায়গাই free space.
  2. space-between — দুই item-এ ঠিক, তিনটিতে ভুল। এটি free space সমান ভাগে item-দের মাঝে মাঝে বসায়। ফলে লিংকের দল ঠিক মাঝখানে চলে গেল, কার্ট ডানে। কোনো দিন একটি “লগইন” বোতাম যোগ হলে আবার সব নড়বে। নকশা নির্ভর করছে item কয়টি, তার ওপর — এটি ভঙ্গুর।
  3. margin-left: auto — জায়গাটা একজনই নেবে। লিংকের দলের বাঁ margin-কে auto করলাম। Flexbox প্রথমে সব free space auto margin-কে দিয়ে দেয়, তাই দল আর কার্ট একসাথে ডানে ঠেলে গেল। এখন ডানে যত item-ই যোগ করুন, লোগো বাঁয়ে থাকবে, বাকিরা ডানে — নকশা item-এর সংখ্যার ওপর নির্ভর করে না।
  4. দুই দিকে auto — জায়গা সমান ভাগ। বাঁ আর ডান দুটি margin-ই auto হলে free space দুই ভাগ হয়ে দুই পাশে যায়, তাই লিংকের দল লোগো আর কার্টের ঠিক মাঝখানে। (margin-inline হলো লেখার দিক অনুযায়ী বাঁ-ডান margin একসাথে — পর্ব ৩৫-এ logical property.) খেয়াল করুন, এটি পুরো পাতার মাঝখান নয়; লোগো আর কার্টের মাপ আলাদা হলে দল একটু সরে থাকে।

এবার একটি পুরো navbar, যা সরু পর্দায় নিজে থেকেই ভাঁজ হয়ে যায় — কোনো media query ছাড়াই:

<style>
  body { margin: 0; font-family: sans-serif; }
  .site-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 24px;                /* সারির ফাঁক 8px, পাশের ফাঁক 24px */
    padding: 12px 16px;
    background: #0d1f3c;
  }
  .logo { color: #ffd166; font-size: 20px; font-weight: bold; text-decoration: none; }
  .site-head nav { margin-left: auto; }
  .menu { display: flex; flex-wrap: wrap; gap: 4px 16px;
          margin: 0; padding: 0; list-style: none; }
  .menu a { display: block; padding: 6px 2px; color: white; text-decoration: none; }
  .menu a:hover, .menu a:focus-visible { text-decoration: underline; }
</style>
<header class="site-head">
  <a class="logo" href="#home">দেশি হাট</a>
  <nav aria-label="প্রধান মেনু">
    <ul class="menu">
      <li><a href="#home">হোম</a></li>
      <li><a href="#shari">শাড়ি</a></li>
      <li><a href="#mango">আম</a></li>
      <li><a href="#offer">অফার</a></li>
      <li><a href="#help">সাহায্য</a></li>
    </ul>
  </nav>
</header>

গাঢ় নীল একটি পট্টি — বাঁয়ে হলুদ “দেশি হাট”, ডানে পাঁচটি সাদা লিংক। এখানে দুটি flex container একটির ভেতরে আরেকটি: বাইরের .site-head লোগো আর nav সাজায়, ভেতরের .menu লিংকগুলো সাজায়। ফোনের মতো সরু পর্দা দেখতে .site-head-এ max-width: 300px; যোগ করুন — nav পরের লাইনে নেমে যায়, আর লিংকগুলোও দরকার হলে দুই লাইনে ভাঁজ হয়। নতুন লাইনেও margin-left: auto মেনুকে ডানে ঠেলে রাখে; বাঁয়ে চাইলে লাইনটি মুছে দেখুন।

লিংক অনেক হলে কী করবেন? দশটি লিংক ফোনে চার লাইন জুড়ে বসলে আসল লেখা অনেক নিচে চলে যায়। তখন প্রচলিত সমাধান একটি “মেনু” বোতাম, যা চাপলে তালিকা খোলে — পর্ব ১১-এর <details> বা popover দিয়ে JavaScript ছাড়াই, অথবা JavaScript দিয়ে। বোতাম যেভাবেই বানান, ভেতরের তালিকাটি একই <nav><ul> থাকবে, আর তার সাজানো একই flex দিয়ে — শুধু flex-direction: column.

৪ · কার্ডের সারি — সমান উচ্চতা, বোতাম সবসময় তলায়

অনলাইন দোকানের তিনটি পণ্য পাশাপাশি। একটির বর্ণনা এক লাইনের, আরেকটির চার লাইনের। ডিজাইনার চান তিনটি কার্ড সমান উঁচু হোক আর “কার্টে যোগ করুন” বোতাম তিনটি একই রেখায় তলায় বসুক। এখন দুটি স্তরের flex যথেষ্ট — বাইরের সারি আর প্রতিটি কার্ডের ভেতর। ধাপে ধাপে দেখুন:

align-items: flex-startযার যতটা লেখা, ততটা উঁচু
আম

৫ কেজির ঝুড়ি।

কিনুন
জামদানি

হাতে বোনা, নারায়ণগঞ্জের তাঁতিদের তৈরি। প্রতিটির নকশা আলাদা।

কিনুন
পাটের ব্যাগ

মজবুত হাতল।

কিনুন
.row { display: flex; align-items: flex-start; }
ডিফল্ট stretchকার্ড সমান উঁচু, বোতাম এলোমেলো
আম

৫ কেজির ঝুড়ি।

কিনুন
জামদানি

হাতে বোনা, নারায়ণগঞ্জের তাঁতিদের তৈরি। প্রতিটির নকশা আলাদা।

কিনুন
পাটের ব্যাগ

মজবুত হাতল।

কিনুন
.row { display: flex; }  /* align-items: stretch ডিফল্ট */
কার্ডের ভেতরও flexবোতাম তলায়, এক রেখায়
আম

৫ কেজির ঝুড়ি।

কিনুন
জামদানি

হাতে বোনা, নারায়ণগঞ্জের তাঁতিদের তৈরি। প্রতিটির নকশা আলাদা।

কিনুন
পাটের ব্যাগ

মজবুত হাতল।

কিনুন
.card { display: flex; flex-direction: column; }
.card .btn { margin-top: auto; }
flex-wrap দিলেসমান উচ্চতা শুধু একই লাইনে
আম

৫ কেজির ঝুড়ি।

কিনুন
জামদানি

হাতে বোনা, নারায়ণগঞ্জের তাঁতিদের তৈরি। প্রতিটির নকশা আলাদা।

কিনুন
পাটের ব্যাগ

মজবুত হাতল।

কিনুন
মধু

সুন্দরবনের খাঁটি মধু, ৫০০ গ্রামের বোতল, কাচের বয়ামে।

কিনুন
.row { display: flex; flex-wrap: wrap; }
.card { flex: 1 1 40%; }  /* প্রতি লাইনে ২টি */
  1. যার যতটা লেখা, ততটা উঁচু। এখানে ইচ্ছা করে align-items: flex-start দিয়েছি, যাতে বোঝা যায় ডিফল্টে কী লুকিয়ে আছে। প্রতিটি কার্ড নিজের content-এর মাপে — মাঝেরটি লম্বা, বাকি দুটি খাটো।
  2. ডিফল্ট stretch — বিনা খরচে সমান উচ্চতা। লাইনটি মুছে দিতেই align-items তার ডিফল্ট মান stretch-এ ফিরল: প্রতিটি কার্ড cross axis-এ টেনে লাইনের সবচেয়ে উঁচু কার্ডের সমান হলো। কিন্তু বোতামগুলো এখনো লেখার ঠিক নিচে — তিনটি তিন উচ্চতায়। কার্ড লম্বা হয়েছে, ভেতরের জিনিস জানে না।
  3. কার্ডের ভেতরটাও flex column. প্রতিটি কার্ডকে নিজেই একটি flex container বানালাম, ওপর থেকে নিচে। এখন কার্ডের ভেতরের বাড়তি খাড়া জায়গাটাও free space. বোতামে margin-top: auto দিতেই সেই জায়গা বোতামের ওপরে চলে গেল — তিনটি বোতাম কার্ডের তলায়, একই রেখায়।
  4. ভাঁজ হলে প্রতিটি লাইন আলাদা। চারটি কার্ড, প্রতি লাইনে দুটি। প্রথম লাইনের দুটি কার্ড পরস্পর সমান, দ্বিতীয় লাইনের দুটিও সমান — কিন্তু দুই লাইনের উচ্চতা আলাদা। কারণ Flexbox প্রতিটি flex line-কে আলাদাভাবে হিসাব করে; এক লাইন আরেক লাইনের খবর রাখে না। পুরো জালে সব সারি-কলাম মেলাতে চাইলে সেটা Grid-এর কাজ — অংশ ৮-এ আবার আসছি।

এবার নিজে চালিয়ে দেখুন। এখানে বোতামগুলো আসল <button>, লিংক নয় — কারণ “কার্টে যোগ” একটি কাজ, অন্য পাতায় যাওয়া নয়:

.row {
  display: flex;
  gap: 12px;
  font-family: sans-serif;
}
.card {
  flex: 1;                 /* তিনটি সমান চওড়া */
  display: flex;           /* কার্ডের ভেতরটাও flex */
  flex-direction: column;
  padding: 12px;
  border: 1px solid #ccd;
  border-radius: 10px;
}
.card h3, .card p { margin: 0 0 8px; }
.btn {
  margin-top: auto;        /* বাড়তি জায়গা ওপরে, বোতাম তলায় */
  padding: 8px;
  border: 0;
  border-radius: 6px;
  background: #006a4e;
  color: white;
  font: inherit;
}

তিনটি সমান চওড়া, সমান উঁচু কার্ড; মাঝেরটির বর্ণনা লম্বা হলেও তিনটি সবুজ বোতাম একই রেখায় তলায়। flex: 1 মানে 1 1 0% — শুরুর মাপ শূন্য ধরে সব জায়গা সমান ভাগ, তাই লেখা কম-বেশি হলেও চওড়া সমান থাকে। চেষ্টা করুন: (১) margin-top: auto; লাইনটি মুছুন — বোতাম লেখার ঠিক নিচে উঠে এসে তিন উচ্চতায় বসে। (২) .row-এ align-items: flex-start; যোগ করুন — কার্ডগুলো আর সমান উঁচু থাকে না। (৩) flex: 1-কে flex: auto করুন — লম্বা লেখার কার্ড বেশি চওড়া হয়, কারণ শুরুর মাপ এখন content থেকে আসে।

আরও গভীরে: spec-এর flex layout algorithm কয়েকটি ধাপে চলে — প্রথমে প্রতিটি item-এর শুরুর মাপ (flex-basis) ঠিক হয়, তারপর item-গুলো লাইনে ভাগ হয়, তারপর প্রতিটি লাইনের free space flex-grow অনুপাতে ভাগ হয় (জায়গা কম পড়লে flex-shrink × শুরুর মাপ অনুপাতে কাটা হয়)। এরপর যদি কোনো main-axis margin auto থাকে, বাকি জায়গা সেগুলোই নেয় — আর তখন justify-content-এর হাতে বিলোনোর মতো কিছু থাকে না। এজন্যই auto margin আর justify-content একসাথে দিলে auto margin জেতে। সবশেষে cross axis: লাইনের উচ্চতা ঠিক হয়, আর stretch পাওয়া item-এর উচ্চতা সেই লাইনের মাপে বসানো হয় — শর্ত হলো item-এর নিজের height auto হতে হবে।

৫ · Media object আর input group — ছবির পাশে লেখা, ঘরের পাশে বোতাম

ফেসবুকের মন্তব্য, পণ্যের রিভিউ, খবরের তালিকা, চ্যাটের বার্তা — সবখানে একটি একই আকৃতি: বাঁ দিকে একটি ছোট ছবি বা আইকন, ডান দিকে বাকি জায়গা জুড়ে লেখা। এর একটি নামও আছে — media object. এখানে তিনটি সিদ্ধান্ত লাগে: ছবি কখনো চাপা পড়বে না (flex: none), লেখা বাকি সব জায়গা নেবে (flex: 1), আর ছবি লেখার উচ্চতায় টেনে লম্বা হবে না (align-items: flex-start).

.media {
  display: flex;
  align-items: flex-start;  /* ছবি টেনে লম্বা হবে না */
  gap: 12px;
  max-width: 460px;
  font-family: sans-serif;
}
.avatar {
  flex: none;               /* কখনো চাপা পড়বে না */
  width: 48px;
  aspect-ratio: 1;          /* উচ্চতা = চওড়া */
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f4b400;
  font-size: 22px;
}
.media-body { flex: 1; min-width: 0; }
.media-body h4 { margin: 0 0 4px; }
.media-body p { margin: 0; line-height: 1.5; }
small { color: #556; font-weight: normal; }

বাঁয়ে হলুদ গোল একটি “র”, ডানে নাম, সময় আর কয়েক লাইনের রিভিউ। খেয়াল করুন, গোলটির ভেতরের অক্ষরও flex দিয়ে মাঝখানে — অংশ ২-এর কৌশল ছোট আকারে। চেষ্টা করুন: (১) align-items: flex-start; লাইনটি মুছুন — গোলটি লেখার পুরো উচ্চতায় টেনে লম্বা ডিম হয়ে যায়, কারণ ডিফল্ট stretch উচ্চতা ঠিক করে দেয় আর aspect-ratio তখন আর কাজ করে না। (২) .media-body-র flex: 1; আর .avatar-এর flex: none; দুটিই মুছুন — লম্বা লেখা জায়গা চাইতে গিয়ে গোলটিকে চেপে সরু করে দেয়। aria-hidden দেওয়া হয়েছে কারণ অক্ষরটি শুধু সাজ; নামটা পাশেই লেখা আছে।

Input group — সার্চের ঘর আর বোতাম এক সাথে

দোকানের মাথায় সার্চ বক্স: একটি লম্বা ঘর, তার গায়ে লাগানো “খুঁজুন” বোতাম। চাওয়া হলো, বোতাম নিজের মাপে থাকবে, ঘরটি বাকি সব চওড়া নেবে, আর ফোনের সরু পর্দায়ও বোতাম বাক্সের বাইরে বেরোবে না। একে বলে input group. HTML-এ একটি জিনিস বাদ দেবেন না — ঘরের label. নকশায় লেবেল দেখানোর জায়গা না থাকলে সেটি চোখের আড়ালে রাখুন (visually hidden), মুছে দেবেন না; placeholder লেবেলের বদলি নয়।

.search {
  display: flex;
  max-width: 420px;
}
.search input {
  flex: 1;
  min-width: 0;      /* সরু পর্দায় ঘর ছোট হতে পারবে */
  padding: 10px 16px;
  border: 2px solid #006a4e;
  border-right: 0;
  border-radius: 999px 0 0 999px;
  font-size: 16px;
}
.search button {
  flex: none;        /* বোতাম নিজের মাপে স্থির */
  padding: 0 20px;
  border: 0;
  border-radius: 0 999px 999px 0;
  background: #006a4e;
  color: white;
  font-size: 16px;
}
.sr-only {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

পিল-আকারের একটি সার্চ বক্স: বাঁয়ে সবুজ বর্ডারের ঘর, ডানে গায়ে লাগানো সবুজ “খুঁজুন”। ঘরে কিছু লিখে Enter চাপুন — প্রিভিউর নিচে দেখাবে কী জমা হতো। চেষ্টা করুন: .search-এর max-width-কে 200px করুন — ঘর ছোট হয়, বোতাম ঠিক থাকে। এবার min-width: 0; লাইনটি মুছে দিন — <input>-এর নিজস্ব একটি ডিফল্ট চওড়া আছে (প্রায় ২০টি অক্ষরের সমান), automatic minimum size তার চেয়ে ছোট হতে দেয় না, তাই বোতাম বাক্সের বাইরে ঠেলে বেরিয়ে যায়। ফোনে এই বাগটিই সবচেয়ে বেশি দেখা যায়। .sr-only লেবেলটিকে চোখের আড়ালে রাখে, কিন্তু screen reader ঘরে ঢুকলেই “পণ্য খুঁজুন” পড়ে।

৬ · পুরো পাতার কাঠামো — sticky footer আর holy-grail-lite

“যোগাযোগ” বা “ধন্যবাদ”-এর মতো ছোট পাতায় একটি পরিচিত দৃশ্য: content মাত্র কয়েক লাইন, তাই footer পাতার মাঝামাঝি উঠে এসেছে, তার নিচে খালি সাদা জায়গা। চাওয়া হলো — content কম হলে footer পর্দার তলায় থাকবে, বেশি হলে স্বাভাবিকভাবে content-এর পরে। একে বলে sticky footer. (এটি position: sticky নয় — পর্ব ২৪-এর সেই জিনিস আলাদা; এখানে footer স্ক্রলের সাথে আটকে থাকে না।)

সমাধান: পুরো body-কে একটি খাড়া flex container বানান, তার উচ্চতা অন্তত পর্দার সমান করুন, আর বাড়তি সব খাড়া জায়গা main-কে দিয়ে দিন। প্রিভিউর ফ্রেম নিজের উচ্চতা content দেখে ঠিক করে, তাই নিচের উদাহরণে body-র বদলে একটি .page বাক্স আর পর্দার উচ্চতার বদলে 340px নিয়েছি — নিয়ম হুবহু এক:

<style>
  body { margin: 0; font-family: sans-serif; }
  .page {
    display: flex;
    flex-direction: column;
    min-height: 340px;     /* আসল সাইটে body-তে: min-height: 100dvh */
    border: 2px dashed #789;
  }
  header, footer { padding: 10px 16px; color: white; background: #0d1f3c; }
  main { flex: 1; padding: 0 16px; }   /* বাড়তি সব খাড়া জায়গা main-এর */
  footer { background: #334; }
</style>
<div class="page">
  <header>দেশি হাট</header>
  <main>
    <h1>ধন্যবাদ!</h1>
    <p>আপনার অর্ডার নম্বর ২০৪৮। বিস্তারিত SMS-এ পাঠানো হয়েছে।</p>
  </main>
  <footer>© দেশি হাট · হেল্পলাইন ১৬xxx</footer>
</div>

ড্যাশ-দেওয়া বাক্সের মাথায় গাঢ় নীল header, তলায় ধূসর-নীল footer, মাঝে অল্প লেখা — আর লেখার নিচের ফাঁকা জায়গাটা main-এর ভেতরে, footer-এর নিচে নয়। চেষ্টা করুন: (১) main-এর flex: 1; মুছুন — footer উঠে এসে লেখার ঠিক নিচে বসে, তলায় খালি জায়গা। (২) আবার ফিরিয়ে <main>-এর ভেতরে আরও ছয়-সাতটি অনুচ্ছেদ যোগ করুন — বাক্স 340px ছাড়িয়ে বড় হয়, footer স্বাভাবিকভাবে content-এর পরে চলে যায়। এজন্যই height নয়, min-height.

আসল সাইটে কোডটি দেখতে এমন:

body {
  margin: 0;                 /* নইলে ডিফল্ট 8px margin-এ স্ক্রলবার আসে */
  display: flex;
  flex-direction: column;
  min-height: 100vh;         /* পুরোনো ব্রাউজারের জন্য fallback */
  min-height: 100dvh;        /* মোবাইলের address bar হিসাব করে */
}
main { flex: 1; }

দুইবার min-height লেখার কারণ পর্ব ২০-এর dvh একক। মোবাইলে স্ক্রল করলে address bar লুকায়-বেরোয়, আর পুরোনো 100vh সাধারণত বড় মাপটা ধরে — ফলে ফোনে footer address bar-এর নিচে লুকাতে পারে। 100dvh বর্তমান দৃশ্যমান উচ্চতা ধরে; এটি এখন Baseline Widely available. যে ব্রাউজার dvh চেনে না, সে দ্বিতীয় লাইনটি বাদ দিয়ে প্রথমটি রাখে — এই হলো CSS-এর সহজ fallback.

Holy-grail-lite — header, তিন কলাম, footer

পুরোনো ওয়েবের একটি বিখ্যাত চ্যালেঞ্জের নাম ছিল holy grail layout: ওপরে header, নিচে footer, মাঝে তিনটি কলাম — বাঁয়ে মেনু, মাঝে মূল লেখা, ডানে বিজ্ঞাপন বা সম্পর্কিত লিংক; তিন কলাম সমান উঁচু, আর মাঝেরটি বাকি সব চওড়া নেবে। এর ইতিহাস ভাবনার প্রশ্ন ২-এ। Flexbox-এ একটি হালকা রূপ খুব সহজ, আর flex-wrap দিলে media query ছাড়াই সরু পর্দায় কলামগুলো একটির নিচে আরেকটি চলে আসে:

.columns { display: flex; flex-wrap: wrap; gap: 16px; }
.columns > nav   { flex: 1 1 160px; }   /* শুরু 160px, বাড়তে পারে */
.columns > main  { flex: 3 1 320px; }   /* বাড়তি জায়গার ৩ ভাগ পাবে */
.columns > aside { flex: 1 1 160px; }

নিচের ডায়াগ্রামে একই তিনটি কলাম, শুধু container-এর চওড়া বদলাচ্ছে — আর শেষ ধাপে একটি বিপদ:

চওড়া পর্দাতিন কলাম এক লাইনে
header
nav (মেনু)
main — মূল লেখা
aside
footer
container: 560px → 110 + 200 + 110 + ফাঁক = 432 ≤ 560 → এক লাইন
মাঝারি পর্দাaside নিচে নেমে গেল
header
nav (মেনু)
main — মূল লেখা
aside
footer
container: 380px → 110 + 200 + ফাঁক = 316 আঁটে, aside আঁটে না → পরের লাইন
ফোনসব একটির নিচে আরেকটি
header
nav (মেনু)
main — মূল লেখা
aside
footer
container: 240px → 110 + 200 আঁটে না → প্রতিটি নিজের লাইনে
HTML-এ main আগে, CSS-এ order দিয়ে nav বাঁয়েTab-এর ক্রম লাফায়
nav
Tab ৩, ৪
main
Tab ১, ২
aside
Tab ৫
<main>…</main> <nav>…</nav> <aside>…</aside>
nav { order: -1; }   /* চোখে বাঁয়ে, Tab-এ মাঝখানে */
  1. চওড়া পর্দা — তিন কলাম। তিনটির শুরুর মাপ (flex-basis) যোগ করলেও container-এর চেয়ে কম, তাই সবাই এক লাইনে। বাড়তি জায়গা ভাগ হয় flex-grow অনুপাতে, ১ : ৩ : ১ — মাঝের কলাম সবচেয়ে বেশি পায়। তিন কলাম সমান উঁচু, কারণ সেই পরিচিত stretch.
  2. মাঝারি পর্দা — একটি কলাম নিচে। ব্রাউজার বাঁ থেকে item বসাতে বসাতে দেখে, aside আর এই লাইনে আঁটছে না, তাই সেটি পরের লাইনে নামে এবং একা পুরো চওড়া জুড়ে বসে। কোনো media query লেখা হয়নি — শুরুর মাপগুলোই “breakpoint”-এর কাজ করছে।
  3. ফোন — একটির নিচে আরেকটি। এখন nav আর main-ও এক লাইনে আঁটে না। তিনটি তিন লাইনে, প্রতিটি পুরো চওড়া।
  4. বিপদ — order দিয়ে সাজানো। পুরোনো holy grail-এ main HTML-এ আগে থাকত, চোখে nav বাঁয়ে; Flexbox-এ order: -1 দিয়ে এটা সহজ। কিন্তু keyboard-এর Tab আর screen reader চলে HTML-এর ক্রমে: প্রথমে main-এর লিংক (১, ২), তারপর হঠাৎ বাঁয়ের nav (৩, ৪), তারপর ডানে aside (৫)। চোখের ক্রম আর focus-এর ক্রম মেলে না — অংশ ৯-এ এর নিয়মটি দেখব।

৭ · ট্যাগ-চিপ আর দুই-ভাগের toolbar

দোকানের পণ্য তালিকার ঠিক ওপরে প্রায়ই একটি পট্টি থাকে: বাঁ দিকে বিভাগের ছোট ছোট গোল বোতাম — “সব”, “শাড়ি”, “আম”, “মধু” — যাদের বলে chip; ডান দিকে “সাজান” ড্রপডাউন। এখানে দুটি প্যাটার্ন একসাথে। চিপের তালিকা হলো flex-wrap আর gap-এর ক্লাসিক কাজ — যত চিপ আঁটে, তত এক লাইনে, বাকিরা পরের লাইনে, সব জায়গায় সমান ফাঁক। আর পুরো পট্টিটি একটি “split” toolbar: দুটি দল, একটি বাঁয়ে, একটি ডানে।

gap আসার আগে চিপের মাঝে ফাঁক দিতে প্রতিটির ডানে-নিচে margin দেওয়া হতো, আর শেষ চিপের বাড়তি margin লুকাতে container-এ negative margin. Flexbox-এ gap ২০১৮ থেকে ২০২১ সালের মধ্যে ধাপে ধাপে সব প্রধান ব্রাউজারে এসেছে, এখন Baseline Widely available — margin-এর কৌশল আর লাগে না।

.toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;  /* দুই দল দুই প্রান্তে */
  align-items: center;
  gap: 12px;
  padding: 10px;
  background: #f4f6fa;
  font-family: sans-serif;
}
.chips {
  display: flex;
  flex-wrap: wrap;                 /* না আঁটলে পরের লাইনে */
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.chips button {
  padding: 6px 14px;
  border: 1px solid #99a;
  border-radius: 999px;
  background: white;
  font: inherit;
}
.chips [aria-pressed="true"] { background: #0d1f3c; color: white; }
.actions { display: flex; align-items: center; gap: 6px; }

হালকা ধূসর পট্টি: বাঁয়ে ছয়টি গোল চিপ (“সব” গাঢ় নীল, কারণ সেটি বেছে নেওয়া), ডানে “সাজান” আর একটি ড্রপডাউন। এখানে space-between ঠিক আছে, কারণ item মাত্র দুটি — চিপের দল আর ডানের দল — আর সংখ্যাটা বদলাবে না। চেষ্টা করুন: (১) .toolbar-এ max-width: 340px; যোগ করুন — চিপগুলো দুই লাইনে ভাঁজ হয়, আর ডানের দল নিজের লাইনে নেমে যায়। (২) .chips-এর gap: 8px; মুছুন — চিপগুলো গায়ে গায়ে লেগে যায়। চিপ আসলে <button>, আর বেছে নেওয়া অবস্থা aria-pressed দিয়ে বলা — তাই screen reader বলে “শাড়ি, toggle button, not pressed”. রঙের বদল CSS সেই attribute selector দিয়েই ধরেছে, আলাদা class লাগেনি।

৮ · প্রজেক্ট: অনলাইন দোকানের পণ্য তালিকা — আর কোথায় Flexbox থামে

এবার সব জোড়া লাগাই। একটি বাংলাদেশি অনলাইন দোকানের পণ্যের তালিকা: প্রতিটি কার্ডে ছবির জায়গা, নাম, দাম টাকায় (৳), কোনোটিতে আগের দাম কাটা, আর তলায় বোতাম। ব্যবহার হলো অংশ ২-এর মাঝখানে বসানো (ছবির ঘরে আইকন), অংশ ৪-এর সমান উচ্চতা আর তলায় বোতাম, অংশ ৭-এর ভাঁজ আর gap. পণ্যের তালিকা আসলে একটি তালিকা, তাই HTML-এ <ul> আর <li>:

<style>
  .shop { display: flex; flex-wrap: wrap; gap: 16px;
          margin: 0; padding: 0; list-style: none; font-family: sans-serif; }
  .item { flex: 1 1 180px; display: flex; flex-direction: column;
          border: 1px solid #dde; border-radius: 12px; overflow: hidden; }
  .thumb { height: 110px; display: flex; justify-content: center;
           align-items: center; font-size: 48px;
           background: linear-gradient(135deg, #e8f5ee, #fff4d6); }
  .info { flex: 1; display: flex; flex-direction: column; gap: 6px; padding: 12px; }
  .info h3 { margin: 0; font-size: 17px; }
  .price { margin: 0; font-weight: bold; color: #006a4e; }
  .price del { color: #778; font-weight: normal; }
  .info button { margin-top: auto; padding: 8px; border: 0; border-radius: 8px;
                 background: #b3122e; color: white; font: inherit; }
</style>
<ul class="shop">
  <li class="item"><div class="thumb" aria-hidden="true">🥭</div><div class="info"><h3>রাজশাহীর হিমসাগর আম, ৫ কেজি</h3><p class="price">৳ ৯৫০</p><button type="button">কার্টে যোগ করুন</button></div></li>
  <li class="item"><div class="thumb" aria-hidden="true">🧵</div><div class="info"><h3>ঢাকাই জামদানি শাড়ি</h3><p class="price">৳ ৮,৫০০ <del>৳ ৯,২০০</del></p><button type="button">কার্টে যোগ করুন</button></div></li>
  <li class="item"><div class="thumb" aria-hidden="true">🍯</div><div class="info"><h3>সুন্দরবনের খাঁটি মধু, ৫০০ গ্রাম</h3><p class="price">৳ ৬৫০</p><button type="button">কার্টে যোগ করুন</button></div></li>
  <li class="item"><div class="thumb" aria-hidden="true">👜</div><div class="info"><h3>পাটের ব্যাগ</h3><p class="price">৳ ৪৫০</p><button type="button">কার্টে যোগ করুন</button></div></li>
  <li class="item"><div class="thumb" aria-hidden="true">🍵</div><div class="info"><h3>শ্রীমঙ্গলের চা পাতা, ৪০০ গ্রাম</h3><p class="price">৳ ৩২০</p><button type="button">কার্টে যোগ করুন</button></div></li>
</ul>

পাঁচটি পণ্যের কার্ড — প্রতিটির ওপরে হালকা সবুজ-হলুদ ঘরে একটি আইকন, নিচে নাম, সবুজ রঙে দাম (জামদানির পাশে কাটা আগের দাম), আর তলায় লাল বোতাম; প্রতিটি লাইনের কার্ড সমান উঁচু, বোতাম এক রেখায়। কিন্তু শেষ লাইনটি খেয়াল করুন: প্রিভিউর চওড়া অনুযায়ী শেষ লাইনে যে এক-দুটি কার্ড পড়েছে, তারা বাকি জায়গা ভরতে ওপরের কার্ডগুলোর চেয়ে বেশি চওড়া হয়ে গেছে। চেষ্টা করুন: (১) .item-এ max-width: 240px; যোগ করুন — শেষ লাইন কম ফোলে, কিন্তু এবার ডানে খালি জায়গা থাকে আর কলামগুলো ওপরের সাথে মেলে না। (২) flex: 1 1 180px-কে flex: 0 0 180px করুন — সব কার্ড সমান, কিন্তু পুরো সারি আর চওড়া ভরে না।

কখন Flexbox ভুল টুল

উপরের শেষ লাইনের সমস্যাটা কোনো বাগ নয় — এটি Flexbox-এর স্বভাব। Flexbox এক দিকের লেআউট: প্রতিটি লাইন নিজের item নিয়ে নিজের মতো জায়গা ভাগ করে, ওপরের লাইনের কলাম কোথায় আছে সে জানে না। কিন্তু পণ্যের জাল, ফটো গ্যালারি বা ড্যাশবোর্ডে আপনি চান দুই দিকে মিল — সারিও মিলবে, কলামও মিলবে, শেষ লাইনের কার্ডও ওপরের কার্ডের সমান চওড়া হবে। এটি Grid-এর কাজ: grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) এক লাইনেই সমস্যাটা মিটিয়ে দেয় (পর্ব ২৭ আর ২৮-এ বিস্তারিত)।

Flexbox বেছে নিন যখন

জিনিসগুলো এক সারি বা এক কলামে বসবে, আর প্রত্যেকের মাপ তার content ঠিক করবে: navbar, বোতামের দল, চিপ, toolbar, কার্ডের ভেতরের সাজানো, media object, input group, মাঝখানে বসানো।

content থেকে বাইরে

Grid বেছে নিন যখন

আগে একটি জাল (সারি আর কলাম) আঁকা হবে, তারপর জিনিস সেই ঘরে বসবে: পণ্য বা ছবির গ্যালারি, পুরো পাতার কাঠামো, ফর্মের লেবেল-ঘরের সারি, ড্যাশবোর্ড।

কাঠামো থেকে ভেতরে

বাস্তবে দুটি প্রায় সবসময় একসাথে চলে: পাতার কাঠামো আর পণ্যের জাল Grid-এ, আর প্রতিটি কার্ডের ভেতরে, header-এর ভেতরে, বোতামের দলে Flexbox. একটি অন্যটির প্রতিযোগী নয়, পরিপূরক।

৯ · প্রচলিত বাগ আর অ্যাক্সেসিবিলিটি — DOM-এর ক্রম = চোখের ক্রম

Flexbox-এর বেশিরভাগ “রহস্যময়” সমস্যা চারটি জায়গা থেকে আসে। প্রতিটির লক্ষণ আর ওষুধ নিচের টেবিলে:

লক্ষণকারণওষুধ
লম্বা লিংক, ইমেইল, কোড বা ইনপুট থাকলে সারি পর্দার বাইরে বেরিয়ে যায়, পাশে স্ক্রলবারflex item-এর min-width: auto — content-এর min-content চওড়ার চেয়ে ছোট হতে দেয় নাযে item বাড়ে-কমে তাতে min-width: 0; দরকারে লেখায় overflow-wrap: anywhere বা text-overflow: ellipsis
সারির ভেতরের ছবি টেনে লম্বা বা চ্যাপ্টা হয়ে যায়ডিফল্ট align-items: stretch ছবির উচ্চতা লাইনের সমান করে দেয়container-এ align-items: flex-start (বা ছবিতে align-self: start); নির্দিষ্ট মাপের বাক্সে ছবি ভরাতে object-fit: cover
width: 300px দিয়েছি, কিন্তু item অন্য মাপেরflex-basis auto না হলে main axis-এ width উপেক্ষিত হয়; তারপর grow/shrink মাপ আরও বদলায়একটিই লিখুন — flex: 0 0 300px; নিশ্চিত সীমা চাইলে min-width/max-width, যা flex-কেও আটকায়
width: 33.33% আর gap দিলে প্রতি লাইনে তিনটির বদলে দুটিশতাংশের যোগফল ১০০%, তার ওপর gap — মোট চওড়া container ছাড়িয়ে যায়flex: 1 1 0, বা calc((100% - 2 * 16px) / 3), বা Grid; gap নিজে শতাংশে না দিয়ে px/rem-এ দিন

শতাংশের gap-এ আরেকটি ফাঁদ: উচ্চতা auto হলে row-gap-এর শতাংশ চক্রাকার হিসাবে পড়ে (উচ্চতা নির্ভর করে gap-এর ওপর, gap উচ্চতার ওপর), ফল প্রায়ই প্রত্যাশামতো হয় না। rem-এ gap দিলে প্রশ্নই ওঠে না।

প্রথম বাগটি সবচেয়ে বেশি দেখা যায়, আর চোখে দেখার চেয়ে মেপে দেখা ভালো। নিচের দুটি সারি হুবহু এক, শুধু দ্বিতীয়টিতে লেখার item-এ min-width: 0. কোডটি দুটি সারির চওড়া আর ভেতরের content-এর আসল চওড়া মেপে দেখায়:

// দুটি সারি: বাক্সের চওড়া বনাম ভেতরের content-এর আসল চওড়া
function check() {
  ['bug', 'fixed'].forEach(function (id) {
    const row = document.getElementById(id);
    const text = row.querySelector('.text');
    console.log(id + ': সারি ' + row.clientWidth + 'px, content ' +
      row.scrollWidth + 'px, min-width = ' + getComputedStyle(text).minWidth);
  });
}
// প্রিভিউ layout হওয়া পর্যন্ত অপেক্ষা, তারপর মাপা
(function wait() {
  document.body.offsetWidth ? check() : setTimeout(wait, 50);
})();

প্রথম লাল ফ্রেমের ভেতর থেকে লম্বা রেফারেন্স কোডটি ডানে বেরিয়ে গেছে; দ্বিতীয়টিতে কোডটি ভেঙে কয়েক লাইনে ফ্রেমের ভেতরেই। আউটপুটে bug সারির content-এর চওড়া সারির চওড়ার চেয়ে অনেক বেশি, আর min-width দেখায় auto; fixed সারিতে দুটি সংখ্যা সমান, min-width 0px. সংখ্যাগুলো ফন্টভেদে একটু আলাদা হতে পারে। চেষ্টা করুন: CSS-এ overflow-wrap: anywhere; মুছে আবার চালান — min-width: 0 বাক্সকে ছোট হতে দেয় ঠিকই, কিন্তু লেখা ভাঙার অনুমতি না পেলে বাক্সের বাইরে উপচে পড়ে। দুটি আলাদা সমস্যা, দুটি আলাদা ওষুধ।

অ্যাক্সেসিবিলিটি — চোখ যা দেখে, keyboard তা-ই মানুক

Flexbox চোখের ক্রম বদলানোর তিনটি উপায় দেয়: flex-direction: row-reverse / column-reverse, flex-wrap: wrap-reverse, আর প্রতিটি item-এ order. তিনটিই শুধু আঁকার ক্রম বদলায়। DOM — অর্থাৎ HTML-এ লেখার ক্রম — একটুও বদলায় না। আর screen reader পড়ে DOM-এর ক্রমে, keyboard-এর Tab যায় DOM-এর ক্রমে (tab order). ফল: চোখে দেখা ক্রম আর শোনা বা Tab-এর ক্রম আলাদা হয়ে যায়।

পরীক্ষার হলে খাতা জমা দেওয়ার কথা ভাবুন। রোল নম্বর অনুযায়ী খাতা স্তূপে রাখা আছে, কিন্তু কেউ টেবিলে খাতাগুলো দেখানোর সময় উল্টো ক্রমে সাজিয়ে রাখলেন। দর্শক দেখছেন ৬০ থেকে ১, অথচ পরীক্ষক তুলছেন ১ থেকে ৬০. keyboard ব্যবহারকারীর অভিজ্ঞতা ঠিক এমন — focus চোখের ক্রমের বদলে লাফাতে থাকে।

WCAG 2.2-এর দুটি নিয়ম এখানে সরাসরি খাটে: 1.3.2 Meaningful Sequence (অর্থ বুঝতে ক্রম জরুরি হলে প্রোগ্রাম সেই ক্রম ঠিকমতো পড়তে পারবে) আর 2.4.3 Focus Order (focus এমন ক্রমে যাবে যাতে অর্থ আর কাজ ঠিক থাকে)। তাই নিয়মটি সহজ: যে ক্রমে পড়া উচিত, HTML সেই ক্রমেই লিখুন; CSS দিয়ে ক্রম বদলাবেন না। ছোটখাটো সাজের ক্ষেত্রে ব্যতিক্রম চলে — যেমন media object-এ ছবি ডানে দেখাতে row-reverse, যেখানে ছবিটি শুধু সাজ, focus পায় না, কিছু পড়ে শোনায় না।

নতুন একটি সমাধান আসছে: CSS Display Level 4-এ reading-flow নামে একটি property প্রস্তাব করা হয়েছে — যেমন reading-flow: flex-visual বললে ব্রাউজার focus আর পড়ার ক্রম চোখের ক্রম অনুযায়ী মেলাবে। এটি সম্প্রতি Chromium-ভিত্তিক ব্রাউজারে (Chrome, Edge) চালু হয়েছে, কিন্তু এখনো Baseline নয়। তাই আজকের নিরাপদ পথ এখনো সেটাই — DOM-এর ক্রম ঠিক রাখা। আর নিজের পাতায় যাচাই করতে মাউস সরিয়ে রেখে শুধু Tab চেপে পুরো পাতা ঘুরে দেখুন; DevTools-এর Elements প্যানেলে flex container-এর পাশে “flex” লেখা ছোট ব্যাজে চাপলে প্রতিটি item, ফাঁক আর free space রঙিন দাগে দেখা যায়।

১০ · AI-এর যুগে — Flexbox-এর কোড যাচাই

“একটা responsive navbar বানিয়ে দাও” বললে AI কয়েক সেকেন্ডে কোড দেয়, আর বেশিরভাগ সময় ব্রাউজারে মোটামুটি ঠিকই দেখায়। কিন্তু এই বিষয়ে কিছু ভুল বারবার আসে — কারণ AI বহু বছরের পুরোনো টিউটোরিয়াল আর কোড পড়ে শিখেছে, আর “দেখতে ঠিক” আর “সঠিক” আলাদা জিনিস:

AI প্রায়ই যা দেয়সমস্যাযা হওয়া উচিত
সব <div class="nav-item"> — কোনো <nav>, <ul> নেইdiv-soup; screen reader মেনু বা তালিকা চেনে না<header><nav><ul><li><a>
float: left, clearfix, বা display: -webkit-boxপুরোনো যুগের কৌশল আর অপ্রয়োজনীয় উপসর্গউপসর্গ ছাড়া display: flex
তিন item-এর navbar-এ justify-content: space-betweenitem বাড়লে-কমলে নকশা নড়ে যায়যে দল ডানে যাবে তাতে margin-left: auto
প্রতিটি item-এ নির্দিষ্ট width px-এ, flex-wrap নেইফোনে সারি পর্দার বাইরে, পাশে স্ক্রলflex-wrap: wrap আর flex: 1 1 200px; দরকারে min-width: 0
মোবাইল লেআউটে order দিয়ে অংশ ওলট-পালটTab আর screen reader-এর ক্রম চোখের সাথে মেলে নাHTML-এর ক্রমই ঠিক করা
পুরো-পর্দার অংশে শুধু height: 100vhফোনে address bar-এর নিচে লুকায়; content বেশি হলে উপচে পড়েmin-height: 100dvh (আগে 100vh fallback)
পণ্যের জাল Flexbox-এ, শেষ লাইন ফোলানোদুই দিকের লেআউট এক দিকের টুলেGrid-এ repeat(auto-fill, minmax(…))

কাজ দেওয়ার সময়ই শর্তগুলো বলে দিন, তাহলে যাচাইয়ের কাজ অর্ধেক কমে যায়:

একটি অনলাইন দোকানের header-এর HTML আর CSS লিখে দাও। শর্ত: ১. HTML হবে semantic: header, nav (aria-label সহ), ul, li, a — কোনো div-soup নয়। ২. লেআউট শুধু আধুনিক Flexbox দিয়ে — কোনো float, clearfix, -webkit- উপসর্গ বা নির্দিষ্ট px width নয়। ৩. লোগো বাঁয়ে, লিংকের দল ডানে margin-left: auto দিয়ে; ছোট পর্দায় flex-wrap আর gap দিয়ে ভাঁজ হবে, পাশে স্ক্রল আসবে না। ৪. order বা row-reverse ব্যবহার করবে না — HTML-এর ক্রমই চোখের ক্রম। ৫. লিংকে দৃশ্যমান :focus-visible থাকবে। ৬. প্রতিটি flex property কেন দিলে, এক লাইনে বাংলায় বুঝিয়ে দাও, আর কোন অংশ ৩২০px চওড়া ফোনে পরীক্ষা করতে হবে বলো।

AI-এর লেখা Flexbox লেআউট হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:

  • একটি div আড়াআড়ি ও খাড়া দুই দিকে মাঝখানে বসাবেন কীভাবে? — parent-এ display: flex; justify-content: center; align-items: center (অথবা item-এ margin: auto, বা Grid-এ place-items: center).
  • Navbar-এ লোগো বাঁয়ে, বাকি সব ডানে — সবচেয়ে মজবুত উপায়? — ডানের দলের প্রথম item-এ margin-left: auto; item-এর সংখ্যা বদলালেও নকশা ঠিক থাকে।
  • flex: 1 মানে কী? — flex: 1 1 0%: বাড়বে, কমবে, শুরুর মাপ শূন্য — তাই সব item বাড়তি জায়গা সমান ভাগে পায়।
  • flex item-এর ভেতরের লম্বা লেখা কেন বাক্স ভেঙে বেরিয়ে যায়? — flex item-এর ডিফল্ট min-width: auto; সমাধান min-width: 0.
  • Flexbox আর Grid-এর মূল পার্থক্য? — Flexbox এক দিকের (সারি বা কলাম), content থেকে মাপ ঠিক হয়; Grid দুই দিকের, আগে জাল আঁকা হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে কোড-কপিকারী থেকে লেআউট-চিন্তক বানাবে।

প্র ০১“একটি div মাঝখানে বসানো” কেন বহু বছর ওয়েব ডেভেলপারদের সবচেয়ে বিখ্যাত ঠাট্টা ছিল? সমস্যাটা আসলে কোথায় ছিল?

সমস্যাটা ছিল CSS-এর মূল নকশায়। শুরুর দিকের CSS তৈরি হয়েছিল দলিল সাজানোর জন্য — ওপর থেকে নিচে অনুচ্ছেদ, শিরোনাম, ছবি, যেন একটি পত্রিকার কলাম। এই normal flow-তে চওড়া জানা থাকে (পাতার চওড়া), কিন্তু উচ্চতা জানা থাকে না — content যত, উচ্চতা তত। তাই আড়াআড়ি মাঝখানে বসানো সহজ ছিল: নির্দিষ্ট চওড়ার বাক্সে margin: 0 auto, বাকি জায়গা দুই পাশে সমান ভাগ। কিন্তু খাড়াভাবে “বাকি জায়গা” হিসাব করার মতো কোনো ব্যবস্থাই ছিল না, কারণ normal flow-তে খাড়া auto margin শূন্য ধরা হয়।

ফলে ডেভেলপাররা নানা কৌশল বের করলেন, প্রতিটির নিজস্ব দাম। প্রথম যুগে টেবিল — <td valign="middle"> — যা লেআউটের জন্য টেবিলের অপব্যবহার। তারপর display: table-cell; vertical-align: middle, যা টেবিলের আচরণ ধার করত। নির্দিষ্ট মাপের বাক্সে position: absolute; top: 50% আর উচ্চতার অর্ধেক negative margin — মাপ না জানলে অচল। পরে transform: translate(-50%, -50%) মাপ না জেনেও কাজ করত, কিন্তু অনেক সময় লেখা আধা-পিক্সেলে বসে ঝাপসা দেখাত, আর বাক্সটি flow থেকে বেরিয়ে যেত।

Flexbox প্রথমবার ব্রাউজারকে দুই axis-এ free space হিসাব করে নির্দিষ্ট নিয়মে বিলোনোর সুযোগ দিল। align-items: center আর খাড়া auto margin তখন অর্থবহ হলো। Grid এনে দিল place-items: center. আর ২০২৪ সালে শেষ ফাঁকটিও ভরল — সাধারণ block বাক্সেও align-content: center কাজ করা শুরু করল, যা বহু বছর শুধু flex আর grid-এ সীমিত ছিল।

মূল উপলব্ধি: একটি “সহজ” কাজ কঠিন হলে প্রায়ই দোষ মানুষের নয়, মডেলের — সিস্টেমটি সেই প্রশ্নের জন্য বানানোই হয়নি। CSS যখন দলিলের ভাষা থেকে অ্যাপের লেআউটের ভাষা হলো, তখন নতুন মডেল (flex, grid) লাগল। পুরোনো কৌশলগুলো চিনে রাখুন, যাতে পুরোনো কোড বা AI-এর উত্তরে দেখলে বুঝতে পারেন এখন সেগুলোর দরকার নেই।

প্র ০২“Holy grail layout”-কে একসময় “পবিত্র পাত্র” বলা হতো কেন? floats-এর যুগে সমান উচ্চতার কলাম বানানো এত কঠিন ছিল কেন?

২০০০-এর দশকের মাঝামাঝি প্রায় প্রতিটি সংবাদপত্র, ব্লগ আর পোর্টালের নকশা ছিল একই: header, তিন কলাম, footer. শর্ত তিনটি: মাঝের কলাম নমনীয়, পাশের দুটি নির্দিষ্ট চওড়া; তিন কলাম সমান উঁচু; আর HTML-এ মূল লেখা আগে, যাতে screen reader আর সার্চ ইঞ্জিন আগে আসল content পায়। কিন্তু তখনকার CSS-এ পাশাপাশি বাক্স বসানোর একমাত্র ব্যবহারযোগ্য উপায় ছিল float, যা আসলে বানানো হয়েছিল লেখার পাশে ছবি ভাসিয়ে রাখার জন্য — পুরো পাতার কাঠামোর জন্য নয়।

তিনটি শর্ত একসাথে মেটানো প্রায় অসম্ভব মনে হতো, তাই নাম হলো “holy grail” — যে পবিত্র পাত্র সবাই খোঁজে, কেউ পায় না। ২০০৬ সালে A List Apart-এ Matthew Levine-এর “In Search of the Holy Grail” লেখায় একটি সমাধান প্রকাশিত হয়: container-এ padding দিয়ে দুই পাশে জায়গা রাখা, তারপর float আর negative margin দিয়ে পাশের কলামগুলোকে সেই জায়গায় টেনে আনা, আর relative positioning দিয়ে শেষ সমন্বয়। কাজ করত, কিন্তু কয়েকটি সংখ্যা ভুল হলেই পুরো পাতা ভেঙে পড়ত, আর ব্যাখ্যা করতে লম্বা প্রবন্ধ লাগত।

সমান উচ্চতা ছিল আলাদা মাথাব্যথা। float-এর বাক্স নিজের content-এর মাপে থাকে; পাশের বাক্স কত উঁচু, সে জানে না। ২০০৪ সালে Dan Cederholm-এর “Faux Columns” কৌশল এর ধোঁকা-সমাধান দিল: পুরো container-এর পেছনে একটি খাড়া ডোরা কাটা ব্যাকগ্রাউন্ড ছবি, যাতে মনে হয় কলামগুলো সমান — আসলে বাক্সগুলো আলাদা উচ্চতারই থাকত। অনেকে JavaScript দিয়ে উচ্চতা মেপে সবচেয়ে বড়টা সবাইকে বসিয়ে দিতেন — যা content বদলালে বা ফন্ট দেরিতে লোড হলে আবার ভুল হতো।

Flexbox-এ এই পুরো সংগ্রাম কয়েক লাইনে নেমে আসে: display: flex দিলেই ডিফল্ট stretch কলামগুলো সমান উঁচু করে, flex: 1 মাঝের কলামকে নমনীয় করে। তবে HTML-এ main আগে রেখে চোখে পেছনে দেখানোর পুরোনো ইচ্ছাটি আজ অ্যাক্সেসিবিলিটির দিক থেকে প্রশ্নবিদ্ধ, কারণ Tab-এর ক্রম আর চোখের ক্রম আলাদা হয়ে যায় — সেজন্য আধুনিক পরামর্শ হলো চোখের ক্রমেই HTML লেখা, আর শুরুতে একটি skip link রাখা।

মূল উপলব্ধি: ভুল টুল দিয়ে সঠিক কাজ করতে গেলে কৌশল জমতে থাকে, আর কৌশল মানেই ভঙ্গুরতা। যখন কোনো সমাধানের ব্যাখ্যায় লম্বা প্রবন্ধ লাগে, সেটা প্রায়ই ইঙ্গিত যে ভাষায় একটি নতুন মৌলিক ধারণা দরকার।

প্র ০৩Flexbox দিয়েই তো পুরো পাতা বানানো যায় — তাহলে Grid আলাদা করে লাগল কেন? “content থেকে বাইরে” আর “কাঠামো থেকে ভেতরে” কথাটির মানে কী?

Flexbox-এ প্রতিটি item-এর মাপের যাত্রা শুরু হয় তার content থেকে। flex-basis: auto মানে “আগে দেখো ভেতরে কত লেখা”, তারপর grow বা shrink. container শুধু বাড়তি বা ঘাটতি জায়গা বিলোয়। এজন্যই একে বলা হয় content-out — ভেতর থেকে বাইরে।

Grid উল্টো দিক থেকে ভাবে। আগে container-এ একটি জাল আঁকা হয় — কয়টি কলাম, কয়টি সারি, কোনটি কত চওড়া — তারপর item সেই ঘরে বসে। এটি layout-in, কাঠামো থেকে ভেতরে। একটি কার্ডের লেখা যত লম্বাই হোক, তার কলাম পাশের সারির কলামের সাথেই মেলে। পত্রিকার পাতা, পণ্যের জাল বা ড্যাশবোর্ডে ঠিক এটাই চাই।

Flexbox দিয়ে পুরো পাতা বানানো সম্ভব, এবং Grid আসার আগের কয়েক বছর অনেকে তা-ই করতেন। কিন্তু দাম ছিল: দুই দিকে মেলাতে item-এ শতাংশের চওড়া, calc(), শেষ লাইন ঠিক করতে ফাঁকা “ভূত” item — মানে আবার কৌশল। আরেকটি সূক্ষ্ম সমস্যার কথা ২০১৪ সালের দিকে Jake Archibald লিখেছিলেন: পুরো পাতার কাঠামো Flexbox-এ হলে ধীর নেটওয়ার্কে পাতা ধাপে ধাপে লোড হওয়ার সময় কলামের চওড়া content আসার সাথে সাথে বদলাতে পারে, ফলে লেআউট লাফায়; Grid-এ কাঠামো আগে থেকে ঠিক থাকে, তাই content দেরিতে এলেও কলাম নড়ে না।

Rachel Andrew-সহ CSS Grid-এর প্রচারকেরা একটি সহজ প্রশ্ন শেখাতেন: “আপনি কি সারি অথবা কলামে নিয়ন্ত্রণ চান, নাকি সারি এবং কলাম দুটোতেই?” প্রথমটি Flexbox, দ্বিতীয়টি Grid. Grid-এর subgrid (পর্ব ২৮) পাশাপাশি কার্ডের ভেতরের শিরোনামও এক উচ্চতায় মেলাতে পারে — Flexbox তা পারে না।

মূল উপলব্ধি: দুটি টুল দুটি ভিন্ন প্রশ্নের উত্তর। ভালো লেআউট সাধারণত দুটিই ব্যবহার করে — বাইরের কাঠামো Grid-এ, ভেতরের সাজানো Flexbox-এ। “কোনটি ভালো” না জিজ্ঞেস করে জিজ্ঞেস করুন “এখানে মাপ কে ঠিক করবে — content, না কাঠামো?”

প্র ০৪CSS যদি চোখের ক্রম বদলাতে পারে, তাহলে keyboard আর screen reader সেই নতুন ক্রম মানে না কেন? ব্রাউজার কি নিজেই মিলিয়ে দিতে পারত না?

এর পেছনে ওয়েবের একটি মৌলিক নীতি: HTML অর্থ বলে, CSS চেহারা। DOM-এর ক্রম হলো দলিলের যৌক্তিক ক্রম — লেখক যে ক্রমে কথাগুলো বলতে চান। screen reader, keyboard navigation, সার্চ ইঞ্জিন, reader mode, অনুবাদ টুল, এমনকি CSS বন্ধ থাকলে বা প্রিন্টে — সবাই এই একই ক্রম ধরে। CSS-কে শুধু দৃশ্যমান রূপের দায়িত্ব দেওয়া হয়েছে, যাতে একই content নানা পর্দায় নানা চেহারা নিতে পারে, কিন্তু অর্থ একই থাকে।

তাহলে ব্রাউজার চোখের ক্রম দেখে focus মিলিয়ে দেয় না কেন? প্রথম কারণ, “চোখের ক্রম” নিজেই অস্পষ্ট — দুই কলামে চোখ আগে বাঁ কলাম পুরো পড়বে, নাকি সারি ধরে বাঁ-ডান? ডান থেকে বাঁয়ে লেখা আরবি পাতায়? ব্রাউজারকে অনুমান করতে হতো, আর ভুল অনুমান আরও বিভ্রান্তি আনত। দ্বিতীয় কারণ, রেসপন্সিভ পাতায় চেহারা পর্দার মাপের সাথে বদলায়; focus-এর ক্রমও যদি সাথে সাথে বদলাত, তাহলে একই পাতা ফোন আর ল্যাপটপে আলাদা যৌক্তিক অর্থ পেত।

তাই spec লেখকদের ওপর দায়িত্ব দিয়েছে: CSS Flexbox spec নিজেই সতর্ক করে যে order শুধু দৃশ্যমান ক্রমের জন্য, যৌক্তিক ক্রম বদলাতে HTML বদলাতে হবে। WCAG 2.2-এর 1.3.2 (Meaningful Sequence) আর 2.4.3 (Focus Order) সেই দায়িত্বকে মাপযোগ্য নিয়মে বাঁধে। এর শিকার হন keyboard ব্যবহারকারী আর চোখে কিছুটা দেখে screen reader ব্যবহার করেন এমন মানুষ।

তবে সমস্যাটা বাস্তব, কারণ কখনো কখনো ফোন আর ডেস্কটপে সত্যিই আলাদা ক্রম ভালো দেখায়। এজন্য CSS Working Group reading-flow property-র কাজ শুরু করে — লেখক স্পষ্টভাবে বলবেন “এই container-এ পড়ার ক্রম চোখের ক্রম মেনে চলো”, ব্রাউজারকে অনুমান করতে হবে না। এটি সম্প্রতি Chromium-এ এসেছে, কিন্তু সব ব্রাউজারে না আসা পর্যন্ত এর ওপর ভরসা করা যায় না।

মূল উপলব্ধি: চেহারা আর অর্থ আলাদা রাখার নীতিটাই ওয়েবকে এত নমনীয় করেছে — কিন্তু সেই নমনীয়তার দাম হলো লেখকের দায়িত্ব। CSS দিয়ে যা সাজাচ্ছেন, তা যেন HTML-এর গল্পের সাথে বিরোধ না করে।

প্র ০৫পুরোনো কোডে বা AI-এর উত্তরে display: -webkit-box বা display: -ms-flexbox দেখা যায় কেন? Flexbox-এর তিনটি ভিন্ন সিনট্যাক্সের গল্প থেকে কী শেখার আছে?

Flexbox একবারে আজকের রূপে আসেনি। ২০০৯ সালের প্রথম খসড়ায় এর নাম ছিল display: box, property ছিল box-orient, box-pack, box-flex — Firefox-এর নিজস্ব একটি পুরোনো লেআউট ব্যবস্থা থেকে নেওয়া। WebKit (Safari, তখনকার Chrome) এটিকে -webkit-box নামে চালু করে। ২০১১ সালে খসড়াটি ব্যাপকভাবে বদলায়, নাম হয় display: flexbox — যাকে অনেকে “tweener” বা মাঝামাঝি সিনট্যাক্স বলেন; Internet Explorer 10 এটিকে -ms-flexbox নামে নেয়। তারপর ২০১২ সালে আবার বদলে আজকের চূড়ান্ত রূপ আসে: display: flex, justify-content, align-items, flex.

ফলে কয়েক বছর ধরে সব ব্রাউজারে কাজ করাতে ডেভেলপারদের একই লেআউট তিন-চারবার লিখতে হতো — তিন রকম নাম, তিন রকম মান, আচরণেও ছোটখাটো পার্থক্য। Autoprefixer-এর মতো টুল (পর্ব ৫০) এই কাজ স্বয়ংক্রিয় করেছিল। সেই যুগের অসংখ্য উত্তর আর টিউটোরিয়াল এখনো ইন্টারনেটে আছে, আর AI সেগুলো থেকেই শিখেছে। তাই AI-এর উত্তরে আজও -webkit-box-pack বা -ms-flex-align দেখা যায়, যদিও বহু বছর ধরে উপসর্গ ছাড়া display: flex সব প্রধান ব্রাউজারে চলে। একটি অদ্ভুত ব্যতিক্রম আজও টিকে আছে: কয়েক লাইনের পরে লেখা কেটে “…” দেখাতে -webkit-line-clamp-এর সাথে আজও display: -webkit-box লাগে — এটি spec-এ স্বীকৃত একটি ঐতিহাসিক ব্যতিক্রম, flex layout নয়।

এই অভিজ্ঞতা থেকেই ব্রাউজার নির্মাতারা শিক্ষা নিয়েছেন। উপসর্গ দিয়ে পরীক্ষামূলক ফিচার সবার হাতে ছেড়ে দিলে ডেভেলপাররা সেটি আসল সাইটে ব্যবহার শুরু করেন, তখন spec বদলানো কঠিন হয়ে পড়ে — পুরোনো সাইট ভেঙে যাবে বলে। তাই এখন নতুন ফিচার সাধারণত সেটিং-এর পেছনে (feature flag) পরীক্ষা হয়; তাই ২০১৭ সালে Grid প্রধান ব্রাউজারগুলোতে উপসর্গ ছাড়াই এসেছিল — ব্যতিক্রম শুধু Internet Explorer-এর পুরোনো খসড়ার -ms-grid, যার আজ আর দরকার নেই।

মূল উপলব্ধি: মানদণ্ড (standard) তৈরি হয় ধাপে ধাপে, আর প্রতিটি ধাপের ছাপ পুরোনো কোডে থেকে যায়। কোডের বয়স চিনতে পারা — “এটা ২০১৩ সালের কৌশল” — একজন অভিজ্ঞ ডেভেলপারের গুরুত্বপূর্ণ দক্ষতা, বিশেষ করে যখন AI সব যুগের কোড মিশিয়ে দেয়।

অনুশীলন

প্রথমটিতে সামান্য CSS লিখবেন; বাকিগুলো দেখা, খোঁজা আর ভাবার কাজ।

  1. অ্যাপের মাথা: একটি মোবাইল ব্যাংকিং অ্যাপের মতো ওপরের পট্টি বানাতে চান — বাঁয়ে “← ফিরে যান” বোতাম, ঠিক মাঝখানে “টাকা পাঠান” শিরোনাম, ডানে “?” সাহায্যের বোতাম। বাঁ আর ডানের বোতাম দুটির চওড়া আলাদা। শিরোনাম যেন পুরো পট্টির ঠিক মাঝখানে থাকে — বোতামের মাপের ওপর নির্ভর না করে। Flexbox দিয়ে কীভাবে করবেন?

    শুধু justify-content: space-between দিলে শিরোনাম দুই বোতামের মাঝখানে বসে, পট্টির মাঝখানে নয় — বাঁয়ের বোতাম বড় হলে শিরোনাম ডানে সরে যায়। কৌশল হলো দুই পাশের অংশকে সমান চওড়া বানানো: দুই পাশে flex: 1 (শুরুর মাপ শূন্য, তাই সমান ভাগ), মাঝখানে flex: none. তারপর ডানের অংশের ভেতরে বোতামটিকে ডানে ঠেলে দিন:

    .appbar { display: flex; align-items: center; gap: 8px; }
    .appbar .side { flex: 1; display: flex; }
    .appbar .side:last-child { justify-content: flex-end; }
    .appbar h1 { flex: none; margin: 0; font-size: 18px; }

    HTML-এর ক্রম রাখুন বাঁ-বোতাম, শিরোনাম, ডান-বোতাম — চোখের ক্রমেই। খুব লম্বা শিরোনামের জন্য flex: none-এর বদলে min-width: 0 আর text-overflow: ellipsis ভাবুন। একই সমস্যার আরেকটি পরিষ্কার সমাধান Grid-এ grid-template-columns: 1fr auto 1fr — পর্ব ২৭-এ চেষ্টা করে দেখবেন।

  2. বাস্তব সাইটে flex খুঁজুন: যেকোনো বড় সংবাদপত্র বা অনলাইন দোকানের ওয়েবসাইট খুলুন। DevTools-এর Elements প্যানেলে header-এর ওপর “flex” লেখা ব্যাজটি খুঁজে চাপুন। কয়টি flex container একটির ভেতরে আরেকটি? লোগো আর মেনুর মাঝের ফাঁকা জায়গা কীভাবে তৈরি — space-between, auto margin, নাকি অন্য কিছু? তারপর device toolbar-এ ফোনের মাপে দেখুন কী বদলায়।

    বেশিরভাগ সাইটে দুই-তিন স্তরের flex পাবেন: বাইরের header (লোগো, মেনু, সার্চ, কার্ট), মেনুর তালিকা, আর কখনো কার্টের আইকন-আর-সংখ্যার ছোট দল। ব্যাজ চাপলে ফাঁকা জায়গায় হালকা ডোরা দেখা যায় — সেটিই free space. অনেক সাইটে space-between পাবেন, কিছু সাইটে margin-left: auto বা margin-inline-start: auto. ফোনের মাপে প্রায়ই মেনু লুকিয়ে একটি “মেনু” বোতাম আসে, আর সার্চ বক্স নিজের লাইনে নেমে যায় — হয় flex-wrap দিয়ে, নয় media query দিয়ে flex-direction: column বা order বদলে। order পেলে Tab চেপে দেখুন focus চোখের ক্রমে যায় কি না।

  3. বাগ শিকারি: একটি চ্যাট অ্যাপের বার্তার সারিতে বাঁয়ে ৪০ পিক্সেলের প্রোফাইল ছবি (<img>), ডানে বার্তা। তিনটি অভিযোগ এসেছে — (ক) বার্তা লম্বা হলে প্রোফাইল ছবি লম্বাটে হয়ে যায়; (খ) কেউ একটি লম্বা লিংক পাঠালে পুরো চ্যাট পাশে স্ক্রল করে; (গ) বার্তার বাক্সে width: 70% দেওয়া, কিন্তু চওড়া ঠিক ৭০% হচ্ছে না। প্রতিটির কারণ আর সমাধান বলুন।

    (ক) ডিফল্ট align-items: stretch ছবির উচ্চতা সারির উচ্চতার সমান করে দিচ্ছে, চওড়া ৪০ পিক্সেলেই থাকছে। সমাধান: সারিতে align-items: flex-start (বা ছবিতে align-self: start), ছবিতে flex: none, আর নিরাপত্তার জন্য height: 40px; object-fit: cover.

    (খ) বার্তার item-এর min-width: auto লম্বা লিংককে ভাঙতে দিচ্ছে না, তাই item সারির চেয়ে চওড়া হয়ে যাচ্ছে। সমাধান: বার্তার item-এ min-width: 0, আর লেখায় overflow-wrap: anywhere.

    (গ) width শুধু শুরুর মাপ (flex-basis: auto হলে)। তারপর flex-shrink (ডিফল্ট 1) জায়গা কম পড়লে এটাকে ছোট করে, আর flex-grow থাকলে বড় করে। ঠিক ৭০% চাইলে flex: 0 0 70%, অথবা “সর্বোচ্চ ৭০%” চাইলে max-width: 70% — চ্যাটের বাবলে দ্বিতীয়টিই ভালো।

  4. Flexbox নাকি Grid? নিচের প্রতিটির জন্য কোনটি বেছে নেবেন, এক লাইনে কারণসহ বলুন — (১) বোতামের দল “বাতিল” আর “নিশ্চিত করুন”; (২) ১২টি ছবির গ্যালারি, প্রতি সারিতে সমান ঘর; (৩) ব্লগ পোস্টের নিচে লেখকের ছবি আর পরিচিতি; (৪) একটি ড্যাশবোর্ড — বাঁয়ে মেনু, ওপরে চারটি সংখ্যার কার্ড, নিচে বড় চার্ট; (৫) পোস্টের ট্যাগের তালিকা।

    (১) Flexbox — এক সারির দুটি বোতাম, মাপ লেখা অনুযায়ী; gap আর দরকারে justify-content: flex-end. (২) Grid — সারি আর কলাম দুই দিকেই মেলাতে হবে, শেষ সারিও সমান ঘরে। (৩) Flexbox — ক্লাসিক media object. (৪) Grid পাতার কাঠামোর জন্য (মেনু, কার্ডের সারি, চার্টের এলাকা), আর প্রতিটি কার্ডের ভেতরের সংখ্যা-আইকন সাজাতে Flexbox — দুটি একসাথে। (৫) Flexbox — flex-wrap আর gap; প্রতিটি ট্যাগ নিজের লেখার মাপে, কলাম মেলানোর দরকার নেই।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমসমান উচ্চতার, তলায় বোতামওয়ালা পণ্য-কার্ডের সারি বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. HTML-এ <ul>-এর ভেতরে প্রতিটি পণ্য একটি <li>, ভেতরে নাম, দাম আর <button>
  2. তালিকায় display: flex, flex-wrap: wrap আর gap
  3. প্রতিটি কার্ডে শুরুর মাপ — flex: 1 1 200px
  4. কার্ডের ভেতরটা display: flex; flex-direction: column
  5. বোতামে margin-top: auto
  6. সরু পর্দায়, লম্বা নামে আর শুধু Tab চেপে পরীক্ষা

একটি header-এ তিনটি item: লোগো, লিংকের <nav> আর কার্ট বোতাম। লোগো বাঁয়ে থাকবে, nav আর কার্ট দুটিই ডানে — পরে আরও item যোগ হলেও এই নকশা ঠিক রাখতে কোনটি সবচেয়ে ভালো?

  • header-এ justify-content: space-between
  • header-এ justify-content: flex-end
  • nav-এ margin-left: auto
  • nav আর কার্টে float: right

একটি flex সারিতে তিনটি কার্ড সমান উঁচু, কিন্তু প্রতিটির ভেতরের বোতাম লেখার ঠিক নিচে, তাই তিনটি তিন উচ্চতায়। সব বোতাম কার্ডের তলায় এক রেখায় আনতে কী করবেন?

  • সারিতে align-items: flex-end
  • কার্ডে display: flex; flex-direction: column, বোতামে margin-top: auto
  • প্রতিটি কার্ডে height: 300px
  • বোতামে position: absolute; bottom: 0

একটি flex item-এর ভেতরে একটি লম্বা, ফাঁকাহীন লিংক আছে, আর পুরো সারি পর্দার বাইরে বেরিয়ে যাচ্ছে। item-এ flex: 1 আগেই দেওয়া আছে। মূল কারণ কোনটি?

  • flex-wrap: wrap দেওয়া হয়নি
  • flex-basis খুব বড়
  • gap খুব বেশি
  • flex item-এর ডিফল্ট min-width: auto

একটি flex row-এর item-এ একসাথে width: 300px আর flex-basis: 200px লেখা আছে, flex-grow আর flex-shrink দুটিই 0. item-এর চওড়া কত হবে?

  • 200px
  • 300px
  • 250px
  • container-এর পুরো চওড়া

একটি flex container-এর তিনটি লিংকের মধ্যে HTML-এ শেষ লিংকটিতে order: -1 দেওয়া হলো, ফলে চোখে সেটি প্রথমে দেখা যায়। keyboard-এর Tab চাপলে focus কোন ক্রমে যাবে?

  • চোখে যে ক্রমে দেখা যায় — শেষ লিংক আগে
  • উল্টো ক্রমে — শেষ থেকে প্রথম
  • HTML-এ লেখার ক্রমে — চোখে প্রথম দেখা লিংকে focus আসে সবার শেষে
  • order দেওয়া লিংকে focus যাবেই না

কম content-এর পাতাতেও footer পর্দার তলায় রাখতে (কিন্তু বেশি content হলে content-এর পরে) কোন CSS সবচেয়ে ঠিক?

  • footer-এ position: fixed; bottom: 0
  • body-তে display: flex; flex-direction: column; min-height: 100dvh আর main-এ flex: 1
  • main-এ height: 80vh
  • body-তে display: flex আর footer-এ margin-left: auto

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. প্রতিটি প্যাটার্ন একটি প্রশ্নের উত্তর: বাড়তি জায়গা কে নেবে? auto margin, flex: 1 / flex: none, flex-wrap + gap, আর min-width: 0 — এই চারটিতেই প্রায় সব নকশা।
  2. মাঝখানে: justify-content + align-items: center, অথবা item-এ margin: auto (বড় content-এও ওপরের অংশ হারায় না)।
  3. Navbar-এ ডানের দলে margin-left: auto; কার্ডের ভেতর flex column আর বোতামে margin-top: auto; sticky footer-এ body flex column, min-height: 100dvh, main { flex: 1 }.
  4. প্রচলিত বাগ: লম্বা content → min-width: 0; ছবি টেনে লম্বা → align-items: flex-start; flex-basis width-এর ওপর জেতে; শতাংশের চওড়া + gap উপচে পড়ে।
  5. সারি-কলাম দুই দিকে মেলানো জাল Grid-এর কাজ; আর order/reverse শুধু চোখের ক্রম বদলায় — DOM-এর ক্রম = চোখের ক্রম রাখুন।

পরের পর্বে: এক দিকের সাজানো শেষ, এবার দুই দিকের। CSS Grid — সারি আর কলামের জাল আঁকা, fr একক, আর এই পর্বের পণ্যের তালিকার শেষ লাইনের সমস্যার সরাসরি সমাধান।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান