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

Grid গভীরে — template areas, auto-fit, minmax আর subgrid

Advanced CSS Grid — Template Areas, Named Lines, minmax(), auto-fill vs auto-fit, Intrinsic Sizing & Subgrid

Taught in Bangla, this lesson goes deeper into CSS Grid: grid-template-areas and rearranging them on small screens without DOM-order pitfalls, named lines for full-bleed article layouts, minmax() and the hidden min-content in 1fr, auto-fill versus auto-fit for responsive cards without media queries, intrinsic sizing with min-content, max-content and fit-content(), subgrid, the masonry outlook, and a news-site magazine layout.

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

  • grid-template-areas দিয়ে নকশার মানচিত্র আঁকা, ছোট পর্দায় এলাকা বদলানো, আর DOM order-এর অ্যাক্সেসিবিলিটি-ঝুঁকি চেনা
  • নাম দেওয়া লাইন আর এলাকার নাম থেকে আপনা-আপনি তৈরি লাইন দিয়ে full-bleed লেখার পাতা বানানো
  • minmax(), min-content, max-content আর fit-content() — ব্রাউজার কলামের মাপ কীভাবে ঠিক করে, আর 1fr কেন উপচে পড়ে
  • auto-fill বনাম auto-fit, আর এক লাইনের সেই বিখ্যাত রেসপন্সিভ কার্ড grid — প্রতিটি অংশ কেন আছে
  • subgrid দিয়ে কার্ড মেলানো, masonry-র বর্তমান অবস্থা, aspect-ratio গ্যালারি আর একটি বাংলা সংবাদ সাইটের ম্যাগাজিন লেআউট

১ · grid-template-areas — CSS-এর ভেতরে লেআউটের মানচিত্র

পর্ব ২৭-এ (CSS Grid — সারি আর কলামে দুই দিকের লেআউট) আপনি grid-এর মূল ধারণাগুলো দেখেছেন — grid container, সারি আর কলাম, fr একক, gap, আর লাইনের নম্বর ধরে item বসানো। এই পর্বে সেগুলো আবার বলব না; বরং দেখব পেশাদার লেআউটে রোজ কোন কৌশল লাগে, আর কোথায় সবাই হোঁচট খান।

প্রথম কৌশলটি সবচেয়ে “দেখতে সুন্দর”। লাইনের নম্বর (grid-column: 1 / 3) দিয়ে item বসানো কাজ করে ঠিকই, কিন্তু কয়েক সপ্তাহ পর কোড খুলে বোঝা কঠিন — “লাইন ৩ মানে যেন কোনটা?” তার বদলে CSS Grid আপনাকে লেআউটের একটি ছোট মানচিত্র সরাসরি CSS-এ আঁকতে দেয়। এর নাম grid-template-areas.

নতুন বাড়ি বানানোর আগে রাজমিস্ত্রি একটি খাতায় ছক কেটে নকশা আঁকেন — ওপরের সারিতে “বারান্দা বারান্দা”, তার নিচে “শোবার-ঘর বসার-ঘর”, একেবারে নিচে “রান্নাঘর ফাঁকা”। একই নাম পাশাপাশি দুই ঘরে লেখা মানে সেটা একটাই বড় ঘর। grid-template-areas ঠিক এই খাতার ছক: প্রতিটি উদ্ধৃতি-চিহ্নের ভেতরের লেখা একটি সারি, প্রতিটি শব্দ একটি ঘর, আর একই নামের ঘরগুলো মিলে একটি এলাকা।

নিচের এডিটরে একটি ছোট দোকানের পাতা আছে। CSS-এর মাঝখানের তিন লাইন যেন একটি ASCII-ছবি — চোখে দেখেই বোঝা যায় পাতাটা কেমন হবে। তারপর প্রতিটি element-কে grid-area দিয়ে বলে দেওয়া হয়েছে সে কোন এলাকায় বসবে।

.page {
  display: grid;
  grid-template-columns: 150px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "head head"
    "side main"
    ".    foot";
  gap: 8px;
  min-height: 260px;
  font-family: sans-serif;
}
.page > * { padding: 10px; border-radius: 6px; margin: 0; }
header { grid-area: head; background: #bfdbfe; }
nav    { grid-area: side; background: #fde68a; }
main   { grid-area: main; background: #bbf7d0; }
footer { grid-area: foot; background: #e9d5ff; }

ওপরে নীল header দুই কলাম জুড়ে, বাঁয়ে হলুদ ক্যাটাগরি, ডানে সবুজ অফার, আর নিচের সারিতে বাঁয়ের ঘরটি খালি (.) রেখে ডানে বেগুনি footer. খেয়াল করুন, item-গুলোর জন্য কোনো লাইনের নম্বর লিখতে হয়নি। চেষ্টা করুন: (১) মাঝের সারি "side main"-কে "main side" করুন — CSS-এর এক জায়গা বদলেই sidebar ডানে চলে গেল (কলামের মাপও সাথে বদলাতে grid-template-columns-কে 1fr 150px করুন)। (২) শেষ সারির .-কে side করুন — হলুদ sidebar নিচ পর্যন্ত লম্বা হলো, কারণ এখন side এলাকা দুই সারি জুড়ে একটি আয়তক্ষেত্র।

এই মানচিত্র লেখার কয়েকটি কড়া নিয়ম আছে। নিয়ম ভাঙলে ব্রাউজার কোনো error দেখায় না — পুরো ঘোষণাটি চুপচাপ বাতিল করে দেয়, তাই নিয়মগুলো জানা জরুরি। নিচের ডায়াগ্রামে দেখুন ব্রাউজার লেখাটি কীভাবে পড়ে:

head
head
side
main
.
foot
head (২ ঘর মিলে ১ এলাকা)
side
main
.
foot
head
side
main
. = খালি ঘর
foot
head
side ✗
side ✗
main
কোনাকুনি দুই ঘর — আয়তক্ষেত্র নয়, পুরো ঘোষণা বাতিল
◀ head-starthead-end ▶
◀ side-startside-end ▶
◀ main-startmain-end ▶
.
◀ foot-startfoot-end ▶
  1. প্রতিটি স্ট্রিং একটি সারি, প্রতিটি শব্দ একটি ঘর। ব্রাউজার "head head" "side main" ". foot" পড়ে প্রথমে শব্দগুলো আলাদা করে। ফাঁকা জায়গা কতটা দিলেন তাতে কিছু যায় আসে না — ওপরের মতো সাজিয়ে লেখা শুধু মানুষের চোখের সুবিধার জন্য। শর্ত একটাই: প্রতিটি সারিতে ঘরের সংখ্যা সমান হতে হবে, নইলে পুরো ঘোষণা অবৈধ।
  2. একই নাম মানে একটি এলাকা। পাশাপাশি দুটি head মিলে একটি এলাকা হলো, যা দুই কলাম জুড়ে আছে। একইভাবে ওপর-নিচে একই নাম লিখলে এলাকা কয়েক সারি জুড়ে লম্বা হয়। এলাকার নাম আপনার ইচ্ছামতো — header, khobor, ad1 — তবে CSS-এর শনাক্তকারী (identifier) হতে হবে, অর্থাৎ সংখ্যা দিয়ে শুরু নয়।
  3. বিন্দু মানে খালি ঘর। . কোনো এলাকার অংশ নয় — সেখানে কেউ বসবে না (চাইলে লাইনের নম্বর দিয়ে কাউকে বসানো যায়)। কলাম মেলাতে সুবিধা হলে একটানা কয়েকটি বিন্দু (...) লিখতে পারেন; ফাঁক ছাড়া পরপর বিন্দুগুলো একটিই খালি ঘর ধরা হয়।
  4. এলাকা অবশ্যই আয়তাকার। এখানে side প্রথম সারির ডানে আর দ্বিতীয় সারির বাঁয়ে — কোনাকুনি দুটি ঘর, জোড়া লাগানো একটি আয়তক্ষেত্র নয়। Grid-এর এলাকা L বা T আকারের হতে পারে না, আবার একই নাম দুই আলাদা জায়গাতেও থাকতে পারে না। এমন কিছু পেলে ব্রাউজার পুরো grid-template-areas ঘোষণাটিই উপেক্ষা করে — কোনো error নেই, শুধু লেআউট ভেঙে যায়। DevTools-এর Styles প্যানেলে ঘোষণাটি কাটা দাগসহ দেখাবে।
  5. এলাকা থেকে নামসহ লাইন। ব্রাউজার প্রতিটি এলাকার চার কিনারায় আপনা-আপনি লাইনের নাম বসিয়ে দেয়: head-start, head-end ইত্যাদি (সারি আর কলাম দুই দিকেই)। তারপর যার grid-area: head, সে ওই লাইনগুলোর মাঝে বসে। এই লুকানো নামগুলো কীভাবে কাজে লাগে, তা অংশ ৩-এ দেখব।
মূল ধারণা

grid-template-areas লেআউটের আকার বলে, মাপ বলে না। কলাম আর সারির চওড়া-উচ্চতা আলাদা করে grid-template-columns আর grid-template-rows-এ দিতে হয়। তিনটি একসাথে লেখার shorthand-ও আছে — grid-template — যা অংশ ৩-এর শেষে দেখব।

২ · ছোট পর্দায় এলাকা বদল — আর DOM order-এর ফাঁদ

Template areas-এর আসল জাদু দেখা যায় রেসপন্সিভ কাজে। মোবাইলে সব এলাকা ওপর-নিচে এক কলামে, বড় পর্দায় পাশাপাশি — শুধু মানচিত্রটা বদলে দিলেই হলো, HTML-এ হাত দিতে হয় না। সাধারণত এটা করা হয় media query দিয়ে (বিস্তারিত পর্ব ৩১-এ)। নিচের এডিটরে একটু ভিন্ন পথ নেওয়া হয়েছে: ড্যাশ দেওয়া বাক্সটি একটি container query-র “পাত্র”, আর বাক্সের ডান-নিচের কোণ টেনে আপনি নিজেই তার চওড়া বদলাতে পারবেন — যেন হাতে ফোন আর ল্যাপটপ পাল্টাচ্ছেন।

<style>
  body { font-family: sans-serif; }
  .frame { container-type: inline-size; resize: horizontal;
           overflow: auto; border: 2px dashed #64748b; padding: 6px; }
  .layout { display: grid; gap: 8px;
            grid-template-areas: "main" "promo"; }
  @container (min-width: 420px) {
    .layout { grid-template-columns: 1fr 2fr;
              grid-template-areas: "promo main"; }
  }
  .main  { grid-area: main;  background: #bbf7d0; padding: 8px; }
  .promo { grid-area: promo; background: #fde68a; padding: 8px; }
  a:focus-visible { outline: 3px solid #dc2626; outline-offset: 2px; }
</style>
<div class="frame">
  <div class="layout">
    <div class="main"><b>আজকের অফার</b><br>
      <a href="#a">১. চাল কিনুন</a> · <a href="#b">২. ডাল কিনুন</a></div>
    <div class="promo"><a href="#c">৩. বিকাশে পেমেন্ট</a></div>
  </div>
</div>
<p>প্রিভিউতে একবার ক্লিক করে Tab চাপুন — লাল দাগ কোন ক্রমে ঘোরে?</p>

চওড়া বাক্সে হলুদ “বিকাশে পেমেন্ট” বাঁয়ে, সবুজ অফার ডানে। কোণ টেনে বাক্স 420px-এর চেয়ে সরু করুন — দুটি ওপর-নিচে চলে আসবে, অফার আগে। এবার আসল পরীক্ষা: চওড়া অবস্থায় প্রিভিউর লেখায় একবার ক্লিক করে Tab চাপতে থাকুন। লাল focus-দাগ যায় ১ → ২ → ৩, অর্থাৎ ডানে থেকে শুরু করে তারপর লাফ দিয়ে বাঁয়ে। চোখ বাঁ থেকে ডানে পড়ে, কিন্তু কীবোর্ড চলছে HTML-এর ক্রমে। চেষ্টা করুন: HTML-এ promo-র div-টি main-এর আগে সরিয়ে দিন — এবার চওড়া অবস্থায় চোখ আর কীবোর্ডের ক্রম মিলে গেল (তবে সরু অবস্থায় আবার অমিল হবে, ভাবুন কেন)।

এই অমিলটাই grid-এর সবচেয়ে বড় অ্যাক্সেসিবিলিটি-ফাঁদ। ব্রাউজারের কাছে পাতার “আসল” ক্রম হলো DOM order — HTML-এ element যে ক্রমে লেখা। Grid (আর flexbox-এর order) শুধু আঁকার জায়গা বদলায়। কীবোর্ডের Tab, screen reader-এর পড়া, ব্রাউজারের reader mode, এমনকি কপি-পেস্ট — সবাই DOM order মেনে চলে। ফলে চোখে যা প্রথম, কীবোর্ড-ব্যবহারকারীর কাছে তা শেষে আসতে পারে।

নিয়ম: অর্থের ক্রম HTML-এ ঠিক করুন, grid দিয়ে শুধু চেহারা সাজান। CSS Grid-এর স্পেসিফিকেশন নিজেই বলে, grid-এর জায়গা বদল কেবল দৃশ্যমান সাজের জন্য — যৌক্তিক ক্রম বদলের জন্য নয়। WCAG 2.2-এর দুটি শর্ত এখানে সরাসরি জড়িত: 1.3.2 (অর্থবহ ক্রম) আর 2.4.3 (focus-এর ক্রম)। সাজসজ্জার জিনিস (ছবি, বিজ্ঞাপন, পাশের তথ্য) সরানো সাধারণত নিরাপদ; কিন্তু ধাপে ধাপে পড়ার জিনিস (ফর্মের ঘর, নির্দেশনা, মূল লেখা) উল্টে দেওয়া বিপজ্জনক।

নতুন সমাধানের খবর: চোখের ক্রম অনুযায়ী focus ঘোরানোর জন্য reading-flow নামে একটি নতুন property প্রস্তাব করা হয়েছে, যা Chromium-ভিত্তিক কিছু ব্রাউজারে এসেছে। এটি এখনো Baseline নয়, তাই এর ওপর ভরসা করে DOM order অগোছালো রাখবেন না — যেসব ব্রাউজারে নেই, সেখানে আগের সমস্যাই থেকে যাবে।

৩ · নামের লাইন — [content-start] থেকে full-bleed লেখার পাতা

পর্ব ২৭-এ লাইনকে নম্বর দিয়ে ডেকেছেন: ১, ২, ৩, শেষেরটি −১। লাইনের নিজের নামও দেওয়া যায় — grid-template-columns-এ মাপের ফাঁকে ফাঁকে বর্গবন্ধনীতে নাম লিখে: [sidebar-start] 200px [sidebar-end main-start] 1fr [main-end]. একটি লাইনের একাধিক নাম থাকতে পারে (এখানে মাঝের লাইনটি একই সাথে sidebar-end আর main-start দুটোই)। এগুলোকে বলে named grid line.

এখানে একটি সুন্দর মিল আছে, যা অনেক অভিজ্ঞ ডেভেলপারও জানেন না। অংশ ১-এ দেখেছেন, এলাকা বানালে ব্রাউজার -start/-end নামের লাইন তৈরি করে। উল্টোটাও সত্য: আপনি যদি content-start আর content-end নামের লাইন বানান, ব্রাউজার আপনা-আপনি content নামের একটি এলাকা ধরে নেয়। তখন grid-column: content লিখলেই item ওই দুই লাইনের মাঝে বসে।

এই কৌশল দিয়ে ব্লগ বা সংবাদের লেখার পাতায় একটি জনপ্রিয় নকশা বানানো হয় — full-bleed লেআউট। লেখা মাঝখানে পড়ার মতো সরু কলামে থাকে, কিন্তু কোনো ছবি বা ব্যানার চাইলে পর্দার এক কিনারা থেকে আরেক কিনারা পর্যন্ত ছড়িয়ে যায় — কোনো বাড়তি wrapper div ছাড়াই।

.article {
  display: grid;
  grid-template-columns:
    [full-start] minmax(12px, 1fr)
    [content-start] minmax(0, 30rem) [content-end]
    minmax(12px, 1fr) [full-end];
  font-family: sans-serif;
}
/* content-start আর content-end থেকে "content" নামের এলাকা নিজেই তৈরি */
.article > * { grid-column: content; }
.article > .wide { grid-column: full; }

.wide {
  height: 90px; display: grid; place-items: center; margin: 0;
  background: linear-gradient(90deg, #0ea5e9, #22c55e);
  color: #fff; font-weight: 700;
}

শিরোনাম আর দুই প্যারাগ্রাফ মাঝখানের সরু কলামে, আর নীল-সবুজ ব্যানারটি পুরো চওড়া জুড়ে। কলাম তিনটি: দুই পাশে minmax(12px, 1fr) — অন্তত 12px, বাকি জায়গা সমান ভাগ — আর মাঝে লেখার কলাম, সর্বোচ্চ 30rem. ছোট পর্দায় দুই পাশে অন্তত 12px ফাঁক থাকে, লেখা কিনারায় লাগে না। চেষ্টা করুন: (১) .wide-এর মান content-start / full-end করুন — ব্যানার লেখার কিনারা থেকে ডান প্রান্ত পর্যন্ত গেল, ম্যাগাজিনের মতো চেহারা। (২) 30rem-কে 20rem করুন — লেখার কলাম সরু হলো, ব্যানার আগের মতোই পুরো চওড়া।

Shorthand: grid-template আর grid — একটু সাবধানে

সারি, কলাম আর এলাকা একসাথে লেখার জন্য grid-template shorthand আছে। প্রতিটি সারির স্ট্রিংয়ের পাশে সেই সারির উচ্চতা, আর শেষে /-এর পরে কলামের মাপ:

.page {
  display: grid;
  grid-template:
    "head head" auto
    "side main" 1fr
    ".    foot" auto
    / 150px 1fr;
}

ছবির মতো পরিষ্কার, তাই অনেকে এটা পছন্দ করেন। কিন্তু তার বড় ভাই grid shorthand নিয়ে সতর্ক থাকুন। grid একসাথে explicit grid (grid-template-*) আর implicit grid-এর (grid-auto-rows, grid-auto-columns, grid-auto-flow) সব property ঠিক করে — যেগুলো লেখেননি, সেগুলো ডিফল্টে ফিরিয়ে দেয়। ফলে আগে কোথাও grid-auto-flow: dense বা grid-auto-rows: 200px লিখে রাখলে, পরে grid: ... লিখলে সেগুলো চুপচাপ মুছে যায়।

আরেকটি পরিচিত ধাঁধা: item-এর grid-area-তে চারটি লাইন দিলে ক্রম হয় সারি-শুরু / কলাম-শুরু / সারি-শেষ / কলাম-শেষ — margin-এর মতো ঘড়ির কাঁটার দিকে নয়। পেশাদার দলগুলোর সাধারণ পরামর্শ: শিখতে বা দলে কাজ করতে আলাদা আলাদা longhand (grid-template-columns, grid-template-areas…) লিখুন; পড়তে সহজ, ভুলও কম।

৪ · minmax() আর 1fr-এর লুকানো min-content

minmax(কমপক্ষে, সর্বোচ্চ) একটি কলাম বা সারিকে (grid-এর ভাষায় track) দুটি সীমা দেয়: এর চেয়ে সরু হবে না, এর চেয়ে চওড়াও হবে না। যেমন minmax(200px, 1fr) মানে “অন্তত 200px, জায়গা থাকলে বাকি অংশের এক ভাগ”। আর minmax(100px, auto) সারিতে দিলে মানে “অন্তত 100px উঁচু, লেখা বেশি হলে আরও উঁচু”।

এবার এমন একটি ঘটনা, যা প্রায় সবার সাথে একবার না একবার ঘটে। সংবাদের সাইটে দুটি সমান কলাম বানালেন — grid-template-columns: 1fr 1fr. সব ঠিকঠাক, যতক্ষণ না একদিন কোনো খবরে একটি লম্বা লিংক বা কোডের লাইন এলো। হঠাৎ কলাম দুটো আর সমান নেই, পুরো লেআউট ডানে উপচে গেল, ফোনে পাশে স্ক্রল শুরু হলো। নিজেই দেখুন:

<style>
  body { font-family: sans-serif; }
  .box { width: 320px; border: 2px dashed #64748b; margin-bottom: 14px; }
  .g { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .g > p { background: #e0f2fe; padding: 6px; margin: 0; }
  /* সমাধান: কলামের নিচের সীমা 0, আর লম্বা শব্দ ভাঙার অনুমতি */
  .fix { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .fix .url { overflow-wrap: anywhere; }
</style>
<b>সমস্যা: 1fr 1fr</b>
<div class="box"><div class="g">
  <p>সংবাদের সারাংশ এখানে কয়েকটি শব্দে লেখা</p>
  <p class="url">https://example.com/bangla_news/metro_rail_new_station_opening_2026</p>
</div></div>
<b>সমাধান: minmax(0, 1fr)</b>
<div class="box"><div class="g fix">
  <p>সংবাদের সারাংশ এখানে কয়েকটি শব্দে লেখা</p>
  <p class="url">https://example.com/bangla_news/metro_rail_new_station_opening_2026</p>
</div></div>

ওপরের বাক্সে বাঁয়ের কলাম অদ্ভুত সরু হয়ে গেছে, ডানের কলাম লম্বা URL-এর সমান চওড়া হয়ে ড্যাশ দেওয়া বাক্সের বাইরে বেরিয়ে গেছে। নিচের বাক্সে দুই কলাম সমান, URL ভেঙে কয়েক লাইনে নেমেছে, কিছুই উপচে পড়েনি। চেষ্টা করুন: (১) .fix .url নিয়মটি মুছে দিন — কলাম তখনো সমান থাকবে (লেআউট নিরাপদ), কিন্তু URL নিজের ঘর থেকে বেরিয়ে পাশে চলে যাবে; অর্থাৎ minmax(0, 1fr) লেআউট বাঁচায়, লেখাটা সামলাতে হয় আলাদাভাবে। (২) minmax(0, 1fr)-এর বদলে শুধু .g > p-তে min-width: 0 যোগ করে দেখুন — একই কাজ হয়।

কেন এমন হয়? কারণ 1fr আসলে একা আসে না। স্পেসিফিকেশন অনুযায়ী 1fr মানে minmax(auto, 1fr) — আর grid item-এর জন্য auto নিচের সীমা মানে সেই item-এর min-content মাপ: লেখাকে যতটা সম্ভব ভেঙেও যতটুকু চওড়া লাগে। বাংলা বা ইংরেজি বাক্য শব্দে শব্দে ভাঙা যায়, তাই তার min-content ছোট। কিন্তু ফাঁকা জায়গাহীন লম্বা লিংক বা কোডের লাইন ভাঙা যায় না — পুরোটাই একটি “শব্দ”। (ব্রাউজার হাইফেন বা কিছু চিহ্নের পরে ভাঙতে পারে, তাই উদাহরণের লিংকে হাইফেনের বদলে _ রাখা হয়েছে।) ধাপে ধাপে হিসাবটা দেখুন:

1.g {2  width: 320px;  /* gap 8px */3  grid-template-columns: 1fr 1fr;4  /* = minmax(auto, 1fr) ×2 */5}6.url  /* লম্বা URL */
কলামের হিসাব (px, আনুমানিক)
ভাগ করার জায়গা
কলাম ১-এর নিচের সীমা
কলাম ২-এর নিচের সীমা
কলাম ১ / কলাম ২
উপচে পড়া
  1. কতটা জায়গা ভাগ হবে। grid container 320px চওড়া, মাঝে 8px gap. তাই দুই কলামের মধ্যে ভাগ করার জায়গা 312px. সরল মনে হিসাব করলে প্রত্যেকে পাবে 156px.
  2. 1fr-এর আসল রূপ। ব্রাউজারের চোখে 1fr হলো minmax(auto, 1fr) — ওপরের সীমা জায়গার এক ভাগ, কিন্তু নিচের সীমা auto. Grid item-এর এই automatic minimum size মানে মোটামুটি “item-এর min-content-এর চেয়ে সরু করো না”। উদ্দেশ্য ভালো: লেখা যেন কাটা না পড়ে।
  3. প্রতিটি item-এর min-content মাপা। বাঁয়ের বাক্যটি শব্দে শব্দে ভাঙা যায়, তাই সবচেয়ে লম্বা শব্দের মাপই তার min-content — ধরা যাক 70px. ডানের URL-এ কোনো ফাঁকা জায়গা নেই, ব্রাউজারের চোখে পুরোটা একটা শব্দ — ধরা যাক 450px. (আসল সংখ্যা ফন্ট আর লেখার ওপর নির্ভর করে; DevTools-এ মেপে দেখতে পারেন।)
  4. ভাগ করতে গিয়ে আটকে গেল। 156px দিলে কলাম ২ তার নিচের সীমা 450-এর চেয়ে সরু হয়ে যায় — তা হবে না। তাই ব্রাউজার কলাম ২-কে 450px-এ স্থির করে বাকি জায়গা আবার ভাগ করে; কিন্তু বাকি থাকে ঋণাত্মক, ফলে কলাম ১ নেমে যায় তার নিজের নিচের সীমায়। মোট 528px, container 320px — বাকি 208px বাইরে উপচে পড়ল। সমান দুই কলামের প্রতিশ্রুতি ভেঙে গেল একটি লিংকের জন্য।
  5. সমাধান: নিচের সীমা নিজে বলে দিন। minmax(0, 1fr) মানে “দরকার হলে শূন্য পর্যন্ত সরু হতে পারো”। এখন দুই কলাম ঠিক 156px করে। কিন্তু URL তো 450px — সে এখন ঘর থেকে বেরিয়ে পাশে যাবে। তাই লেখাটির জন্যও ব্যবস্থা নিন: সাধারণ লেখায় overflow-wrap: anywhere (পর্ব ২১), আর <pre> কোড বা চওড়া টেবিলে overflow-x: auto — যাতে শুধু সেই অংশটি নিজের ভেতরে স্ক্রল হয়।
1fr = minmax(auto, 1fr) — নিচের সীমা লেখার min-content
minmax(0, 1fr) = সত্যিকারের সমান ভাগ, লেখা সামলান আলাদাভাবে

আরও গভীরে: স্পেসিফিকেশনে grid-এর মাপ ঠিক করার একটি নির্দিষ্ট ধাপের অ্যালগরিদম আছে — track sizing algorithm. প্রথমে প্রতিটি track-এর নিচের সীমা থেকে “base size” আর ওপরের সীমা থেকে “growth limit” ঠিক হয়; তারপর min-content/max-content চাওয়া item-গুলোর মাপ যোগ হয় (কয়েক কলাম জুড়ে থাকা item-গুলো পরে, ছোট span থেকে বড় span ক্রমে); তারপর বাড়তি জায়গা বাড়ানো হয়; সবশেষে fr track-গুলো প্রসারিত হয় — আর কোনো fr track তার base size-এর নিচে নামতে চাইলে তাকে “অনমনীয়” ধরে হিসাব আবার শুরু হয়, ঠিক যেমন ধাপ ৪-এ দেখলেন। একটি সূক্ষ্ম কথা: item নিজে scroll container হলে (যেমন overflow-x: auto দেওয়া <pre>) তার automatic minimum size শূন্য ধরা হয় — তাই সেখানে 1fr-ও উপচে পড়ে না।

৫ · auto-fill বনাম auto-fit — media query ছাড়া রেসপন্সিভ কার্ড

অনলাইন দোকানের পণ্যের তালিকার কথা ভাবুন। বড় মনিটরে চারটি কার্ড পাশাপাশি, ট্যাবলেটে দুটি, ফোনে একটি। আগে এর জন্য কয়েকটি media query লিখে প্রতিটিতে কলামের সংখ্যা হাতে বলে দিতে হতো। Grid একটি লাইনেই এই কাজ করে দেয়, কারণ repeat()-এ সংখ্যার বদলে দুটি বিশেষ শব্দ দেওয়া যায়: auto-fill আর auto-fit. দুটিরই মানে “যতগুলো কলাম আঁটে, ততগুলো বানাও” — পার্থক্য শুধু খালি কলাম নিয়ে।

auto-fill — চেয়ার পেতে রাখে

যতগুলো কলাম আঁটে সবগুলো বানিয়ে রাখে, item কম থাকলেও। বাকি কলামগুলো খালি পড়ে থাকে, item-গুলো নিজের মাপেই থাকে। বিয়ের অনুষ্ঠানে অতিথি কম এলেও সব চেয়ার পাতা থাকার মতো।

item কম হলে: ফাঁকা কলাম থাকে

auto-fit — খালি চেয়ার তুলে নেয়

একইভাবে কলাম গোনে, কিন্তু যে কলামে কেউ বসেনি সেগুলো শূন্য চওড়ায় গুটিয়ে ফেলে। তখন 1fr থাকলে বাকি item-গুলো ছড়িয়ে পুরো সারি ভরে দেয়।

item কম হলে: item-গুলো বড় হয়

সারিতে item যথেষ্ট থাকলে দুটির কোনো পার্থক্য চোখে পড়ে না — পার্থক্য দেখা যায় কেবল তখন, যখন একটি সারি ভরানোর মতো item নেই। এবার সেই বিখ্যাত এক লাইনের কোড, প্রতিটি অংশসহ:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: 1rem;
}

বাংলায় পড়লে: “যতগুলো কলাম আঁটে বানাও (auto-fit); প্রতিটি কলাম অন্তত 250px — তবে container এর চেয়ে সরু হলে container-এর পুরো চওড়া (min(250px, 100%)); আর বাড়তি জায়গা 1fr নিয়মে সবাই সমান ভাগ করে নাও।” লেখক Una Kravets-সহ অনেকে এর নাম দিয়েছেন RAM — Repeat, Auto, Minmax. এবার নিজের হাতে বাক্সের চওড়া বদলে দেখুন:

<style>
  body { font-family: sans-serif; }
  .frame { resize: horizontal; overflow: auto; border: 2px dashed #64748b;
           padding: 8px; }
  .cards {
    display: grid;
    /* বাস্তবে সাধারণত 250px; প্রিভিউ ছোট বলে এখানে 150px */
    grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
    gap: 10px;
  }
  .card { background: #fef3c7; border: 1px solid #f59e0b;
          border-radius: 8px; padding: 10px; }
</style>
<div class="frame">
  <div class="cards">
    <article class="card">🍚 মিনিকেট চাল<br>৳৭২/কেজি</article>
    <article class="card">🥣 মসুর ডাল<br>৳১১০/কেজি</article>
    <article class="card">🛢️ সয়াবিন তেল<br>৳১৭০/লিটার</article>
  </div>
</div>
<p>ড্যাশ দেওয়া বাক্সের ডান-নিচের কোণ টেনে সরু-চওড়া করুন।</p>

চওড়া বাক্সে তিনটি কার্ড পাশাপাশি, পুরো সারি জুড়ে। কোণ টেনে সরু করতে থাকুন — কোনো media query ছাড়াই প্রথমে দুই কলাম, তারপর এক কলাম হবে। চেষ্টা করুন: (১) auto-fit-কে auto-fill করুন, বাক্স পুরো চওড়া রাখুন — কার্ডগুলো সরু হয়ে বাঁয়ে সরে গেল, ডানে দু-একটি অদৃশ্য খালি কলাম রয়ে গেছে। (২) আবার auto-fit করে min(150px, 100%)-কে শুধু 150px করুন, তারপর বাক্সটি 150px-এর চেয়ে সরু করুন — কার্ড বাক্সের বাইরে বেরিয়ে যাবে। min() রাখলে সেটা হয় না।

শেষ পরীক্ষাটিই এই কোডের সবচেয়ে কম বোঝা অংশ। পুরোনো অনেক টিউটোরিয়ালে (আর AI-এর উত্তরে) শুধু minmax(250px, 1fr) লেখা থাকে। সাধারণ ফোনে ঝামেলা হয় না, কিন্তু 250px-এর চেয়ে সরু জায়গায় — পুরোনো ছোট ফোন, পাশে padding থাকা container, বা একটি সরু sidebar-এর ভেতরে — কার্ড বাইরে উপচে পড়ে। নিচের ডায়াগ্রামে container-এর চওড়া বদলে ব্রাউজারের হিসাব দেখুন (gap 16px ধরে):

≈289
≈289
≈289
container 900px
272
272
container 560px
400
container 400px
250 ✗
container 200px — 50px বাইরে
200 ✓
container 200px — min() রক্ষা করল
auto-fill (২টি item):
≈289
≈289
খালি
auto-fit (২টি item):
442
442
  1. 900px — তিন কলাম। কতগুলো কলাম আঁটবে, তা হিসাবের সময় ব্রাউজার 1fr-কে উপেক্ষা করে নিচের সীমা 250px ধরে, আর gap-ও গোনে। ৩টি কলাম আর ২টি gap: 3×250 + 2×16 = 782 ≤ 900, কিন্তু ৪টিতে 1048 লাগত। তাই ৩টি কলাম; তারপর 1fr বাকি জায়গা সমান ভাগ করে প্রত্যেককে ≈289px দেয়।
  2. 560px — দুই কলাম। ৩টি কলামে 782px লাগত, আঁটে না। ২টি আঁটে, লাগে 516px. ব্রাউজার নিজেই কলাম কমিয়ে দিল — কোনো media query নেই, ব্রেকপয়েন্টের সংখ্যাও কেউ লেখেনি। লেআউট ঠিক হচ্ছে container-এর জায়গা দেখে।
  3. 400px — এক কলাম। এখন শুধু একটি কলাম আঁটে, আর 1fr তাকে পুরো 400px দিল। কার্ডগুলো ওপর-নিচে সাজল — ফোনের জন্য ঠিক যা চাই।
  4. 200px, min() ছাড়া — বিপদ। শুধু minmax(250px, 1fr) হলে একটি কলামও আঁটে না, কিন্তু নিয়ম অনুযায়ী অন্তত একটি কলাম বানাতেই হয়। সেই কলাম 250px-এর নিচে নামবে না, তাই 50px বাইরে বেরিয়ে যায় — পুরো পাতায় পাশে স্ক্রল, যা অ্যাক্সেসিবিলিটিরও লঙ্ঘন (WCAG-এর reflow শর্ত)।
  5. 200px, min(250px, 100%) সহ — নিরাপদ। min() দুটি মানের মধ্যে ছোটটি নেয়। এখানে 100% মানে container-এর চওড়া 200px, তাই নিচের সীমা হলো 200px. কলাম container-এর ঠিক সমান — কিছুই উপচে পড়ল না। বড় পর্দায় 100% অনেক বড়, তখন min() আগের মতোই 250px দেয়; অর্থাৎ এই অংশটি শুধু বিপদের সময় কাজ করে।
  6. auto-fill বনাম auto-fit — item মাত্র দুটি। 900px-এ ৩টি কলাম আঁটে, কিন্তু item ২টি। auto-fill তৃতীয় কলামটি খালি রেখে দেয়, তাই কার্ড ≈289px-এই থাকে। auto-fit খালি কলামকে শূন্যে গুটিয়ে ফেলে (তার পাশের gap-ও), আর 1fr দুই কার্ডকে 442px করে দেয়। কোনটি ভালো? পণ্যের কার্ড এক-দুটি হলে বিশাল হয়ে যাওয়া খারাপ দেখায় — সেখানে auto-fill; সারি সবসময় ভরা দেখাতে চাইলে auto-fit.

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

  • auto-fill আর auto-fit-এর পার্থক্য কী? — দুটিই যত কলাম আঁটে তত বানায়; auto-fill খালি কলাম রেখে দেয়, auto-fit খালি কলাম শূন্যে গুটিয়ে item-গুলোকে ছড়িয়ে দেয়।
  • media query ছাড়া রেসপন্সিভ কার্ড grid কীভাবে? — repeat(auto-fit, minmax(min(250px, 100%), 1fr)).
  • 1fr কলাম লম্বা লেখায় উপচে পড়ে কেন, সমাধান কী? — 1fr = minmax(auto, 1fr), নিচের সীমা min-content; minmax(0, 1fr) বা item-এ min-width: 0.
  • grid-template-areas-এর নিয়ম কী? — প্রতিটি সারিতে ঘর সমান, প্রতিটি এলাকা আয়তাকার, . খালি ঘর; নিয়ম ভাঙলে পুরো ঘোষণা বাতিল।
  • Grid দিয়ে ক্রম বদলালে সমস্যা কী? — Tab আর screen reader DOM order মানে, তাই চোখের ক্রম আর পড়ার ক্রম আলাদা হয়ে যায়।

৬ · কনটেন্ট দেখে মাপ — min-content, max-content আর fit-content()

এতক্ষণ কলামের মাপ দিয়েছি নির্দিষ্ট সংখ্যায় (150px) বা ভাগে (1fr) লিখে। তৃতীয় একটি পথ আছে: ভেতরের লেখা দেখে মাপ ঠিক করা। একে বলে intrinsic sizing — “নিজের ভেতরের জিনিস অনুযায়ী মাপ”। তিনটি শব্দ মনে রাখুন:

মানমানেদোকানের উদাহরণ
min-contentলেখা যতটা সম্ভব ভেঙে সবচেয়ে সরু মাপ — সবচেয়ে লম্বা শব্দের চওড়াতাকে বই খাড়া করে ঠেসে রাখা
max-contentএকটুও না ভেঙে এক লাইনে পুরো লেখার চওড়াসব বই পাশাপাশি শুইয়ে রাখা
fit-content(সীমা)max-content পর্যন্ত বাড়ে, কিন্তু সীমার বেশি নয়; তারপর লেখা ভাঙেদরকার মতো জায়গা, কিন্তু তাকের নির্দিষ্ট অংশের বেশি নয়
<style>
  body { font-family: sans-serif; }
  .g { display: grid; gap: 6px; margin-bottom: 16px;
       grid-template-columns: min-content max-content fit-content(170px) 1fr; }
  .g > div { background: #ede9fe; padding: 6px; }
  /* বাস্তব কাজ: লেবেল যত চওড়া দরকার, বাকিটা ইনপুটের */
  .form { display: grid; grid-template-columns: max-content 1fr;
          gap: 8px 12px; align-items: center; max-width: 420px; }
</style>
<div class="g">
  <div>min-content: যত সরু সম্ভব</div>
  <div>max-content: এক লাইনে পুরোটা</div>
  <div>fit-content(170px): দরকার মতো চওড়া, তবে 170px-এর বেশি কখনো নয়</div>
  <div>1fr: বাকি জায়গা</div>
</div>
<form class="form">
  <label for="n">নাম</label> <input id="n">
  <label for="m">মোবাইল নম্বর</label> <input id="m" inputmode="tel">
</form>

প্রথম সারিতে বাঁয়ের ঘর শব্দে শব্দে ভেঙে খুব সরু, দ্বিতীয়টি এক লাইনে পুরোটা, তৃতীয়টি 170px-এ থেমে লেখা ভেঙেছে, আর চতুর্থটি বাকি জায়গা নিয়েছে। নিচের ফর্মে লেবেলের কলাম ঠিক সবচেয়ে লম্বা লেবেলটির (“মোবাইল নম্বর”) সমান — কোনো সংখ্যা আন্দাজ করে লিখতে হয়নি। চেষ্টা করুন: (১) fit-content(170px)-কে fit-content(600px) করুন — চওড়া প্রিভিউতে লেখা এক লাইনে চলে এলো, কারণ তার max-content (প্রায় 470px) 600px-এর চেয়ে ছোট। (২) “মোবাইল নম্বর” লেবেলটিকে “আপনার মোবাইল নম্বর (বিকাশ)” করুন — লেবেলের কলাম নিজেই চওড়া হলো।

এই শব্দগুলো শুধু grid-এ নয়, width-এও কাজ করে: width: fit-content দিলে একটি block element (যেমন একটি বোতামের মতো লিংক) তার লেখার সমান চওড়া হয়, পুরো লাইন জোড়ে না। এগুলো Baseline Widely available — নিশ্চিন্তে ব্যবহার করতে পারেন। একটি সতর্কতা: max-content কখনো লেখা ভাঙে না, তাই লম্বা লেখায় দিলে অংশ ৪-এর মতোই উপচে পড়বে।

৭ · subgrid — পাশের কার্ডের সাথে সারি মেলানো, আর masonry-র খবর

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

subgrid এই দেয়াল ভেঙে দেয়। কার্ডকে বলা হয়: “নিজের সারি বানিয়ো না, বাইরের grid-এর সারিগুলোই ধার নাও।” তখন সব কার্ডের শিরোনাম একই সারিতে, আর সেই সারির উচ্চতা ঠিক হয় সবচেয়ে লম্বা শিরোনাম দেখে। অনেকটা পরীক্ষার খাতার ছাপানো লাইনের মতো — সবার খাতায় একই লাইন, তাই প্রশ্ন নম্বর ১-এর উত্তর সবার খাতায় একই উচ্চতায় শুরু হয়।

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
  gap: 10px;
  font-family: sans-serif;
}
.card {
  display: grid;
  grid-row: span 3;              /* বাইরের grid-এর ৩টি সারি জুড়ে */
  grid-template-rows: subgrid;   /* সেই ৩টি সারিই ভেতরে ব্যবহার */
  gap: 6px;
  border: 1px solid #94a3b8; border-radius: 8px; padding: 10px;
}
.card h3, .card p { margin: 0; }
.card h3 { font-size: 1rem; background: #e0f2fe; }
.card a { align-self: end; background: #16a34a; color: #fff;
          padding: 6px; text-align: center; border-radius: 6px; }

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

কয়েকটি বিষয় খেয়াল করুন। প্রথমত, grid-row: span 3 দিয়ে কার্ডকে বাইরের grid-এর তিনটি সারি জুড়তে হয় — subgrid শুধু সেই সারিগুলোই ধার নিতে পারে, যেগুলো সে জুড়ে আছে। দ্বিতীয়ত, subgrid এক দিকেও হয় (শুধু সারি বা শুধু কলাম), দুই দিকেও।

ব্রাউজার সাপোর্ট

Subgrid প্রথম আসে Firefox-এ (২০১৯), তারপর Safari-তে (২০২২), আর Chrome ও Edge-এ ২০২৩ সালে — তখন থেকে এটি Baseline. এখন প্রধান সব ব্রাউজারে বেশ কিছুদিন ধরে আছে — অর্থাৎ Baseline Widely available. খুব পুরোনো ব্রাউজারে subgrid না বুঝলে ঘোষণাটি বাদ পড়ে আর কার্ড সাধারণ grid হিসেবে দেখায় — শুধু সারিগুলো মেলে না, কিছু ভাঙে না। এটাই progressive enhancement.

যা এখনো আসেনি — masonry, ওরফে grid-lanes

Pinterest-এর মতো ছবির দেয়াল দেখেছেন? কলাম সমান, কিন্তু প্রতিটি ছবি নিজের উচ্চতায়, আর ছোট ছবির নিচে ফাঁক না রেখে পরের ছবি উঠে আসে — ইটের গাঁথুনির মতো। একে বলে masonry layout. সাধারণ grid এটা পারে না, কারণ grid-এ সারি থাকে, আর একটি সারির উচ্চতা ঠিক হয় তার সবচেয়ে লম্বা item দেখে — ফলে ছোট ছবির নিচে ফাঁক থেকে যায়। এতদিন এর জন্য JavaScript library বা CSS multi-column-এর মতো আধা-সমাধান ব্যবহার হয়েছে, যেখানে ক্রম ওপর-নিচে চলে, পাশাপাশি নয়।

বর্তমান অবস্থা (সতর্কভাবে পড়ুন): CSS Working Group বেশ কয়েক বছর ধরে CSS-এ masonry আনার উপায় নিয়ে আলোচনা করছে। শুরুতে প্রস্তাব ছিল grid-এর ভেতরে (grid-template-rows: masonry), যা Firefox অনেক দিন ধরে একটি পরীক্ষামূলক flag-এর পেছনে রেখেছে; আরেক প্রস্তাব ছিল আলাদা display: masonry. দীর্ঘ বিতর্কের পর সাম্প্রতিক আলোচনায় display: grid-lanes নামটি সামনে এসেছে, আর কিছু ব্রাউজারের পরীক্ষামূলক বা preview সংস্করণে এর পরীক্ষা চলছে। এটি Baseline নয়, সিনট্যাক্স ও খুঁটিনাটি এখনো বদলাতে পারে। বাস্তব সাইটে এখনই ব্যবহার করবেন না; ব্যবহার করলে @supports দিয়ে যাচাই করে সাধারণ grid-কে fallback রাখুন। কেউ (বা কোনো AI) যদি বলে “masonry এখন সব ব্রাউজারে চলে” — MDN আর Can I use-এ নিজে দেখে নিন।

৮ · সব একসাথে — একটি বাংলা সংবাদ সাইটের ম্যাগাজিন লেআউট

খবরের কাগজের প্রথম পাতা আসলে একটি grid — ওপরে কাগজের নাম, তার নিচে বড় ছবিসহ প্রধান খবর, পাশে “সর্বশেষ” কলাম, নিচে ছোট ছোট খবর, তারপর ছবির সারি। কাগজের সম্পাদক যেমন পাতা “মেকআপ” করেন, আমরাও তেমনি এই পর্বের প্রায় সব কৌশল এক জায়গায় আনব: template areas, minmax(0, 1fr), ছোট পর্দায় এলাকা বদল, auto-fill ছবির টালি আর aspect-ratio.

ছবির গ্যালারিতে aspect-ratio একটি বড় সমস্যা মেটায়: সব টালির অনুপাত এক রাখা (যেমন বর্গাকার 1 বা চওড়া 16 / 9), কলাম যত চওড়াই হোক। ছবি আসার আগেই জায়গা ঠিক থাকে, তাই লোড হওয়ার সময় পাতা লাফায় না (পর্ব ৬-এর layout shift দেখুন)। আসল ছবি দিলে সাথে object-fit: cover দেবেন, যাতে ছবি না চেপে টালি ভরে। এখানে ছবির বদলে রং আর emoji ব্যবহার করা হয়েছে।

.paper {
  display: grid; gap: 10px; font-family: sans-serif;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-areas:
    "mast mast mast mast"
    "lead lead s1   side"
    "lead lead s2   side"
    "gal  gal  gal  gal";
}
@media (max-width: 520px) {
  .paper { grid-template-columns: minmax(0, 1fr);
           grid-template-areas: "mast" "lead" "s1" "s2" "side" "gal"; }
}
.mast { grid-area: mast; text-align: center; border-bottom: 3px double #111; }
.lead { grid-area: lead; } .s1 { grid-area: s1; } .s2 { grid-area: s2; }
.side { grid-area: side; background: #f1f5f9; padding: 8px; }
.gal  { grid-area: gal; }
.paper h2, .paper h3, .paper p { margin: 4px 0; }
.pic { aspect-ratio: 16 / 9; border-radius: 6px;
       background: linear-gradient(135deg, #0ea5e9, #22c55e); }
.tiles { display: grid; gap: 6px;
  grid-template-columns: repeat(auto-fill, minmax(min(110px, 100%), 1fr)); }
.tiles div { aspect-ratio: 1; border-radius: 6px; display: grid;
  place-items: center; font-size: 28px; background: #fde68a; }

ওপরে কাগজের নাম, তার নিচে বাঁয়ে দুই কলাম আর দুই সারি জুড়ে প্রধান খবর (১৬:৯ ছবির জায়গাসহ), তার পাশে ওপর-নিচে দুটি ছোট খবর, একেবারে ডানে ধূসর “সর্বশেষ” কলাম, আর সবশেষে বর্গাকার ছবির টালি — কলাম যত চওড়াই হোক, প্রতিটি টালি বর্গই থাকে। চেষ্টা করুন: (১) দুই সারিতেই s1/s2 আর side-এর জায়গা অদলবদল করুন — “সর্বশেষ” মাঝে চলে এলো। তারপর শুধু একটি সারির s1-কে lead করুন — lead আর আয়তাকার নেই, পুরো ঘোষণা বাতিল হয়ে সব এলোমেলো হলো। (২) .tiles div-এর aspect-ratio: 1-কে 3 / 4 করুন — টালিগুলো খাড়া হলো। এই পাতা ফোনে খুললে (৫২০px-এর কম চওড়ায়) সব এলাকা এক কলামে নেমে আসে।

নকশাটির কয়েকটি সূক্ষ্ম সিদ্ধান্ত খেয়াল করুন। কলাম repeat(4, 1fr) নয়, repeat(4, minmax(0, 1fr)) — খবরে লম্বা লিংক বা ইংরেজি শব্দ এলেও পাতা ভাঙবে না। HTML-এ খবরগুলো গুরুত্বের ক্রমে লেখা: নাম, প্রধান খবর, ছোট খবর, তারপর “সর্বশেষ” — তাই চওড়া পর্দায় “সর্বশেষ” ডানে সরলেও screen reader-এ পড়ার ক্রম অর্থবহই থাকে। আর প্রতিটি খবর <article>, পাশের কলাম <aside> — grid-এর নকশা যা-ই হোক, semantic HTML (পর্ব ১০) একই থাকে।

“Grid না Flexbox?” — বাজারের তাকের মতো এক দিকে সাজানো হলে Flexbox, খাতার ছকের মতো সারি আর কলাম দুই দিকেই মেলাতে হলে Grid. বাস্তব পাতায় দুটো একসাথে থাকে: পাতার কাঠামো grid-এ, কার্ডের ভেতরের বোতামের সারি flex-এ। প্রচলিত পুরো লেআউটগুলো আসবে পর্ব ২৯-এ।

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

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

AI সাধারণত যেসব ভুল করে

AI-এর ভুলআপনি যা দেখবেনঠিক উপায়
minmax(250px, 1fr), min() ছাড়াসরু container-এ কার্ড বাইরে, পাশে স্ক্রলminmax(min(250px, 100%), 1fr)
লেখা বা কোডের কলামে 1frলম্বা লিংক এলেই লেআউট উপচে পড়েminmax(0, 1fr) + overflow-wrap / overflow-x: auto
template areas দিয়ে মূল লেখা আর ফর্মের ক্রম উল্টানোTab আর screen reader এলোমেলো লাফায়HTML-এ অর্থের ক্রম, grid শুধু চেহারায়
প্রতি ব্রেকপয়েন্টে repeat(4, 1fr), repeat(2, 1fr)…অনেক লাইন, মাঝের মাপে অদ্ভুত চেহারাauto-fit/auto-fill দিয়ে কনটেন্ট-নির্ভর লেআউট
কার্ড মেলাতে নির্দিষ্ট height বা JavaScriptলম্বা বাংলা শিরোনামে লেখা কাটা পড়েgrid-template-rows: subgrid
grid-template-rows: masonry বা display: masonry “চলে” বলাবেশিরভাগ ব্রাউজারে কিছুই হয় নাএখনো Baseline নয় — @supports + সাধারণ grid
display: -ms-grid, float বা টেবিল দিয়ে পাতার কাঠামোঅপ্রয়োজনীয়, পুরোনো কোডprefix ছাড়া display: grid

AI-কে কাজ দেওয়ার সময় শর্তগুলো আগেই বলে দিন, আর শেষে তাকে নিজের সিদ্ধান্তের কারণ লিখতে বলুন — তাতে ভুল প্রায়ই নিজেই বেরিয়ে আসে।

তুমি একজন অভিজ্ঞ ফ্রন্ট-এন্ড ডেভেলপার। আমার বাংলা অনলাইন দোকানের পণ্যের তালিকার জন্য semantic HTML আর আধুনিক CSS Grid দাও। শর্ত: ১. কার্ডের grid হবে repeat(auto-fit বা auto-fill, minmax(min(240px, 100%), 1fr)) — কোনটা বেছে নিলে আর কেন, এক লাইনে বলবে। ২. প্রতিটি কার্ডে ছবি, নাম, দাম, বোতাম; subgrid দিয়ে সব কার্ডের নাম আর বোতাম এক সারিতে রাখবে। ৩. ছবিতে aspect-ratio আর object-fit; কোনো নির্দিষ্ট height নয়। ৪. লেখার কলামে 1fr-এর বদলে minmax(0, 1fr) কোথায় দরকার, দেখাবে। ৫. কোনো float, Bootstrap, -ms- prefix বা masonry ব্যবহার করবে না; ক্রম বদলাতে order বা grid-area ব্যবহার করবে না। শেষে ৩২০px আর ১২০০px চওড়ায় কয়টি কলাম হবে, হিসাব করে দেখাও।

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

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

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

প্র ০১ওয়েবের প্রায় ২৫ বছর পর্যন্ত CSS-এ সত্যিকারের লেআউট-ব্যবস্থা ছিল না কেন? Grid আসতে এত দেরি হলো কেন?

শুরুর দিকের CSS তৈরি হয়েছিল লেখার পাতার জন্য — শিরোনাম, প্যারাগ্রাফ, রং, ফন্ট। ১৯৯০-এর দশকে কেউ ভাবেনি যে ওয়েবে একদিন ব্যাংকের অ্যাপ, খবরের কাগজ আর অনলাইন দোকান চলবে। তাই ডেভেলপাররা যা হাতের কাছে পেয়েছেন, তা দিয়েই লেআউট বানিয়েছেন:

  • টেবিল লেআউট (১৯৯০-এর দশক): পুরো পাতা একটি বিশাল <table>. দেখতে কাজ করত, কিন্তু screen reader পুরো পাতাকে তথ্যের টেবিল ভেবে পড়ত, আর নকশা বদলাতে HTML ভাঙতে হতো (পর্ব ৭)।
  • Float লেআউট (২০০০-এর দশক): float আসলে বানানো হয়েছিল লেখার পাশে ছবি ভাসানোর জন্য। সেটি দিয়ে কলাম বানাতে লাগত “clearfix” নামের কৌশল, সমান উচ্চতার কলাম প্রায় অসম্ভব ছিল। পুরোনো Bootstrap-এর সারি-কলামও এর ওপর দাঁড়িয়ে ছিল।
  • Flexbox (২০১০-এর দশক): প্রথম সত্যিকারের লেআউট-টুল, কিন্তু এক দিকের — সারি অথবা কলাম।

দুই দিকের grid-এর ধারণা অনেক পুরোনো, কিন্তু সমস্যা ছিল কঠিন: কনটেন্টের মাপ, ফাঁকা জায়গা আর ভাগ — সব একসাথে হিসাব করার একটি নির্ভরযোগ্য অ্যালগরিদম, যা প্রতিটি ব্রাউজার একইভাবে বাস্তবায়ন করবে। বর্তমান CSS Grid-এর মূল প্রস্তাব এসেছিল Microsoft-এর কাছ থেকে, আর Internet Explorer 10 ২০১২ সালে -ms-grid নামে একটি পুরোনো রূপ চালু করে। তারপর কয়েক বছর ধরে স্পেসিফিকেশন নতুন করে লেখা হয়। ২০১৭ সালের মার্চে Chrome, Firefox আর Safari প্রায় একই সাথে নতুন grid চালু করে, আর সেই বছরের শেষে Edge-ও — ব্রাউজারগুলোর মধ্যে এমন সমন্বয় তখন বিরল ছিল।

মূল উপলব্ধি: একটি টুল যে কাজের জন্য বানানো, তার বাইরে ব্যবহার করলে “hack” জন্মায় — টেবিল আর float তার উদাহরণ। আজ AI যখন পুরোনো float-কোড দেয়, তখন সে আসলে ওয়েবের ইতিহাসের একটি পর্যায় নকল করছে; ইতিহাস জানলেই সেটি চেনা যায়।

প্র ০২Grid বলে “HTML-এর ক্রম থেকে নকশা মুক্ত”। তাহলে চোখের ক্রম বদলানো খারাপ কেন — আর কখন ঠিক আছে?

Grid-এর একটি বড় প্রতিশ্রুতি ছিল source-order independence — HTML যে ক্রমেই লিখুন, CSS দিয়ে যেকোনো জায়গায় বসানো যাবে। এটি সত্যিই শক্তিশালী: একই HTML থেকে ফোনে এক রকম, ডেস্কটপে আরেক রকম নকশা। কিন্তু এর একটি দাম আছে, যা প্রথম দিকে অনেকে খেয়াল করেননি।

পাতা শুধু চোখ দিয়ে ব্যবহার হয় না। যাঁরা চোখে দেখেন না, তাঁরা screen reader-এ পাতা শোনেন — এবং সেটি চলে DOM order-এ (পর্ব ১২-এর accessibility tree দেখুন)। যাঁরা মাউস চালাতে পারেন না, বা কীবোর্ডেই দ্রুত কাজ করেন, তাঁরা Tab চাপেন — সেটিও DOM order-এ। একজন দৃষ্টিসম্পন্ন কীবোর্ড-ব্যবহারকারীর অবস্থা সবচেয়ে কঠিন: তিনি চোখে দেখছেন একটা ক্রম, আর focus লাফাচ্ছে অন্য ক্রমে।

এজন্য WCAG-এর দুটি শর্ত সরাসরি প্রাসঙ্গিক: 1.3.2 অর্থবহ ক্রম — যেখানে ক্রমের ওপর অর্থ নির্ভর করে, সেখানে প্রোগ্রাম যে ক্রম পড়ে তাও সঠিক হতে হবে; আর 2.4.3 focus-এর ক্রম — focus এমন ক্রমে ঘুরবে যাতে অর্থ আর ব্যবহার ঠিক থাকে। CSS Grid আর Flexbox-এর স্পেসিফিকেশনও লেখকদের সতর্ক করে যে জায়গা বদল কেবল দৃশ্যমান সাজের জন্য।

কখন ঠিক আছে? যখন ক্রম বদলালে অর্থ বদলায় না: একটি ছবিকে লেখার ডান থেকে বাঁয়ে আনা, সাজসজ্জার ব্যানার সরানো, পাশের তথ্যের কলাম ডান থেকে বাঁয়ে নেওয়া (যেখানে কোনো ইন্টারঅ্যাকটিভ জিনিস নেই)। কখন বিপজ্জনক? ফর্মের ঘর, ধাপে ধাপে নির্দেশনা, মূল লেখার অনুচ্ছেদ, বা লিংক-বোতামের ক্রম উল্টালে। বাস্তব পরামর্শ: আগে HTML-কে এমন ক্রমে লিখুন যেটা CSS ছাড়াও অর্থবহ — তারপর grid দিয়ে সাজান, এবং প্রতিটি ব্রেকপয়েন্টে একবার Tab চেপে দেখুন।

মূল উপলব্ধি: “দেখতে ঠিক আছে” আর “ব্যবহারে ঠিক আছে” এক জিনিস নয়। CSS-এর ক্ষমতা যত বাড়ে, HTML-এর ক্রমের দায়িত্বও তত বাড়ে।

প্র ০৩Media query থাকতে auto-fit-এর মতো “নিজে নিজে মানিয়ে নেওয়া” লেআউটের দরকার পড়ল কেন? কখন কোনটি বেছে নেবেন?

২০১০ সালে Ethan Marcotte “Responsive Web Design” নামের প্রবন্ধে media query, নমনীয় grid আর নমনীয় ছবির সমন্বয়ের কথা বলেন। ধারণাটা ছিল বিপ্লবী — এক সাইট, সব পর্দা। কিন্তু media query-র একটি মৌলিক সীমা আছে: সে প্রশ্ন করে পুরো পর্দা (viewport) কত চওড়া, অথচ একটি কার্ডের আসল প্রশ্ন হলো আমি যে জায়গায় বসেছি সেটা কত চওড়া।

একটি উদাহরণ: পণ্যের কার্ডের একই component পাতার মাঝখানের চওড়া কলামেও আছে, আবার পাশের সরু sidebar-এও। ১২০০px পর্দায় media query দুই জায়গাতেই বলবে “চার কলাম” — sidebar-এ তা ঠেসে যাবে।

এই সীমা থেকেই আসে intrinsic layout-এর চিন্তা — লেআউট ঠিক হবে কনটেন্ট আর তার জায়গা দেখে। Jen Simmons ২০১৮ সালে এর নাম দেন “Intrinsic Web Design”. repeat(auto-fit, minmax(…)), flex-wrap, clamp(), min() — এগুলো সব একই দর্শনের অংশ: আপনি সীমা বলে দিন (“কার্ড অন্তত 250px”), ব্রাউজার বাকি সিদ্ধান্ত নিক। পরবর্তী ধাপ হলো container query (পর্ব ৩১), যেখানে component নিজের container-এর মাপ দেখে চেহারা বদলাতে পারে।

কখন কোনটি?

  • auto-fit/auto-fill: একই রকম অনেক item — পণ্য, ছবি, খবরের কার্ড। কলামের সংখ্যা নিয়ে আপনার নির্দিষ্ট চাওয়া নেই।
  • Media query: পুরো পাতার কাঠামো বদল (sidebar ওপরে না পাশে), navigation-এর ধরন বদল, বা যখন নকশাকার নির্দিষ্ট মাপে নির্দিষ্ট চেহারা চান।
  • Container query: একটি component যখন নানা মাপের জায়গায় বসে।

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

প্র ০৪Masonry লেআউট CSS-এ আনা নিয়ে এত বছর বিতর্ক কেন — এটা কি grid-এর অংশ হওয়া উচিত, না আলাদা কিছু?

২০১০-এর দশকের শুরুতে Pinterest-এর মতো সাইট ছবির “ইটের দেয়াল” নকশা জনপ্রিয় করে। তখন থেকে এর জন্য JavaScript library ব্যবহার হয়েছে, যারা প্রতিটি ছবির উচ্চতা মেপে position: absolute দিয়ে বসায়। এতে পাতা লোডের সময় ঝাঁকুনি হয়, আর JavaScript না চললে লেআউট ভাঙে। তাই CSS-এ সরাসরি masonry আনার চাহিদা পুরোনো।

কঠিন প্রশ্নটি দর্শনের: masonry কি grid? এক দিকে (একটি পক্ষ, যার পক্ষে WebKit দল জোরালো যুক্তি দিয়েছে) — কলামের মাপ, gap, repeat(), minmax(), নামের লাইন সবই grid-এর মতো, তাই grid-এর ভেতরে রাখলে ডেভেলপারদের নতুন কিছু শিখতে হয় না। অন্য দিকে (Chrome দলের প্রস্তাব) — masonry-তে আসলে সারি নেই, item-এর জায়গা ঠিক হয় আগের item-গুলোর উচ্চতা দেখে; grid-এর মাপের অ্যালগরিদমের সাথে এর মৌলিক পার্থক্য আছে। grid-এর ভেতরে রাখলে কিছু grid property masonry-তে অর্থহীন হয়ে যায়, আর পারফরম্যান্সের ঝুঁকিও আছে — হাজার item-এর সব মাপ জানতে হলে ব্রাউজারকে অনেক কাজ করতে হয়।

অ্যাক্সেসিবিলিটির প্রশ্নও আছে: item যায় “সবচেয়ে খাটো কলামে”, ফলে চোখের ক্রম আর DOM order সহজেই আলাদা হয়ে যায় — প্র ০২-এর সমস্যাই আরও বড় আকারে। Firefox বহু বছর ধরে grid-ভিত্তিক সংস্করণ পরীক্ষামূলক flag-এর পেছনে রেখেছে। দীর্ঘ আলোচনার পর সাম্প্রতিক সময়ে CSS Working Group display: grid-lanes ধরনের একটি নামের দিকে এগিয়েছে — নামেই বোঝা যায়, এটি grid-পরিবারের, কিন্তু সারির বদলে “লেন” (রাস্তার লেনের মতো কলাম)। কিছু ব্রাউজারের preview সংস্করণে পরীক্ষা চলছে; স্থিরভাবে সবার জন্য চালু হয়নি, Baseline-ও নয়।

ব্রাউজার নির্মাতারা প্রকাশ্যে ডেভেলপারদের মতামতও চেয়েছেন। কারণ একবার চালু হলে কোনো CSS আর ফেরত নেওয়া যায় না — কোটি কোটি সাইট ভেঙে যাবে — তাই তাড়াহুড়োর চেয়ে সঠিক নকশা বেশি জরুরি।

মূল উপলব্ধি: “এই ফিচারটা কেন এখনো নেই?” প্রশ্নের উত্তর প্রায়ই প্রযুক্তিগত নয়, নকশাগত। আর নতুন ফিচার নিয়ে কোনো দাবি (মানুষের বা AI-এর) শুনলে উৎস দেখুন — স্পেসিফিকেশন, MDN, Can I use.

প্র ০৫Subgrid না থাকার সময় কার্ড মেলাতে মানুষ কী করত? subgrid বাস্তবায়ন এত কঠিন ছিল কেন?

Subgrid-এর আগে কার্ডের শিরোনাম আর বোতাম মেলানোর কয়েকটি প্রচলিত উপায় ছিল, প্রতিটির নিজস্ব দাম:

  • নির্দিষ্ট উচ্চতা: শিরোনামে height: 3em. লেখা ছোট হলে ফাঁকা, বড় হলে কাটা — আর বাংলা অনুবাদে লেখা প্রায়ই ইংরেজির চেয়ে লম্বা হয়।
  • JavaScript দিয়ে সমান করা: পাতা লোড হলে সব শিরোনামের উচ্চতা মেপে সবচেয়ে বড়টা সবাইকে দেওয়া। jQuery-র যুগে এর অনেক plugin ছিল। জানালার মাপ বদলালে আবার মাপতে হতো, ফন্ট দেরিতে এলে হিসাব ভুল হতো, আর পাতা লাফাত।
  • display: contents: কার্ডের বাক্সটাকেই “অদৃশ্য” করে ভেতরের element-গুলোকে বাইরের grid-এর সরাসরি সন্তান বানানো। কিন্তু তখন কার্ডের border বা ব্যাকগ্রাউন্ড দেওয়া যায় না, আর বেশ কয়েক বছর কিছু ব্রাউজারে এতে element-এর অ্যাক্সেসিবিলিটি-তথ্য (যেমন list বা button-এর ভূমিকা) হারিয়ে যাওয়ার বাগ ছিল।

কেন কঠিন? সাধারণ grid-এ মাপের হিসাব চলে ওপর থেকে নিচে: বাইরের grid নিজের track ঠিক করে, তারপর ভেতরের item নিজের কাজ করে। Subgrid এই শৃঙ্খল ভেঙে দেয় — বাইরের সারির উচ্চতা এখন নির্ভর করে ভেতরের নাতি-element-দের (কার্ডের শিরোনামের) মাপের ওপর, আর ভেতরের element-দের জায়গা নির্ভর করে বাইরের সারির ওপর। দুই স্তরের হিসাব একসাথে মেলাতে হয়, subgrid-এর নিজের padding আর gap-ও হিসাবে ঢোকে। এজন্য grid-এর প্রথম স্পেসিফিকেশনে subgrid রাখা হয়নি, এসেছে Grid Level 2-তে। Firefox ২০১৯ সালে প্রথম চালু করে; Chromium-এ আসে grid-এর ভেতরের ইঞ্জিন নতুন করে লেখার পর, ২০২৩ সালে।

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

অনুশীলন

বেশিরভাগ কাজে কোড লিখতে হবে না — হাতে-কলমে দেখা আর হিসাব করাই আসল কাজ।

  1. বৈধ না অবৈধ? নিচের তিনটি grid-template-areas-এর কোনটি কাজ করবে, কোনটি বাতিল হবে, আর কেন? (ক) "a a b" "a a b" "c c c" (খ) "a b" "b a" (গ) "a a" "b"

    (ক) বৈধ। a ২×২-এর একটি আয়তক্ষেত্র, b ডানে দুই সারি জুড়ে, c নিচে পুরো সারি জুড়ে। এটি প্রধান খবর + পাশের কলাম + নিচের ফিতার মতো ক্লাসিক নকশা।

    (খ) অবৈধ। a আর b দুজনেই দুই আলাদা কোণে — কোনোটিই আয়তাকার একটি এলাকা নয়। পুরো ঘোষণা বাতিল হবে।

    (গ) অবৈধ। প্রথম সারিতে ২টি ঘর, দ্বিতীয়টিতে ১টি — প্রতিটি সারিতে ঘর সমান হতে হয়। ঠিক করতে লিখুন "a a" "b ." অথবা "a a" "b b".

  2. হিসাব করুন: grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; — container 700px চওড়া হলে কয়টি কলাম হবে, প্রতিটি কত চওড়া? আর item মাত্র ২টি থাকলে auto-fill-এর বদলে auto-fit দিলে কী বদলাবে?

    কলাম গোনার সূত্র: n×200 + (n−1)×20 ≤ 700. n = 3 হলে 600 + 40 = 640 ≤ 700 ✓; n = 4 হলে 800 + 60 = 860 ✗. তাই ৩টি কলাম। বাকি জায়গা: 700 − 40 (দুটি gap) = 660, ভাগে প্রতিটি 220px.

    item ২টি হলে auto-fill-এ তৃতীয় কলাম খালি থাকে, কার্ড 220px-ই থাকে। auto-fit-এ খালি কলাম আর তার gap গুটিয়ে যায়: 700 − 20 = 680, ভাগে প্রতিটি কার্ড 340px.

  3. DevTools-এ grid দেখুন: যেকোনো বড় সংবাদ বা ই-কমার্স সাইট খুলে F12 চাপুন। Elements প্যানেলে যে element-এর পাশে ছোট grid ব্যাজ আছে, সেটিতে ক্লিক করুন। Layout ট্যাবে “Show line names” আর “Show area names” চালু করুন। কী দেখলেন? সাইটটি কি auto-fit/auto-fill ব্যবহার করেছে?

    ব্যাজে ক্লিক করলে পাতার ওপর রঙিন grid overlay আসে — লাইনের নম্বর, ফাঁকা gap-এর জায়গা দাগ দিয়ে দেখায়। লেখক এলাকা বা লাইনের নাম দিলে সেগুলোও লেখা থাকে (যেমন header, main, content-start ইত্যাদি)। Styles প্যানেলে grid-template-columns-এ repeat(auto-fill, ...) পেলে জানালার মাপ বদলে দেখুন কলামের সংখ্যা নিজে বদলায় কি না। অনেক বড় সাইট এখনো পুরোনো float বা flex-ভিত্তিক framework ব্যবহার করে — পুরোনো কোড বছরের পর বছর টিকে থাকে।

  4. নিজে লিখুন: একটি রেস্তোরাঁর মেনু-পাতার CSS লিখুন: ওপরে নাম (পুরো চওড়া), নিচে খাবারের কার্ড, যেগুলো media query ছাড়াই বড় পর্দায় কয়েক কলাম আর ফোনে এক কলাম হবে, প্রতিটি কার্ডে ছবির জায়গা ৪:৩ অনুপাতে, আর সব কার্ডের দাম ও “অর্ডার” বোতাম এক সারিতে।

    একটি সম্ভাব্য সমাধান (HTML-এ প্রতিটি কার্ড <article class="dish">, ভেতরে .photo, h3, p.price, button):

    .menu {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
      gap: 1rem;
    }
    .menu > h2 { grid-column: 1 / -1; }   /* নাম পুরো চওড়া */
    .dish {
      display: grid;
      grid-row: span 4;
      grid-template-rows: subgrid;   /* ছবি, নাম, দাম, বোতাম — ৪ সারি */
    }
    .dish .photo { aspect-ratio: 4 / 3; background: #fde68a; }
    .dish button { align-self: end; }

    এখানে auto-fill বেছে নেওয়া হয়েছে, যাতে মেনুতে দুটি খাবার থাকলেও কার্ড বিশাল না হয়। grid-column: 1 / -1 মানে প্রথম থেকে শেষ লাইন পর্যন্ত — কলাম যতই হোক, নাম পুরো চওড়া। subgrid না বুঝলে পুরোনো ব্রাউজারে কার্ডগুলো শুধু অমিল দেখাবে, ভাঙবে না।

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

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

ক্রমrepeat(auto-fit, minmax(min(250px, 100%), 1fr)) হিসাবের সময় ব্রাউজারের কাজগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📏 grid container-এর চওড়া জানা
  2. 🔽 min(250px, 100%) থেকে কলামের নিচের সীমা ঠিক করা
  3. 🔢 gap ধরে কয়টি কলাম আঁটে, তা গোনা
  4. 🗜️ item ছাড়া খালি কলামগুলো শূন্যে গুটিয়ে ফেলা
  5. ➗ বাকি জায়গা 1fr অনুযায়ী সমান ভাগ করা

CSS-এ grid-template-areas: "a b" "b b"; লেখা হলো। ব্রাউজার কী করবে?

  • b-কে নিজে থেকে আয়তাকার করে নেবে
  • Console-এ একটি error দেখিয়ে পাতা লোড বন্ধ করবে
  • এলাকাটি আয়তাকার নয়, তাই পুরো ঘোষণাটি উপেক্ষা করবে
  • দুটি আলাদা b এলাকা বানাবে

দুই কলামের grid grid-template-columns: 1fr 1fr-এ একটি ঘরে ফাঁকা জায়গাহীন লম্বা URL আসায় কলাম অসমান হয়ে লেআউট উপচে পড়ল। এর মূল কারণ কী?

  • fr একক সব ব্রাউজারে ভুল হিসাব করে
  • 1fr-এর নিচের সীমা auto, যা item-এর min-content — URL-এর চেয়ে সরু হতে পারে না
  • gap খুব বেশি দেওয়া হয়েছে
  • box-sizing: border-box দেওয়া হয়নি

একটি সারিতে ৫টি কলাম আঁটে, কিন্তু item আছে মাত্র ২টি। repeat(auto-fit, minmax(150px, 1fr)) দিলে কী হবে?

  • ৫টি কলাম থাকবে, ২টি item 150px-এর কাছাকাছি, বাকি ৩টি খালি
  • প্রথম থেকেই ২টি কলাম গোনা হবে, item-এর সংখ্যা দেখে
  • item-গুলো ঠিক 150px থাকবে, বাঁয়ে সরে যাবে
  • খালি ৩টি কলাম গুটিয়ে যাবে, ২টি item ছড়িয়ে পুরো সারি ভরবে

minmax(250px, 1fr)-এর বদলে minmax(min(250px, 100%), 1fr) লেখার সুবিধা কী?

  • container 250px-এর চেয়ে সরু হলেও কলাম বাইরে উপচে পড়ে না
  • বড় পর্দায় কলামের সংখ্যা দ্বিগুণ হয়
  • কীবোর্ডের Tab-এর ক্রম ঠিক থাকে
  • পুরোনো ব্রাউজারে grid চালু করে

HTML-এ ক্রম: মূল লেখা, তারপর একটি পাশের কলাম (দুটিতেই লিংক আছে)। grid-template-areas দিয়ে পাশের কলামটি চোখে বাঁয়ে আনা হলো। কীবোর্ডে Tab চাপলে কী হবে?

  • Tab বাঁ থেকে ডানে, চোখের ক্রমে চলবে
  • Tab HTML-এর ক্রমে চলবে — আগে ডানের মূল লেখা, পরে বাঁয়ের কলাম
  • সরানো কলামের লিংকে focus যাবেই না
  • ব্রাউজার একটি সতর্কবার্তা দেখাবে

পাশাপাশি কয়েকটি কার্ডের শিরোনাম আর বোতাম এক সারিতে মেলাতে আধুনিক CSS-এর সবচেয়ে উপযুক্ত উপায় কোনটি?

  • প্রতিটি শিরোনামে নির্দিষ্ট height দেওয়া
  • JavaScript দিয়ে সব শিরোনাম মেপে সমান করা
  • কার্ডে grid-row: span দিয়ে grid-template-rows: subgrid
  • display: grid-lanes (masonry)

শব্দকোষ

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

মনে রাখুন

  1. grid-template-areas: প্রতিটি স্ট্রিং একটি সারি, শব্দ একটি ঘর, . খালি; সারিতে ঘর সমান আর প্রতিটি এলাকা আয়তাকার — নইলে পুরো ঘোষণা বাতিল।
  2. Grid শুধু চোখের ক্রম বদলায়; Tab আর screen reader মানে DOM order — অর্থের ক্রম HTML-এ রাখুন।
  3. [content-start]/[content-end] লাইন থেকে content এলাকা আপনা-আপনি তৈরি হয় — full-bleed নকশার মূল কৌশল। grid shorthand বাকি property ডিফল্টে ফেরায়।
  4. 1fr = minmax(auto, 1fr); লম্বা লেখায় উপচে পড়লে minmax(0, 1fr) + লেখার নিজের ব্যবস্থা (overflow-wrap, overflow-x: auto ইত্যাদি)।
  5. repeat(auto-fit, minmax(min(250px, 100%), 1fr)) — media query ছাড়া রেসপন্সিভ; auto-fill খালি কলাম রাখে, auto-fit গুটায়। কার্ড মেলাতে subgrid (Baseline); masonry/grid-lanes এখনো Baseline নয়।

পরের পর্বে: হাতে এখন flexbox আর grid-এর পুরো বাক্স। এবার সেগুলো দিয়ে বাস্তবের প্রচলিত লেআউটগুলো বানাব — sidebar, holy grail, dashboard আর ছবির gallery — আর দেখব কোন নকশায় কোন টুলটি সবচেয়ে মানানসই।

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

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