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

প্রচলিত লেআউট প্যাটার্ন — sidebar, holy grail, dashboard আর gallery

Common CSS Layout Patterns — Primitives, Sidebar, Holy Grail, Full-bleed, Dashboard & Gallery

Taught in Bangla, this lesson collects reusable CSS layout patterns: layout primitives such as stack and cluster, a sidebar that wraps without media queries, the pancake stack and holy grail page frames, a centred reading column with full-bleed sections using named lines, an admin dashboard for a union e-service portal, an image gallery and pricing table, and a guide to choosing the right pattern.

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

  • Every Layout-এর layout primitive-এর ধারণা, আর Stack, Cluster, Center, Switcher-এর নিজের হাতে লেখা রূপ
  • media query ছাড়া ভাঁজ হওয়া sidebar — ব্রাউজার ঠিক কোন হিসাবে সিদ্ধান্ত নেয়
  • pancake stack, holy grail আর full-bleed পড়ার কলাম — পুরো পাতার তিনটি মূল কাঠামো
  • একটি ইউনিয়ন পরিষদের ই-সেবা ড্যাশবোর্ড, ছবির গ্যালারি আর প্রাইসিং টেবিল বানানো
  • কোন সমস্যায় কোন প্যাটার্ন — সিদ্ধান্তের টেবিল, আর রেসপন্সিভ আচরণের প্রথম ঝলক

১ · প্যাটার্ন কেন — একই সমস্যা, বারবার একই সমাধান

পর্ব ২৫ আর ২৬-এ আপনি Flexbox শিখেছেন, পর্ব ২৭ আর ২৮-এ Grid. অর্থাৎ যন্ত্রপাতি এখন আপনার হাতে। কিন্তু নতুনরা প্রায়ই একটি জায়গায় আটকে যান: একটি তৈরি ওয়েবসাইটের ছবি দেখে ঠিক বুঝতে পারেন না, “এটা কোথা থেকে শুরু করব?” এই পর্বের কাজ সেই ফাঁকটুকু ভরানো। আমরা নতুন কোনো property শিখব না; বরং দেখব, চেনা property-গুলো জুড়ে বাস্তবের পরিচিত কাঠামোগুলো কীভাবে তৈরি হয়।

ঢাকার যেকোনো এলাকায় নতুন বাড়ি তৈরি হতে দেখলে খেয়াল করবেন, রাজমিস্ত্রি প্রতিবার নতুন করে ভাবেন না সিঁড়ি কোথায় যাবে বা বারান্দা কেমন হবে। কয়েকটি পরীক্ষিত নকশা আছে — সিঁড়িঘর মাঝে, দুই পাশে দুই ফ্ল্যাট; সামনে বারান্দা, পেছনে রান্নাঘর। ওয়েবেও তাই। প্রায় প্রতিটি সাইট ঘুরেফিরে কয়েকটি একই কাঠামো ব্যবহার করে। এগুলোকে বলে layout pattern. একবার চিনলে যেকোনো নকশা দেখে আপনি মনে মনে ভাগ করতে পারবেন: “ওপরে pancake, ভেতরে sidebar, ডানে কার্ডের grid.”

দর্জির দোকানের কথা ভাবুন। একজন অভিজ্ঞ দর্জি প্রতিটি পাঞ্জাবি শূন্য থেকে আঁকেন না। তাঁর কাছে কয়েকটি প্যাটার্ন কাগজ থাকে — কলার, হাতা, পকেট। মাপ বদলে বদলে সেই একই প্যাটার্ন দিয়ে বড়-ছোট সবার জামা হয়। লেআউট প্যাটার্নও তাই: একবার বানানো, বারবার ব্যবহার, শুধু ভেতরের মাপ বদলায়।

লেআউটকে দুই স্তরে ভাবা কাজে দেয়। বড় কাঠামো (macro layout) — পুরো পাতা কয়টি এলাকায় ভাগ হবে: header, nav, main, aside, footer. ছোট কাঠামো (micro layout) — একটি কার্ড বা ফর্মের ভেতরে জিনিসগুলো কীভাবে বসবে। সাধারণ নিয়ম: যেখানে সারি আর কলাম একসাথে মেলাতে হবে, সেখানে Grid; যেখানে জিনিস এক দিকে লাইন ধরে বসবে আর দরকারে ভাঁজ হবে, সেখানে Flexbox. বেশিরভাগ বাস্তব পাতায় দুটো একসাথেই থাকে — বাইরে Grid, ভেতরে Flexbox, বা উল্টোটাও।

মূল ধারণা

ভালো লেআউট পর্দার মাপ “মুখস্থ” করে না, বরং নিয়ম দেয়: “sidebar-এর আদর্শ চওড়া এত, মূল অংশ অন্তত অর্ধেক; না আঁটলে নিচে নামো।” ব্রাউজার তখন যেকোনো চওড়ায় নিজেই হিসাব করে নেয়। এই ধরনের নিজে-মানিয়ে-নেওয়া নকশাকে বলে intrinsic layout. এই পর্বের প্রায় সব প্যাটার্ন এই চিন্তায় লেখা।

২ · Layout primitive — ছোট ইট দিয়ে বড় বাড়ি

২০১৯ সালে Heydon Pickering আর Andy Bell “Every Layout” নামে একটি বই ও ওয়েবসাইট প্রকাশ করেন। তাঁদের মূল কথা: প্রতিটি পাতার জন্য আলাদা আলাদা লেআউট-কোড না লিখে কয়েকটি ছোট, একক-দায়িত্বের নিয়ম বানান — প্রতিটি শুধু একটি কাজ করবে, কিন্তু খুব ভালোভাবে। এগুলোকে তাঁরা বলেন layout primitive. নিচের টেবিলের নামগুলো তাঁদের দেওয়া; কোডগুলো এই কোর্সের জন্য নিজের মতো করে লেখা সরল রূপ, তাঁদের বইয়ের হুবহু কোড নয় (মূল রূপগুলো আরও নমনীয়, CSS variable দিয়ে সাজানো)।

Primitiveকোন সমস্যা সমাধান করেমূল CSS
Stackওপর-নিচে সাজানো জিনিসের মাঝে সমান ফাঁকflex-direction: column; gap
Clusterট্যাগ, বোতাম, মেনুর মতো ছোট জিনিস পাশাপাশি, না আঁটলে পরের লাইনেflex-wrap: wrap; gap
Centerপড়ার লেখা একটি আরামদায়ক চওড়ায়, পাতার মাঝখানেmax-inline-size; margin-inline: auto
Sidebarপাশে একটি সরু অংশ, বাকিটা মূল অংশ; জায়গা কম হলে ওপর-নিচেflex-basis + flex-grow: 999
Switcherকয়েকটি সমান অংশ: চওড়া জায়গায় সবাই এক সারিতে, সরু জায়গায় সবাই ওপর-নিচেflex-basis: calc((30rem - 100%) * 999)
Coverপর্দাজোড়া hero অংশ, একটি জিনিস ঠিক মাঝখানেmin-block-size: 100dvh + flex column
Gridযত খুশি কার্ড, কলামের সংখ্যা নিজে নিজে ঠিক হবেrepeat(auto-fit, minmax(…, 1fr))

এখানে কয়েকটি শব্দ নতুন মনে হতে পারে। inline-size মানে লেখার দিকের মাপ — বাংলা বা ইংরেজির মতো বাঁ-থেকে-ডানের লেখায় সেটি চওড়া; block-size মানে উচ্চতা। এদের বলে logical property; পুরো গল্প পর্ব ৩৫-এ। আর dvh হলো ফোনের ঠিকানা-বার ওঠানামা হিসাবে ধরা পর্দার উচ্চতা (dynamic viewport একক, পর্ব ২০-এ দেখেছেন)।

নিচে একটি চাকরির বিজ্ঞপ্তির কার্ড — তিনটি primitive একসাথে: বাইরে Center, কার্ডের ভেতরে Stack, আর দক্ষতার ট্যাগগুলো Cluster. প্রতিটি নিয়ম আলাদা, কেউ কারও কাজে নাক গলায় না।

.center {                 /* Center */
  box-sizing: content-box;
  max-inline-size: 40ch;
  margin-inline: auto;
  padding-inline: 1rem;
}
.stack {                  /* Stack */
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.cluster {                /* Cluster */
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
/* শুধু সাজসজ্জা */
.card { border: 1px solid #cbd5e1; border-radius: 12px;
        padding: 1rem; font-family: sans-serif; }
.cluster span { background: #e0f2fe; border-radius: 999px;
                padding: 0.2rem 0.7rem; }
h2, p { margin: 0; }

কার্ডটি পাতার মাঝখানে, প্রায় ৪০ অক্ষর চওড়া। ভেতরের শিরোনাম, লেখা, ট্যাগের সারি আর লিংক — সবার মাঝে ঠিক সমান ফাঁক, কারণ ফাঁকের দায়িত্ব একা .stack-এর gap; কোনো element-এ আলাদা margin নেই। ট্যাগগুলো এক লাইনে না আঁটলে নিজে থেকেই পরের লাইনে নামে — সেটা Cluster-এর flex-wrap. চেষ্টা করুন: .center-এর max-inline-size-কে 25ch করুন — ট্যাগ আরও লাইনে ভাঙবে, কিন্তু ফাঁকগুলো একই থাকবে। তারপর .stack-এর gap-কে 2rem করে দেখুন — একটি মাত্র সংখ্যা বদলে পুরো কার্ডের ছন্দ বদলে গেল।

একটি প্রশ্ন জাগতে পারে: Center-এ box-sizing: content-box কেন, যখন পর্ব ১৯-এ সবাইকে border-box করতে শিখেছেন? কারণ এখানে আমরা চাই লেখার চওড়া ৪০ অক্ষর থাকুক, আর padding তার বাইরে যোগ হোক — ফোনে লেখা কিনারায় লেগে যাবে না, অথচ বড় পর্দায় লেখার মাপ ঠিক থাকবে। প্যাটার্ন মানেই অন্ধ নিয়ম নয়; প্রতিটি লাইনের পেছনে একটি কারণ থাকে।

Switcher — একটি অদ্ভুত কিন্তু চমৎকার হিসাব

তিনটি সমান বাক্স চাই: চওড়া জায়গায় পাশাপাশি, সরু জায়গায় ওপর-নিচে — কিন্তু কখনোই “দুটি ওপরে, একটি নিচে” নয়। Heydon Pickering এর একটি কৌশল লেখেন, যার মজার নাম দেন “Flexbox Holy Albatross”:

.switcher {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.switcher > * {
  flex-grow: 1;
  /* container 30rem-এর বেশি চওড়া: ফল ঋণাত্মক → 0 ধরা হয় → সবাই এক সারিতে
     container 30rem-এর কম চওড়া: ফল বিশাল ধনাত্মক → প্রত্যেকে আলাদা সারিতে */
  flex-basis: calc((30rem - 100%) * 999);
}

এখানে 100% মানে container-এর চওড়া। container 40rem হলে (30rem − 40rem) × 999 একটি বড় ঋণাত্মক সংখ্যা, আর flex-basis ঋণাত্মক হতে পারে না, তাই ব্রাউজার 0 ধরে — সবাই এক সারিতে বসে বাড়তি জায়গা সমান ভাগ করে নেয়। container 25rem হলে ফল প্রায় ৫০০০rem — কোনো সারিতেই দুটি আঁটে না, তাই প্রত্যেকে নিজের সারিতে। মাঝামাঝি কোনো অবস্থা নেই; সিদ্ধান্তটা হয় ঠিক 30rem-এ। আর এই সিদ্ধান্ত নেয় container-এর চওড়া, পুরো পর্দার নয় — অর্থাৎ একই কম্পোনেন্ট sidebar-এর সরু জায়গায় এক রকম, মূল অংশের চওড়া জায়গায় আরেক রকম আচরণ করে।

৩ · Media query ছাড়াই ভাঁজ হওয়া sidebar

চাকরির সাইট, অনলাইন দোকান, সংবাদের আর্কাইভ — প্রায় সবখানে একটি পরিচিত কাঠামো: বাঁয়ে ফিল্টার বা মেনু, ডানে মূল তালিকা। বড় পর্দায় পাশাপাশি, ফোনে ফিল্টার ওপরে আর তালিকা নিচে। পুরোনো পদ্ধতিতে এর জন্য একটি media query লিখতে হতো: “পর্দা 768px-এর কম হলে এক কলাম।” সমস্যা হলো, পর্দা নয় — আসল প্রশ্ন হলো এই অংশটির জন্য কতটা জায়গা আছে। Every Layout-এর Sidebar primitive প্রশ্নটা ঠিক এভাবে ব্রাউজারকে দেয়:

.with-sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.sidebar {
  flex-basis: 15rem;      /* আদর্শ চওড়া */
  flex-grow: 1;
}
.not-sidebar {
  flex-basis: 0;
  flex-grow: 999;         /* বাড়তি জায়গার প্রায় সবটাই এর */
  min-inline-size: 50%;   /* এর চেয়ে সরু হতে রাজি নয় */
}
/* দেখানোর জন্য: কোণ টেনে চওড়া বদলানো যায় */
.frame { resize: horizontal; overflow: auto; box-sizing: border-box;
         max-inline-size: 100%; border: 2px dashed #64748b; padding: 8px; }
.sidebar, .not-sidebar { padding: 0.75rem; border-radius: 8px;
                         font-family: sans-serif; }
.sidebar { background: #fef3c7; }
.not-sidebar { background: #dcfce7; }

চওড়া জায়গায় হলুদ ফিল্টার বাঁয়ে প্রায় 15rem চওড়া, সবুজ তালিকা বাকি সবটা। ডটেড বাক্সের ডান-নিচের কোণ ধরে বাঁ দিকে টানুন — ভেতরের অংশ প্রায় 608px-এর কম হলেই হঠাৎ ফিল্টার ওপরে আর তালিকা নিচে চলে যাবে, দুটোই পুরো চওড়া। (এখানে দুই অংশের 0.75rem করে padding-ও হিসাবে ঢোকে; নিচের ডায়াগ্রামে padding ছাড়া সরল হিসাবে সীমাটি 512px.) কোনো @media লেখা নেই! (ফোনে এই পাতা খুললে প্রিভিউ শুরু থেকেই সরু, তাই আগেই ভাঁজ দেখবেন।) চেষ্টা করুন: min-inline-size-কে 60% করুন — এবার আরও আগে, চওড়া জায়গাতেই ভাঁজ হবে। তারপর flex-grow: 999-কে 1 করুন — চওড়া জায়গায় ফিল্টার অকারণে মোটা হয়ে গেল কি?

জাদুটা কোথায়, ধাপে ধাপে দেখি। ধরুন root font-size 16px, তাই 15rem = 240px আর gap 16px. Flexbox প্রথমে প্রতিটি item-এর “শুরুর মাপ” ধরে দেখে সবাই এক সারিতে আঁটে কি না; তারপর বাড়তি জায়গা flex-grow-এর অনুপাতে ভাগ করে।

1.sidebar { flex-basis: 15rem; flex-grow: 1; }2.not-sidebar {3  flex-basis: 0;4  flex-grow: 999;5  min-inline-size: 50%;6}7/* gap: 1rem = 16px */
container আর দুই অংশের চওড়া (px)
container
sidebar
মূল অংশ
সারি
  1. আঁটবে কি না — প্রথম প্রশ্ন। container 900px. ব্রাউজার প্রতিটি item-এর শুরুর মাপ ধরে: sidebar 240px. মূল অংশের flex-basis 0, কিন্তু min-inline-size: 50% থাকায় সে 450px-এর কম হতে পারে না, তাই হিসাবে ধরা হয় 450. সাথে gap 16 — মোট 706, যা 900-এর মধ্যে। তাই দুজনেই এক সারিতে।
  2. বাড়তি জায়গার ভাগ। সারি ঠিক হওয়ার পর বাড়তি জায়গা হিসাব হয় flex-basis থেকে: 900 − 16 − 240 − 0 = 644px. এটি ভাগ হয় 1 : 999 অনুপাতে — sidebar পায় এক পিক্সেলেরও কম, মূল অংশ প্রায় সবটা। ফল: sidebar ঠিক তার আদর্শ চওড়ার কাছাকাছি, মূল অংশ ≈ 643px.
  3. container সরু হলো — 600px. এবার মূল অংশের সর্বনিম্ন 50% = 300px. 240 + 16 + 300 = 556, এখনো 600-এর মধ্যে। sidebar আগের মতোই ≈ 240px; ছোট হয়েছে শুধু মূল অংশ।
  4. 500px — ভাঁজের মুহূর্ত। সর্বনিম্ন 250px ধরলে 240 + 16 + 250 = 506 — 500-এ আঁটে না। তাই মূল অংশ পরের সারিতে নামল। এখন প্রতিটি সারিতে একজন করে, আর flex-grow থাকায় দুজনেই পুরো 500px নিল। ভাঁজের সীমা হলো সেই চওড়া, যেখানে 256 + অর্ধেক চওড়া = পুরো চওড়া, অর্থাৎ 512px. কোনো breakpoint লিখিনি — নিয়ম থেকেই সংখ্যাটা বেরিয়ে এলো।
  5. 999 কেন, 1 নয়? দুজনের flex-grow সমান হলে 900px-এ বাড়তি 644px আধাআধি ভাগ হতো; মূল অংশ পেত 322 — তার সর্বনিম্ন 450-এর কম, তাই ব্রাউজার তাকে 450-এ আটকে বাকিটা sidebar-কে দিত: sidebar 434px! মোটা, বেঢপ ফিল্টার। 999 দিলে বাড়তি জায়গা প্রায় পুরোটা মূল অংশে যায়, sidebar তার আদর্শ মাপে থাকে।

আরও গভীরে: CSS Flexbox spec অনুযায়ী item-গুলো কোন সারিতে যাবে, সেই সিদ্ধান্ত হয় প্রতিটি item-এর “hypothetical main size” দিয়ে — অর্থাৎ flex-basis-কে min আর max সীমার ভেতরে আটকে পাওয়া মাপ। তাই flex-basis: 0 হলেও min-inline-size: 50% ভাঁজের হিসাবে ঢুকে পড়ে। অথচ বাড়তি জায়গার ভাগ শুরু হয় আসল flex-basis থেকে, তারপর কেউ সীমা ভাঙলে তাকে “freeze” করে বাকিদের মাঝে আবার ভাগ হয়। ধাপ ৫-এর 434px ঠিক এই freeze-এর ফল। পুরো প্যাটার্নটি তাই spec-এর দুটি আলাদা ধাপকে বুদ্ধি করে কাজে লাগায়।

৪ · Pancake stack আর holy grail — পুরো পাতার কাঠামো

এবার ছোট কম্পোনেন্ট থেকে পুরো পাতায় যাই। প্রথম সমস্যাটি সবাই কখনো না কখনো দেখেছেন: কোনো পাতায় লেখা কম, তাই footer পর্দার মাঝখানে ঝুলে আছে, নিচে ফাঁকা সাদা জায়গা। সমাধানের নাম pancake stack — header, main আর footer তিনটি প্যানকেকের মতো ওপর-নিচে; মাঝেরটি বাড়তি জায়গা খেয়ে নেয়। Una Kravets ২০২০ সালে “এক লাইনের CSS-এ দশটি আধুনিক লেআউট” নামের একটি জনপ্রিয় লেখায় এই নামটি ছড়িয়ে দেন। এখানে footer-এর ভেতরে কলামও আছে — সেটি Grid primitive.

<style>
  .page {
    min-block-size: 340px;   /* আসল সাইটে: 100vh, তারপর 100dvh */
    display: grid;
    grid-template-rows: auto 1fr auto;
    font-family: sans-serif;
  }
  .page > header, .page > footer { background: #0f766e; color: #fff; padding: 0.75rem 1rem; }
  .page > main { padding: 1rem; }
  .cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 1rem;
  }
  .cols h3 { margin: 0 0 0.25rem; font-size: 1rem; }
  .cols ul, .cols p { margin: 0; padding: 0; list-style: none; }
  .cols a { color: inherit; }
</style>
<div class="page">
  <header>পদ্মা ট্রাভেলস (কাল্পনিক)</header>
  <main><p>লেখা মাত্র এক লাইন, তবু footer নিচেই।</p></main>
  <footer class="cols">
    <section><h3>সেবা</h3><ul><li><a href="#bus">বাস টিকিট</a></li><li><a href="#launch">লঞ্চ টিকিট</a></li></ul></section>
    <section><h3>সাহায্য</h3><ul><li><a href="#contact">যোগাযোগ</a></li><li><a href="#refund">রিফান্ড নীতি</a></li></ul></section>
    <section><h3>ঠিকানা</h3><p>সদরঘাট, ঢাকা</p></section>
  </footer>
</div>

main-এ মাত্র এক লাইন, তবু সবুজ footer বাক্সের একেবারে নিচে বসে আছে — কারণ মাঝের সারি 1fr, অর্থাৎ header আর footer নিজের মাপ নেওয়ার পর বাকি সব জায়গা main-এর। footer-এর তিনটি অংশ চওড়া জায়গায় পাশাপাশি তিন কলাম; ফোনে প্রিভিউ সরু হলে নিজে থেকেই দুই বা এক কলামে নামে — auto-fit আর minmax()-এর কাজ, পর্ব ২৮-এ দেখেছেন। চেষ্টা করুন: grid-template-rows লাইনটি মুছে দিন — footer উঠে এসে লেখার ঠিক নিচে বসবে, নিচে ফাঁকা জায়গা। তারপর main-এ অনেকগুলো প্যারাগ্রাফ যোগ করুন — বাক্স লম্বা হবে, footer স্বাভাবিকভাবে নিচে সরে যাবে; min-block-size শুধু সর্বনিম্ন সীমা।

আসল সাইটে .page-এর বদলে সাধারণত body-তেই এই grid দেওয়া হয়, আর উচ্চতা লেখা হয় দুই লাইনে: আগে min-block-size: 100vh;, তারপর min-block-size: 100dvh;. যে ব্রাউজার dvh চেনে সে দ্বিতীয়টি নেয়, যে চেনে না সে দ্বিতীয় লাইন বাদ দিয়ে প্রথমটিতে থেকে যায় — CSS-এর অচেনা মান উপেক্ষা করার স্বভাবকে কাজে লাগানো এই সহজ fallback. (dvh এখন Baseline, তবু পুরোনো ফোনের জন্য এই অভ্যাস নিরাপদ।)

Holy grail — নকশার একটি মানচিত্র আঁকুন

পুরোনো সংবাদপত্রের ওয়েবসাইট মনে করুন: ওপরে নাম আর মেনু, নিচে footer, মাঝে তিন কলাম — বাঁয়ে বিভাগের তালিকা, মাঝে খবর, ডানে বিজ্ঞাপন বা “সর্বাধিক পঠিত”। তিন কলাম সমান উঁচু, footer সবার নিচে। একে বলে holy grail layout. Grid-এর grid-template-areas দিয়ে এটি লেখা যায় প্রায় ছবি আঁকার মতো। নিচের ধাপগুলোতে দেখুন:

header
nav
main
aside
footer
header
nav
main
aside
footer
grid-template-areas:
  "header header header"
  "nav    main   aside"
  "footer footer footer";
header
nav
main
aside
footer
header
nav
main
aside
footer
  1. শুরুতে শুধু HTML. header, nav, main, aside, footer — পর্ব ১০-এ শেখা landmark element, পড়ার স্বাভাবিক ক্রমে। CSS ছাড়া এরা ওপর-নিচে সাজানো। এই ক্রমটাই screen reader পড়বে আর কীবোর্ডের Tab মানবে — তাই এটি আগে ঠিক করুন।
  2. শুধু কলাম দিলে গোলমাল। grid-template-columns: 1fr 2fr 1fr দিলে ব্রাউজার item-গুলোকে একে একে ঘরে বসায়: header প্রথম ঘরে, nav দ্বিতীয়তে, main তৃতীয়তে… নকশার সাথে কোনো মিল নেই। Grid-কে বলে দিতে হবে কে কোথায় যাবে।
  3. মানচিত্র আঁকা। প্রতিটি উদ্ধৃতিচিহ্ন একটি সারি, ভেতরের প্রতিটি শব্দ একটি কলাম। একই নাম পাশাপাশি তিনবার মানে সেই এলাকা তিন কলাম জুড়ে। নিয়ম দুটি: প্রতিটি সারিতে কলামের সংখ্যা সমান হতে হবে, আর প্রতিটি নামের এলাকা হতে হবে একটি পূর্ণ আয়তক্ষেত্র — L আকৃতি বা ছেঁড়া এলাকা হলে পুরো লাইনটিই অবৈধ। খালি ঘরের জন্য একটি বিন্দু . লেখা যায়।
  4. প্রত্যেককে তার জায়গায় পাঠানো। প্রতিটি child-এ grid-area দিয়ে নাম বলে দিলেই সে মানচিত্রের সেই এলাকায় বসে। একই সারির তিন কলাম সবসময় সমান উঁচু — float-এর যুগে যেটা ছিল সবচেয়ে কঠিন কাজ, Grid-এ সেটা আপনা-আপনি।
  5. ফোনের জন্য শুধু মানচিত্র বদলান। সরু পর্দায় মানচিত্রটি এক কলামের করে দিলেই হলো: প্রতিটি নাম আলাদা সারিতে। কোনো child-এর CSS বদলাতে হয় না। কখন বদলাবেন, সেই শর্ত লেখা হয় media বা container query দিয়ে — পর্ব ৩১-এর বিষয়; এখানে শুধু জেনে রাখুন বদলটা কত ছোট।
.layout {
  display: grid;
  grid-template-columns: minmax(6rem, 10rem) 1fr minmax(6rem, 10rem);
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  gap: 0.5rem;
  min-block-size: 300px;
  font-family: sans-serif;
}
.layout > header { grid-area: header; }
.layout > nav    { grid-area: nav; }
.layout > main   { grid-area: main; }
.layout > aside  { grid-area: aside; }
.layout > footer { grid-area: footer; }
/* সাজসজ্জা */
.layout > * { padding: 0.75rem; border-radius: 8px; background: #e2e8f0; }
.layout > main { background: #dbeafe; }
.layout h2, .layout p { margin: 0 0 0.5rem; }

ওপরে পুরো চওড়া header, নিচে footer, মাঝে তিন কলাম — বাঁয়ে বিভাগ, মাঝে নীল খবর, ডানে “সর্বাধিক পঠিত”; তিনটি সমান উঁচু, যদিও ভেতরের লেখা আলাদা মাপের। চেষ্টা করুন: মাঝের সারিটি "aside main nav" করে দিন — বিভাগ আর পঠিত তালিকা জায়গা বদলাল, HTML একটুও না ছুঁয়ে। তারপর grid-template-columns: 1fr; লিখে মানচিত্রটি পাঁচ সারির করুন ("header" "nav" "main" "aside" "footer") — এটাই ফোনের রূপ। ইচ্ছা করে একটি ভুলও করে দেখুন: প্রথম সারিতে একটি header মুছে দুই শব্দ রাখুন — সারিগুলোর কলাম অসমান হওয়ায় পুরো grid-template-areas লাইনটি বাতিল হয়ে যাবে, আর লেআউট ভেঙে পড়বে।

“Holy grail” নামটি মজা করে দেওয়া — ইউরোপের পুরাণে holy grail এমন এক পাত্র, যা খুঁজে পাওয়া প্রায় অসম্ভব। ২০০০-এর দশকে CSS দিয়ে তিনটি সমান উঁচু কলাম আর তার নিচে footer বানানো ঠিক ততটাই কঠিন ছিল। লেআউটের জন্য তখন ছিল শুধু float আর table. ২০০৪ সালে Dan Cederholm “Faux Columns” কৌশল লেখেন — কলাম আসলে সমান উঁচু নয়, পেছনে একটি ডোরাকাটা ব্যাকগ্রাউন্ড ছবি দিয়ে সমান দেখানো! ২০০৬ সালে A List Apart-এ Matthew Levine-এর “In Search of the Holy Grail” লেখাটি negative margin আর float দিয়ে একটি সমাধান দেয়, যাতে মূল লেখা HTML-এ প্রথমে থাকে। ২০১৭ সালের মার্চে Chrome, Firefox আর Safari প্রায় একসাথে CSS Grid চালু করে, আর বছরের শেষে Edge — তারপর থেকে যে সমস্যা নিয়ে এত লেখা, তা নেমে এসেছে দশ লাইনের CSS-এ।

৫ · মাঝখানে পড়ার কলাম, কিনারা-ছোঁয়া সেকশন — নামওয়ালা লাইন

ব্লগ বা খবরের লেখায় একটি দ্বিধা থাকে। লেখা আরামে পড়তে হলে লাইন খুব লম্বা হওয়া চলবে না — পর্ব ২১-এ দেখেছেন, এক লাইনে মোটামুটি ৪৫–৭৫ অক্ষর আদর্শ (measure). কিন্তু মাঝে মাঝে একটি ছবি, রঙিন উদ্ধৃতি বা মানচিত্র চাই পর্দার এক কিনারা থেকে অন্য কিনারা পর্যন্ত — ছাপাখানার ভাষায় full-bleed. Center primitive দিলে সবকিছু সরু কলামে আটকে যায়, আর ছবিকে বের করতে লাগে নেগেটিভ margin-এর ঝামেলা। Grid-এর named grid line এর পরিষ্কার সমাধান দেয়। এই কৌশল Josh W. Comeau-সহ অনেকের লেখায় জনপ্রিয় হয়েছে:

.article {
  display: grid;
  grid-template-columns:
    [full-start] minmax(1rem, 1fr)
    [content-start] min(65ch, 100%) [content-end]
    minmax(1rem, 1fr) [full-end];
}
.article > *           { grid-column: content; }
.article > .full-bleed { grid-column: full; }
1fr
min(65ch, 100%)
1fr
1234
full-startcontent-startcontent-endfull-end
grid-column: content → 2 / 3
grid-column: full → 1 / 4
 
ফোন: 100%
 
  1. তিনটি কলাম, চারটি লাইন। মাঝের কলাম পড়ার লেখার, দুই পাশে দুটি “গদি” কলাম, যারা বাকি জায়গা সমান ভাগ করে নেয়। Grid-এ কলামের মাঝের দাগগুলোকে বলে লাইন; তিন কলামে লাইন চারটি, ব্রাউজার নিজে থেকে 1 থেকে 4 নম্বর দেয়।
  2. লাইনের নাম। বন্ধনীর ভেতরের শব্দগুলো লাইনের নাম: লাইন 1 = full-start, লাইন 2 = content-start, লাইন 3 = content-end, লাইন 4 = full-end. সংখ্যা মনে রাখার চেয়ে নাম মনে রাখা অনেক সহজ, আর পরে কলাম যোগ হলেও নাম বদলায় না।
  3. -start আর -end-এর জাদু। একই নামের -start আর -end জোড়া থাকলে Grid আপনা-আপনি সেই নামের একটি এলাকা ধরে নেয়। তাই grid-column: content লিখলেই মানে লাইন 2 থেকে 3 — প্রতিটি প্যারাগ্রাফ মাঝের কলামে।
  4. Full-bleed. একইভাবে grid-column: full মানে লাইন 1 থেকে 4 — এক কিনারা থেকে অন্য কিনারা। ছবি বা ব্যানারে শুধু .full-bleed class দিলেই হলো; বাকি লেখা মাঝেই থাকে।
  5. ফোনে কী হয়? min(65ch, 100%) দুটির মধ্যে ছোটটি নেয়, তাই সরু পর্দায় মাঝের কলাম হতে চায় container-এর পুরো চওড়া। কিন্তু দুই পাশের কলাম অন্তত 1rem করে চায়! মোট দাঁড়ায় 100% + 2rem — ফলে পাতা 2rem পাশে বেরিয়ে যায়। নিচের সতর্কবার্তায় এর সমাধান।

বহু লেখা আর AI-এর কোডে হুবহু min(65ch, 100%) দেখবেন, অথচ পাশের কলামে minmax(1rem, 1fr). বড় পর্দায় কোনো সমস্যা নেই, কিন্তু 360px চওড়া ফোনে পাতা ডানে 32px বেরিয়ে যায় আর পাশে স্ক্রল আসে। দুটি সহজ সমাধান: মাঝের কলাম লিখুন min(65ch, 100% - 2rem) (min()-এর ভেতরে সরাসরি যোগ-বিয়োগ চলে, calc() লাগে না), অথবা পাশের কলাম 1fr রেখে container-এ padding-inline: 1rem দিন। নিচের এডিটরে প্রথমটি ব্যবহার করা হয়েছে।

.article {
  display: grid;
  grid-template-columns:
    [full-start] minmax(1rem, 1fr)
    [content-start] min(65ch, 100% - 2rem) [content-end]
    minmax(1rem, 1fr) [full-end];
  row-gap: 1rem;
  font-family: sans-serif;
  line-height: 1.7;
}
.article > * { grid-column: content; margin: 0; }
body { margin: 0; }   /* প্রিভিউর ডিফল্ট 8px সরিয়ে কিনারা পর্যন্ত */
.article > .full-bleed { grid-column: full; }

.banner {
  background: linear-gradient(90deg, #0369a1, #0d9488);
  color: #fff; padding: 2.5rem 1rem; text-align: center;
}
.quote { background: #fef3c7; padding: 1rem;
         border-inline-start: 4px solid #d97706; }

শিরোনাম, প্যারাগ্রাফ আর হলুদ উদ্ধৃতি মাঝের কলামে; নীল-সবুজ ব্যানার প্রিভিউর এক কিনারা থেকে অন্য কিনারা পর্যন্ত। প্রিভিউ চওড়া হলে দুই পাশে সমান ফাঁকা গদি দেখবেন। চেষ্টা করুন: উদ্ধৃতিকে একটু “বেরিয়ে আসা” চওড়া করতে .quote-এ grid-column: full-start / content-end; দিন — সে বাঁ কিনারা থেকে শুরু হয়ে লেখার ডান সীমায় থামবে; নামওয়ালা লাইন যেকোনো জোড়ায় মেলানো যায়। তারপর 65ch-কে 30ch করুন — লেখার কলাম সরু হলো, কিন্তু ব্যানার একই রইল।

৬ · অ্যাডমিন ড্যাশবোর্ড — একটি ইউনিয়ন পরিষদের ই-সেবা পোর্টাল

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

বাইরের কাঠামো

ওপরে header, বাঁয়ে nav, ডানে কাজের এলাকা — দুই কলামের grid-template-areas. holy grail-এর ছোট ভাই।

macro · Grid

স্ট্যাট কার্ড

চার-পাঁচটি stat card, কলামের সংখ্যা জায়গা দেখে — Grid primitive, auto-fit.

micro · Grid

চার্ট

বারগুলো এক সারিতে, নিচের দাগে বসানো — Flexbox, align-items: flex-end.

micro · Flexbox

টেবিল

আসল <table> (পর্ব ৭), চওড়া হলে নিজের ভেতরে পাশে স্ক্রল — scroll container.

ডেটা · table
.dash {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas: "top top" "nav main";
  min-block-size: 520px;
  font-family: sans-serif;
}
.top  { grid-area: top; background: #065f46; color: #fff; padding: 0.75rem 1rem; }
.side { grid-area: nav; background: #ecfdf5; padding: 1rem 0.75rem; }
.side ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.main {
  grid-area: main;
  display: grid; gap: 1rem; align-content: start;
  padding: 1rem;
  min-inline-size: 0;            /* টেবিল যেন লেআউট না ঠেলে */
}
.stats { display: grid; gap: 0.75rem;
         grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); }
.stat { border: 1px solid #a7f3d0; border-radius: 10px; padding: 0.75rem; }
.stat b { display: block; font-size: 1.5rem; }
.chart-box { margin: 0; }
.chart { display: flex; align-items: flex-end; gap: 0.5rem;
         block-size: 120px; border-block-end: 2px solid #94a3b8; }
.chart span { flex: 1; background: #10b981; border-radius: 4px 4px 0 0; }
.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; inline-size: 100%; min-inline-size: 30rem; }
caption { text-align: start; font-weight: 700; padding-block-end: 0.25rem; }
th, td { border-block-end: 1px solid #e2e8f0; padding: 0.4rem; text-align: start; }

ওপরে গাঢ় সবুজ header, বাঁয়ে হালকা সবুজ মেনু, ডানে চারটি স্ট্যাট কার্ড, তার নিচে ছয়টি সবুজ বারের চার্ট আর সাম্প্রতিক আবেদনের টেবিল। প্রিভিউ সরু হলে কার্ডগুলো দুই বা এক কলামে নামে, আর টেবিল নিজের বাক্সের ভেতরেই পাশে স্ক্রল হয় — পুরো পাতা নয়। এর চাবি .main-এর min-inline-size: 0. চেষ্টা করুন: এই লাইনটি মুছে দিন, তারপর ফোনের মতো সরু প্রিভিউতে দেখুন (চওড়া পর্দায় দেখতে টেবিলের min-inline-size-কে 60rem করুন) — টেবিল এবার পুরো main-কে ঠেলে বাইরে বের করে দেবে। লাইনটি ফিরিয়ে আনলে একই চওড়া টেবিল আবার নিজের বাক্সেই পাশে স্ক্রল হবে। এরপর .stats-এর 8rem-কে 12rem করুন — কার্ড আরও আগে নিচে নামবে।

লাইনটি এত গুরুত্বপূর্ণ কেন? flex আর grid item-এর ডিফল্ট সর্বনিম্ন চওড়া auto — মানে “ভেতরের জিনিস যতটা সরু হতে পারে, তার চেয়ে সরু নয়” (min-width: auto). 30rem চওড়া টেবিল ভেতরে থাকলে main নিজেও 30rem-এর কম হতে রাজি হয় না, আর 1fr কলাম তাকে জায়গা দিতে পাতা পাশে ফুলে যায়। min-inline-size: 0 (বা কলামে minmax(0, 1fr)) বলে দেয়, “item সরু হতে পারবে; ভেতরের চওড়া জিনিস নিজের scroll container-এ সামলানো হবে।” ড্যাশবোর্ড, কোড-ব্লক আর লম্বা URL-ওয়ালা লেআউটের সবচেয়ে প্রচলিত বাগ এটিই।

অ্যাক্সেসিবিলিটির তিনটি ছোট কাজ খেয়াল করুন। (১) মেনু <nav aria-label>, আর বর্তমান পাতার লিংকে aria-current="page" — screen reader বলে দেয় “বর্তমান পাতা”। (২) চার্টের বারগুলো দেখতে শুধু রঙিন বাক্স, তাই পুরো চার্টে role="img" আর aria-label-এ আসল সংখ্যাগুলো লেখা; বাস্তবে একটি ছোট টেবিল বা লেখাও পাশে দেওয়া ভালো। (৩) স্ক্রল হওয়া টেবিলের wrapper-এ role="region", নাম আর tabindex="0" — কীবোর্ড ব্যবহারকারীও Tab চেপে সেখানে গিয়ে তীর-কী দিয়ে পাশে সরতে পারেন। এই কোর্সের প্রতিটি টেবিল ঠিক এভাবে মোড়ানো।

৭ · ছবির গ্যালারি আর প্রাইসিং টেবিল

ভ্রমণ এজেন্সি বা ফটোগ্রাফারের সাইটে প্রায়ই এমন গ্যালারি দেখবেন, যেখানে কিছু ছবি বড়, কিছু ছোট, অথচ মাঝে কোনো ফাঁকা গর্ত নেই। Grid-এ এর উপকরণ তিনটি: কলামের কাঠামো, বড় ছবির জন্য span, আর গর্ত ভরাতে grid-auto-flow: dense. নিচের এডিটরে ক্রমের ব্যাপারটা স্পষ্ট দেখাতে কলাম ৪টি স্থির রাখা হয়েছে; বাস্তবের সাইটে সাধারণত repeat(auto-fill, minmax(8rem, 1fr)) লেখা হয়, যাতে কলামের সংখ্যা জায়গা দেখে বদলায়।

.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);  /* বোঝার সুবিধায় ৪টি স্থির কলাম */
  grid-auto-rows: 6rem;
  gap: 0.5rem;
  grid-auto-flow: dense;     /* পেছনের ছোট ছবি এনে গর্ত ভরাও */
}
.wide { grid-column: span 2; }
.tall { grid-row: span 2; }
.big  { grid-column: span 2; grid-row: span 2; }

figure {
  margin: 0;
  border-radius: 8px;
  display: grid;
  place-items: end start;
  padding: 0.5rem;
  color: #fff;
  font: 600 0.85rem sans-serif;
}

বড় “কক্সবাজার” দুই কলাম আর দুই সারি জুড়ে, “সুন্দরবন”, “চা-বাগান” আর “সূর্যাস্ত” চওড়া, “রাতারগুল” লম্বা — তবু চার সারির গ্রিডে একটিও ফাঁকা ঘর নেই। এবার নম্বরগুলো খেয়াল করুন: তৃতীয় সারির শেষে বসেছে ৮, আর ৭ নেমে গেছে চতুর্থ সারিতে; চতুর্থ সারির শুরুতে ৯. কারণ চওড়া ৭ তৃতীয় সারির শেষের একটি ঘরে আঁটেনি, তাই dense পেছনের ছোট ৮ আর ৯-কে টেনে এনে আগের গর্তগুলো ভরিয়েছে। চেষ্টা করুন: grid-auto-flow: dense; লাইনটি মুছে দিন — ক্রম আবার ১ থেকে ৯, কিন্তু তৃতীয় সারির শেষে আর চতুর্থ সারির শুরুতে দুটি খালি ঘর, আর ৮-৯ চলে যাবে পঞ্চম সারিতে। তারপর কলামের লাইনটি repeat(auto-fill, minmax(8rem, 1fr)) করে দেখুন — কলামের সংখ্যা প্রিভিউর চওড়া দেখে বদলাবে, আর কোন ছবি কোথায় বসবে তাও বদলে যাবে।

dense চোখের জন্য সুন্দর, কিন্তু HTML-এর ক্রম বদলায় না। screen reader আর কীবোর্ডের Tab তবুও ১, ২, ৩… ক্রমে যাবে, অথচ চোখে দেখা ক্রম ভিন্ন — একে বলে visual order আর DOM order-এর অমিল। ছবিগুলো শুধু দেখার জিনিস হলে সমস্যা ছোট; কিন্তু প্রতিটি যদি লিংক বা বোতাম হয়, কীবোর্ড ব্যবহারকারীর tab order এলোমেলো লাফাবে। WCAG 2.2-এর “Meaningful Sequence” (1.3.2) আর “Focus Order” (2.4.3) নিয়ম এই ঝুঁকির কথাই বলে। তাই যেখানে ক্রমের অর্থ আছে — ধাপে ধাপে নির্দেশনা, তারিখ অনুযায়ী খবর — সেখানে dense বা order দিয়ে ক্রম ওলটপালট করবেন না। এ সমস্যার জন্য CSS-এ reading-flow নামে একটি নতুন property এসেছে, কিন্তু এটি এখনো Baseline নয়; আপাতত HTML-এর ক্রমই আপনার ভরসা।

আর Pinterest-এর মতো “masonry” — যেখানে ভিন্ন উচ্চতার কার্ড কলামে কলামে ইটের গাঁথুনির মতো বসে? এর জন্য আলাদা CSS নিয়ম নিয়ে ব্রাউজার আর CSS Working Group-এ কাজ চলছে, কিন্তু এখনো Baseline নয়। আজকের দিনে সবচেয়ে সহজ বিকল্প columns: 15rem (multi-column layout) — তবে সেখানে ক্রম যায় ওপর থেকে নিচে, কলাম ধরে; তারিখ-ক্রমের তালিকায় সেটা বিভ্রান্তিকর হতে পারে।

প্রাইসিং টেবিল — subgrid দিয়ে লাইনে লাইন মেলানো

ইন্টারনেট প্রোভাইডার বা হোস্টিং কোম্পানির সাইটে তিন-চারটি প্যাকেজ পাশাপাশি থাকে: নাম, দাম, সুবিধার তালিকা, বোতাম। সমস্যা: একটির সুবিধার তালিকা লম্বা হলে বাকিগুলোর বোতাম ওপরে-নিচে এলোমেলো হয়ে যায়। কার্ডগুলো আলাদা আলাদা বাক্স, একজন জানে না পাশেরটির তালিকা কত লম্বা। subgrid এই সমস্যা মেটায় — প্রতিটি কার্ড নিজের সারি না বানিয়ে বাইরের grid-এর সারি ধার করে, ফলে সব কার্ডের “দাম” সারি একই উচ্চতায়, “বোতাম” সারি একই লাইনে। subgrid ২০২৩ সাল থেকে সব প্রধান ব্রাউজারে চলে (Baseline); খুব পুরোনো ব্রাউজারে লাইনটি উপেক্ষিত হয়, কার্ড তবুও ঠিকঠাক দেখায় — শুধু লাইনগুলো মেলে না।

.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 1rem;
  font-family: sans-serif;
}
.plan {
  display: grid;
  grid-row: span 4;              /* নাম, দাম, তালিকা, বোতাম = ৪ সারি */
  grid-template-rows: subgrid;   /* সারিগুলো বাইরের grid থেকে ধার */
  row-gap: 0.5rem;
  border: 1px solid #cbd5e1; border-radius: 12px; padding: 1rem;
}
.plan h3, .plan p, .plan ul { margin: 0; }
.plan ul { padding-inline-start: 1.1rem; }
.price { font-size: 1.4rem; font-weight: 700; color: #1d4ed8; }
.plan a { align-self: end; text-align: center; padding: 0.5rem;
          background: #1d4ed8; color: #fff; border-radius: 8px;
          text-decoration: none; }
.popular { border-color: #1d4ed8; box-shadow: 0 0 0 1px #1d4ed8; }

তিনটি প্যাকেজ পাশাপাশি; মাঝেরটির তালিকা চার লাইনের, বাকি দুটির দুই লাইনের — তবু তিনটির নাম, দাম আর তালিকার শুরু একই লাইনে, আর তিনটি নীল বোতাম একেবারে একই উচ্চতায়। (জনপ্রিয় প্যাকেজের মোটা নীল পাড় box-shadow দিয়ে আঁকা, যাতে border মোটা হয়ে ভেতরের জিনিস এক পিক্সেলও না সরে।) চেষ্টা করুন: grid-template-rows: subgrid; লাইনটি মুছে দিন — কার্ডগুলো এখনো সমান উঁচু থাকবে, কিন্তু ভেতরের দাম আর তালিকা প্রতিটি কার্ডে আলাদা উচ্চতায় বসবে, লাইন আর মিলবে না। প্রিভিউ সরু হলে কার্ডগুলো নিচে নিচে নামে; তখন প্রতিটি কার্ড নিজের চার সারির দল পায়।

৮ · কোন প্যাটার্ন কখন — সিদ্ধান্তের টেবিল আর রেসপন্সিভ আচরণের ঝলক

নতুন কোনো নকশা হাতে পেলে প্রথমে কোড নয়, কাগজে আয়তক্ষেত্র আঁকুন: পুরো পাতা কয় ভাগ, প্রতিটি ভাগের ভেতরে কী। তারপর প্রতিটি আয়তক্ষেত্রকে নিচের টেবিলের একটি প্রশ্নের সাথে মেলান।

আপনার প্রশ্নপ্যাটার্নমূল CSSখেয়াল রাখুন
লেখা কম হলেও footer নিচে থাকবে?Pancake stackgrid-template-rows: auto 1fr autoউচ্চতায় 100vh, তারপর 100dvh
header, দুই পাশের কলাম, footer — পুরো পাতা?Holy grailgrid-template-areasফোনে মানচিত্র এক কলামে; HTML ক্রম ঠিক রাখুন
পাশে সরু অংশ, জায়গা কম হলে ওপর-নিচে?Sidebarflex-basis + flex-grow: 999 + min-inline-sizeভাঁজের সীমা নিয়ম থেকে আসে, breakpoint নয়
কয়েকটি সমান অংশ — হয় সব পাশাপাশি, নয় সব ওপর-নিচে?Switchercalc((30rem - 100%) * 999)৪-৫টির বেশি item হলে Grid ভালো
পড়ার কলাম মাঝে, কিছু অংশ কিনারা পর্যন্ত?Full-bleedনামওয়ালা লাইন + min()ফোনে বাড়তি 2rem বেরোনো এড়ান
অজানা সংখ্যক কার্ড বা পণ্য?Grid primitiverepeat(auto-fit, minmax(…, 1fr))খুব সরু পর্দায় min(…, 100%)
ট্যাগ, বোতাম, ছোট মেনু?Clusterflex-wrap: wrap; gapmargin দিয়ে ফাঁক নয়, gap
ভিন্ন মাপের ছবি, ফাঁক ছাড়া?Galleryspan + denseক্রমের অর্থ থাকলে dense নয়
পাশাপাশি কার্ডের ভেতরের অংশ একই লাইনে?Pricing rowgrid-template-rows: subgridকার্ডে grid-row: span N
নম্বর, চার্ট, তালিকা এক পাতায়?Dashboardদুই কলামের areas + ভেতরে উপরের সবmin-inline-size: 0, টেবিলে scroll container

রেসপন্সিভ আচরণের এক ঝলক

খেয়াল করেছেন, এই পর্বের বেশিরভাগ প্যাটার্ন কোনো @media ছাড়াই ফোনে ঠিক থাকে? Cluster ভাঁজ হয়, Sidebar আর Switcher নিজে সিদ্ধান্ত নেয়, auto-fit কলাম গোনে, পড়ার কলাম min() দিয়ে সরু হয়। এটাই intrinsic layout-এর শক্তি — নিয়ম দিন, ব্রাউজার হিসাব করবে। তবে সব কিছু নিয়ম দিয়ে হয় না। holy grail-এর মানচিত্র তিন কলাম থেকে এক কলামে বদলানো একটি নকশার সিদ্ধান্ত — ব্রাউজার নিজে জানে না aside-কে main-এর আগে না পরে দেবেন। সেখানে একটি শর্ত লিখতে হয়:

/* পর্ব ৩১-এ বিস্তারিত — এখানে শুধু আকারটা দেখুন */
@media (width < 48rem) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-areas: "header" "nav" "main" "aside" "footer";
  }
}

width < 48rem হলো media query-র নতুন “range” লেখার রূপ, যা এখন Baseline; পুরোনো রূপ (max-width: 47.99rem). আরও এক ধাপ এগিয়ে, পুরো পর্দা নয় বরং কম্পোনেন্টের নিজের container কত চওড়া তা দেখে সাজ বদলাতে আছে container query (@container, ২০২৩ সাল থেকে Baseline). একটি কার্ড sidebar-এর সরু জায়গায় এক রকম, main-এর চওড়া জায়গায় আরেক রকম — এ কাজ media query পারে না, container query পারে। পর্ব ৩০-এ মোবাইল-ফার্স্ট ভাবনা আর viewport, পর্ব ৩১-এ media আর container query বিস্তারিত দেখবেন। আজকের নিয়ম মনে রাখুন: আগে intrinsic প্যাটার্ন দিয়ে যতটা সম্ভব সামলান, যেখানে নকশা সত্যিই বদলাতে হবে শুধু সেখানে breakpoint.

HTML আগে, পড়ার ক্রমে → বড় কাঠামো Grid-এ → ভেতরে primitive → শেষে যেখানে দরকার, শুধু সেখানে query

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

  • holy grail layout কী, আর Grid দিয়ে কীভাবে বানাবেন? — header, footer আর মাঝে nav-main-aside তিন কলাম; grid-template-areas-এ মানচিত্র এঁকে প্রতিটি child-এ grid-area.
  • footer-কে পাতার নিচে আটকাবেন কীভাবে, যখন লেখা কম? — container-এ min-block-size: 100dvh আর grid-template-rows: auto 1fr auto (pancake stack).
  • Flexbox আর Grid — লেআউটে কখন কোনটি? — এক দিকে সাজানো আর ভাঁজ হওয়া জিনিসে Flexbox; সারি-কলাম একসাথে মেলাতে Grid; বাস্তবে দুটো একসাথে।
  • media query ছাড়া রেসপন্সিভ কার্ড গ্রিড? — grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)).
  • grid item-এর ভেতরের চওড়া টেবিল লেআউট ভেঙে দিচ্ছে — কেন? — grid/flex item-এর ডিফল্ট min-width: auto; item-এ min-inline-size: 0 বা track-এ minmax(0, 1fr), আর টেবিলের wrapper-এ overflow-x: auto.

৯ · AI-এর যুগে — লেআউটের কাজ AI-কে দিলে কী দেখবেন

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

AI সাধারণত যেসব লেআউট-ভুল করে

AI-এর ভুলআপনি যা দেখবেনঠিক উপায়
সব কিছু <div class="sidebar">, <div class="header"> — div-soupscreen reader-এ কোনো landmark নেইheader, nav, main, aside, footer
float আর clearfix দিয়ে কলাম, বা পুরো পাতা <table>-একলাম অসমান উঁচু, ফোনে ভাঙেGrid বা Flexbox; table শুধু ডেটার জন্য
width: 250px sidebar + width: calc(100% - 250px) mainফোনে main প্রায় অদৃশ্য সরুSidebar primitive বা minmax()
প্রতিটি কম্পোনেন্টে ৩-৪টি @media, ভিন্ন ভিন্ন সংখ্যায়কিছু মাপে হঠাৎ ভাঙা লেআউটআগে intrinsic প্যাটার্ন, query শুধু দরকারে
নকশা মেলাতে order বা dense দিয়ে ক্রম বদলTab চাপলে focus এদিক-ওদিক লাফায়HTML-এর ক্রমই ঠিক করুন
footer নিচে রাখতে position: absolute; bottom: 0লেখা বাড়লে footer লেখার ওপর চাপেPancake stack
height: 100vh (min নয়)ফোনে লেখা কাটা পড়ে বা নিচে লুকায়min-block-size: 100dvh

AI-কে লেআউটের কাজ দেওয়ার সময় প্যাটার্নের নাম আর শর্তগুলো আগেই বলে দিন। প্যাটার্নের নাম জানা থাকলে AI-এর সাথে কথা বলাও অনেক নির্দিষ্ট হয় — “sidebar primitive” বললে সে ঠিক বোঝে আপনি কী চান।

তুমি একজন অভিজ্ঞ ফ্রন্ট-এন্ড ডেভেলপার। একটি ইউনিয়ন পরিষদের ই-সেবা ড্যাশবোর্ডের HTML আর CSS বানাও (header, বাঁয়ে মেনু, ৪টি স্ট্যাট কার্ড, একটি বার চার্ট, সাম্প্রতিক আবেদনের টেবিল)। শর্ত: ১. semantic HTML: header, nav (aria-label সহ), main; কোনো div-soup নয়। HTML-এর ক্রমই পড়ার ক্রম — order বা grid-auto-flow: dense দিয়ে ক্রম বদলাবে না। ২. বাইরের কাঠামো CSS Grid-এ grid-template-areas দিয়ে; float, clearfix বা table-লেআউট নয়। ৩. স্ট্যাট কার্ড repeat(auto-fit, minmax(min(10rem, 100%), 1fr)) দিয়ে; কোনো নির্দিষ্ট px width নয়। ৪. টেবিল আসল table, caption আর th scope সহ; role="region", aria-label আর tabindex="0" দেওয়া wrapper-এ overflow-x: auto; main-এ min-inline-size: 0. ৫. @media শুধু একটি জায়গায় — বাইরের মানচিত্র এক কলামে আনতে; কেন লাগল এক লাইনে বলবে। ৬. কোনো -webkit- prefix বা height: 100vh নয়; দরকার হলে min-block-size: 100dvh. শেষে ৩২০px চওড়া পর্দায় পাতাটি কেমন দেখাবে, অংশ ধরে ধরে লিখে দেখাও।

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

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

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

প্র ০১Holy grail একসময় সত্যিই “দুর্লভ ধন” ছিল কেন? Grid আসার আগে মানুষ কী কী কৌশল করত, আর সেখান থেকে কী শেখার আছে?

মূল কারণ: CSS-এর প্রথম দশকে লেআউটের জন্য কোনো টুলই ছিল না। CSS1 (১৯৯৬) আর CSS2 (১৯৯৮) মূলত লেখা সাজানোর ভাষা — রং, ফন্ট, margin. পাশাপাশি কলাম বানানোর জন্য ডেভেলপাররা দুটি জিনিস “ধার” করতেন, যাদের কোনোটিই এই কাজের জন্য তৈরি নয়।

  • Table লেআউট: পুরো পাতা একটি বিশাল <table>, কলামগুলো <td>. সমান উচ্চতা আপনা-আপনি মিলত, কিন্তু HTML-এর অর্থ নষ্ট হতো — screen reader পুরো পাতাকে ডেটার টেবিল ভাবত, আর নকশা বদলাতে HTML-ই নতুন করে লিখতে হতো।
  • Float: আসলে ছবির পাশে লেখা জড়ানোর জন্য তৈরি। কলাম বানাতে float দিলে parent-এর উচ্চতা শূন্য হয়ে যেত, তাই লাগত “clearfix” কৌশল। আর float করা কলামগুলো কখনোই সমান উঁচু হতো না।

সমান উঁচু কলামের জন্য ২০০৪ সালে Dan Cederholm-এর “Faux Columns” — পেছনে ডোরাকাটা ব্যাকগ্রাউন্ড ছবি, যাতে ছোট কলামও লম্বা দেখায়। ২০০৬ সালে Matthew Levine-এর holy grail সমাধান — negative margin আর relative position-এর এমন জটিল হিসাব, যাতে মূল লেখা HTML-এ প্রথমে থেকেও মাঝের কলামে বসে। অনেক সাইট আলাদা করে পুরোনো Internet Explorer-এর জন্যও বাড়তি কৌশল রাখত। এরপর এলো Flexbox (২০১০-এর দশকের মাঝামাঝি থেকে ব্যবহারযোগ্য), আর ২০১৭ সালে Grid — যা প্রথমবারের মতো দুই দিকের লেআউটকে CSS-এর মূল কাজ হিসেবে স্বীকৃতি দিল।

শিক্ষাটা শুধু ইতিহাস নয়। আজও ইন্টারনেটে, পুরোনো টিউটোরিয়ালে আর AI-এর উত্তরে সেই যুগের কোড ঘুরে বেড়ায় — float: left দিয়ে কলাম, clearfix, faux column. এগুলো চিনতে পারলেই বুঝবেন কোডটি পুরোনো ধাঁচের, আর আধুনিক সমাধান কতটা সরল হতে পারে।

মূল উপলব্ধি: যখন কোনো টুল এক কাজের জন্য তৈরি অথচ অন্য কাজে ব্যবহার হয়, তখন “hack” জন্মায়, আর hack সবসময় ভঙ্গুর। সঠিক টুল এলে আগের জটিল কৌশল সরিয়ে ফেলাও একটি দক্ষতা।

প্র ০২Media query দিয়ে breakpoint লেখা, নাকি intrinsic প্যাটার্ন — রেসপন্সিভ ডিজাইনের চিন্তা কীভাবে বদলেছে?

২০১০ সালে Ethan Marcotte A List Apart-এ “Responsive Web Design” লেখাটি লেখেন। তাঁর তিন উপকরণ ছিল: নমনীয় grid (শতাংশে কলাম), নমনীয় ছবি, আর media query. তখনকার জন্য এটি বিপ্লব — একই পাতা ফোনে আর ল্যাপটপে আলাদা সাজে। কিন্তু কয়েক বছরেই একটি অভ্যাস তৈরি হলো: কয়েকটি জনপ্রিয় ডিভাইসের মাপ (যেমন 320, 768, 1024) ধরে breakpoint লেখা, আর প্রতিটি কম্পোনেন্টে সেই সংখ্যাগুলোর জন্য আলাদা নিয়ম।

সমস্যা দুটি। প্রথমত, ডিভাইসের মাপের শেষ নেই — ভাঁজ করা ফোন, ট্যাবলেট, বড় মনিটরে ভাগ করা জানালা। দ্বিতীয়ত, আরও বড় সমস্যা: একটি কম্পোনেন্ট জানে না সে কোথায় বসবে। একটি পণ্য-কার্ড main-এর চওড়া জায়গায়ও থাকতে পারে, sidebar-এর সরু জায়গায়ও। পর্দা 1200px হলেও sidebar-এ তার জায়গা হয়তো 250px. পর্দার মাপ দেখে লেখা media query তখন ভুল সিদ্ধান্ত নেয়।

২০১৮ সালে Jen Simmons এই নতুন চিন্তার নাম দেন “Intrinsic Web Design” — Grid, Flexbox, minmax(), fr, min()/max() দিয়ে এমন নিয়ম লেখা, যাতে কম্পোনেন্ট নিজের পাওয়া জায়গা দেখে মানিয়ে নেয়। Every Layout-এর primitive-গুলো ঠিক এই দর্শনের ব্যবহারিক রূপ: Sidebar, Switcher, auto-fit grid — কোনোটিতেই breakpoint নেই। আর ২০২৩ সালে container query সব প্রধান ব্রাউজারে আসার পর যেখানে সত্যিই শর্ত লাগে, সেখানেও পর্দার বদলে container দেখে লেখা যায়।

তাহলে কি media query বাদ? না। পাতার বড় কাঠামো বদলানো (holy grail থেকে এক কলাম), প্রিন্টের জন্য আলাদা স্টাইল, ব্যবহারকারীর পছন্দ — dark mode, কম অ্যানিমেশন — এসবের জন্য media query-ই ঠিক টুল। পার্থক্যটা হলো ভূমিকায়: আগে media query ছিল প্রধান হাতিয়ার, এখন সেটি শেষ ভরসা।

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

প্র ০৩CSS দিয়ে চোখে দেখা ক্রম বদলানো এত সহজ — order, grid-template-areas, dense. তাহলে এটা বিপজ্জনক কেন, আর কতটুকু করা চলে?

একটি পাতা অন্তত তিনভাবে “পড়া” হয়। চোখ দিয়ে — CSS যেভাবে সাজিয়েছে। Screen reader দিয়ে — HTML-এর ক্রমে, কারণ সে accessibility tree পড়ে, যা DOM থেকে তৈরি। আর কীবোর্ড দিয়ে — Tab চাপলেও focus সরে DOM-এর ক্রমে। CSS শুধু প্রথমটি বদলায়, বাকি দুটি নয়।

ধরুন একটি চাকরির আবেদন ফর্মে নকশা মেলাতে “জমা দিন” বোতামকে order: -1 দিয়ে চোখে ওপরে আনা হলো। দৃষ্টিহীন ব্যবহারকারী বোতামটি পাবেন সবার শেষে; আর যিনি চোখে দেখেন কিন্তু হাতের সমস্যার কারণে কীবোর্ড ব্যবহার করেন, তিনি Tab চাপলে দেখবেন focus নিচের ঘরগুলোতে লাফিয়ে শেষে হঠাৎ ওপরে উঠছে। WCAG 2.2-এর 1.3.2 “Meaningful Sequence” বলে, যেখানে ক্রমের অর্থ আছে সেখানে প্রোগ্রাম যে ক্রম পড়ে তা অর্থবহ হতে হবে; আর 2.4.3 “Focus Order” বলে focus-এর ক্রম অর্থ ও ব্যবহার বজায় রাখবে।

তাহলে কি ক্রম বদলানো নিষেধ? না, পার্থক্যটা অর্থে। holy grail-এ nav বাঁয়ে না ডানে, তাতে কারও পড়ার অর্থ বদলায় না — চলে। ফোনে aside-কে main-এর নিচে পাঠানো — HTML-এও যদি aside পরে থাকে, চলে। কিন্তু ধাপে ধাপে নির্দেশনা, তারিখ-ক্রমের খবর, ফর্মের ঘর, বা লিংকে ভরা গ্যালারির ক্রম ওলটপালট — চলবে না। নিরাপদ নিয়ম: HTML-এ আগে সঠিক পড়ার ক্রম লিখুন, তারপর এমন লেআউট বেছে নিন যা সেই ক্রমকে সম্মান করে।

এই সমস্যা এত সাধারণ যে CSS Working Group reading-flow নামে একটি নতুন property আনছে, যাতে ব্রাউজার চোখের ক্রম অনুযায়ী focus আর পড়ার ক্রম সাজাতে পারে। এটি Chromium-ভিত্তিক ব্রাউজারে এসেছে, কিন্তু এখনো Baseline নয় — তাই আজকের কাজে এর ওপর ভরসা করা চলে না।

মূল উপলব্ধি: CSS-এর ক্ষমতা হলো চেহারা বদলানো, অর্থ নয়। যেখানে ক্রমই অর্থ, সেখানে ক্রম বদলানোর জায়গা HTML.

প্র ০৪flex-grow: 999 বা calc((30rem - 100%) * 999) — এগুলো কি নোংরা “hack”, নাকি বৈধ নকশা? পেশাদার কোডে এমন কৌশল কতটা চলে?

প্রথম দেখায় 999 সংখ্যাটা খটকা লাগে — যেন কেউ জোর করে কাজ চালাচ্ছে। কিন্তু hack আর কৌশলের পার্থক্য বোঝার একটি পরিষ্কার মাপকাঠি আছে: এটি কি spec-এ লেখা আচরণের ওপর দাঁড়িয়ে, নাকি কোনো ব্রাউজারের ভুলের ওপর?

পুরোনো যুগের অনেক hack দাঁড়িয়ে ছিল ব্রাউজারের ভুলের ওপর — যেমন Internet Explorer-এর কোনো ভুল পার্সিং কাজে লাগিয়ে শুধু তাকে আলাদা CSS খাওয়ানো। ব্রাউজার ভুল সারালেই সেই কোড ভেঙে যেত। অন্যদিকে flex-grow: 999 সম্পূর্ণ spec-মাফিক: flex-grow একটি অনুপাত, আর 1 : 999 অনুপাতে ভাগ করা নিখুঁতভাবে নির্ধারিত। calc((30rem - 100%) * 999)-ও তাই — ঋণাত্মক flex-basis 0-তে আটকে যাওয়া আর বড় মানে সারি ভাঙা, দুটোই নিয়মে লেখা। Heydon Pickering এই কৌশলের মজার নাম দেন “Flexbox Holy Albatross”, আর Every Layout পরে এটিকে Switcher primitive হিসেবে গুছিয়ে নেয়।

তবে spec-মাফিক হলেই কৌশল ভালো হয় না; আরও দুটি প্রশ্ন আছে। পড়া যায় কি? 999 দেখে পরের ডেভেলপার বিভ্রান্ত হতে পারেন — তাই একটি মন্তব্য, বা --sidebar-threshold-এর মতো নামওয়ালা CSS variable (পর্ব ৩৩) দিয়ে উদ্দেশ্য স্পষ্ট করুন। এক জায়গায় আছে কি? প্রতিটি কম্পোনেন্টে কৌশলটা নতুন করে না লিখে একটি primitive class-এ রাখুন, বাকিরা শুধু class ব্যবহার করবে — পর্ব ৪৬-এর CSS architecture-এ এই চিন্তা আরও বিস্তারিত।

আজ বিকল্পও আছে: container query দিয়ে একই কাজ আরও সরাসরি লেখা যায় (“container 30rem-এর কম হলে flex-direction: column”). কোনটি বেছে নেবেন, সেটি দলের অভ্যাস আর কোডের পাঠযোগ্যতার প্রশ্ন; দুটোই বৈধ।

মূল উপলব্ধি: কৌশল তখনই টেকসই, যখন তা নিয়মের ওপর দাঁড়িয়ে, নামে-মন্তব্যে বোঝানো, আর এক জায়গায় রাখা। অদ্ভুত দেখানো আর ভঙ্গুর হওয়া এক জিনিস নয়।

প্র ০৫Bootstrap-এর মতো framework-এর “১২ কলামের grid” আর CSS Grid-এর নিজের নামওয়ালা এলাকা — আজ কোনটি বেছে নেবেন?

২০০৮ সালের দিকে Nathan Smith-এর “960 Grid System” জনপ্রিয় হয়: 960px চওড়া পাতা, ১২ বা ১৬টি কলাম, আর .grid_4-এর মতো class দিয়ে কোন অংশ কয় কলাম নেবে। ১২ সংখ্যাটি বেছে নেওয়ার কারণ সহজ — একে ২, ৩, ৪ আর ৬ দিয়ে ভাগ করা যায়, তাই অর্ধেক, তিন ভাগের এক, চার ভাগের এক সব সহজে মেলে। ২০১১ সালে Twitter-এর দুই প্রকৌশলী Mark Otto আর Jacob Thornton Bootstrap প্রকাশ করেন, আর তার ১২ কলামের class (.col-md-4-এর মতো) কোটি সাইটে ছড়িয়ে পড়ে। তখন CSS-এ নিজের grid ছিল না, তাই এই class-গুলো ভেতরে float, পরে flexbox দিয়ে কলাম বানাত।

CSS Grid আসার পর হিসাব বদলেছে। নিজের Grid-এর সুবিধা: কলামের সংখ্যা যা খুশি, fr আর minmax() দিয়ে নমনীয়; grid-template-areas-এ নকশা এক জায়গায় পড়া যায়; HTML-এ সারি-মোড়ানো বাড়তি div লাগে না; আর ফোনের জন্য মানচিত্র বদলালেই হলো — প্রতিটি element-এ পাঁচটি করে class নয়। Framework-এর সুবিধা: বড় দলে সবাই একই ভাষায় কথা বলে, প্রস্তুত কম্পোনেন্ট মেলে, আর নতুন সদস্য দ্রুত কাজ শুরু করতে পারেন।

বাস্তবে একটি মাঝামাঝি পথ জনপ্রিয়: পুরো পাতার কাঠামো নিজের Grid-এ (areas দিয়ে), আর ভেতরের ছোট সাজ framework-এর utility class বা নিজের primitive দিয়ে। Bootstrap-ও এখন flexbox-ভিত্তিক grid ব্যবহার করে আর CSS Grid-এর জন্য আলাদা ব্যবস্থাও রেখেছে। Tailwind CSS-এর মতো utility framework তো সরাসরি grid-cols-3-এর মতো class দিয়ে CSS Grid-ই লেখায়। কোনটি কখন, তা পর্ব ৫১-এ বিস্তারিত দেখবেন।

একটি সতর্কতা: framework ব্যবহার করলেও ভেতরে কী হচ্ছে তা না জানলে বিপদ। .col-md-4 ফোনে কেন পুরো চওড়া হয়, বা টেবিল কেন কলাম ঠেলে বের করে দিচ্ছে — এগুলো বুঝতে এই পর্বের Grid আর Flexbox-ই লাগবে।

মূল উপলব্ধি: ১২ কলাম ছিল টুলের অভাবের যুগের চমৎকার সমাধান। আজ ব্রাউজার নিজেই grid দেয় — framework বেছে নিন দলের সুবিধার জন্য, লেআউটের অভাবের জন্য নয়।

অনুশীলন

হাতে হিসাব, এডিটরে পরীক্ষা আর DevTools-এ দেখা — তিনটিই কাজে লাগান।

  1. ভাঁজের সীমা হিসাব করুন: একটি Sidebar-এ sidebar-এর flex-basis: 20rem, মূল অংশে flex-basis: 0; flex-grow: 999; min-inline-size: 60%, আর gap: 1rem. root font-size 16px. container কত চওড়ার কম হলে দুই অংশ ওপর-নিচে চলে যাবে?

    20rem = 320px, gap = 16px. এক সারিতে আঁটার শর্ত: 320 + 16 + 0.6W ≤ W, অর্থাৎ 336 ≤ 0.4W, অর্থাৎ W ≥ 840px. তাই container 840px-এর কম হলেই ভাঁজ। খেয়াল করুন, min-inline-size 50% থেকে 60% করায় সীমা 672px থেকে লাফিয়ে 840px হলো — শতাংশটা ছোট পরিবর্তন মনে হলেও ফল বড়। সাধারণ সূত্র: সীমা = (sidebar + gap) ÷ (1 − শতাংশ)।

  2. Holy grail-এর ফোন-রূপ লিখুন: অংশ ৪-এর holy grail-এর জন্য একটি @media (width < 40rem) নিয়ম লিখুন, যাতে ফোনে ক্রম হয় header → main → nav → aside → footer. HTML-এ nav আছে main-এর আগে। এতে কোনো অ্যাক্সেসিবিলিটি ঝুঁকি আছে কি?
    @media (width < 40rem) {
      .layout {
        grid-template-columns: 1fr;
        grid-template-rows: auto;
        grid-template-areas: "header" "main" "nav" "aside" "footer";
      }
    }

    grid-template-rows: auto দেওয়ার কারণ: বড় পর্দার auto 1fr auto তিন সারির জন্য লেখা; পাঁচ সারির মানচিত্রে বাকি সারিগুলো আপনা-আপনি auto হবে, তবু পরিষ্কারের জন্য বদলে দেওয়া ভালো। ঝুঁকি: চোখে main আগে, কিন্তু HTML-এ nav আগে — Tab চাপলে প্রথমে nav-এর লিংকগুলো focus পাবে, যা পর্দায় তখন main-এর নিচে। ক্ষতি ছোট, কারণ এই দুটির ক্রমে অর্থ বদলায় না; তবু ভালো সমাধান হলো HTML-এর ক্রম ঠিক করা আর ওপরে একটি “সরাসরি মূল লেখায় যান” skip link রাখা (পর্ব ১২)।

  3. Dense-এর ক্রম খুঁজুন: অংশ ৭-এর গ্যালারি এডিটরে চোখে দেখা ক্রমে (ওপর-বাঁ থেকে ডানে, তারপর নিচের সারি) ছবির নম্বরগুলো লিখুন। তারপর dense মুছে আবার লিখুন। কোন অবস্থায় ক্রম ১, ২, ৩… এর সাথে মেলে, আর কোথায় ফাঁকা ঘর দেখা দিল?

    dense থাকলে সারি ধরে পড়লে: সারি ১-এ ১, ২; সারি ২-এ (১-এর বাকি অংশ), ৩; সারি ৩-এ ৪, ৫, ৬, ৮; সারি ৪-এ ৯, (৫-এর বাকি অংশ), ৭. অর্থাৎ চোখের ক্রম ১, ২, ৩, ৪, ৫, ৬, ৮, ৯, ৭ — HTML-এর ক্রমের সাথে মেলে না, যদিও কোনো ফাঁকা ঘর নেই। dense মুছলে ক্রম ঠিক ১ থেকে ৯, কিন্তু চওড়া ৭ তৃতীয় সারির শেষের এক ঘরে না আঁটায় সেই ঘর খালি থাকে; ৭ বসে চতুর্থ সারির ডান দিকের দুই ঘরে, কারণ বাঁয়ের দ্বিতীয় ঘরে লম্বা ৫ নেমে এসেছে — ফলে চতুর্থ সারির প্রথম ঘরও খালি, আর ৮-৯ যায় পঞ্চম সারিতে। এটাই dense-এর বিনিময়: ফাঁক কম, কিন্তু ক্রম ভিন্ন। DevTools-এ .gallery-এর পাশের grid ব্যাজ চাপলে লাইন নম্বরসহ ঘরগুলো দেখা যাবে — খালি ঘর খুঁজতে খুব কাজের।

  4. AI অডিট: যেকোনো AI-কে বলুন “একটি অনলাইন দোকানের পণ্য-তালিকা পাতা বানাও — বাঁয়ে ফিল্টার, ডানে পণ্যের কার্ড।” তারপর অংশ ৯-এর ভুলের টেবিল আর চেকলিস্ট ধরে কোডটি যাচাই করুন। কোন কোন প্যাটার্ন ব্যবহার করা উচিত ছিল?

    সাধারণত যা পাওয়া যায়:

    • ফিল্টারের নির্দিষ্ট width: 250px আর পণ্য-অংশে calc(100% - 250px), সাথে ৭৬৮px-এ একটি media query — অথচ Sidebar primitive দিয়ে এটি নিজেই ভাঁজ হতো।
    • কার্ডের জন্য তিনটি আলাদা media query-তে ৪, ৩, ২ কলাম — repeat(auto-fit, minmax(min(12rem, 100%), 1fr)) এক লাইনেই সব সামলাত।
    • ফিল্টার <div class="sidebar"> — হওয়া উচিত <aside> বা ফর্ম হলে <form>, আর শিরোনাম সহ।
    • কার্ডের দাম আর বোতাম এলোমেলো উচ্চতায় — subgrid বা কার্ডের ভেতরে flex column + বোতামে margin-block-start: auto দিয়ে মেলানো যেত।

    প্রতিটি সমস্যা DevTools-এর device toolbar আর Grid overlay দিয়ে প্রমাণ করুন, তারপর AI-কে প্যাটার্নের নাম ধরে নির্দিষ্ট করে বলুন কী বদলাতে হবে।

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

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

ক্রমএকটি নতুন পাতার লেআউট বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📝 semantic HTML লেখা, পড়ার স্বাভাবিক ক্রমে
  2. 🥞 পুরো পাতার সারি ঠিক করা — header, main, footer (pancake)
  3. 🗺️ বড় কাঠামোর কলাম — grid-template-areas
  4. 🧱 ভেতরের কম্পোনেন্টে primitive — Stack, Cluster, Sidebar, Grid
  5. 🔍 সরু পর্দা, ২০০% zoom আর Tab-ক্রমে যাচাই, দরকার হলে query

একটি container-এ min-block-size: 100dvh; display: grid; grid-template-rows: auto 1fr auto; দেওয়া, ভেতরে header, main আর footer. এর মূল ফল কী?

  • তিনটি অংশ সমান উঁচু হয়
  • লেখা কম হলেও footer পর্দার নিচে থাকে, main বাড়তি জায়গা নেয়
  • footer স্ক্রল করলেও পর্দায় আটকে থাকে
  • main-এর বাড়তি লেখা লুকিয়ে যায়

নিচের কোন grid-template-areas মানটি অবৈধ, ফলে পুরো declaration বাতিল হবে?

  • "header header" "nav main"
  • ". main" "foot foot"
  • "a b a"
  • "a a" "a a"

একটি flex container-এ flex-wrap: wrap; gap: 1rem. sidebar-এ flex-basis: 15rem; flex-grow: 1, মূল অংশে flex-basis: 0; flex-grow: 999; min-inline-size: 50%. root font-size 16px. কখন দুই অংশ ওপর-নিচে যাবে?

  • container প্রায় 512px-এর কম চওড়া হলে
  • কখনোই না, কারণ মূল অংশের flex-basis 0
  • পর্দা 768px-এর কম হলে
  • container ঠিক 240px হলে

Grid-এ কলামের লাইনের নাম দেওয়া হয়েছে [content-start] আর [content-end]. একটি item-এ grid-column: content লিখলে কী হয়?

  • ভুল, কারণ grid-template-areas-এ content নাম নেই
  • item শুধু content-start লাইনে শুরু হয়, এক কলাম নেয়
  • item পুরো চওড়া জুড়ে বসে
  • item content-start থেকে content-end লাইন পর্যন্ত বসে

ছবির গ্যালারিতে grid-auto-flow: dense ব্যবহারের প্রধান ঝুঁকি কোনটি?

  • বেশিরভাগ ব্রাউজারে কাজ করে না
  • চোখে দেখা ক্রম আর HTML/Tab-এর ক্রম আলাদা হয়ে যেতে পারে
  • ছবিগুলো চেপ্টা হয়ে যায়
  • span দেওয়া item আর কাজ করে না

একটি grid-এর 1fr কলামের ভেতরে 30rem চওড়া টেবিল; সরু পর্দায় পুরো পাতা ডানে বেরিয়ে যাচ্ছে, যদিও টেবিলের wrapper-এ overflow-x: auto আছে। সবচেয়ে সরাসরি সমাধান কোনটি?

  • টেবিলে z-index: 1
  • body-তে overflow: hidden
  • টেবিলের parent grid item-এ min-inline-size: 0
  • টেবিলে width: 300px

পাশাপাশি তিনটি প্রাইসিং কার্ডে সুবিধার তালিকা ভিন্ন লম্বা; চাই সব কার্ডের দাম আর বোতাম একই লাইনে থাকুক। কোন CSS এর জন্য তৈরি?

  • কার্ডে grid-row: span 4; grid-template-rows: subgrid
  • grid-auto-flow: dense
  • প্রতিটি কার্ডে height: 400px
  • flex-wrap: wrap

শব্দকোষ

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

মনে রাখুন

  1. লেআউট প্যাটার্ন হলো বারবার দেখা সমস্যার পরীক্ষিত সমাধান; ছোট primitive (Stack, Cluster, Center, Sidebar, Switcher, Cover, Grid) জুড়ে বড় পাতা — নামগুলো Every Layout-এর, Heydon Pickering ও Andy Bell-এর।
  2. Sidebar: flex-wrap + sidebar-এ flex-basis + মূল অংশে flex-grow: 999 আর min-inline-size — ভাঁজের সীমা নিয়ম থেকেই আসে।
  3. Pancake: grid-template-rows: auto 1fr auto; holy grail: grid-template-areas-এর মানচিত্র, ফোনে শুধু মানচিত্র বদলান।
  4. Full-bleed: [full-start] … [content-start] min(65ch, 100% - 2rem) [content-end] … [full-end]; grid-column: content বা full.
  5. ড্যাশবোর্ডে min-inline-size: 0 আর টেবিলের scroll container; গ্যালারিতে span + dense, কিন্তু চোখের ক্রম আর HTML-এর ক্রম মেলান; কার্ডের লাইন মেলাতে subgrid.

পরের পর্বে: এই পর্বের প্যাটার্নগুলো প্রায় নিজে নিজেই ফোনে মানিয়ে নেয়। এবার পুরো ছবিটা দেখব — viewport meta tag কী করে, “মোবাইল-ফার্স্ট” মানে কী, আর একটি সাইটকে শুরু থেকেই সব পর্দার জন্য কীভাবে ভাবতে হয়।

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

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