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

UI বানানোর চ্যালেঞ্জ — ইন্টারভিউর লাইভ কোডিং

HTML & CSS UI Challenges — Live Coding, Machine Coding & Take-home Practice

Taught in Bangla, this page offers sixteen HTML and CSS UI challenges ordered from easy to hard for live coding, machine coding and take-home interviews. It teaches a seven-step method from brief to accessible UI, then challenges such as cards, pricing tables, forms, navbars, holy grail layout, galleries, accordions, modals, tooltips, dark mode and skeletons, each with brief, acceptance criteria, starter code, hints and solution, and closes the course.

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

  • UI ইন্টারভিউর ধরন — live coding, machine coding আর take-home — আর মূল্যায়নে কী কী থাকে
  • যেকোনো UI ব্রিফে খাটে এমন সাত ধাপের পদ্ধতি: প্রশ্ন → HTML → লেআউট → স্টাইল → অবস্থা → রেসপন্সিভ → অ্যাক্সেসিবিলিটি যাচাই
  • কার্ড, গ্রিড, প্রাইসিং, ফর্ম, navbar, sticky header, holy grail আর gallery — লেআউটের ক্লাসিক কাজ
  • accordion, modal, tooltip, dark mode, skeleton — কম্পোনেন্ট আর তার অবস্থা, নিজস্ব HTML দিয়ে
  • বাস্তব কনটেন্টের পাতা আর বিবরণ থেকে নকশা — বাংলা খবর, চ্যাট, পেমেন্টের রসিদ

১ · UI ইন্টারভিউ কেমন হয় — আর কী মাপা হয়

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

ধাপের নাম আর সময় প্রতিষ্ঠানভেদে আলাদা। সাধারণ রূপগুলো এমন:

ধরনসাধারণত কী হয়এই পাতার কোন অংশ
Live coding৩০–৬০ মিনিট, শেয়ার করা editor বা নিজের পর্দা শেয়ার। ছোট একটি UI: কার্ড, ফর্ম, navbar. মাঝে মাঝে প্রশ্ন: “এটা ফোনে কেমন দেখাবে?”চ্যালেঞ্জ ১–৮
Machine coding৬০–৯০ মিনিটে চালানো যায় এমন একটি কম্পোনেন্ট: accordion, modal, tooltip, tabs, থিম বদল। অবস্থা (state) আর কি-বোর্ড চলাচল বিশেষভাবে দেখা হয়।চ্যালেঞ্জ ৯–১৩
Take-homeকয়েক ঘণ্টা থেকে কয়েক দিনের কাজ: একটি স্ক্রিনশট বা বিবরণ দেখে পুরো পাতা। পরে কোড নিয়ে আলোচনা।চ্যালেঞ্জ ১৪–১৬, অংশ ৬-এর শেষ
Code reviewকারও লেখা (প্রায়ই ইচ্ছা করে ভুলে ভরা) HTML-CSS দেখিয়ে বলা হয়, “কী কী ঠিক করবেন?”প্রতিটি starter কোড — সেগুলো ইচ্ছা করেই div-soup

ইন্টারভিউয়ারের মূল্যায়নে কী থাকে

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

কাঠামো ও অ্যাক্সেসিবিলিটি

ঠিক element বেছেছেন কি? বোতাম আসলেই <button>, লিংক <a href>, ঘরের সাথে label? শুধু কি-বোর্ডে পুরোটা চলে কি? এটাই সবচেয়ে বড় পার্থক্য গড়ে।

লেআউট ও রেসপন্সিভ

Flexbox না Grid — কেন? ৩২০ পিক্সেলে ভাঙে কি? স্থির প্রস্থ আর জাদুকরী সংখ্যার বদলে minmax(), max-inline-size, gap ব্যবহার করেছেন কি?

অবস্থা আর খুঁটিনাটি

hover, focus, ভুল, ফাঁকা, লোড হচ্ছে, লম্বা নাম — প্রতিটি অবস্থা ভেবেছেন কি? এগুলো নিজে থেকে তুললে বোঝা যায় আপনি বাস্তব প্রজেক্টে কাজ করেছেন।

যোগাযোগ ও কোডের মান

শুরুতে প্রশ্ন করেছেন কি? সিদ্ধান্ত জোরে বলেছেন কি (think aloud)? class-এর নাম পরিষ্কার, specificity কম, রং টোকেনে?

মূল ধারণা

UI চ্যালেঞ্জ একটি কাজের নমুনা। ইন্টারভিউয়ার মনে মনে জিজ্ঞেস করেন: “এই মানুষটি আমাদের সাইটে একটি নতুন কম্পোনেন্ট বানালে আমাকে কতটা ঠিক করতে হবে?” সুন্দর কিন্তু কি-বোর্ডে অচল একটি modal-এর চেয়ে সাদামাটা কিন্তু সঠিক একটি <dialog> প্রায় সবসময় বেশি নম্বর পায়।

এই পাতার ১৬টি চ্যালেঞ্জ

প্রতিটির পাশে রিভিশনের পর্ব — সমাধান খোলার আগে সেই পর্বের মূল অংশ একবার দেখে নিন।

অংশচ্যালেঞ্জরিভিশনের পর্ব
৩ · সহজ দিয়ে শুরু১. মাঝখানে প্রোফাইল কার্ড · ২. পণ্যের কার্ড-গ্রিড · ৩. প্রাইসিং টেবিল · ৪. ভুল-ঠিক দেখানো ফর্ম১৯, ২৬, ২৮, ০৮, ০৯, ৪৩
৪ · নেভিগেশন ও পাতার লেআউট৫. মোবাইল মেনুসহ navbar · ৬. sticky header আর scroll-padding · ৭. Holy grail পাতা · ৮. ছবির গ্যালারি২৪, ২৭, ২৯, ৩০, ৩১, ৪২
৫ · কম্পোনেন্ট ও অবস্থা৯. Accordion · ১০. Modal · ১১. Tooltip · ১২. Dark mode বোতাম১১, ১২, ৩৩, ৩৪, ৩৬, ৪৫
৬ · পলিশ ও বাস্তব কনটেন্ট১৩. Skeleton loader · ১৪. বাংলা খবরের পাতা · ১৫. চ্যাটের বাবল · ১৬. বিবরণ থেকে নকশা১৪, ২১, ৩৫, ৩৯, ৪৪, ৪৬

২ · ব্রিফ থেকে UI — সাত ধাপের পদ্ধতি

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

প্রশ্ন করে বোঝা → HTML কাঠামো → লেআউট → স্টাইল → অবস্থা → রেসপন্সিভ → অ্যাক্সেসিবিলিটি যাচাই

পদ্ধতিটি চ্যালেঞ্জ ১-এর প্রোফাইল কার্ডে প্রয়োগ করে দেখা যাক। “পরে” চাপুন:

  1. ১ · প্রশ্নশর্ত আর সীমা
  2. ২ · HTMLঅর্থবহ কাঠামো
  3. ৩ · লেআউটflex না grid
  4. ৪ · স্টাইলরং, ফাঁক, অক্ষর
  5. ৫ · অবস্থাhover, focus, ভুল
  6. ৬ · রেসপন্সিভ৩২০px থেকে বড়
  7. ৭ · যাচাইকি-বোর্ড, zoom, contrast
আপনি জিজ্ঞেস করছেনকোড লেখার আগে
- কার্ডটি পাতার কোথায়? পুরো পর্দার মাঝখানে, নাকি একটি অংশের?
- "যোগাযোগ" চাপলে কী হবে — অন্য পাতা (লিংক), নাকি কোনো কাজ (বোতাম)?
- ছবি না থাকলে কী দেখাব? নাম খুব লম্বা হলে?
- কোন ব্রাউজার সমর্থন করতে হবে? framework চলবে?
HTML কাঠামোCSS-এর আগে
<main class="stage">
  <article class="card">
    <div class="avatar" aria-hidden="true">রা</div>
    <h2>রাফিয়া ইসলাম</h2>
    <p>ফ্রন্ট-এন্ড ডেভেলপার, চট্টগ্রাম</p>
    <a href="#contact">যোগাযোগ করুন</a>
  </article>
</main>
লেআউটএক লাইনে মাঝখানে
.stage {
  min-height: 22rem;      /* আসল পাতায়: 100dvh */
  display: grid;
  place-items: center;
}
স্টাইলফাঁকের একটি মাপকাঠি
.card {
  padding: 1.5rem;  border-radius: 1rem;
  background: #fff;
  box-shadow: 0 4px 16px rgb(0 0 0 / .1);
}
.card p { color: #4b5563; }  /* সাদায় 7:1-এর বেশি */
অবস্থাযা চোখে পড়ে না
.card a:hover { background: #1e40af; }
.card a:focus-visible {
  outline: 3px solid #f59e0b; outline-offset: 2px;
}
লম্বা নাম? -> লেখা ভাঙে, কার্ড বাড়ে — ঠিক আছে
রেসপন্সিভস্থির প্রস্থ নয়
.card {
  inline-size: 100%;
  max-inline-size: 20rem;  /* বড় পর্দায় 20rem, ছোটতে পুরো */
}
DevTools-এর ফোন-মোডে 320px পর্যন্ত টেনে দেখুন
শেষ যাচাই"শেষ" বলার আগে
[✓] শুধু Tab দিয়ে লিংকে পৌঁছানো যায়, focus দেখা যায়
[✓] ব্রাউজার zoom 200% — কিছু কাটা পড়ে না
[✓] লেখা আর পটভূমির contrast 4.5:1-এর বেশি
[✓] heading আছে, সাজানোর অক্ষর screen reader-এ লুকানো
[✓] Lighthouse / axe-এ কোনো লাল দাগ নেই
  1. প্রশ্ন করে বোঝা। ব্রিফ প্রায়ই ইচ্ছা করে অস্পষ্ট রাখা হয়। “চাপলে কী হবে” — এই এক প্রশ্নই ঠিক করে দেয় লিংক না বোতাম। প্রশ্নের উত্তর থেকে একটি ছোট acceptance criteria-র তালিকা বানিয়ে ফেলুন; শেষে সেটাই মিলিয়ে দেখবেন।
  2. HTML কাঠামো। CSS ছাড়া পাতাটি কি অর্থবহ? Semantic HTML আগে লিখুন: কার্ড একটি স্বাধীন অংশ, তাই <article>; নামের জন্য heading; অন্য জায়গায় যাওয়া মানে লিংক। নামের আদ্যক্ষর শুধু সাজ, তাই aria-hidden দিয়ে screen reader থেকে লুকানো।
  3. লেআউট। আগে বড় সিদ্ধান্ত: কোন অংশ কোথায় বসবে। এক বস্তুকে দুই দিকেই মাঝখানে বসাতে grid-এর place-items: center সবচেয়ে ছোট উপায় (পর্ব ২৬-এ flexbox দিয়েও দেখেছেন)। সাজের কাজ পরে।
  4. স্টাইল। রং, অক্ষর, ফাঁক। ফাঁকের জন্য একটি মাপকাঠি (যেমন .25, .5, 1, 1.5rem) মেনে চলুন — জাদুকরী সংখ্যা যেমন 13px, 27px ইন্টারভিউয়ারের চোখে পড়ে। ধূসর লেখার contrast এখনই মেপে নিন।
  5. অবস্থা। UI-এর আসল পরীক্ষা অবস্থায়: hover, :focus-visible, চাপা, নিষ্ক্রিয়, ভুল, ফাঁকা, লোড হচ্ছে। লম্বা নাম বা বড় সংখ্যা বসিয়ে দেখুন কার্ড ভাঙে কি না। এই ধাপটি অনেকে বাদ দেন — তাই আপনি করলে আলাদা হয়ে যাবেন।
  6. রেসপন্সিভ। স্থির width: 320px নয়; inline-size: 100% আর max-inline-size-এর জুটি। এখানে কোনো media query-ই লাগেনি — ভালো লেআউট প্রায়ই নিজে থেকেই রেসপন্সিভ (intrinsic layout বলে)।
  7. অ্যাক্সেসিবিলিটি যাচাই। শেষ পাঁচ মিনিট এর জন্য রেখে দিন: Tab চেপে ঘুরুন, zoom 200% করুন, contrast দেখুন, DevTools-এর Accessibility অংশে নাম আর ভূমিকা দেখুন। WCAG-এর বড় নিয়মগুলো এভাবেই দুই মিনিটে ধরা পড়ে।

কথা বলতে বলতে কোড লেখা

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

  • শুরুতে: “ধরে নিচ্ছি ‘যোগাযোগ’ চাপলে অন্য পাতায় যাবে, তাই লিংক নিচ্ছি — ঠিক আছে?”
  • লেআউটে: “এটা এক সারির সাজ, তাই flex; কার্ডের সারি-কলাম দুই দিকে, তাই grid.”
  • সময় কম হলে: “আগে কাঠামো আর কি-বোর্ড ঠিক করছি, ছায়া আর অ্যানিমেশন শেষে সময় থাকলে।”
  • শেষে: “৩২০ পিক্সেলে দেখে নিয়েছি, Tab-এ focus দেখা যায়। আরও সময় পেলে ছবি না থাকার অবস্থাটা সাজাতাম।”

এই পাতায় কীভাবে অনুশীলন করবেন: প্রতিটি starter কোড ইচ্ছা করেই অসম্পূর্ণ আর ভুলে ভরা — TODO মন্তব্যগুলো আপনার কাজ। starter-এর “কপি” বোতাম চেপে নিজের editor-এ বসান, ব্রাউজারে খুলে রাখুন, DevTools-এর device toolbar (পর্ব ০২) চালু রাখুন। আসল ইন্টারভিউতেও সাধারণত এমন পরিবেশই থাকে। সমাধানের এডিটরগুলো এই পাতাতেই চলে — সেখানে কোড বদলালে প্রিভিউ নিজে থেকে বদলায়, আর “আগের কোড” চাপলে আসলটি ফেরে।

৩ · সহজ দিয়ে শুরু — কার্ড, গ্রিড, প্রাইসিং আর ফর্ম

এই চারটি চ্যালেঞ্জ প্রায় সব ফ্রন্ট-এন্ড ইন্টারভিউর প্রথম ১৫–৩০ মিনিটে আসার মতো। দেখতে সহজ, কিন্তু প্রতিটিতে একটি করে “ফাঁদ” লুকানো আছে — সেটাই ইন্টারভিউয়ার খোঁজেন।

চ্যালেঞ্জ ১ · মাঝখানে বসানো প্রোফাইল কার্ড (সহজ)

ব্রিফ: একটি ফ্রিল্যান্সারের প্রোফাইল কার্ড — নামের আদ্যক্ষরের গোল ছবি, নাম, পরিচয় আর “যোগাযোগ করুন” লিংক। কার্ডটি তার অংশের ঠিক মাঝখানে (ওপর-নিচ আর ডান-বাম দুই দিকেই) বসবে।

<style>
  /* TODO ১: .stage-এর ভেতরে কার্ডটি দুই দিকেই মাঝখানে বসান */
  /* TODO ২: কার্ড সর্বোচ্চ 20rem চওড়া, ছোট পর্দায় পুরো প্রস্থ */
  /* TODO ৩: বোতামের focus যেন চোখে পড়ে */
</style>
<div class="stage">
  <!-- TODO ৪: div-soup সরিয়ে অর্থবহ ট্যাগ দিন -->
  <div class="card">
    <div class="avatar">রা</div>
    <b>রাফিয়া ইসলাম</b>
    <span>ফ্রন্ট-এন্ড ডেভেলপার, চট্টগ্রাম</span>
    <div class="btn" onclick="location.hash='contact'">যোগাযোগ করুন</div>
  </div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • মাঝখানে বসানোর দুই লাইন: পাত্রে display: grid আর place-items: center. পাত্রের নিজের উচ্চতা থাকতে হবে, নইলে “ওপর-নিচে মাঝখান” বলে কিছু নেই।
  • onclick-ওয়ালা div-কে <a href> বানান — তাহলে কি-বোর্ড, নতুন ট্যাবে খোলা আর screen reader সব বিনামূল্যে পাবেন।
  • গোল ছবি: সমান প্রস্থ-উচ্চতার জন্য aspect-ratio: 1, তারপর border-radius: 50%.
<style>
  .stage { min-height: 22rem; display: grid; place-items: center;
           padding: 1rem; background: #eef2f7; font-family: system-ui, sans-serif; }
  .card { inline-size: 100%; max-inline-size: 20rem; padding: 1.5rem;
          text-align: center; background: #fff; border-radius: 1rem;
          box-shadow: 0 4px 16px rgb(0 0 0 / .1); }
  .avatar { inline-size: 5rem; aspect-ratio: 1; margin: 0 auto .75rem;
            display: grid; place-items: center; border-radius: 50%;
            background: #1d4ed8; color: #fff; font-size: 1.5rem; }
  .card h2 { margin: 0; font-size: 1.25rem; }
  .card p { margin: .25rem 0 1rem; color: #4b5563; }
  .card a { display: inline-block; padding: .6rem 1.2rem; border-radius: .5rem;
            background: #1d4ed8; color: #fff; text-decoration: none; }
  .card a:hover { background: #1e40af; }
  .card a:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
<main class="stage">
  <article class="card">
    <div class="avatar" aria-hidden="true">রা</div>
    <h2>রাফিয়া ইসলাম</h2>
    <p>ফ্রন্ট-এন্ড ডেভেলপার, চট্টগ্রাম</p>
    <a href="#contact">যোগাযোগ করুন</a>
  </article>
</main>

প্রিভিউতে হালকা ধূসর পটভূমির ঠিক মাঝখানে সাদা কার্ড: নীল গোলে “রা”, নিচে নাম, ধূসর পরিচয় আর নীল বোতামের মতো দেখতে লিংক। চেষ্টা করুন: (১) .stage থেকে min-height মুছে দিন — কার্ড ওপরে উঠে যায়, কারণ পাত্রের উচ্চতা এখন শুধু কার্ডের সমান। (২) নামটিকে খুব লম্বা করে দিন — লেখা ভেঙে নতুন লাইনে যায়, কার্ড বাড়ে কিন্তু ভাঙে না। আসল পাতায় পুরো পর্দার মাঝখানে চাইলে min-height: 100dvh লিখুন।

  • ইন্টারভিউয়ার যা দেখেন: div-কে লিংক বানানো, heading, সাজের অক্ষর aria-hidden, আর স্থির width-এর বদলে max-inline-size. ফলো-আপ প্রায়ই আসে: “flexbox দিয়ে করুন” — display: flex; justify-content: center; align-items: center.
  • সাধারণ ভুল: position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) — কাজ করে, কিন্তু কার্ড flow থেকে বেরিয়ে যায় আর লম্বা হলে ওপর থেকে কাটা পড়ে। আর outline: none লিখে focus মুছে ফেলা।

চ্যালেঞ্জ ২ · অনলাইন দোকানের পণ্যের কার্ড-গ্রিড (সহজ)

ব্রিফ: একটি দেশি পণ্যের অনলাইন দোকানের তালিকা — প্রতিটি কার্ডে ছবি, নাম, দাম আর “কার্টে যোগ করুন”। পর্দা যত চওড়া, তত বেশি কলাম।

<style>
  .grid { /* TODO ১: media query ছাড়াই যত কলাম আঁটে তত কলাম */ }
  .card { /* TODO ২: বোতাম যেন সব কার্ডে একই লাইনে নিচে থাকে */ }
  .thumb { /* TODO ৩: ছবির জায়গা 4:3, লেআউট যেন না লাফায় */ }
</style>
<div class="grid">
  <div class="card"><div class="thumb"></div>
    <h3>জামদানি শাড়ি</h3><p>৳ ৪,৫০০</p><button>কার্টে যোগ করুন</button></div>
  <div class="card"><div class="thumb"></div>
    <h3>নকশিকাঁথা — হাতে সেলাই করা, বড় মাপের</h3><p>৳ ২,৮০০</p><button>কার্টে যোগ করুন</button></div>
  <!-- আরও দুটি কার্ড … -->
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • পর্ব ২৮-এর বিখ্যাত লাইন: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)). ভেতরের min(100%, …) সরু পর্দায় কার্ডকে পাত্রের চেয়ে চওড়া হতে দেয় না।
  • বোতাম নিচে নামাতে কার্ডকে flex-direction: column করুন, আর বোতামে margin-top: auto. Grid-এর সারিতে সব কার্ড এমনিতেই সমান উচ্চতা পায়।
  • একই লেখার অনেক বোতাম? aria-describedby দিয়ে পণ্যের নামের সাথে জুড়ে দিন।
<style>
  .grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem;
          grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
          font-family: system-ui, sans-serif; }
  .card { display: flex; flex-direction: column; overflow: hidden;
          border: 1px solid #d1d5db; border-radius: .75rem; background: #fff; }
  .thumb { aspect-ratio: 4 / 3; background: linear-gradient(135deg, var(--c), #1f2937); }
  .body { flex: 1; display: flex; flex-direction: column; gap: .4rem; padding: .75rem; }
  .body h3 { margin: 0; font-size: 1rem; }
  .price { margin: 0; font-weight: 700; color: #15803d; }
  .body button { margin-top: auto; padding: .5rem; border: 0; border-radius: .5rem;
                 background: #111827; color: #fff; font: inherit; cursor: pointer; }
  .body button:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
<ul class="grid">
  <li class="card"><div class="thumb" style="--c:#f97316"></div><div class="body">
    <h3 id="p1">জামদানি শাড়ি</h3><p class="price">৳ ৪,৫০০</p>
    <button type="button" aria-describedby="p1">কার্টে যোগ করুন</button></div></li>
  <li class="card"><div class="thumb" style="--c:#16a34a"></div><div class="body">
    <h3 id="p2">নকশিকাঁথা — হাতে সেলাই করা, বড় মাপের</h3><p class="price">৳ ২,৮০০</p>
    <button type="button" aria-describedby="p2">কার্টে যোগ করুন</button></div></li>
  <li class="card"><div class="thumb" style="--c:#2563eb"></div><div class="body">
    <h3 id="p3">মাটির হাঁড়ি</h3><p class="price">৳ ৩৫০</p>
    <button type="button" aria-describedby="p3">কার্টে যোগ করুন</button></div></li>
  <li class="card"><div class="thumb" style="--c:#db2777"></div><div class="body">
    <h3 id="p4">পাটের ব্যাগ</h3><p class="price">৳ ৬০০</p>
    <button type="button" aria-describedby="p4">কার্টে যোগ করুন</button></div></li>
</ul>

প্রিভিউর চওড়া জায়গায় চারটি কার্ড পাশাপাশি। দ্বিতীয় কার্ডের নাম লম্বা, দুই লাইনে ভেঙেছে, তবু চারটি কালো বোতাম একই সরলরেখায় — margin-top: auto বাড়তি জায়গাটুকু বোতামের ওপরে ঠেলে দিয়েছে। চেষ্টা করুন: (১) 11rem-কে 16rem করুন — কলাম কমে তিনটি বা দুটি হয়। (২) বোতাম থেকে margin-top: auto মুছুন — বোতামগুলো এলোমেলো উচ্চতায় চলে আসে। আসল দোকানে .thumb-এর জায়গায় width, height, alt আর loading="lazy"-সহ <img> বসবে।

  • ইন্টারভিউয়ার যা দেখেন: auto-fit আর minmax() চেনেন কি, min(100%, …)-এর কারণ বলতে পারেন কি, আর aspect-ratio দিয়ে CLS ঠেকানোর কথা নিজে তোলেন কি। auto-fit আর auto-fill-এর পার্থক্যও প্রায়ই জিজ্ঞেস করা হয়: কম আইটেম হলে auto-fill খালি কলাম রেখে দেয়, auto-fit সেগুলো গুটিয়ে আইটেমকে বড় করে।
  • সাধারণ ভুল: width: 23% আর float দিয়ে কলাম, তিনটি media query দিয়ে ৪-৩-২-১ কলাম, আর ছবির ওপর বোতাম রেখে পুরো কার্ডকে <a> দিয়ে মোড়ানো — তখন লিংকের ভেতরে বোতাম, যা বৈধ HTML নয়।

চ্যালেঞ্জ ৩ · ইন্টারনেট প্যাকেজের প্রাইসিং টেবিল (সহজ → মাঝারি)

ব্রিফ: একটি ব্রডব্যান্ড প্রতিষ্ঠানের তিনটি প্যাকেজ পাশাপাশি। মাঝেরটি “সবচেয়ে জনপ্রিয়” — আলাদা করে চোখে পড়বে। প্রতিটির নিচে “বেছে নিন”।

<style>
  .plans { /* TODO ১: তিনটি প্যাকেজ পাশাপাশি, জায়গা কম হলে নিচে নিচে */ }
  .plan  { /* TODO ২: "বেছে নিন" সব কার্ডে একই উচ্চতায় */ }
  .featured { /* TODO ৩: জনপ্রিয় প্যাকেজ আলাদা করে চোখে পড়ুক — শুধু রং দিয়ে নয় */ }
</style>
<h2>ইন্টারনেট প্যাকেজ</h2>
<div class="plans">
  <div class="plan"><h3>বেসিক</h3><p>৳ ৫০০ / মাস</p>
    <p>১০ Mbps গতি<br>রাতে বাড়তি গতি</p><a href="#buy-basic">বেছে নিন</a></div>
  <div class="plan featured"> … স্ট্যান্ডার্ড, ৳ ৮০০ … </div>
  <div class="plan"> … প্রো, ৳ ১,২০০ … </div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • চ্যালেঞ্জ ২-এর একই কৌশল: grid দিয়ে কার্ড, প্রতিটি কার্ড flex column, লিংকে margin-top: auto.
  • “সবচেয়ে জনপ্রিয়” একটি দৃশ্যমান লেখা হোক — একে বলে রং ছাড়া তথ্য দেওয়া (WCAG-এর use of color নিয়ম)।
  • টিক চিহ্ন ::before দিয়ে, আর content-এর alt text: content: "✓ " / "". যে ব্রাউজার এটি চেনে না, তার জন্য আগে সাধারণ content লিখে রাখুন।
<style>
  .plans { display: grid; gap: 1rem; font-family: system-ui, sans-serif;
           grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }
  .plan { display: flex; flex-direction: column; padding: 1.25rem;
          border: 1px solid #d1d5db; border-radius: 1rem; }
  .plan h3 { margin: 0; }
  .price { margin: .5rem 0; font-size: 1.75rem; font-weight: 800; }
  .price small { font-size: 1rem; font-weight: 400; color: #4b5563; }
  .plan ul { list-style: none; margin: 0 0 1rem; padding: 0; line-height: 1.9; }
  .plan li::before { content: "✓ "; content: "✓ " / ""; color: #15803d; }
  .plan a { margin-top: auto; padding: .6rem; text-align: center; font-weight: 600;
            border: 2px solid #1d4ed8; border-radius: .5rem; color: #1d4ed8; text-decoration: none; }
  .plan a:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
  .featured { border: 2px solid #1d4ed8; box-shadow: 0 8px 24px rgb(29 78 216 / .15); }
  .featured a { background: #1d4ed8; color: #fff; }
  .badge { align-self: start; margin-bottom: .5rem; padding: .1rem .6rem; border-radius: 99px;
           background: #dbeafe; color: #1e3a8a; font-size: .85rem; }
</style>
<h2>ইন্টারনেট প্যাকেজ</h2>
<div class="plans">
  <article class="plan"><h3>বেসিক</h3><p class="price">৳ ৫০০ <small>/ মাস</small></p>
    <ul><li>১০ Mbps গতি</li><li>রাতে বাড়তি গতি</li></ul><a href="#buy-basic">বেছে নিন</a></article>
  <article class="plan featured"><p class="badge">সবচেয়ে জনপ্রিয়</p><h3>স্ট্যান্ডার্ড</h3>
    <p class="price">৳ ৮০০ <small>/ মাস</small></p>
    <ul><li>২০ Mbps গতি</li><li>রাতে বাড়তি গতি</li><li>ফ্রি রাউটার</li></ul><a href="#buy-std">বেছে নিন</a></article>
  <article class="plan"><h3>প্রো</h3><p class="price">৳ ১,২০০ <small>/ মাস</small></p>
    <ul><li>৪০ Mbps গতি</li><li>রাতে বাড়তি গতি</li><li>ফ্রি রাউটার</li><li>২৪ ঘণ্টা সাপোর্ট</li></ul>
    <a href="#buy-pro">বেছে নিন</a></article>
</div>

তিনটি প্যাকেজ পাশাপাশি; মাঝেরটির মোটা নীল বর্ডার, ছায়া, ওপরে “সবচেয়ে জনপ্রিয়” লেবেল আর ভরাট নীল বোতাম। প্রোর চারটি সুবিধা, বেসিকের দুটি — তবু তিনটি বোতাম একই লাইনে। চেষ্টা করুন: (১) .plan a থেকে margin-top: auto মুছে দেখুন বোতামগুলো কীভাবে ওঠানামা করে। (২) 13rem-কে 20rem করুন — জায়গা না আঁটায় তৃতীয় কার্ড নিচের সারিতে নেমে যায়।

  • ইন্টারভিউয়ার যা দেখেন: একই নকশা তিনবার কপি না করে একটি class আর একটি বাড়তি .featured modifier (পর্ব ৪৬-এর BEM ভাবনা)। দাম লেখা আর তার একক আলাদা করে সাজানো। ফলো-আপ: “মাসিক/বার্ষিক টগল যোগ করুন” — সেটা দুটি radio বোতামের fieldset, আর দাম বদলাতে একটু JavaScript.
  • সাধারণ ভুল: নামের দিক থেকে “টেবিল” শুনে <table> দিয়ে কার্ড সাজানো — তুলনার সারি-কলাম না থাকলে এটা টেবিল-ডেটা নয়। আর “বেছে নিন” তিনবার — কোনটি কোন প্যাকেজের, শুধু লেখা থেকে বোঝা যায় না; পাশে লুকানো লেখা বা aria-describedby দিন।

চ্যালেঞ্জ ৪ · ভুল-ঠিক দেখানো নিবন্ধন ফর্ম (মাঝারি)

ব্রিফ: নাম আর বাংলাদেশি মোবাইল নম্বর নেওয়ার ছোট ফর্ম। ভুল হলে লাল বর্ডার আর বার্তা, ঠিক হলে সবুজ — কিন্তু পাতা খোলামাত্র সব লাল দেখানো যাবে না।

<style>
  /* TODO ১: প্রতিটি ঘরের ওপরে label, নিচে ছোট নির্দেশনা */
  /* TODO ২: ভুল হলে লাল বর্ডার আর বার্তা — কিন্তু লেখা শুরুর আগেই নয় */
  /* TODO ৩: ঠিক হলে সবুজ বর্ডার */
</style>
<form>
  <input placeholder="পুরো নাম">
  <input placeholder="মোবাইল নম্বর">
  <div class="btn">নিবন্ধন করুন</div>
</form>
ইঙ্গিত দেখুন — আটকে গেলে
  • required আর pattern="01[3-9][0-9]{8}" — ব্রাউজারের নিজস্ব validation (পর্ব ০৯)।
  • :invalid পাতা খোলামাত্রই লাগে; :user-invalid লাগে ব্যবহারকারী কিছু করার পরে — এটিই চাই।
  • বার্তা দেখাতে parent থেকে child ধরুন: .field:has(input:user-invalid) .error (:has())।
<style>
  form { display: grid; gap: 1rem; max-inline-size: 24rem; font-family: system-ui, sans-serif; }
  .field { display: grid; gap: .3rem; }
  label { font-weight: 600; }
  input { font: inherit; padding: .55rem .7rem; border: 1px solid #6b7280; border-radius: .5rem; }
  input:focus-visible { outline: 3px solid #2563eb; outline-offset: 1px; }
  .hint { margin: 0; font-size: .9rem; color: #4b5563; }
  .error { display: none; margin: 0; font-size: .9rem; color: #b91c1c; }
  input:user-invalid { border: 2px solid #b91c1c; }
  input:user-valid { border: 2px solid #15803d; }
  .field:has(input:user-invalid) .error { display: block; }
  button { justify-self: start; font: inherit; padding: .6rem 1.2rem; border: 0;
           border-radius: .5rem; background: #1d4ed8; color: #fff; }
</style>
<form>
  <div class="field">
    <label for="name">পুরো নাম</label>
    <input id="name" name="name" autocomplete="name" required>
    <p class="error">নামটি লিখুন</p>
  </div>
  <div class="field">
    <label for="phone">মোবাইল নম্বর</label>
    <input id="phone" name="phone" type="tel" inputmode="numeric" autocomplete="tel"
           pattern="01[3-9][0-9]{8}" required aria-describedby="phone-hint">
    <p class="hint" id="phone-hint">১১ অঙ্ক, ইংরেজি অঙ্কে — যেমন 01712345678</p>
    <p class="error">01 দিয়ে শুরু ১১ অঙ্কের নম্বর লিখুন</p>
  </div>
  <button>নিবন্ধন করুন</button>
</form>

শুরুতে দুটি সাধারণ ঘর, কোনো লাল নেই। মোবাইল নম্বরে 123 লিখে বাইরে ক্লিক করুন — বর্ডার লাল হয়, নিচে লাল বার্তা আসে। ঠিক নম্বর (যেমন 01712345678) লিখলে সবুজ। ভুল অবস্থায় “নিবন্ধন করুন” চাপলে ব্রাউজারের নিজস্ব বার্তা দেখাবে; সব ঠিক থাকলে নিচে লেখা আসবে “ফর্ম জমা হতো → name=…&phone=…”. চেষ্টা করুন: :user-invalid-কে :invalid করুন — এবার কিছু লেখার আগেই দুটি ঘর লাল, যা ব্যবহারকারীর কাছে বকুনির মতো লাগে।

  • ইন্টারভিউয়ার যা দেখেন: label-এর for/id, autocomplete, inputmode, aria-describedby, আর :user-invalid (Baseline Widely available) চেনেন কি না। ভালো ফলো-আপ উত্তর: “শুধু CSS-এ ভুল বার্তা screen reader-এর সাথে জোড়া যায় না; বাস্তবে একটু JavaScript দিয়ে aria-invalid বসাই আর বার্তার id-ও aria-describedby-তে যোগ করি; সার্ভারেও আবার যাচাই করি।”
  • সাধারণ ভুল: label-এর বদলে শুধু placeholder (লিখতে শুরু করলেই হারিয়ে যায়), type="number" দিয়ে ফোন নম্বর (শুরুর 0 হারায়, স্ক্রল চাকায় মান বদলায়), আর শুধু লাল রং দিয়ে ভুল বোঝানো। মনে রাখুন, এই pattern বাংলা অঙ্ক (০১৭…) মানবে না — নির্দেশনায় সেটা বলে দেওয়াই সৎ নকশা।

৪ · নেভিগেশন ও পাতার লেআউট — navbar থেকে gallery

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

চ্যালেঞ্জ ৫ · মোবাইল মেনুসহ রেসপন্সিভ navbar (মাঝারি)

ব্রিফ: একটি দোকানের ওপরের বার: বাঁয়ে লোগো, ডানে চারটি লিংক। ছোট পর্দায় লিংকের বদলে “মেনু” বোতাম; চাপলে লিংকগুলো একটি ভাসমান তালিকায় খোলে।

<style>
  /* TODO ১: ছোট পর্দায় শুধু লোগো আর "মেনু" বোতাম; বোতাম চাপলে মেনু খুলবে */
  /* TODO ২: বড় পর্দায় (60rem বা বেশি) বোতাম লুকিয়ে লিংকগুলো পাশাপাশি */
  /* TODO ৩: JavaScript ছাড়া, Esc আর বাইরে ক্লিকে মেনু বন্ধ */
</style>
<div class="bar">
  <div class="logo">দোকান.বিডি</div>
  <span class="menu-btn">☰</span>
  <div id="menu">
    <a href="#home">হোম</a> <a href="#shop">পণ্য</a> <a href="#offer">অফার</a> <a href="#contact">যোগাযোগ</a>
  </div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • পর্ব ১১-এর popover: <nav id="menu" popover> আর বোতামে popovertarget="menu". Esc আর বাইরে ক্লিকে বন্ধ হওয়া (light dismiss) ব্রাউজার নিজেই দেয়।
  • popover বন্ধ থাকলে ব্রাউজারের নিজস্ব স্টাইল display: none দেয়। বড় পর্দায় আপনার CSS-এ display: block; position: static লিখে সেটি সাধারণ মেনু বানিয়ে দিন।
  • মোবাইল-ফার্স্ট: আগে ছোট পর্দার নিয়ম, তারপর @media (width >= 60rem).
<style>
  body { margin: 0; font-family: system-ui, sans-serif; }
  .bar { display: flex; align-items: center; justify-content: space-between;
         gap: 1rem; padding: .75rem 1rem; background: #0f172a; }
  .logo { color: #fff; font-weight: 800; text-decoration: none; }
  .menu-btn { font: inherit; padding: .4rem .8rem; color: #fff; background: none;
              border: 1px solid #94a3b8; border-radius: .5rem; }
  #menu { inset: 3.5rem 1rem auto auto; margin: 0; padding: .5rem; border: 0;
          border-radius: .75rem; box-shadow: 0 10px 30px rgb(0 0 0 / .3); }
  #menu ul { list-style: none; margin: 0; padding: 0; }
  #menu a { display: block; padding: .6rem 1rem; color: #0f172a; text-decoration: none; }
  #menu a:hover, #menu a:focus-visible { background: #e2e8f0; }
  main { min-height: 15rem; padding: 1rem; }
  @media (width >= 60rem) {
    .menu-btn { display: none; }
    #menu { display: block; position: static; padding: 0; background: none; box-shadow: none; }
    #menu ul { display: flex; gap: .25rem; }
    #menu a { color: #fff; border-radius: .5rem; }
    #menu a:hover, #menu a:focus-visible { background: #1e293b; }
  }
</style>
<header class="bar">
  <a class="logo" href="#home">দোকান.বিডি</a>
  <button class="menu-btn" type="button" popovertarget="menu">মেনু ☰</button>
  <nav id="menu" popover aria-label="প্রধান মেনু">
    <ul><li><a href="#home">হোম</a></li><li><a href="#shop">পণ্য</a></li>
        <li><a href="#offer">অফার</a></li><li><a href="#contact">যোগাযোগ</a></li></ul>
  </nav>
</header>
<main><h1>ঈদের অফার চলছে</h1><p>“মেনু ☰” চাপুন, তারপর Esc বা বাইরে ক্লিক করে দেখুন।</p></main>

প্রিভিউ 60rem-এর চেয়ে সরু, তাই দেখবেন গাঢ় নীল বারে লোগো আর “মেনু ☰” বোতাম। বোতাম চাপুন — ডান দিকে সাদা ভাসমান তালিকায় চারটি লিংক খোলে; Esc চাপলে বা বাইরে ক্লিক করলে বন্ধ হয়। চেষ্টা করুন: (১) 60rem-কে 40rem করুন — এবার ডেস্কটপ চেহারা: বোতাম উধাও, লিংক বারের ভেতরে পাশাপাশি। (২) Tab চেপে বোতামে যান, Enter দিন, তারপর Tab চালিয়ে যান — focus মেনুর লিংকে ঢোকে।

  • ইন্টারভিউয়ার যা দেখেন: popover বা details-এর মতো নিজস্ব HTML চেনেন কি। popover-এর বোতামের খোলা/বন্ধ অবস্থা আধুনিক ব্রাউজার নিজেই সহায়ক প্রযুক্তিকে জানায় — হাতে aria-expanded বদলাতে হয় না। popover এখন Baseline Newly available; খুব পুরোনো ব্রাউজারে মেনু খুলবে না, তাই @supports not selector(:popover-open)-এর ভেতরে মেনুটি সবসময় দেখানোর একটি fallback রাখুন — মূল কাজ (চলাচল) যেন কখনো আটকে না যায়।
  • সাধারণ ভুল: <div class="hamburger">-এ click, “☰” চিহ্ন একা (নাম ছাড়া বোতাম), আর লুকানো checkbox-এর কৌশল (checkbox hack) — এতে screen reader শোনে “চেকবক্স, চেক করা নেই”, যা মেনুর সাথে মেলে না।

চ্যালেঞ্জ ৬ · sticky header আর পাতার ভেতরে লাফ (মাঝারি)

ব্রিফ: একটি ভর্তি নির্দেশিকার লম্বা পাতা। ওপরের header স্ক্রল করলেও আটকে থাকবে; header-এর লিংক চাপলে পাতার সেই অংশে যাবে।

<style>
  /* TODO ১: ওপরের header স্ক্রল করলেও ওপরে আটকে থাকুক */
  /* TODO ২: "ফি"-তে চাপলে শিরোনাম যেন header-এর নিচে চাপা না পড়ে */
  /* TODO ৩: মসৃণ স্ক্রল — কিন্তু reduced motion চাইলে নয় */
</style>
<header>
  <strong>ভর্তি নির্দেশিকা</strong>
  <a href="#rules">নিয়ম</a> <a href="#fees">ফি</a> <a href="#docs">কাগজপত্র</a>
</header>
<section id="rules"><h2>ভর্তির নিয়ম</h2><p>…</p></section>
<section id="fees"><h2>ফি</h2><p>…</p></section>
<section id="docs"><h2>কাগজপত্র</h2><p>…</p></section>
ইঙ্গিত দেখুন — আটকে গেলে
  • position: sticky; top: 0 — sticky flow-এ জায়গা ধরে রাখে, fixed-এর মতো লেখার ওপর বসে না (পর্ব ২৪)।
  • স্ক্রলের পাত্রে (html) scroll-padding-top = header-এর উচ্চতার চেয়ে একটু বেশি।
  • scroll-behavior: smooth-কে @media (prefers-reduced-motion: no-preference)-এর ভেতরে রাখুন।
<style>
  html { scroll-padding-top: 4.5rem; }
  @media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
  body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.7; }
  header { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap;
           align-items: center; gap: .5rem 1.25rem; padding: .75rem 1rem;
           background: #fff; border-bottom: 1px solid #d1d5db; }
  header ul { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; }
  header a { color: #1d4ed8; }
  section { padding: 0 1rem 9rem; }
  h2 { margin: 0; padding-top: 1rem; }
  :target h2 { background: #fef9c3; }
</style>
<header>
  <strong>ভর্তি নির্দেশিকা</strong>
  <nav aria-label="এই পাতায়">
    <ul><li><a href="#rules">নিয়ম</a></li><li><a href="#fees">ফি</a></li><li><a href="#docs">কাগজপত্র</a></li></ul>
  </nav>
</header>
<main>
  <section id="rules"><h2>ভর্তির নিয়ম</h2><p>আবেদন অনলাইনে, নির্ধারিত সময়ের মধ্যে।</p></section>
  <section id="fees"><h2>ফি</h2><p>ভর্তি ফি মোবাইল ব্যাংকিং বা ব্যাংকে জমা দেওয়া যায়।</p></section>
  <section id="docs"><h2>কাগজপত্র</h2><p>ছবি, জন্মনিবন্ধন আর আগের পরীক্ষার নম্বরপত্র।</p></section>
</main>

প্রিভিউর ভেতরেই স্ক্রল করা যায়। নিচে নামুন — সাদা header ওপরে আটকে থাকে। header-এর “ফি” চাপুন — পাতা মসৃণভাবে নামে, আর “ফি” শিরোনাম হলুদ হয়ে header-এর ঠিক নিচে দেখা যায় — এটি :target-এর কাজ। চেষ্টা করুন: প্রথম লাইনের scroll-padding-top: 4.5rem মুছে আবার “ফি” চাপুন — এবার শিরোনামটি header-এর নিচে লুকিয়ে যায়। এই একটি লাইনই চ্যালেঞ্জের মূল।

  • ইন্টারভিউয়ার যা দেখেন: sticky আর fixed-এর পার্থক্য, scroll-padding (বা প্রতিটি অংশে scroll-margin-top), reduced motion — আর WCAG 2.2-এর নতুন নিয়ম Focus Not Obscured: Tab-এ নিচের কোনো লিংকে গেলে সেটি যেন sticky header-এর নিচে পুরো ঢাকা না পড়ে। scroll-padding এখানেও সাহায্য করে, কারণ ব্রাউজার focus-এর জন্য স্ক্রল করার সময়ও এটি মানে।
  • সাধারণ ভুল: sticky কাজ না করা — প্রায় সবসময় কারণ কোনো পূর্বপুরুষের overflow: hidden, অথবা top না দেওয়া। আর ফোনে খুব উঁচু sticky header, যা ছোট পর্দার অর্ধেক খেয়ে ফেলে।

চ্যালেঞ্জ ৭ · Holy grail পাতা — header, তিন কলাম, footer (মাঝারি)

ব্রিফ: একটি সংবাদ পোর্টালের কঙ্কাল: ওপরে header, মাঝে বাঁয়ে বিভাগের মেনু, মাঝখানে মূল লেখা, ডানে সর্বশেষ খবর, নিচে footer. ফোনে সব একটির নিচে আরেকটি।

<style>
  /* TODO ১: ফোনে সব অংশ একটির নিচে আরেকটি */
  /* TODO ২: 40rem-এর বেশি চওড়া হলে: ওপরে header, মাঝে nav | main | aside, নিচে footer */
  /* TODO ৩: লেখা কম হলেও footer যেন নিচেই থাকে */
</style>
<div class="page">
  <div class="header">সংবাদ পোর্টাল</div>
  <div class="nav">জাতীয় · খেলা · প্রযুক্তি</div>
  <div class="main">প্রধান খবর</div>
  <div class="aside">সর্বশেষ খবর</div>
  <div class="footer">© ২০২৬</div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • grid-template-areas-এ নকশাটা প্রায় ছবির মতো লেখা যায় — ফোনে এক কলাম, বড় পর্দায় তিন কলাম (পর্ব ২৯)।
  • footer নিচে রাখতে সারির মাপ auto 1fr auto, আর পাত্রের min-height.
<style>
  body { margin: 0; font-family: system-ui, sans-serif; }
  .page { min-height: 26rem; display: grid; gap: .5rem; padding: .5rem;
          grid-template-rows: auto auto 1fr auto auto;
          grid-template-areas: "head" "nav" "main" "side" "foot"; }
  .page > * { padding: .75rem 1rem; border-radius: .5rem; }
  header { grid-area: head; background: #1e3a8a; color: #fff; }
  nav    { grid-area: nav;  background: #dbeafe; }
  nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .25rem 1rem; }
  main   { grid-area: main; border: 1px solid #cbd5e1; }
  aside  { grid-area: side; background: #fef3c7; }
  footer { grid-area: foot; background: #e5e7eb; }
  @media (width >= 40rem) {
    .page { grid-template-columns: 10rem 1fr 12rem; grid-template-rows: auto 1fr auto;
            grid-template-areas: "head head head" "nav main side" "foot foot foot"; }
    nav ul { flex-direction: column; }
  }
</style>
<div class="page">
  <header><strong>সংবাদ পোর্টাল</strong></header>
  <nav aria-label="বিভাগ"><ul><li><a href="#national">জাতীয়</a></li>
    <li><a href="#sports">খেলা</a></li><li><a href="#tech">প্রযুক্তি</a></li></ul></nav>
  <main><h1>প্রধান খবর</h1><p>মূল লেখা এখানে বসবে।</p></main>
  <aside><h2>সর্বশেষ</h2><p>ছোট খবরের তালিকা</p></aside>
  <footer>© ২০২৬ সংবাদ পোর্টাল (কাল্পনিক)</footer>
</div>

প্রিভিউ 40rem-এর চেয়ে চওড়া, তাই holy grail চেহারা: নীল header, নিচে বাঁয়ে হালকা নীল মেনু, মাঝে সাদা মূল অংশ, ডানে হলুদ “সর্বশেষ”, আর একদম নিচে ধূসর footer — লেখা কম হলেও মাঝের সারি 1fr হয়ে বাকি জায়গা নিয়েছে। চেষ্টা করুন: (১) 40rem-কে 60rem করুন — সব অংশ একটির নিচে আরেকটি, ফোনের মতো। (২) বড় পর্দার areas-এ "nav main side"-কে "side main nav" করুন — চোখে জায়গা বদলায়, কিন্তু Tab-এর ক্রম HTML-এর মতোই থাকে।

  • ইন্টারভিউয়ার যা দেখেন: areas দিয়ে পরিষ্কার নকশা, মোবাইল-ফার্স্ট, আর “চোখের ক্রম বনাম DOM-এর ক্রম” নিয়ে সচেতনতা — CSS দিয়ে খুব বেশি ওলটপালট করলে কি-বোর্ড ব্যবহারকারী বিভ্রান্ত হন। আসল পাতায় min-height: 100dvh. ফলো-আপ: “sidebar স্থির, মাঝেরটা স্ক্রল” — তখন position: sticky আর align-self: start.
  • সাধারণ ভুল: float আর clearfix দিয়ে কলাম (পুরোনো যুগের কৌশল), প্রতিটি অংশে div class="header", আর footer নিচে রাখতে position: absolute; bottom: 0 — লেখা বেশি হলে footer লেখার ওপর চড়ে বসে।

চ্যালেঞ্জ ৮ · বেড়ানোর জায়গার ছবির গ্যালারি (মাঝারি)

ব্রিফ: বাংলাদেশের বেড়ানোর জায়গার একটি গ্যালারি। কিছু ছবি চওড়া (দুই কলাম), কিছু লম্বা (দুই সারি); ক্যাপশন ছবির ওপরে নিচের দিকে।

<style>
  .gallery { /* TODO ১: সমান কলামের গ্রিড, কলাম সংখ্যা জায়গা অনুযায়ী */ }
  .wide { /* TODO ২: দুই কলাম জুড়ে */ }
  .tall { /* TODO ৩: দুই সারি জুড়ে; ফাঁকা গর্ত যেন না থাকে */ }
  /* TODO ৪: ক্যাপশন ছবির ওপরে নিচের দিকে, পড়ার মতো কনট্রাস্টে */
</style>
<div class="gallery">
  <div class="wide"><div class="ph"></div>কক্সবাজার সৈকত</div>
  <div class="tall"><div class="ph"></div>সুন্দরবন</div>
  <div><div class="ph"></div>রাতারগুল</div>
  <!-- আরও কয়েকটি … -->
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • grid-auto-rows দিয়ে সব সারির এক উচ্চতা, তারপর grid-column: span 2 আর grid-row: span 2 (span ব্যবহার করে)।
  • গর্ত ভরাতে grid-auto-flow: dense (dense packing) — পরের ছোট ছবি এসে আগের ফাঁকে বসে।
  • ক্যাপশন: figure-এ position: relative, figcaption-এ position: absolute; inset: auto 0 0 আর আধা-স্বচ্ছ কালো পটভূমি।
<style>
  .gallery { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem;
             grid-template-columns: repeat(auto-fill, minmax(min(100%, 8rem), 1fr));
             grid-auto-rows: 7rem; grid-auto-flow: dense; font-family: system-ui, sans-serif; }
  .gallery figure { position: relative; block-size: 100%; margin: 0;
                    border-radius: .5rem; overflow: hidden; }
  .ph { block-size: 100%; background: linear-gradient(160deg, var(--c), #0f172a); }
  .wide { grid-column: span 2; }
  .tall { grid-row: span 2; }
  figcaption { position: absolute; inset: auto 0 0; padding: .3rem .5rem;
               background: rgb(0 0 0 / .65); color: #fff; font-size: .85rem; }
  @media (width < 20rem) { .wide { grid-column: auto; } }
</style>
<ul class="gallery">
  <li class="wide"><figure><div class="ph" style="--c:#0ea5e9" role="img" aria-label="ঢেউ আছড়ে পড়ছে বালুর সৈকতে"></div>
    <figcaption>কক্সবাজার সৈকত</figcaption></figure></li>
  <li class="tall"><figure><div class="ph" style="--c:#15803d" role="img" aria-label="ঘন ম্যানগ্রোভ বন আর খাল"></div>
    <figcaption>সুন্দরবন</figcaption></figure></li>
  <li><figure><div class="ph" style="--c:#65a30d" role="img" aria-label="পানিতে দাঁড়িয়ে থাকা গাছের সারি"></div>
    <figcaption>রাতারগুল</figcaption></figure></li>
  <li><figure><div class="ph" style="--c:#a16207" role="img" aria-label="পাহাড়ের গায়ে চা বাগান"></div>
    <figcaption>শ্রীমঙ্গল</figcaption></figure></li>
  <li class="wide"><figure><div class="ph" style="--c:#7c3aed" role="img" aria-label="মেঘের ওপর পাহাড়ের চূড়া"></div>
    <figcaption>সাজেক ভ্যালি</figcaption></figure></li>
  <li><figure><div class="ph" style="--c:#be123c" role="img" aria-label="পুরোনো লাল ইটের প্রাসাদ"></div>
    <figcaption>আহসান মঞ্জিল</figcaption></figure></li>
  <li><figure><div class="ph" style="--c:#c2410c" role="img" aria-label="লাল ইটের দুর্গের ফটক"></div>
    <figcaption>লালবাগ কেল্লা</figcaption></figure></li>
</ul>

রঙিন টালির গ্যালারি: “কক্সবাজার সৈকত” আর “সাজেক ভ্যালি” দুই কলাম চওড়া, “সুন্দরবন” দুই সারি লম্বা; প্রতিটির নিচে কালো পটিতে সাদা ক্যাপশন। (আসল ছবির বদলে এখানে gradient — বাস্তবে .ph-এর জায়গায় <img alt="…" width height loading="lazy"> আর object-fit: cover.) চেষ্টা করুন: (১) grid-auto-flow: dense মুছে দিন — ক্রম কঠোরভাবে মানতে গিয়ে কোথাও ফাঁকা গর্ত তৈরি হয় কি না দেখুন। (২) 8rem-কে 12rem করুন — কলাম কমে, টালিগুলো নতুন করে সাজে।

  • ইন্টারভিউয়ার যা দেখেন: span, dense আর ছোট পর্দার জন্য span সরানো। সবচেয়ে বড় প্রশ্নটা অ্যাক্সেসিবিলিটির: dense চোখের ক্রম বদলায়, কিন্তু Tab আর screen reader HTML-এর ক্রম মানে — তাই গ্যালারির ছবি যদি লিংক হয়, অতিরিক্ত ওলটপালট এড়ান। Pinterest-ধাঁচের masonry লেআউটের জন্য CSS-এ নতুন প্রস্তাব আছে, কিন্তু সেটি এখনো Baseline নয়।
  • সাধারণ ভুল: alt না দেওয়া বা ক্যাপশনের লেখাই হুবহু alt-এ বসানো (screen reader দুবার শোনায়), ছবির width/height না দেওয়ায় লেআউট লাফানো, আর ক্যাপশনের নিচে পটভূমি ছাড়া সাদা লেখা — হালকা ছবিতে পড়াই যায় না।

৫ · কম্পোনেন্ট আর তার অবস্থা — accordion, modal, tooltip, dark mode

Machine coding round-এ সবচেয়ে বেশি আসে এই ধরনের কম্পোনেন্ট। এক যুগ আগে এগুলোর প্রতিটির জন্য jQuery plugin লাগত। আজ বেশিরভাগের জন্য HTML-এর নিজস্ব element আছে — আর ইন্টারভিউয়ার ঠিক এটাই দেখেন: আপনি কি চাকা নতুন করে বানাবেন, নাকি ব্রাউজারের দেওয়া চাকা চেনেন?

চ্যালেঞ্জ ৯ · একটিমাত্র খোলা থাকে এমন FAQ accordion (মাঝারি)

ব্রিফ: অনলাইন দোকানের “সাধারণ প্রশ্ন” অংশ। প্রশ্নে চাপলে উত্তর খোলে; একটি খুললে আগেরটি বন্ধ হয়। ডান পাশে +/− চিহ্ন।

<style>
  /* TODO ১: প্রতিটি প্রশ্নে চাপলে উত্তর খুলবে — JavaScript ছাড়া */
  /* TODO ২: একসাথে একটিই খোলা থাকবে */
  /* TODO ৩: ডান পাশে +/− চিহ্ন, কি-বোর্ড focus স্পষ্ট */
</style>
<h2>সাধারণ প্রশ্ন</h2>
<div class="q" onclick="this.nextElementSibling.hidden = !this.nextElementSibling.hidden">ডেলিভারি কত দিনে পাব?</div>
<div class="a" hidden>ঢাকার ভেতরে ১–২ দিন, ঢাকার বাইরে ৩–৫ দিন।</div>
<!-- আরও দুটি প্রশ্ন … -->
ইঙ্গিত দেখুন — আটকে গেলে
  • <details> + <summary> — একটি তৈরি disclosure widget, কি-বোর্ড আর খোলা/বন্ধ অবস্থা সব নিজে থেকেই।
  • সবগুলোতে একই name="faq" দিলে হয় exclusive accordion.
  • নিজের চিহ্ন বসাতে summary-র নিজস্ব ত্রিভুজ সরান: list-style: none, পুরোনো Safari-র জন্য ::-webkit-details-marker.
<style>
  .faq { max-inline-size: 36rem; font-family: system-ui, sans-serif; }
  .faq details { margin-block: .5rem; border: 1px solid #cbd5e1; border-radius: .5rem; }
  .faq details[open] { background: #f8fafc; }
  .faq summary { display: flex; justify-content: space-between; gap: 1rem;
                 padding: .75rem 1rem; font-weight: 600; cursor: pointer; list-style: none; }
  .faq summary::-webkit-details-marker { display: none; }
  .faq summary::after { content: "+"; content: "+" / ""; font-size: 1.25rem; line-height: 1; }
  .faq details[open] > summary::after { content: "−"; content: "−" / ""; }
  .faq summary:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }
  .faq details p { margin: 0; padding: 0 1rem 1rem; }
</style>
<section class="faq" aria-labelledby="faq-h">
  <h2 id="faq-h">সাধারণ প্রশ্ন</h2>
  <details name="faq" open><summary>ডেলিভারি কত দিনে পাব?</summary>
    <p>ঢাকার ভেতরে ১–২ দিন, ঢাকার বাইরে ৩–৫ দিন।</p></details>
  <details name="faq"><summary>ক্যাশ অন ডেলিভারি আছে?</summary>
    <p>হ্যাঁ, সারা দেশে পণ্য হাতে পেয়ে টাকা দিতে পারবেন।</p></details>
  <details name="faq"><summary>পণ্য ফেরত দেওয়া যায়?</summary>
    <p>সাত দিনের মধ্যে, ট্যাগসহ অক্ষত অবস্থায়।</p></details>
</section>

তিনটি বর্ডার দেওয়া প্রশ্ন; প্রথমটি খোলা, ডানে “−”, বাকি দুটিতে “+”. দ্বিতীয় প্রশ্নে চাপুন — সেটি খোলে আর প্রথমটি নিজে থেকেই বন্ধ হয়। চেষ্টা করুন: (১) তিনটির যেকোনো একটি থেকে name="faq" মুছে দিন — সেটি এখন বাকিদের থেকে স্বাধীন। (২) Tab চেপে প্রশ্নগুলোতে ঘুরুন, Enter দিন — নীল focus রেখা আর খোলা-বন্ধ, দুটোই কাজ করে।

  • ইন্টারভিউয়ার যা দেখেন: details চেনা, name attribute (Baseline Newly available — পুরোনো ব্রাউজারে শুধু একাধিক খোলা থাকতে পারে, কিছু ভাঙে না), আর summary-র ভেতরে বোতাম বা লিংক না বসানো। ফলো-আপ প্রায়ই আসে: “এবার tabs বানান।” Tabs-এর জন্য নিজস্ব HTML element নেই; সঠিক উপায় ARIA-র tab নকশা — role="tablist", tab, tabpanel, aria-selected, আর তীর-চাবিতে চলাচলের জন্য JavaScript. ছোট পর্দায় অনেকে tabs-কে accordion বানিয়ে দেন।
  • সাধারণ ভুল: div-এ onclick আর display: none টগল — কি-বোর্ডে চলে না, অবস্থা জানা যায় না। আর লুকানো radio দিয়ে CSS-শুধু tabs — দেখতে ঠিক, কিন্তু screen reader শোনে “রেডিও বোতাম ১, ৩-এর মধ্যে”।

চ্যালেঞ্জ ১০ · অর্ডার বাতিলের নিশ্চিতকরণ modal (মাঝারি)

ব্রিফ: “অর্ডার বাতিল করুন” চাপলে মাঝখানে একটি বাক্স: “অর্ডারটি বাতিল করবেন?” আর দুটি বোতাম। পেছনের পাতা অন্ধকার আর নিষ্ক্রিয়।

<style>
  /* TODO ১: বোতাম চাপলে মাঝখানে নিশ্চিতকরণের বাক্স, পেছনটা অন্ধকার */
  /* TODO ২: খোলা থাকলে পেছনের পাতায় ক্লিক বা Tab করা যাবে না; Esc-এ বন্ধ */
  /* TODO ৩: বন্ধ হলে কোন বোতাম চাপা হলো তা জানা যাবে */
</style>
<button>অর্ডার বাতিল করুন</button>
<div class="overlay" style="display:none">
  <div class="box">
    <b>অর্ডারটি বাতিল করবেন?</b>
    <span class="btn">না, রাখুন</span> <span class="btn">হ্যাঁ, বাতিল</span>
  </div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • <dialog> আর showModal() — পেছনের পাতা নিজে থেকেই inert, বাক্স যায় top layer-এ, Esc কাজ করে।
  • <form method="dialog">-এর বোতাম চাপলে বাক্স বন্ধ হয়, আর বোতামের value পাওয়া যায় returnValue-তে।
  • পেছনের অন্ধকার: ::backdrop.
<style>
  body { min-height: 16rem; font-family: system-ui, sans-serif; }
  dialog { max-inline-size: min(26rem, 100% - 2rem); padding: 1.5rem; border: 0;
           border-radius: 1rem; box-shadow: 0 20px 50px rgb(0 0 0 / .3); }
  dialog::backdrop { background: rgb(15 23 42 / .6); }
  dialog h2 { margin-top: 0; }
  .actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .5rem; }
  button { font: inherit; padding: .55rem 1rem; border: 1px solid #64748b;
           border-radius: .5rem; background: #fff; color: #111827; }
  .danger { background: #b91c1c; border-color: #b91c1c; color: #fff; }
</style>
<button type="button" id="open">অর্ডার বাতিল করুন</button>
<p id="result" aria-live="polite"></p>
<dialog id="confirm" aria-labelledby="dlg-title">
  <h2 id="dlg-title">অর্ডারটি বাতিল করবেন?</h2>
  <p>বাতিল করলে টাকা ৭ কর্মদিবসের মধ্যে ফেরত আসবে।</p>
  <form method="dialog" class="actions">
    <button value="keep" autofocus>না, রাখুন</button>
    <button value="cancel" class="danger">হ্যাঁ, বাতিল</button>
  </form>
</dialog>
<script>
  const dlg = document.getElementById('confirm');
  document.getElementById('open').addEventListener('click', () => {
    dlg.returnValue = ''; dlg.showModal();
  });
  dlg.addEventListener('close', () => {
    document.getElementById('result').textContent = 'ফল: ' + (dlg.returnValue || 'Esc চেপে বন্ধ');
  });
</script>

শুরুতে শুধু একটি বোতাম। চাপুন — পুরো প্রিভিউ ধূসর-অন্ধকার, মাঝখানে সাদা বাক্স, focus “না, রাখুন”-এ। “হ্যাঁ, বাতিল” চাপলে নিচে লেখা আসে “ফল: cancel”; আবার খুলে Esc চাপলে “ফল: Esc চেপে বন্ধ”। চেষ্টা করুন: (১) showModal()-কে show() করুন — এবার পেছনে অন্ধকার নেই, পেছনের বোতামেও ক্লিক যায়: এটি non-modal. (২) autofocus মুছে দেখুন খোলার পর focus কোথায় যায়।

  • ইন্টারভিউয়ার যা দেখেন: modal আর non-modal-এর পার্থক্য, focus কোথায় যায় আর ফেরে, ধ্বংসাত্মক কাজে নিরাপদ বোতামে আগে focus রাখা। JavaScript ছাড়াও এখন খোলা যায়: <button commandfor="confirm" command="show-modal"> — invoker commands, Baseline Newly available; পুরোনো ব্রাউজারের জন্য ওপরের মতো ছোট script-টি fallback হিসেবে রাখুন।
  • সাধারণ ভুল: position: fixed div আর বিশাল z-index: 99999 — focus পেছনের পাতায় পালিয়ে যায়, Esc কাজ করে না, screen reader পেছনের লেখাও পড়ে। আর খোলা অবস্থায় পেছনের পাতা স্ক্রল হওয়া — দরকার হলে html:has(dialog[open]) { overflow: hidden; }.

চ্যালেঞ্জ ১১ · অ্যাক্সেসিবল tooltip — প্রায় শুধু CSS (কঠিন)

ব্রিফ: একটি ফর্মে “জাতীয় পরিচয়পত্র নম্বর”-এর পাশে “?” বোতাম। মাউস রাখলে বা কি-বোর্ডে focus এলে ওপরে ছোট ব্যাখ্যা ভেসে ওঠে।

<style>
  /* TODO ১: "?" বোতামের ওপরে ছোট ব্যাখ্যা — মাউস রাখলে আর Tab করে focus এলেও */
  /* TODO ২: মাউস টিপসের ওপর নিয়ে গেলেও যেন না হারায় */
  /* TODO ৩: screen reader যেন ব্যাখ্যাটিও শোনায়; Esc-এ লুকাবে */
</style>
<label for="nid">জাতীয় পরিচয়পত্র নম্বর</label>
<span class="q" title="কার্ডের পেছনে ১০ বা ১৭ অঙ্কের নম্বর">?</span>
<input id="nid">
ইঙ্গিত দেখুন — আটকে গেলে
  • title attribute কি-বোর্ড আর টাচে দেখা যায় না — এটি বাদ দিন। tooltip-এর লেখা একটি আসল element, role="tooltip", আর বোতামে aria-describedby.
  • দেখানো: পাত্রে :is(:hover, :focus-within). বোতাম আর tooltip-এর মাঝের ফাঁকে মাউস হারায় — একটি স্বচ্ছ ::after “সেতু” বানান।
  • WCAG 1.4.13 (Content on Hover or Focus) Esc-এ বন্ধ চায় — এটুকু শুধু CSS-এ হয় না; তিন লাইনের JavaScript লাগবে।
<style>
  body { padding: 4.5rem 1rem 1rem; font-family: system-ui, sans-serif; }
  .tip-wrap { position: relative; display: inline-block; }
  .tip-btn { inline-size: 1.75rem; block-size: 1.75rem; border-radius: 50%; font: inherit;
             border: 1px solid #475569; background: #f1f5f9; cursor: help; }
  .tip-btn:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }
  [role="tooltip"] { position: absolute; bottom: calc(100% + .5rem); left: 50%; translate: -50% 0;
       inline-size: max-content; max-inline-size: 14rem; padding: .4rem .6rem; border-radius: .4rem;
       background: #111827; color: #fff; font-size: .875rem; opacity: 0; visibility: hidden; }
  [role="tooltip"]::after { content: ""; position: absolute; inset: 100% 0 -.5rem; } /* হোভারের সেতু */
  .tip-wrap:is(:hover, :focus-within) [role="tooltip"] { opacity: 1; visibility: visible; }
  .tip-wrap.closed [role="tooltip"] { opacity: 0; visibility: hidden; }
  @media (prefers-reduced-motion: no-preference) { [role="tooltip"] { transition: opacity .15s; } }
</style>
<label for="nid">জাতীয় পরিচয়পত্র নম্বর</label>
<span class="tip-wrap">
  <button class="tip-btn" type="button" aria-label="সাহায্য" aria-describedby="nid-tip">?</button>
  <span role="tooltip" id="nid-tip">কার্ডের পেছনে ১০ বা ১৭ অঙ্কের নম্বর</span>
</span>
<input id="nid" inputmode="numeric" aria-describedby="nid-tip">
<script>
  const wrap = document.querySelector('.tip-wrap');
  document.addEventListener('keydown', (e) => { if (e.key === 'Escape') wrap.classList.add('closed'); });
  ['pointerleave', 'focusout'].forEach((ev) => wrap.addEventListener(ev, () => wrap.classList.remove('closed')));
</script>

প্রিভিউতে লেবেল, গোল “?” বোতাম আর একটি ঘর। “?”-এর ওপর মাউস রাখুন — ওপরে কালো tooltip: “কার্ডের পেছনে ১০ বা ১৭ অঙ্কের নম্বর”; মাউস ধীরে tooltip-এর ওপর নিয়ে গেলেও সেটি থাকে। Tab চেপে বোতামে এলেও দেখা যায়; তখন Esc চাপলে লুকায়। চেষ্টা করুন: (১) ::after-এর “সেতু”-র লাইনটি মুছে দিন — এবার বোতাম থেকে tooltip-এ যাওয়ার পথেই সেটি হারিয়ে যায়। (২) :focus-within মুছে দিন — মাউসে চলে, কিন্তু কি-বোর্ডে আর দেখা যায় না: ঠিক এই ভুলটিই সবচেয়ে বেশি হয়।

  • ইন্টারভিউয়ার যা দেখেন: WCAG 1.4.13-এর তিন শর্ত — dismissible (Esc), hoverable (সেতু), persistent (নিজে থেকে হারায় না) — আর tooltip-এ শুধু বাড়তি তথ্য, জরুরি কিছু নয় (টাচ-ফোনে hover নেই)। ফলো-আপ: “tooltip পর্দার কিনারায় কাটা পড়লে?” — anchor positioning আর position-try দিয়ে ব্রাউজার নিজেই জায়গা খোঁজে; এটি সব প্রধান ব্রাউজারে পৌঁছানোর পথে, তাই এখনকার মতো ওপরের মতো সাধারণ position-কে fallback রাখুন। popover-এর নতুন "hint" মানও এখনো Baseline নয়।
  • সাধারণ ভুল: display: none → block শুধু :hover-এ, title attribute-এ ভরসা, আর tooltip-এর ভেতরে লিংক বা বোতাম রাখা — ইন্টারঅ্যাকটিভ কিছু হলে সেটি tooltip নয়, popover বা dialog.

চ্যালেঞ্জ ১২ · টোকেন দিয়ে dark mode বোতাম (কঠিন)

ব্রিফ: একটি আবহাওয়ার কার্ড। পাশে থিম বাছাইয়ের ঘর: “সিস্টেম অনুযায়ী / লাইট / ডার্ক”। সব রং একবারই লেখা থাকবে।

<style>
  /* TODO ১: সব রং একবার টোকেনে লিখুন — লাইট আর ডার্ক দুই মানসহ */
  /* TODO ২: select-এ "সিস্টেম / লাইট / ডার্ক" বাছলে পুরো পাতা বদলাবে, JavaScript ছাড়া */
  /* TODO ৩: ফর্মের ঘর আর স্ক্রলবারও যেন থিম মানে */
  body { background: #fff; color: #111; }
  .card { background: #eee; }
</style>
<select><option>সিস্টেম অনুযায়ী</option><option>লাইট</option><option>ডার্ক</option></select>
<div class="card"><h2>আজকের আবহাওয়া</h2><p>ঢাকা: আংশিক মেঘলা, ৩১°C</p></div>
ইঙ্গিত দেখুন — আটকে গেলে
  • :root { color-scheme: light dark; } — ব্রাউজারকে বলে দুই থিমই চলে; ফর্মের ঘর আর স্ক্রলবার নিজে থেকে মানিয়ে নেয় (color-scheme এর কাজ)।
  • light-dark() একই টোকেনে দুই মান রাখে, আর কোনটি নেবে তা ঠিক হয় color-scheme থেকে।
  • বাছাই ধরতে: :root:has(#theme option[value="dark"]:checked).
<style>
  :root { color-scheme: light dark;
    --bg: light-dark(#ffffff, #0f172a);    --text: light-dark(#111827, #e5e7eb);
    --muted: light-dark(#4b5563, #94a3b8); --card: light-dark(#f1f5f9, #1e293b);
    --accent: light-dark(#1d4ed8, #93c5fd); }
  :root:has(#theme option[value="light"]:checked) { color-scheme: light; }
  :root:has(#theme option[value="dark"]:checked)  { color-scheme: dark; }
  body { margin: 0; padding: 1rem; background: var(--bg); color: var(--text);
         font-family: system-ui, sans-serif; }
  .card { margin-top: 1rem; padding: 1rem; border-radius: .75rem; background: var(--card); }
  .card h2 { margin-top: 0; }
  .card p { color: var(--muted); }
  a { color: var(--accent); }
  select { font: inherit; padding: .3rem; }
</style>
<label for="theme">থিম:</label>
<select id="theme">
  <option value="system">সিস্টেম অনুযায়ী</option>
  <option value="light">লাইট</option>
  <option value="dark">ডার্ক</option>
</select>
<article class="card">
  <h2>আজকের আবহাওয়া</h2>
  <p>ঢাকা: আংশিক মেঘলা, ৩১°C</p>
  <a href="#details">বিস্তারিত দেখুন</a>
</article>

আপনার সিস্টেম লাইট হলে সাদা পাতা আর হালকা ধূসর কার্ড দেখবেন (ডার্ক হলে শুরু থেকেই গাঢ়)। বাছাই থেকে “ডার্ক” নিন — পটভূমি গাঢ় নীল, লেখা হালকা, লিংক হালকা নীল, আর select-এর ঘরটিও গাঢ় হয়ে যায়। চেষ্টা করুন: (১) প্রথম লাইনের color-scheme: light dark মুছে “সিস্টেম অনুযায়ী” রাখুন — light-dark() তখন সবসময় লাইট মান নেয়। (২) --accent-এর ডার্ক মান #1d4ed8 করে দিন — গাঢ় পটভূমিতে লিংক প্রায় পড়াই যায় না: দুই থিমের contrast আলাদা করে মাপতে হয়।

  • ইন্টারভিউয়ার যা দেখেন: টোকেনের চিন্তা (semantic token — --bg, --text, রঙের নাম নয়), color-scheme, light-dark() (Baseline Newly available) আর :has() (Widely available). ফলো-আপ প্রায় নিশ্চিত: “পছন্দটা মনে রাখবেন কীভাবে?” — localStorage-এ রাখা, আর পাতা আঁকার আগেই <head>-এর ছোট script-এ data-theme বসানো, নইলে খোলার মুহূর্তে সাদা ঝলক (theme flash) দেখা যায়।
  • সাধারণ ভুল: প্রতিটি নিয়ম .dark .card {…} দিয়ে দুবার লেখা, filter: invert(1)-এর মতো শর্টকাট (ছবিও উল্টে যায়), আর শুধু টগল বোতাম — “সিস্টেম অনুযায়ী” ফেরার পথ না রাখা।

আরও গভীরে — top layer আর z-index-এর যুদ্ধের শেষ: modal <dialog> আর popover খোলা হলে ব্রাউজার সেগুলোকে DOM-এর জায়গা থেকে না সরিয়েই একটি আলাদা স্তরে আঁকে — top layer, যা পাতার সব stacking context-এর ওপরে। তাই কোনো পূর্বপুরুষের overflow: hidden বা ছোট z-index এদের কাটতে বা চাপা দিতে পারে না; পরে যেটি খোলা হয় সেটি ওপরে। top layer ধারণাটি প্রথমে এসেছিল পুরো পর্দার (fullscreen) API-এর জন্য, পরে dialog আর popover-এর স্পেসিফিকেশনে জায়গা পায়। এজন্যই আধুনিক UI-তে “z-index: 999999” লেখার দিন ফুরিয়েছে।

৬ · পলিশ ও বাস্তব কনটেন্ট — skeleton, খবর, চ্যাট আর বিবরণ থেকে নকশা

শেষ চারটি চ্যালেঞ্জ take-home বা দীর্ঘ machine coding-এর মতো। এখানে নকশা আর কনটেন্ট দুটোই বাস্তব — বাংলা লেখা, লম্বা লিংক, সময়, টাকার অঙ্ক। ছোট ছোট সিদ্ধান্তগুলোই আপনাকে আলাদা করবে।

চ্যালেঞ্জ ১৩ · reduced motion মানা skeleton loader (মাঝারি)

ব্রিফ: একটি সামাজিক ফিডের পোস্ট আসতে সময় লাগে। ততক্ষণ পোস্টের আকারের ধূসর “কঙ্কাল” দেখাবে, হালকা চকচকে নড়াচড়াসহ।

<style>
  /* TODO ১: ডেটা আসার আগে কার্ডের আকারের ধূসর "কঙ্কাল" */
  /* TODO ২: হালকা চকচকে নড়াচড়া — কিন্তু reduced motion চাইলে স্থির */
  /* TODO ৩: screen reader যেন শোনে "লোড হচ্ছে", কঙ্কালের খালি বাক্স নয় */
</style>
<h2>সর্বশেষ পোস্ট</h2>
<div class="card">
  <div class="sk avatar"></div>
  <div class="sk line"></div>
  <div class="sk line short"></div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • চকচকে ভাব: ধূসর-সাদা-ধূসর linear-gradient, background-size বড় করে background-position-এ অ্যানিমেশন।
  • মোশন-নিরাপদ নিয়ম: অ্যানিমেশন লিখুন @media (prefers-reduced-motion: no-preference)-এর ভেতরে (পর্ব ৪৫)।
  • কঙ্কালে aria-hidden="true", পাত্রে aria-busy="true", আর লুকানো একটি role="status" লেখা।
<style>
  .card { display: grid; grid-template-columns: 3rem 1fr; gap: .75rem; max-inline-size: 22rem;
          margin-bottom: .75rem; padding: 1rem; border: 1px solid #e5e7eb; border-radius: .75rem; }
  .sk { border-radius: .4rem; background: #e5e7eb; }
  .avatar { block-size: 3rem; border-radius: 50%; }
  .line { block-size: .8rem; margin-block: .35rem; }
  .short { inline-size: 60%; }
  @media (prefers-reduced-motion: no-preference) {
    .sk { background: linear-gradient(90deg, #e5e7eb 30%, #f9fafb 50%, #e5e7eb 70%) 0 0 / 300% 100%;
          animation: shimmer 1.5s linear infinite; }
  }
  @keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
  .vh { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden;
        clip-path: inset(50%); white-space: nowrap; }
</style>
<section aria-labelledby="feed-h" aria-busy="true">
  <h2 id="feed-h" style="font-family: system-ui, sans-serif">সর্বশেষ পোস্ট</h2>
  <p class="vh" role="status">পোস্ট লোড হচ্ছে…</p>
  <div class="card" aria-hidden="true">
    <div class="sk avatar"></div>
    <div><div class="sk line"></div><div class="sk line short"></div></div>
  </div>
  <div class="card" aria-hidden="true">
    <div class="sk avatar"></div>
    <div><div class="sk line"></div><div class="sk line short"></div></div>
  </div>
</section>

“সর্বশেষ পোস্ট” শিরোনামের নিচে দুটি কার্ড-আকৃতির কঙ্কাল: বাঁয়ে গোল, ডানে দুটি ধূসর লাইন (দ্বিতীয়টি ছোট), আর তাদের ওপর দিয়ে হালকা আলোর ঢেউ বয়ে যায়। আপনার সিস্টেমে “অ্যানিমেশন কমান” চালু থাকলে ঢেউ দেখবেন না — কঙ্কাল স্থির। চেষ্টা করুন: (১) no-preference-কে reduce করুন — উল্টো হয়ে যায়, এখন শুধু কম-নড়াচড়া চাওয়া মানুষই ঢেউ দেখেন: শর্তের দিকটা কত জরুরি দেখুন। (২) 1.5s-কে 4s করুন — ধীর ঢেউ অনেক শান্ত লাগে।

  • ইন্টারভিউয়ার যা দেখেন: skeleton screen-এর আসল উদ্দেশ্য (অপেক্ষাকে ছোট মনে হওয়া আর CLS কমানো), prefers-reduced-motion, আর live region. পারফরম্যান্সের ফলো-আপ: background-position-এর অ্যানিমেশন প্রতি ফ্রেমে নতুন করে আঁকায় (paint); অনেক কঙ্কাল থাকলে একটি pseudo-element-এ gradient রেখে transform: translateX() সরানো সস্তা, কারণ সেটি compositor-এ চলে (পর্ব ৩৮–৩৯)।
  • সাধারণ ভুল: অনন্তকাল ঘোরা spinner, যার সাথে কোনো লেখা নেই; ডেটা এলেও aria-busy="true" রয়ে যাওয়া; আর কঙ্কাল আসল কার্ডের চেয়ে অনেক ছোট — ডেটা এলেই পাতা ধাক্কা খায়।

চ্যালেঞ্জ ১৪ · বাংলা খবরের পাতার লেআউট (কঠিন)

ব্রিফ: একটি বাংলা সংবাদ সাইটের একক খবরের পাতা: বিভাগ, শিরোনাম, প্রতিবেদক ও তারিখ, ছবি ও ক্যাপশন, মূল কথা (lead), অনুচ্ছেদ আর একটি উদ্ধৃতি। ফোনে প্রথম পড়া হবে এমন ধরে নিন।

<style>
  /* TODO ১: পড়তে আরামদায়ক লাইন-দৈর্ঘ্য আর বাংলার উপযোগী লাইনের উচ্চতা */
  /* TODO ২: শিরোনাম পর্দা অনুযায়ী বড়-ছোট (কিন্তু zoom মেনে) */
  /* TODO ৩: বাইলাইন, তারিখ, ছবি-ক্যাপশন আর উদ্ধৃতির সঠিক ট্যাগ */
</style>
<div class="news">
  <span class="cat">প্রযুক্তি</span>
  <div class="title">উপজেলায় বিনা মূল্যে ওয়েব ডিজাইন প্রশিক্ষণ</div>
  <span>নিজস্ব প্রতিবেদক, ২০ অক্টোবর ২০২৬</span>
  <div class="img"></div> <i>ছবির ক্যাপশন</i>
  <div>প্রথম অনুচ্ছেদ …</div>
  <div>"উদ্ধৃতি …"</div>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • কাঠামো: <article>, <h1>, <time datetime>, <figure>/<figcaption>, <blockquote>. পাতার <html lang="bn"> ভুলবেন না (পর্ব ১৪)।
  • fluid typography: clamp(1.5rem, 1.1rem + 2vw, 2.25rem) — মাঝের অংশে rem থাকায় zoom কাজ করে।
  • বাংলা লেখায় line-height ১.৭–১.৯-এর কাছাকাছি; শিরোনামে text-wrap: balance.
<style>
  body { margin: 0; background: #fafaf9; }
  article { max-inline-size: 38rem; margin-inline: auto; padding: 1.25rem; color: #1c1917;
            font-family: "Noto Sans Bengali", "Hind Siliguri", system-ui, sans-serif; line-height: 1.8; }
  .kicker { margin: 0; color: #b91c1c; font-weight: 700; }
  h1 { margin: .25rem 0 .5rem; font-size: clamp(1.5rem, 1.1rem + 2vw, 2.25rem);
       line-height: 1.35; text-wrap: balance; }
  .byline { margin: 0 0 1rem; color: #57534e; font-size: .95rem; }
  figure { margin: 0 0 1rem; }
  .ph { aspect-ratio: 16 / 9; border-radius: .5rem; background: linear-gradient(135deg, #14532d, #65a30d); }
  figcaption { font-size: .875rem; color: #57534e; }
  .lead { font-size: 1.15rem; font-weight: 500; }
  blockquote { margin: 1.25rem 0; padding-inline-start: 1rem; border-inline-start: 4px solid #b91c1c; }
</style>
<article>
  <p class="kicker">প্রযুক্তি · কাল্পনিক উদাহরণ</p>
  <h1>উপজেলা পর্যায়ে বিনা মূল্যে ওয়েব ডিজাইন প্রশিক্ষণ শুরু</h1>
  <p class="byline">নিজস্ব প্রতিবেদক · <time datetime="2026-10-20">২০ অক্টোবর ২০২৬</time></p>
  <figure>
    <div class="ph" role="img" aria-label="প্রশিক্ষণ কক্ষে ল্যাপটপে কাজ করছেন কয়েকজন তরুণ"></div>
    <figcaption>প্রশিক্ষণ কক্ষ · প্রতীকী ছবি</figcaption>
  </figure>
  <p class="lead">তিন মাসের এই প্রশিক্ষণে HTML, CSS আর ফ্রিল্যান্সিংয়ের প্রাথমিক কাজ শেখানো হবে।</p>
  <p>প্রতিটি ব্যাচে ত্রিশজন অংশ নিতে পারবেন। ক্লাস হবে সপ্তাহে তিন দিন, বিকেলে।</p>
  <blockquote><p>“শেখার পর অনেকে ঘরে বসেই কাজ শুরু করতে পারবেন।” — একজন প্রশিক্ষক</p></blockquote>
</article>

হালকা পটভূমিতে মাঝখানে একটি পাঠযোগ্য কলাম: লাল “প্রযুক্তি · কাল্পনিক উদাহরণ”, বড় শিরোনাম (দুই লাইনে প্রায় সমান ভাগে ভাঙা), ধূসর বাইলাইন, সবুজ ১৬:৯ ছবির জায়গা আর ক্যাপশন, একটু বড় lead, অনুচ্ছেদ, আর বাঁ পাশে লাল দাগসহ উদ্ধৃতি। ফন্টের তালিকার প্রথম দুটি এখানে লোড করা নেই, তাই আপনার সিস্টেমের বাংলা ফন্টে দেখাচ্ছে — এটাই font fallback. চেষ্টা করুন: (১) line-height: 1.8-কে 1.2 করুন — ওপর-নিচের কার-চিহ্ন প্রায় ছুঁয়ে যায়। (২) h1 থেকে text-wrap: balance মুছে দেখুন শেষ লাইনে একটি শব্দ একা পড়ে থাকে কি না।

  • ইন্টারভিউয়ার যা দেখেন: টাইপোগ্রাফির বোধ (measure, line-height, স্কেল), logical property (border-inline-start — ভবিষ্যতে উর্দু বা আরবির মতো ডান-থেকে-বাঁয়ে সংস্করণেও কাজ করবে), আর বাংলা নিয়ে সচেতনতা: text-align: justify বাংলায় শব্দের মাঝে বড় ফাঁক তৈরি করে, লম্বা লেখায় start ভালো। text-wrap: balance Baseline Newly available; না চিনলে শুধু সাধারণভাবে ভাঙে।
  • সাধারণ ভুল: শিরোনাম font-size: 5vw (zoom-এ বড় হয় না, WCAG-এর লেখা বড় করার নিয়ম ভাঙে), তারিখ শুধু লেখা হিসেবে, ছবির নিচে <i>-এ ক্যাপশন, আর ইংরেজি ফন্টে বাংলা — ভাঙা যুক্তাক্ষর বা অন্য ফন্টে লাফিয়ে যাওয়া।

চ্যালেঞ্জ ১৫ · সাপোর্ট চ্যাটের বাবল লেআউট (কঠিন)

ব্রিফ: একটি দোকানের সাপোর্ট চ্যাট উইজেট: অন্যের বার্তা বাঁয়ে ধূসর বাবলে, নিজের বার্তা ডানে নীল বাবলে, প্রতিটিতে সময়; নিচে লেখার ঘর আর “পাঠান”।

<style>
  /* TODO ১: অন্যের বার্তা বাঁয়ে, নিজের বার্তা ডানে, বাবলের প্রস্থ সর্বোচ্চ 75% */
  /* TODO ২: লম্বা লিংক যেন বাবল ভেঙে বাইরে না যায় */
  /* TODO ৩: বার্তার জায়গা নিজে স্ক্রল হবে, লেখার ঘর নিচে স্থির */
  /* TODO ৪: নতুন বার্তা screen reader নিজে থেকে শোনাবে */
</style>
<div class="chat">
  <div class="msg">আসসালামু আলাইকুম! কীভাবে সাহায্য করতে পারি?</div>
  <div class="msg me">আমার অর্ডারটি কোথায় আছে?</div>
  <input placeholder="বার্তা লিখুন"><span class="btn">পাঠান</span>
</div>
ইঙ্গিত দেখুন — আটকে গেলে
  • পুরো উইজেট flex column: ওপরে বার্তার অংশ flex: 1; overflow-y: auto, নিচে ফর্ম। বার্তাগুলো একটি <ol>, প্রতিটি বাবল align-self দিয়ে বাঁয়ে বা ডানে।
  • লম্বা লিংক: overflow-wrap: anywhere. লেখার ঘরে min-inline-size: 0, নইলে flex-এ সেটি সরু হতে চায় না।
  • বার্তার পাত্রে role="log" (নিজে থেকেই polite live region); কে বলছেন তা লুকানো লেখায় — visually hidden.
<style>
  .chat { display: flex; flex-direction: column; block-size: 22rem; max-inline-size: 26rem;
          border: 1px solid #d1d5db; border-radius: 1rem; overflow: hidden; font-family: system-ui, sans-serif; }
  .log { flex: 1; overflow-y: auto; padding: 1rem; background: #f8fafc; }
  .log ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
  .msg { align-self: flex-start; max-inline-size: 75%; padding: .5rem .75rem; border-radius: 1rem;
         border-end-start-radius: .25rem; background: #e2e8f0; overflow-wrap: anywhere; }
  .msg.me { align-self: flex-end; background: #1d4ed8; color: #fff;
            border-end-start-radius: 1rem; border-end-end-radius: .25rem; }
  .msg time { display: block; font-size: .75rem; opacity: .85; text-align: end; }
  .composer { display: flex; gap: .5rem; padding: .5rem; border-top: 1px solid #d1d5db; }
  .composer input { flex: 1; min-inline-size: 0; font: inherit; padding: .5rem;
                    border: 1px solid #64748b; border-radius: .5rem; }
  .composer button { font: inherit; padding: .5rem .9rem; border: 0; border-radius: .5rem;
                     background: #1d4ed8; color: #fff; }
  .vh { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
</style>
<section class="chat" aria-labelledby="chat-h">
  <h2 id="chat-h" class="vh">সাপোর্ট চ্যাট</h2>
  <div class="log" role="log" aria-label="বার্তা" tabindex="0"><ol>
    <li class="msg"><span class="vh">সাপোর্ট:</span> আসসালামু আলাইকুম! কীভাবে সাহায্য করতে পারি? <time datetime="10:02">১০:০২</time></li>
    <li class="msg me"><span class="vh">আপনি:</span> আমার অর্ডার A1234 কোথায় আছে? <time datetime="10:03">১০:০৩</time></li>
    <li class="msg"><span class="vh">সাপোর্ট:</span> এই লিংকে দেখুন: https://example.com/track/A1234-dhaka-hub-2026 <time datetime="10:04">১০:০৪</time></li>
    <li class="msg me"><span class="vh">আপনি:</span> ধন্যবাদ! <time datetime="10:05">১০:০৫</time></li>
  </ol></div>
  <form class="composer"><label class="vh" for="m">বার্তা লিখুন</label>
    <input id="m" name="message" autocomplete="off" required><button>পাঠান</button></form>
</section>

গোল কোণার একটি চ্যাট বাক্স: বাঁয়ে ধূসর বাবল, ডানে নীল বাবল, প্রতিটির নিচের কোণায় সময়; প্রতিটি বাবলের নিচের একটি কোণা চোখা, যেন কে বলছেন সেদিকে ইঙ্গিত। লম্বা লিংকটি বাবলের ভেতরেই ভেঙে পরের লাইনে গেছে। বার্তার অংশ বাক্সের ভেতরে স্ক্রল হয়, নিচের ঘর স্থির। চেষ্টা করুন: (১) .msg থেকে overflow-wrap: anywhere মুছুন — লিংকটি বাবল ফুঁড়ে বেরিয়ে যায়। (২) ঘরে কিছু লিখে “পাঠান” চাপুন — নিচে দেখাবে “ফর্ম জমা হতো → message=…”; আসল অ্যাপে JavaScript নতুন <li> যোগ করবে আর log সেটি পড়ে শোনাবে।

  • ইন্টারভিউয়ার যা দেখেন: flex-এ align-self, min-inline-size: 0-এর ফাঁদ, logical radius (border-end-end-radius), <time datetime>, role="log" আর স্ক্রল পাত্রে tabindex="0". ফলো-আপ: “নতুন বার্তায় নিচে নিজে থেকে নামবে কীভাবে?” — JavaScript-এ scrollIntoView(), কিন্তু পাঠক ওপরে পুরোনো বার্তা পড়ছেন তখন জোর করে নামানো উচিত নয়।
  • সাধারণ ভুল: বাবলে স্থির width, flex-direction: column-reverse দিয়ে নিচ থেকে সাজানো (চোখের আর screen reader-এর ক্রম উল্টে যায়), আর লেখার ঘরের placeholder-ই একমাত্র label.

চ্যালেঞ্জ ১৬ · লেখা বিবরণ থেকে নকশা — পেমেন্ট সফলের রসিদ (কঠিন)

ব্রিফ (ডিজাইনারের নোট হুবহু): “কার্ড সর্বোচ্চ ২২rem চওড়া, মাঝখানে, সাদা, কোণ ১rem, খুব হালকা ছায়া। ওপরে ৩.৫rem-এর সবুজ গোল আইকন, ভেতরে সাদা টিক। ‘পেমেন্ট সফল’, নিচে বড় অঙ্কে টাকা। তারপর লেনদেন আইডি, তারিখ, মাধ্যম — নাম বাঁয়ে ধূসর, মান ডানে মোটা। ড্যাশড দাগের পর দুটি সমান চওড়া বোতাম পাশাপাশি: ‘রসিদ ডাউনলোড’ (শুধু বর্ডার), ‘হোমে ফিরুন’ (ভরাট)। জায়গা কম হলে বোতাম একটির নিচে আরেকটি। সব ফাঁক ৮ পিক্সেলের গুণিতক।”

<!-- বিবরণ থেকে নকশা: নিচের কাঠামো পূর্ণ করে CSS লিখুন -->
<section class="receipt">
  <!-- সবুজ গোল আইকন, ভেতরে সাদা টিক (SVG) -->
  <!-- শিরোনাম: পেমেন্ট সফল -->
  <!-- বড় অঙ্কে পরিমাণ: ৳ ১,২০০ -->
  <!-- লেনদেন আইডি / তারিখ / মাধ্যম — নাম বাঁয়ে ধূসর, মান ডানে -->
  <!-- ড্যাশড বিভাজকের পর দুটি বোতাম: রসিদ ডাউনলোড (outline), হোমে ফিরুন (ভরাট) -->
</section>
ইঙ্গিত দেখুন — আটকে গেলে
  • আগে নোট থেকে টোকেন বের করুন: --s1: .5rem (৮px), --s2: 1rem, --s3: 1.5rem, সবুজ একটি রং।
  • নাম-মানের দুই কলাম: dl-এ grid-template-columns: auto 1fr.
  • বোতাম পাশাপাশি থেকে নিচে: flex-wrap: wrap আর প্রতিটিতে flex: 1 1 8rem — দুটি 8rem-এর বোতাম তাদের padding, border আর মাঝের gap-সহ (মোট প্রায় ১৯rem) না আঁটলেই নিজে থেকে ভাঙে, media query লাগে না।
  • টিক: ছোট inline SVG, aria-hidden="true" (পর্ব ৪৪)।
<style>
  :root { --s1: .5rem; --s2: 1rem; --s3: 1.5rem; --ok: #15803d; --muted: #64748b; }
  body { margin: 0; padding: var(--s2); background: #f1f5f9; font-family: system-ui, sans-serif; }
  .receipt { max-inline-size: 22rem; margin-inline: auto; padding: var(--s3); text-align: center;
             background: #fff; border-radius: 1rem; box-shadow: 0 6px 20px rgb(0 0 0 / .08); }
  .icon { inline-size: 3.5rem; aspect-ratio: 1; margin: 0 auto var(--s2); display: grid;
          place-items: center; border-radius: 50%; background: var(--ok); }
  h1 { margin: 0; font-size: 1.25rem; }
  .amount { margin: var(--s1) 0 var(--s3); font-size: 2rem; font-weight: 800; }
  dl { display: grid; grid-template-columns: auto 1fr; gap: var(--s1) var(--s2); margin: 0; text-align: start; }
  dt { color: var(--muted); }
  dd { margin: 0; text-align: end; font-weight: 600; overflow-wrap: anywhere; }
  .actions { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s3);
             padding-top: var(--s3); border-top: 2px dashed #cbd5e1; }
  .actions a { flex: 1 1 8rem; padding: var(--s1); border: 2px solid var(--ok); border-radius: .6rem;
               color: var(--ok); font-weight: 600; text-decoration: none; }
  .actions .primary { background: var(--ok); color: #fff; }
  .actions a:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
<main><section class="receipt" aria-labelledby="r-title">
  <div class="icon"><svg width="28" height="28" viewBox="0 0 24 24" aria-hidden="true">
    <path d="M5 12l5 5 9-10" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
  <h1 id="r-title">পেমেন্ট সফল</h1>
  <p class="amount">৳ ১,২০০</p>
  <dl><dt>লেনদেন আইডি</dt><dd>TXN8F27K1</dd>
      <dt>তারিখ</dt><dd><time datetime="2026-10-20T15:45">২০ অক্টোবর ২০২৬, বিকেল ৩:৪৫</time></dd>
      <dt>মাধ্যম</dt><dd>মোবাইল ব্যাংকিং</dd></dl>
  <div class="actions"><a href="receipt-TXN8F27K1.pdf" download>রসিদ ডাউনলোড</a>
    <a class="primary" href="#home">হোমে ফিরুন</a></div>
</section></main>

হালকা ধূসর পটভূমিতে মাঝখানে সাদা রসিদ: সবুজ গোলের ভেতর সাদা টিক, “পেমেন্ট সফল”, বড় করে “৳ ১,২০০”, তিন সারির বিবরণ (বাঁয়ে ধূসর নাম, ডানে মোটা মান), ড্যাশড দাগ, আর সমান চওড়া দুটি সবুজ বোতাম। “রসিদ ডাউনলোড” চাপলে নিচে লগ আসে “লিংকে ক্লিক → receipt-TXN8F27K1.pdf…” — প্রিভিউ অন্য ফাইল খোলে না। চেষ্টা করুন: (১) --s3-কে 2rem করুন — একটি টোকেন বদলে সব বড় ফাঁক একসাথে বদলায়: এটাই টোকেনের শক্তি। (২) 8rem-কে 12rem করুন — বোতাম দুটি পাশাপাশি না এঁটে একটির নিচে আরেকটি নামে।

  • ইন্টারভিউয়ার যা দেখেন: নোট পড়ে প্রশ্ন করা (“বোতাম দুটো কি লিংক না কাজ? টাকা কি সবসময় বাংলা অঙ্কে?”), মাপকে টোকেনে রূপান্তর, আর “pixel-perfect” আর রেসপন্সিভের মধ্যে ভারসাম্য — ডিজাইনের ছবি একটি নির্দিষ্ট প্রস্থের, আপনার কোডকে সব প্রস্থে ঠিক থাকতে হবে। DevTools-এর Computed প্যানেলে (পর্ব ৪৭) মাপ মিলিয়ে নেওয়াও দেখাতে পারেন।
  • সাধারণ ভুল: প্রতিটি মাপ আলাদা জাদুকরী সংখ্যায় (margin-top: 23px), টিকের জন্য ✓ অক্ষর (ফন্টভেদে আকার বদলায়), বিবরণ <table> বা <br>-এ, আর দেখতে বোতাম বলে <button> দিয়ে অন্য পাতায় যাওয়া।

Take-home কাজে মান দেখানোর উপায়

বাড়িতে বসে করা কাজে সময় বেশি, তাই প্রত্যাশাও বেশি। আবার অনেকে কয়েক দিনের কাজকে কয়েক সপ্তাহে টেনে নিয়ে যান — সেটাও ভুল সংকেত। একটি সুশৃঙ্খল জমা দেওয়া কাজে সাধারণত থাকে:

  • README: কীভাবে খুলবেন, কোন সিদ্ধান্ত কেন নিলেন, কী বাদ রেখেছেন আর সময় পেলে কী করতেন। “সময় তিন ঘণ্টা বলা ছিল; আমি সাড়ে তিন ঘণ্টা নিয়েছি” — এমন সৎ লাইন বিশ্বাস বাড়ায়।
  • যাচাইয়ের প্রমাণ: HTML validator, Lighthouse বা axe-এর ফল, ৩২০ পিক্সেল আর বড় পর্দার স্ক্রিনশট, শুধু কি-বোর্ডে ঘোরার ছোট নোট।
  • পরিষ্কার git ইতিহাস: ছোট ছোট অর্থবহ commit (পর্ব ৫৩), আর GitHub Pages বা Netlify-তে একটি চালানো যায় এমন লিংক।
  • সংযম: যা চাওয়া হয়েছে তার বাইরে বিশাল framework বা অ্যানিমেশন নয়। একটি সঠিক, অ্যাক্সেসিবল, রেসপন্সিভ পাতা দশটি চমকের চেয়ে ভালো।

৭ · ভাবনার প্রশ্ন — ইন্টারভিউর বড় trade-off

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

প্র ০১লাইভ কোডিংয়ে Tailwind বা Bootstrap ব্যবহার করব, নাকি সাধারণ CSS? (মাঝারি)

প্রথম কাজ: জিজ্ঞেস করুন। “framework চলবে, নাকি আপনি সাধারণ CSS দেখতে চান?” — এই এক প্রশ্নেই বোঝা যায় আপনি দুটোই চেনেন আর নির্দেশ মেনে চলেন। অনেক প্রতিষ্ঠান ইচ্ছা করেই framework ছাড়া চ্যালেঞ্জ দেয়, কারণ তারা দেখতে চায় framework-এর নিচে কী আছে আপনি তা বোঝেন কি না।

কেন এই সতর্কতা? Bootstrap ২০১১ সালে Twitter-এর ভেতরের একটি টুল হিসেবে প্রকাশ পায়, আর তখনকার float-ভিত্তিক গ্রিড আর ব্রাউজারের বৈষম্যের যুগে এটি সত্যিই জীবন বাঁচাত। Tailwind (২০১৭ থেকে) utility class-এর ধারণাকে জনপ্রিয় করেছে — দ্রুত, ধারাবাহিক, ছোট CSS ফাইল। কিন্তু দুটোরই একটি ফাঁদ আছে: framework চেহারা দেয়, অর্থ দেয় না। class="btn" লাগানো একটি div এখনো বোতাম নয়; Tailwind-এর flex items-center লিখতে পারেন মানেই flexbox-এর free space বা min-width: auto-এর ফাঁদ বোঝেন, তা নয়।

যদি অনুমতি থাকে, তবুও বিবেচনা করুন: ইন্টারভিউর ৪৫ মিনিটে CDN লোড, class মনে করা আর কাস্টমাইজ করার সময়টাও হিসাবে আসে। ছোট কম্পোনেন্টে সাধারণ CSS প্রায়ই দ্রুততর, আর আলোচনায় আপনার প্রতিটি সিদ্ধান্ত পরিষ্কার দেখা যায়। বড় take-home-এ, যেখানে প্রতিষ্ঠান নিজেই Tailwind ব্যবহার করে, সেটি ব্যবহার করা বুদ্ধিমানের — কিন্তু README-তে লিখুন কেন। পর্ব ৫১-এ দেখেছেন, framework বেছে নেওয়া দলের সিদ্ধান্ত; ইন্টারভিউতে আপনি দেখাচ্ছেন সেই সিদ্ধান্ত নেওয়ার যোগ্যতা।

মূল উপলব্ধি: framework একটি গতি-বাড়ানোর যন্ত্র, ভিত্তির বদলি নয়। আগে জিজ্ঞেস করুন, তারপর এমন পথ বাছুন যাতে আপনার HTML আর CSS-এর বোঝাপড়া স্পষ্ট দেখা যায়।

প্র ০২সময়ের চাপে div দিয়ে তাড়াতাড়ি বানিয়ে পরে ARIA জুড়ে দিলে কী সমস্যা? (মাঝারি)

সমস্যাটা হলো, ARIA শুধু ঘোষণা করে, আচরণ দেয় না। একটি div-এ role="button" দিলে screen reader বলবে “বোতাম” — কিন্তু Tab-এ পৌঁছাতে tabindex="0", Enter ও Space-এ চলতে দুটি keydown handler, নিষ্ক্রিয় অবস্থার জন্য aria-disabled আর তার যুক্তি — সবই হাতে লিখতে হয়। <button> এগুলো বিনামূল্যে দেয়, ফর্ম জমা দেওয়াসহ। তাই “পরে জুড়ে দেব” আসলে বেশি কাজ, কম নয়।

WAI-ARIA তৈরি হয়েছিল এমন এক সময়ে, যখন HTML-এ tabs, tree, slider-এর মতো জটিল উইজেট বানানোর কোনো নিজস্ব উপায় ছিল না, অথচ ওয়েব অ্যাপগুলো সেগুলো div আর JavaScript দিয়ে বানাচ্ছিল। ARIA ছিল সেই ফাঁক জোড়ার সেতু; W3C এর ১.০ সংস্করণ ২০১৪ সালে সুপারিশ (Recommendation) হিসেবে প্রকাশ করে। একই সময়ে “ARIA ব্যবহারের প্রথম নিয়ম” জনপ্রিয় হয়: যে কাজের জন্য নিজস্ব HTML element আছে, সেটিই ব্যবহার করুন। পরে বিভিন্ন জরিপে বারবার দেখা গেছে, যেসব পাতায় বেশি ARIA, সেগুলোতে গড়ে অ্যাক্সেসিবিলিটির সমস্যাও বেশি — কারণ ভুল ARIA কোনো ARIA না থাকার চেয়েও খারাপ: এটি screen reader-কে মিথ্যা বলে।

ইন্টারভিউর চাপে সবচেয়ে দ্রুত পথটাই আসলে সঠিক পথ: শুরু থেকে ঠিক element লেখা। <button> লিখতে <div>-এর চেয়ে বেশি সময় লাগে না। ARIA রাখুন সেই জায়গার জন্য যেখানে HTML সত্যিই কিছু দেয় না — যেমন tabs-এর aria-selected, বা একটি ঘরের সাথে ভুল বার্তা জুড়তে aria-describedby.

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

প্র ০৩স্ক্রিনশট দেখে “pixel-perfect” বানানো, নাকি সব পর্দায় মানিয়ে নেওয়া — কোনটা আসল লক্ষ্য? (কঠিন)

দুটোই, কিন্তু ভিন্ন অর্থে। একটি ডিজাইনের ছবি (Figma বা স্ক্রিনশট) একটি নির্দিষ্ট প্রস্থে, নির্দিষ্ট লেখা দিয়ে আঁকা একটি মুহূর্ত। ওয়েবপাতা একটি জীবন্ত জিনিস — ৩২০ পিক্সেলের পুরোনো Android থেকে চওড়া মনিটর, ১০০% থেকে ২০০% zoom, ছোট নাম থেকে তিন লাইনের নাম, ইংরেজি ফন্ট না পেয়ে বাংলা সিস্টেম ফন্ট। ছবির প্রস্থে হুবহু মেলানো সম্ভব আর তা দেখানোও উচিত; কিন্তু বাকি সব অবস্থায় “ভদ্রভাবে মানিয়ে নেওয়া”-ই আসল মান।

এই চিন্তার একটি ইতিহাস আছে। ২০১০ সালে Ethan Marcotte “Responsive Web Design” প্রবন্ধে fluid grid, নমনীয় ছবি আর media query-র ত্রয়ী তুলে ধরেন। ২০১৮ সালের দিকে Jen Simmons “Intrinsic Web Design” কথাটি জনপ্রিয় করেন: Grid, minmax(), fr, min()-এর মতো টুল দিয়ে লেআউট নিজেই বিষয়বস্তু আর জায়গা বুঝে সাজবে, অনেক breakpoint ছাড়াই। আর Heydon Pickering ও Andy Bell-এর “Every Layout” দেখায়, ছোট কিছু লেআউট-প্রাথমিক নকশা (stack, cluster, sidebar) একসাথে জুড়ে প্রায় যেকোনো ডিজাইন বানানো যায়।

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

মূল উপলব্ধি: ছবিটি একটি উদাহরণ, চুক্তি নয়। নির্দিষ্ট প্রস্থে বিশ্বস্ত থাকুন, আর বাকি সব প্রস্থে নীতিতে বিশ্বস্ত থাকুন — সেটাই পেশাদার ফ্রন্ট-এন্ড।

প্র ০৪“শুধু CSS দিয়ে বানান” — এই শর্ত কখন ভালো অনুশীলন, আর কখন ভুল নকশার দিকে ঠেলে? (কঠিন)

CSS-শুধু চ্যালেঞ্জের একটি সুন্দর দিক আছে: এটি দেখায় আপনি প্ল্যাটফর্মকে কতটা চেনেন। আজ অনেক কিছু সত্যিই JavaScript ছাড়া হয় — details দিয়ে accordion, popover দিয়ে মেনু, :has() দিয়ে parent-এর অবস্থা, :user-invalid দিয়ে ফর্মের ভুল, scroll-snap দিয়ে carousel-এর মতো স্ক্রল। কম JavaScript মানে দ্রুত পাতা, কম ভাঙার জায়গা, আর progressive enhancement.

কিন্তু ২০১০-এর দশকে “CSS-only” একটি খেলায় পরিণত হয়েছিল। লুকানো checkbox আর :checked + label দিয়ে মেনু, tabs, modal, এমনকি ছোট গেম — সেই “checkbox hack” তখন চতুর মনে হতো, কারণ নিজস্ব HTML ছিল না। সমস্যা হলো, এর অর্থ ভুল: screen reader শোনে “চেকবক্স”, অথচ চোখে দেখা যায় মেনু। Esc-এ বন্ধ হওয়া, focus নিয়ন্ত্রণ, খোলা অবস্থার সঠিক ঘোষণা — কিছুই থাকে না। আজ details, dialog আর popover আসার পর এই কৌশলের প্রায় কোনো প্রয়োজন নেই।

তাই নিয়মটা হলো: CSS-শুধু তখনই, যখন নিজস্ব HTML-ই আচরণটা দেয়। যেখানে দেয় না — tabs-এর তীর-চাবি, tooltip-এর Esc, সময়মতো চ্যাট স্ক্রল — সেখানে ছোট, পরিষ্কার JavaScript-ই সঠিক উত্তর। ইন্টারভিউতে শর্তটা যদি “শুধু CSS” হয় আর তাতে অ্যাক্সেসিবিলিটি ভাঙে, তবে সেটি বলাই সবচেয়ে ভালো উত্তর: “শুধু CSS-এ এটা হুবহু হবে, কিন্তু Esc কাজ করবে না — আমি তিন লাইনের script যোগ করার পরামর্শ দেব।” চ্যালেঞ্জ ১১-এ ঠিক এটাই করা হয়েছে।

মূল উপলব্ধি: লক্ষ্য “কত কম JavaScript” নয়, লক্ষ্য “সবার জন্য সঠিক আচরণ সবচেয়ে সরল উপায়ে”। কখনো সেটা শূন্য লাইন JavaScript, কখনো তিন লাইন।

প্র ০৫কয়েক দিনের take-home পেলে কতটা করবেন — আর AI ব্যবহার করলে কী বলবেন? (মাঝারি)

Take-home-এর লুকানো প্রশ্ন একটাই: “এই মানুষটি বাস্তব কাজে কেমন সিদ্ধান্ত নেন?” তাই পরিমাণের চেয়ে বিচার বেশি গুরুত্বপূর্ণ। শুরুতেই সময় বেঁধে নিন — প্রতিষ্ঠান যা বলেছে তার কাছাকাছি। আগে শর্তগুলো একটি তালিকায় লিখুন, সেগুলো পুরো করুন, তারপর বাকি সময়ে মান বাড়ান: অবস্থা, অ্যাক্সেসিবিলিটি যাচাই, রেসপন্সিভ পরীক্ষা, README. চাওয়া হয়নি এমন বিশাল ফিচার জুড়লে অনেক সময় উল্টো সংকেত যায় — নির্দেশ না মানা, আর পর্যালোচনার সময় বাড়ানো।

অনেকের মনে প্রশ্ন জাগে, take-home কি ন্যায্য? আলোচনাটা পুরোনো। সমালোচকরা বলেন, যাঁদের সংসার বা চাকরির চাপ আছে, তাঁদের পক্ষে কয়েক দিন অবৈতনিক কাজ কঠিন; সমর্থকরা বলেন, হোয়াইটবোর্ডের চাপের চেয়ে এটি বাস্তব কাজের বেশি কাছাকাছি। এজন্য অনেক প্রতিষ্ঠান এখন সময় ছোট রাখে, বা জমা দেওয়ার পর কোডটি নিয়ে লাইভ আলোচনা করে — যাতে বোঝা যায় কাজটি আসলে কে বুঝে করেছেন।

এখানেই AI-এর প্রশ্ন। নিয়ম আগে জেনে নিন — কিছু প্রতিষ্ঠান AI-এর ব্যবহার স্পষ্টভাবে অনুমতি দেয়, কিছু নিষেধ করে। অনুমতি থাকলে README-তে সৎভাবে লিখুন কোথায় কী কাজে ব্যবহার করেছেন (“প্রথম খসড়া, তারপর নিজে div-soup সরিয়ে semantic করেছি, axe দিয়ে যাচাই করেছি”)। আর মনে রাখুন, পরের আলোচনায় প্রতিটি লাইন আপনাকেই ব্যাখ্যা করতে হবে: “এখানে min-inline-size: 0 কেন?” — উত্তর দিতে না পারলে সেটিই সবচেয়ে বড় ক্ষতি।

মূল উপলব্ধি: take-home-এ আপনি একটি পাতা নয়, নিজের কাজের ধরন জমা দিচ্ছেন। সময়-জ্ঞান, সততা আর ব্যাখ্যা করার ক্ষমতা — এই তিনটিই আসল নম্বর।

৮ · AI দিয়ে প্রস্তুতি — প্রশ্নকর্তা আর সমালোচক হিসেবে

AI-কে দিয়ে UI বানিয়ে নেওয়া সহজ। কিন্তু ইন্টারভিউর প্রস্তুতিতে AI-এর সবচেয়ে ভালো ব্যবহার উল্টো দিক থেকে: AI চ্যালেঞ্জ দেবে, আপনি বানাবেন, তারপর AI কঠোর সমালোচকের মতো পর্যালোচনা করবে। মনে রাখবেন, AI ইন্টারনেটের পুরোনো কোড থেকেও শিখেছে — তাই তার নিজের সমাধানে প্রায়ই div-soup, outline: none, স্থির প্রস্থ, অপ্রয়োজনীয় -webkit- prefix আর ভুল ARIA থাকে।

AI প্রায়ই যা দেয়কেন সমস্যাযা চাইবেন
<div onclick> মেনু ও modalকি-বোর্ডে অচল, focus আটকায় না<button>, popover, <dialog>
outline: none, বিকল্প ছাড়াকি-বোর্ড ব্যবহারকারী পথ হারান:focus-visible-এ স্পষ্ট outline
স্থির width: 375px, অনেক breakpointমাঝের প্রস্থে ভাঙেmax-inline-size, minmax(), auto-fit
tooltip শুধু :hover-একি-বোর্ড আর টাচে অদৃশ্য:focus-within, Esc, aria-describedby
নতুন ফিচারকে “সব জায়গায় চলে” বলাপুরোনো ব্রাউজারে মূল কাজ আটকে যায়Baseline অবস্থা আর fallback

নিচের prompt-টি AI-কে ইন্টারভিউয়ার বানায়। শেষের অংশটি খেয়াল করুন — আপনি সমাধান চাইছেন না, পর্যালোচনা চাইছেন।

তুমি একজন অভিজ্ঞ ফ্রন্ট-এন্ড ইন্টারভিউয়ার। আমাকে HTML আর CSS-এর একটি মাঝারি মানের UI চ্যালেঞ্জ দাও (যেমন একটি কম্পোনেন্ট বা ছোট পাতা), ৪০ মিনিট সময়ের জন্য। শর্ত: ১. ব্রিফ আর ৪–৬টি গ্রহণযোগ্যতার শর্ত দাও — অন্তত দুটি অ্যাক্সেসিবিলিটি আর একটি রেসপন্সিভ নিয়ে। ২. সমাধান দেবে না। আমি প্রশ্ন করলে শুধু ইন্টারভিউয়ার যতটা বলেন ততটা বলবে। ৩. আমি কোড পাঠালে কঠোরভাবে পর্যালোচনা করবে: semantic HTML, কি-বোর্ড, focus, contrast, ৩২০px, অবস্থা (hover, focus, ভুল, ফাঁকা), CSS-এর মান। ৪. যে নতুন ফিচার আমি ব্যবহার করব (popover, :has(), light-dark() ইত্যাদি), তার Baseline অবস্থা বলবে; নিশ্চিত না হলে স্পষ্ট বলবে "নিশ্চিত নই, MDN দেখো"। ৫. শেষে ১০-এর মধ্যে নম্বর দেবে আর তিনটি ফলো-আপ প্রশ্ন করবে, যা আসল ইন্টারভিউতে আসতে পারে।

AI দিয়ে অনুশীলনের চেকলিস্ট

৯ · কোর্স সমাপ্তি — ৫৯টি পর্বের পরে আপনি কোথায়

এটি ৮টি মডিউল আর ৫৯টি পর্বের এই কোর্সের শেষ পর্ব। একবার পেছনে তাকান। পর্ব ০১-এ প্রশ্ন ছিল, ব্রাউজার একটি পাতা পায় কোথা থেকে। পর্ব ০৩-এ প্রথম <h1> লিখেছিলেন। আজ আপনি popover দিয়ে মোবাইল মেনু, dialog দিয়ে নিরাপদ modal, টোকেন দিয়ে dark mode আর একটি লেখা বিবরণ থেকে পুরো একটি কম্পোনেন্ট বানাতে পারেন — আর বলতে পারেন প্রতিটি সিদ্ধান্ত কেন। আন্তরিক অভিনন্দন।

এখন আপনি যা পারেন

  • ওয়েব ও HTML-এর ভিত্তি (পর্ব ০১–০৭): ব্রাউজার আর সার্ভারের কথোপকথন, DevTools, ডকুমেন্টের গঠন, অর্থবহ লেখা, লিংক ও path, ছবি-ভিডিও, তালিকা আর টেবিল।
  • গঠন, ফর্ম ও অ্যাক্সেসিবিলিটি (পর্ব ০৮–১৫): ফর্ম ও validation, semantic layout, details-dialog-popover, screen reader আর ARIA-র সঠিক ব্যবহার, SEO ও Open Graph, ওয়েবে বাংলা — আর একটি পূর্ণ HTML সাইট।
  • CSS-এর ভিত্তি (পর্ব ১৬–২২): selector, cascade ও specificity, box model, একক ও রং, টাইপোগ্রাফি, gradient আর ছায়া।
  • লেআউট (পর্ব ২৩–২৯): normal flow, position, Flexbox, Grid, template areas, subgrid আর প্রচলিত লেআউট প্যাটার্ন।
  • রেসপন্সিভ ও আধুনিক CSS (পর্ব ৩০–৩৭): মোবাইল-ফার্স্ট, media ও container query, রেসপন্সিভ ছবি, custom property, :has(), nesting, @layer, dark mode আর একেবারে নতুন CSS.
  • মোশন, ইফেক্ট ও পলিশ (পর্ব ৩৮–৪৫): transition, animation, transform, filter ও clip-path, scroll effect, ফর্মের সাজ, SVG আর অ্যাক্সেসিবল CSS.
  • পেশাদার কাজের ধারা (পর্ব ৪৬–৫৪): CSS architecture, debugging, ব্রাউজার সাপোর্ট, পারফরম্যান্স, Sass ও PostCSS, framework, Web Components, Git ও deploy — আর নিজের portfolio-র ক্যাপস্টোন।
  • পরীক্ষা ও ইন্টারভিউ (পর্ব ৫৫–৫৯): রেন্ডার অনুমান, MCQ, লিখিত ও ভাইভা, ইন্টারভিউর প্রশ্ন আর এই পর্বের হাতে-কলমে চ্যালেঞ্জ।

পরীক্ষা ও সার্টিফিকেট

লগইন করলে ড্যাশবোর্ডে শেখার সাতটি মডিউলের (পর্ব ০১–৫৪) প্রতিটির একটি টেস্ট আর একটি ফাইনাল পরীক্ষা পাবেন। শেখার এই সব পর্ব পড়া শেষ করে ফাইনালে ৬০% বা তার বেশি পেলে একটি ফ্রি কোর্স-সমাপ্তির সার্টিফিকেট পাবেন। প্রতিটি পর্বের “পড়েছি” বোতাম চাপা থাকলে অগ্রগতি সেখানে দেখা যাবে — অগ্রগতি সেভ হয় শুধু লগইন করা অবস্থায়। পরীক্ষার আগে পর্ব ৫৫–৫৮-এর প্রশ্নব্যাংক একবার ঘুরে আসুন।

এই ১৬টি চ্যালেঞ্জকে portfolio বানান

প্রতিটি সমাধান একটি ছোট, বাস্তব কম্পোনেন্ট। নিজের মতো করে আবার বানান — নিজের রং, নিজের কনটেন্ট, একটি বাড়তি অবস্থা — তারপর একটি GitHub repository-তে রাখুন আর GitHub Pages-এ প্রকাশ করুন (পর্ব ৫৩)। পর্ব ৫৪-এর portfolio সাইটে “কম্পোনেন্ট গ্যালারি” নামে একটি পাতা যোগ করুন, প্রতিটির পাশে দুই লাইনে লিখুন কোন অ্যাক্সেসিবিলিটি সিদ্ধান্ত নিয়েছেন। ইন্টারভিউতে “একটা কাজ দেখান” বললে এটাই আপনার সবচেয়ে বিশ্বাসযোগ্য উত্তর।

এরপর কোন পথে

JavaScript প্রোগ্রামিং

সবচেয়ে স্বাভাবিক পরের ধাপ। পাতাকে জীবন্ত করা — DOM, event, ফর্ম, fetch — আর এই পর্বের “এখানে একটু JavaScript লাগবে” জায়গাগুলো নিজে লেখা। শুরু করুন প্রথম পর্ব থেকে, বা সরাসরি DOM-এর ভিত্তি।

React, Node ও full-stack

JavaScript-এর পরে component, state, routing আর server — কিন্তু মনে রাখুন, প্রতিটি React component শেষে এই কোর্সের HTML আর CSS-ই বানায়।

Human-Computer Interaction

মানুষ আসলে কীভাবে ইন্টারফেস ব্যবহার করে — usability, নকশার নীতি আর ব্যবহারকারী-গবেষণা। ভালো UI-এর পেছনের বিজ্ঞান।

নিজের portfolio বাড়ান

পর্ব ৫৪-এর সাইটে নতুন প্রজেক্ট, নিয়মিত Lighthouse রিপোর্ট আর পরিষ্কার README — কাজের নমুনা যত বাস্তব, ইন্টারভিউর গল্প তত শক্ত।

ট্যাগ মুখস্থ করা শুরু মাত্র।
সবার জন্য কাজ করে এমন পাতা বানানো, যাচাই করা আর ব্যাখ্যা করতে পারা — এটাই পেশা।
শেষ কথা

৫৯টি পর্ব শেষ করা সহজ কাজ নয় — আপনার ধৈর্য আর পরিশ্রমকে আন্তরিক অভিনন্দন। প্রথম না-মেলা </div>, প্রথম “margin কেন ভেঙে পড়ল”, প্রথম “sticky কেন আটকাচ্ছে না” — সেই বিভ্রান্তিগুলোই আজ আপনার অভিজ্ঞতা। ইন্টারভিউতে এক-দুবার আটকে যাওয়া খুব স্বাভাবিক; প্রতিটি থেকে একটি শিক্ষা খাতায় লিখে রাখুন, আর পরেরটিতে আরও প্রস্তুত হয়ে যান। শুভকামনা।

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

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

ক্রমএকটি UI ইন্টারভিউ চ্যালেঞ্জে কাজের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. প্রশ্ন করে শর্ত আর সীমা বোঝা
  2. অর্থবহ HTML কাঠামো লেখা
  3. লেআউট — flex না grid, কোন অংশ কোথায়
  4. রং, অক্ষর আর ফাঁকের স্টাইল
  5. hover, focus, ভুল, ফাঁকা — অবস্থাগুলো
  6. ছোট পর্দা, কি-বোর্ড আর contrast যাচাই

(সহজ) একটি পাত্রের নির্দিষ্ট উচ্চতা আছে। তার ভেতরের একটি কার্ডকে ওপর-নিচ আর ডান-বাম দুই দিকেই মাঝখানে বসানোর সবচেয়ে ছোট আধুনিক উপায় কোনটি?

  • পাত্রে display: grid; place-items: center;
  • পাত্রে text-align: center;
  • কার্ডে margin: auto;, পাত্রে কিছু না
  • কার্ডে vertical-align: middle;

(মাঝারি) কার্ড-গ্রিডে grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)) লেখা হলো। এখানে min(100%, 12rem) লেখার মূল কারণ কী?

  • কলাম যেন কখনো 12rem-এর বেশি চওড়া না হয়
  • পাত্র 12rem-এর চেয়ে সরু হলে কার্ড যেন পাত্রের বাইরে উপচে না পড়ে
  • ফাঁকা কলামগুলো গুটিয়ে আইটেমকে বড় করা
  • যে ব্রাউজার minmax() চেনে না তার জন্য fallback

(মাঝারি) পাশাপাশি তিনটি প্রাইসিং কার্ড grid-এ আছে, প্রতিটি কার্ড display: flex; flex-direction: column. সুবিধার তালিকা ছোট-বড় হলেও তিনটি “বেছে নিন” বোতাম একই লাইনে নিচে রাখতে বোতামে কী লিখবেন?

  • position: absolute; bottom: 0;
  • align-self: flex-end;
  • margin-top: auto;
  • প্রতিটি তালিকায় একই স্থির height

(মাঝারি) পাতার ওপরে একটি sticky header আছে। পাতার ভেতরের #fees লিংকে চাপলে “ফি” শিরোনামটি header-এর নিচে চাপা পড়ে যায়। সবচেয়ে পরিষ্কার সমাধান কোনটি?

  • header-কে position: fixed করা
  • header-এর z-index কমিয়ে দেওয়া
  • প্রতিটি শিরোনামের ওপরে একটি ফাঁকা div বসানো
  • স্ক্রল পাত্রে (html) header-এর উচ্চতার মতো scroll-padding-top

(মাঝারি) “অর্ডার বাতিল করবেন?” নিশ্চিতকরণের জন্য এমন বাক্স দরকার, যা খোলা থাকলে পেছনের পাতা নিষ্ক্রিয় থাকে আর Esc-এ বন্ধ হয়। সবচেয়ে উপযুক্ত উপায় কোনটি?

  • <dialog>, খোলা হবে showModal() দিয়ে
  • <dialog>, খোলা হবে show() দিয়ে
  • position: fixed একটি div, বড় z-index-সহ
  • একটি popover element

(কঠিন) একটি tooltip শুধু .wrap:hover .tip নিয়মে দেখা যায়। অ্যাক্সেসিবিলিটির দিক থেকে সবচেয়ে বড় ঘাটতি কী, আর তার সমাধান?

  • tooltip-এ অ্যানিমেশন নেই — transition যোগ করতে হবে
  • কি-বোর্ড focus-এ দেখা যায় না — :focus-within-এ দেখান, Esc-এ লুকান, aria-describedby দিয়ে জুড়ুন
  • tooltip-এর বদলে title attribute ব্যবহার করতে হবে
  • tooltip-এর z-index খুব বেশি করতে হবে

(মাঝারি) একটি skeleton loader-এর চকচকে অ্যানিমেশন যেন শুধু তাঁরাই দেখেন, যাঁরা সিস্টেমে কম নড়াচড়া চাননি। মোশন-নিরাপদ লেখার ধরন কোনটি?

  • অ্যানিমেশন @media (prefers-reduced-motion: reduce)-এর ভেতরে
  • অ্যানিমেশন শুধু :hover-এ চালু
  • অ্যানিমেশন @media (prefers-reduced-motion: no-preference)-এর ভেতরে
  • animation-duration বাড়িয়ে ধীর করা, শর্ত ছাড়া

(কঠিন) একটি FAQ-তে একসাথে একটিমাত্র উত্তর খোলা রাখতে হবে, JavaScript ছাড়া, কি-বোর্ডে চলবে আর খোলা/বন্ধ অবস্থা screen reader জানবে। কোনটি সঠিক?

  • লুকানো radio বোতাম আর :checked + label-এর কৌশল
  • প্রতিটি উত্তর একটি div, :target দিয়ে দেখানো
  • আলাদা আলাদা <details>, কোনো attribute ছাড়া
  • প্রতিটি <details>-এ একই name attribute

শব্দকোষ

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

মনে রাখুন

  1. UI ইন্টারভিউতে মাপা হয় কাঠামো ও অ্যাক্সেসিবিলিটি, লেআউট ও রেসপন্সিভ, অবস্থা, আর যোগাযোগ — শুধু চেহারা নয়।
  2. প্রতিটি ব্রিফে সাত ধাপ: প্রশ্ন → HTML → লেআউট → স্টাইল → অবস্থা → রেসপন্সিভ → যাচাই, আর পুরো সময় সিদ্ধান্ত জোরে বলা।
  3. লেআউটের চাবি: place-items, auto-fit + minmax(min(100%, …)), margin-top: auto, grid-template-areas, span + dense, scroll-padding.
  4. কম্পোনেন্টে আগে নিজস্ব HTML: details[name], dialog + showModal(), popover; ফাঁক থাকলে অল্প ARIA আর অল্প JavaScript (tooltip-এর Esc, tabs-এর তীর-চাবি)।
  5. পলিশ: টোকেন + color-scheme + light-dark(), reduced motion, বাংলার line-height, overflow-wrap — আর পরীক্ষা ও সার্টিফিকেটের জন্য ড্যাশবোর্ড, portfolio-র জন্য GitHub Pages.

এরপর: এটিই কোর্সের শেষ পর্ব — আর কোনো পরের পর্ব নেই। কোর্স হোম-এ ফিরে পুরো রোডম্যাপ দেখুন, ড্যাশবোর্ডে মডিউল টেস্ট ও ফাইনাল পরীক্ষা দিন, আর শুরু করুন JavaScript প্রোগ্রামিং কোর্স।

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

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