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

CSS architecture — BEM, utility class আর বড় প্রজেক্টে গোছানো CSS

CSS Architecture — BEM, OOCSS, SMACSS, ITCSS & @layer, Utility-First vs Semantic Classes, Scoping, Linting & Refactoring

Taught in Bangla, this lesson explains why CSS in large projects tangles over time and the principles that prevent it, then covers BEM naming, OOCSS, SMACSS, ITCSS with today's @layer, CUBE CSS, an honest utility-first versus semantic class comparison, scoping with CSS Modules, framework styles and @scope, file structure, tokens, documentation, Stylelint and Prettier, and refactoring a messy stylesheet step by step.

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

  • বড় প্রজেক্টে CSS কেন সময়ের সাথে জট পাকায় — আর কোন নকশার নীতি সেই জট আটকায়
  • BEM দিয়ে একটি এলোমেলো কার্ডকে ধাপে ধাপে গোছানো নামের কার্ডে বদলানো
  • OOCSS, SMACSS, ITCSS, CUBE CSS আর utility-first — কোনটি কী সমস্যার উত্তর, আর @layer-এর সাথে কীভাবে মেলে
  • CSS Modules, framework-এর scoped style আর @scope — নাম-সংঘাতের অন্য সমাধান
  • ফাইল-ফোল্ডার সাজানো, Stylelint-Prettier, নথি লেখা, আর একটি পুরোনো stylesheet নিরাপদে refactor করার পদ্ধতি

১ · বড় প্রজেক্টে CSS কেন এলোমেলো হয়

একটি ছোট দোকানের ওয়েবসাইট শুরু হয় এক পাতা দিয়ে, CSS ফাইলে পঞ্চাশ লাইন। সব পরিষ্কার। তারপর আসে পণ্যের পাতা, কার্ট, অফারের ব্যানার, ঈদের থিম, মোবাইল মেনু। দুই বছরে তিনজন ডেভেলপার কাজ করে চলে গেছেন, CSS ফাইল এখন পাঁচ হাজার লাইন। নতুন একটি বোতামের রং বদলাতে গিয়ে আপনি দেখলেন, চেকআউট পাতার একটি লেখা হঠাৎ লাল হয়ে গেছে। কেউ জানে না কেন।

এটি কোনো একজনের অযোগ্যতা নয় — CSS-এর নকশার মধ্যেই কয়েকটি বৈশিষ্ট্য আছে, যা বড় প্রজেক্টে সমস্যা তৈরি করে। CSS architecture মানে এই সমস্যাগুলো মাথায় রেখে CSS লেখার একটি পরিকল্পিত পদ্ধতি — নাম রাখার নিয়ম, ফাইল সাজানোর নিয়ম, আর কোন নিয়ম কোথায় বসবে তার সিদ্ধান্ত।

🌐 Global scope

CSS-এর প্রতিটি selector পুরো পাতার জন্য খাটে। .title লিখলে পাতার সব .title ধরা পড়ে — কে কোথায় লিখেছে তা দেখে না। এটিই global scope.

নামের সংঘাত

⚔️ Specificity-র যুদ্ধ

একটি নিয়ম কাজ না করলে কেউ selector ভারী করেন, পরের জন আরও ভারী, শেষে !important. প্রতিটি “জয়” পরের পরিবর্তনকে আরও কঠিন করে।

পর্ব ১৮-এর cascade

🪦 অব্যবহৃত কোড

পুরোনো ব্যানার মুছে গেছে, কিন্তু তার CSS রয়ে গেছে। এমন dead code ফাইল ভারী করে, আর পড়ার সময় বিভ্রান্ত করে।

ফাইল শুধু বাড়ে

😰 মুছতে ভয়

কোনো নিয়ম আর লাগে কি না, নিশ্চিত হওয়া কঠিন — অন্য কোনো পাতা, JavaScript বা পুরোনো ব্লগ পোস্ট হয়তো ব্যবহার করে। তাই সবাই শুধু নিচে যোগ করেন, কেউ মোছেন না।

append-only stylesheet

প্রথম সমস্যাটি নিজের চোখে দেখুন। রাফি তিন মাস আগে সাইটের শিরোনামের জন্য .title বানিয়েছিলেন। মিম আজ পণ্যের কার্ডে নামের জন্য একই নাম দিলেন — দুজনেই নিজের ফাইলে, দুজনেই ঠিক কাজ করছেন বলে ভাবছেন:

/* রাফির ফাইল — header.css */
.title {
  font-size: 34px;
  color: navy;
}

/* মিমের ফাইল — product.css (তিন মাস পরে) */
.title {
  font-size: 18px;
  color: darkgreen;
}

.product {
  border: 1px solid #ccc;
  padding: 12px;
  margin-top: 12px;
}

দুটি শিরোনামই এখন ছোট আর সবুজ — দোকানের নাম “আমার দোকান” তার বড় নীল চেহারা হারিয়েছে। দুই নিয়মের specificity সমান, তাই পরে লেখাটি জিতেছে। মিম নিজের কার্ড দেখে খুশি, ভাঙনটা ঘটেছে অন্য পাতায়। চেষ্টা করুন: HTML বদলাতে পারছেন না ধরে নিয়ে, মিমের selector-টি .product .title করে দিন — শিরোনাম ঠিক হবে, কিন্তু specificity বাড়ল। এরপর ভাবুন, নাম দুটিই যদি শুরু থেকে আলাদা হতো (.site-header__title, .product__title)? এই পর্বের বাকি অংশ সেই উত্তরের খোঁজ।

একটি বড় যৌথ পরিবারের কথা ভাবুন, যেখানে সবার ডাকনাম “বাবু”। মা ছাদ থেকে ডাকলেন “বাবু, ভাত খেতে আয়!” — পাঁচজন একসাথে দৌড়ে এল। CSS-এর class ঠিক এরকম: নাম ধরে ডাকলে বাড়ির সবাই সাড়া দেয়। সমাধান দুটি — হয় প্রত্যেককে আলাদা পুরো নাম দিন (নামকরণের পদ্ধতি, যেমন BEM), নয়তো প্রতিটি ঘরে আলাদা করে ডাকুন (scope, যেমন CSS Modules বা @scope). এই পর্বে দুটি পথই দেখব।

২ · ভালো CSS-এর তিন নীতি

BEM, ITCSS বা Tailwind — যেকোনো পদ্ধতির নিচে একই কয়েকটি চিন্তা কাজ করে। পদ্ধতির নাম ভুলে গেলেও এই তিনটি মনে রাখলে আপনার CSS অনেকখানি গোছানো থাকবে।

নীতি ১ — কম ও সমান specificity

বেশিরভাগ selector হোক একটি class — ওজন (0,1,0). id দিয়ে স্টাইল নয়, লম্বা descendant শিকল (#main .content ul li a) নয়। সব selector সমান ভারী হলে “কে জিতবে” প্রশ্নের উত্তর সহজ: যে পরে লেখা, বা যে পরের @layer-এ। আর কিছু বদলাতে হলে আরেকটি এক-class-এর নিয়মই যথেষ্ট।

নীতি ২ — একটি class, একটি কাজ

Single responsibility মানে প্রতিটি class-এর দায়িত্ব একটি। .card কার্ডের চেহারা ঠিক করে, কিন্তু পাতায় কার্ডটি কোথায় বসবে (margin, grid-এর ঘর) সেটা তার কাজ নয় — সেটা চারপাশের layout-এর কাজ। এতে একই কার্ড সাইডবারে, গ্রিডে বা পপআপে — সব জায়গায় অপরিবর্তিত বসানো যায়।

নীতি ৩ — জুড়ে জুড়ে বানানো

Composition মানে বড় নকশা বানানো ছোট ছোট স্বাধীন অংশ মিলিয়ে: একটি layout-এর class (যেমন .stack, লম্বালম্বি ফাঁক দেওয়া), একটি component (.card), আর দরকার হলে একটি ছোট সাহায্যকারী (.visually-hidden). নতুন নকশা মানে প্রায়ই নতুন CSS নয়, পুরোনো অংশের নতুন জোড়া।

এর সাথে দুটি অভ্যাস যোগ করুন। প্রথমত, নাম রাখুন কাজ দেখে, চেহারা দেখে নয় — .price ভালো, .green-big খারাপ (পর্ব ১৭-এ এর কারণ দেখেছেন)। দ্বিতীয়ত, CSS যেন HTML-এর গঠনের ওপর বেশি নির্ভর না করে। .sidebar div > ul li:first-child a লিখলে HTML-এ একটি <div> যোগ করলেই নকশা ভাঙে। একটি class সরাসরি element-এ থাকলে HTML-এর গঠন বদলালেও স্টাইল বদলায় না।

নিয়মের ব্যতিক্রম কোথায়? সব জায়গায় class বসাতে হবে, এমন নয়। Blog পোস্ট বা CMS থেকে আসা লেখায় (যেখানে HTML আপনার হাতে নেই) .prose p, .prose h2-এর মতো element selector-ই ঠিক পথ — তখন একটি class-এর ভেতরে element-গুলো ধরা হয়, যাকে বলা যায় “নিয়ন্ত্রিত ব্যতিক্রম”। আর পুরো সাইটের মৌলিক চেহারা (body-র ফন্ট, লিংকের রং) element selector-এই লেখা হয়, তবে একটি নিচের layer-এ।

৩ · BEM — block__element--modifier

BEM তৈরি হয়েছিল রাশিয়ার Yandex কোম্পানিতে, আর ২০১০ সালের আশেপাশে বাইরের দুনিয়ায় ছড়িয়ে পড়ে। নামটি তিনটি শব্দের আদ্যক্ষর:

অংশঅর্থলেখার ধরনউদাহরণ
Blockনিজে নিজে অর্থবহ, স্বাধীন একটি অংশ.block.card, .menu, .search-form
Elementblock-এর ভেতরের অংশ, block ছাড়া যার অর্থ নেই.block__element (দুটি underscore).card__title, .menu__item
Modifierরূপ বা অবস্থার ভিন্নতা.block--modifier বা .block__element--modifier (দুটি hyphen).card--sale, .menu__item--active

দেখতে একটু অদ্ভুত লাগে — দুটি underscore, দুটি hyphen. কিন্তু এর লাভ বিশাল: HTML-এ class="card__price" দেখামাত্র বোঝা যায় এটি card-এর ভেতরের দাম, আর CSS ফাইলে কোথায় খুঁজতে হবে। নিচের ডায়াগ্রামে একটি সাধারণ, এলোমেলো কার্ডকে ধাপে ধাপে BEM-এ বদলে দেখুন:

  1. আগে.products div h3গঠন-নির্ভর
  2. Block.cardস্বাধীন অংশ
  3. Element.card__titleভেতরের অংশ
  4. Modifier.card--saleভিন্ন রূপ
  5. ফল(0,1,0)সমান ওজন
আগেHTML-এর গঠনের ওপর ভর করা CSS
<div class="products">
  <div class="sale">
    <h3>জামদানি শাড়ি</h3>
    <p><span>৳ ৩,৫০০</span></p>
  </div>
</div>

.products div h3        { … }   /* (0,1,2) */
.products div p span    { … }   /* (0,1,3) */
.products .sale p span  { … }   /* (0,2,2) */
ধাপ ২স্বাধীন অংশটি খুঁজুন — Block
<article class="card"> … </article>

.card {
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 12px;
}   /* কোথায় বসবে (margin, grid) তা এখানে নয় */
ধাপ ৩ভেতরের অংশগুলোর নাম — Element
<article class="card">
  <h3 class="card__title">জামদানি শাড়ি</h3>
  <p class="card__price">৳ ৩,৫০০</p>
  <button class="card__button">কার্টে যোগ করুন</button>
</article>

.card__title  { … }   /* nested নয় — সরাসরি এক class */
.card__price  { … }
ধাপ ৪ভিন্নতাটুকু আলাদা — Modifier
<article class="card card--sale">   ← block-এর class থাকে, পাশে modifier

.card--sale              { border-color: crimson; }
.card__price--old        { text-decoration: line-through; }
ফলসব selector এক class
.card              (0,1,0)
.card__title       (0,1,0)
.card__price       (0,1,0)
.card--sale        (0,1,0)
h3 → h2 বদলালেও, div যোগ করলেও — কিছু ভাঙে না
  1. শুরুর অবস্থা। CSS এখানে HTML-এর গঠন মুখস্থ করে রেখেছে — “products-এর ভেতরে div-এর ভেতরে h3”. ডিজাইনার যদি h3-কে h2 করতে বলেন, বা একটি মোড়ক <div> যোগ হয়, স্টাইল নীরবে হারিয়ে যায়। আর specificity তিন রকম, তাই কোনটি জিতবে, হিসাব করে বের করতে হয়।
  2. Block খুঁজুন। নিজে নিজে অর্থবহ অংশটি হলো “পণ্যের কার্ড” — তাকে একটি নাম দিন: .card. Block পাতার যেকোনো জায়গায় বসতে পারে, তাই তার নিজের CSS-এ বাইরের margin বা অবস্থান রাখা হয় না।
  3. Element-এর নাম দিন। শিরোনাম, দাম, বোতাম — এরা কার্ডের অংশ, কার্ড ছাড়া অর্থহীন। তাই নাম card__title, card__price. খেয়াল করুন, CSS-এ .card .card__title লেখার দরকার নেই — নামটিই বলে দিচ্ছে এটি কার্ডের, তাই একটি class-ই যথেষ্ট।
  4. Modifier দিয়ে ভিন্নতা। ছাড়ের কার্ডটি আলাদা দেখাতে নতুন block নয়, একটি modifier: card--sale. HTML-এ মূল card class-টি থাকে, modifier শুধু যা আলাদা সেটুকু বলে। পুরোনো দামের জন্য element-এর modifier: card__price--old.
  5. ফল — সমতল, সমান ওজনের CSS. প্রায় সব selector এখন (0,1,0). HTML-এর গঠন বদলালেও স্টাইল টিকে থাকে, আর কোন নিয়ম কোথায়, নাম দিয়েই খুঁজে পাওয়া যায় — ফাইলে “card__price” লিখে search করলেই হলো।

এবার নিজের হাতে BEM কার্ড চালিয়ে দেখুন। দুটি কার্ড — একটি সাধারণ, একটি ছাড়ের:

.card {
  max-width: 260px;
  padding: 16px;
  margin-block-end: 12px;
  border: 2px solid #ddd;
  border-radius: 12px;
}
.card__title  { margin: 0 0 4px; font-size: 20px; }
.card__price  { margin: 0 0 12px; font-weight: bold; }
.card__button {
  padding: 8px 14px;
  border: 0;
  border-radius: 8px;
  background: teal;
  color: white;
}

/* Modifier — শুধু যা আলাদা */
.card--sale        { border-color: crimson; }
.card__price--old  { color: gray; font-weight: normal; }

দুটি কার্ডের কাঠামো একই; দ্বিতীয়টির শুধু লাল বর্ডার আর ধূসর কাটা দাম — কারণ HTML-এ তার পাশে card--sale আছে। (এখানে margin-block-end দিয়েছি শুধু প্রিভিউ পরিষ্কার রাখতে; বাস্তবে দুটি কার্ডের মাঝের ফাঁক চারপাশের layout-এর কাজ।) চেষ্টা করুন: একটি নতুন modifier লিখুন .card--featured { background: #fff8e1; border-color: orange; }, তারপর data-html বদলাতে না পারলেও ভাবুন প্রথম কার্ডে class="card card--featured" দিলে কী হতো। আরেকটি পরীক্ষা: .card__button-এর রং crimson করুন — দুটি কার্ডেই বদলাবে, কারণ element-এর নিয়ম block-এর সব কপিতে খাটে।

BEM-এর সাধারণ ভুল

  • নাতি-নাতনির নাম: .card__body__title নয়। BEM-এ element সবসময় সরাসরি block-এর নামে — .card__title, HTML-এ সে যত গভীরেই থাকুক। নাম HTML-এর গাছ নকল করলে গঠন বদলালেই নামও বদলাতে হয়।
  • শুধু modifier, block ছাড়া: class="card--sale" একা দিলে মূল কার্ডের স্টাইলই আসবে না। লিখুন class="card card--sale".
  • Modifier-এর ভেতরে element বদলানো: .card--sale .card__price { color: crimson; } চলে, আর BEM-এর নিয়মেও অনুমোদিত — তবে ওজন (0,2,0). কম ব্যবহার করুন, আর যেখানে করবেন, জেনেশুনে করুন।
  • বড় block-এর ভেতরে আরেকটি block: কার্ডের ভেতরের বোতাম যদি সাইটের সাধারণ বোতাম হয়, তাকে card__button না বলে আলাদা block .button বানান। একটি element-এ দুই ভূমিকা একসাথেও দেওয়া যায়: class="button card__button" — একে বলে mix; .button চেহারা দেয়, .card__button কার্ডের ভেতরে অবস্থান।

বাংলা প্রজেক্টে নাম — লেখা বাংলায়, কোড ইংরেজিতে

CSS-এর নিয়মে class-এর নামে Unicode অক্ষর চলে — .দাম { color: red; } আধুনিক ব্রাউজারে কাজও করবে। তবু পেশাদার প্রজেক্টে class, id, ফাইল আর ফোল্ডারের নাম রাখুন ছোট হাতের ইংরেজি ASCII অক্ষরে (kebab-case, BEM-এর __ আর -- সহ)। কারণগুলো বাস্তব:

  • কোড লেখার সময় বারবার বাংলা-ইংরেজি কিবোর্ড বদলাতে হয়, আর য়, ড়-এর মতো কিছু অক্ষর একাধিক Unicode উপায়ে লেখা যায় (পর্ব ১৪-এ দেখেছেন) — চোখে এক, কম্পিউটারের কাছে আলাদা নাম।
  • অনেক টুল, linter আর পুরোনো build script ASCII ধরে নিয়ে বানানো; বিদেশি সহকর্মী বা ওপেন-সোর্স অবদানকারীও নাম পড়তে পারেন না।
  • রোমান হরফে বাংলা (.dam, .kinun-btn) আরও খারাপ — বানানের কোনো নির্দিষ্ট নিয়ম নেই, একজন লেখেন dam, আরেকজন daam.

নিয়মটি সহজ: মানুষ যা পড়বে, তা বাংলায়; কম্পিউটার যা পড়বে, তা ইংরেজিতে। বোতামের লেখা “কার্টে যোগ করুন”, class-এর নাম card__button. CSS-এর মন্তব্য অবশ্য বাংলায় লিখতে পারেন — দলের সবাই বাংলাভাষী হলে সেটাই সবচেয়ে পরিষ্কার।

৪ · OOCSS আর SMACSS — কাঠামো, রং আর ভূমিকা আলাদা করা

BEM বলে নাম কীভাবে রাখবেন। কিন্তু কোন অংশকে আলাদা class বানাবেন, সেই প্রশ্নের উত্তর দেয় আরও দুটি পুরোনো, প্রভাবশালী চিন্তা।

OOCSS — একই কাঠামো, ভিন্ন চামড়া

২০০০-এর দশকের শেষ দিকে Nicole Sullivan প্রস্তাব করেন OOCSS (Object-Oriented CSS). তাঁর দুটি মূল কথা: কাঠামো (structure) আর রূপ (skin) আলাদা রাখুন, আর ধারক (container) আর ভেতরের জিনিস (content) আলাদা রাখুন — অর্থাৎ একটি বস্তু কোথায় বসেছে তার ওপর তার চেহারা নির্ভর করবে না। তাঁর সবচেয়ে বিখ্যাত উদাহরণ media object: বাঁয়ে একটি ছবি, ডানে লেখা। মন্তব্য, অর্ডারের নোটিফিকেশন, প্রোফাইল — ওয়েবের অসংখ্য জায়গায় এই একই কাঠামো:

/* Structure — কাঠামো, একবারই লেখা */
.media { display: flex; gap: 12px; align-items: flex-start;
         padding: 12px; margin-block-end: 8px; }
.media-figure { flex: none; inline-size: 48px; block-size: 48px;
                border-radius: 50%; display: grid; place-items: center;
                font-size: 24px; background: #eee; }
.media-body { flex: 1; }
.media-body p { margin: 4px 0 0; }

/* Skin — রূপ, আলাদা class-এ */
.skin-soft   { background: #eef6f6; border-radius: 10px; }
.skin-alert  { background: #fff1f0; border-inline-start: 4px solid crimson; }
.skin-avatar { background: teal; color: white; }

দুটি বাক্সের কাঠামো হুবহু এক — বাঁয়ে গোল ছবি, ডানে লেখা — কিন্তু চেহারা আলাদা, কারণ “skin” class আলাদা। কাঠামোর কোড একবারই লেখা হয়েছে। চেষ্টা করুন: .media-তে flex-direction: row-reverse; যোগ করুন — দুটি বাক্সেই ছবি ডানে চলে যাবে। তারপর .skin-alert-এর রং বদলে দেখুন, প্রথম বাক্স অপরিবর্তিত থাকে। (কৌতূহলী পাঠকের জন্য: .media-body p দুই অংশের selector — OOCSS-এর যুগেও এমন ছোট ব্যতিক্রম স্বাভাবিক ছিল।)

SMACSS — প্রতিটি নিয়মের একটি বিভাগ

২০১১ সালে Jonathan Snook একটি বই লেখেন SMACSS (Scalable and Modular Architecture for CSS) নামে। তাঁর মূল প্রস্তাব: প্রতিটি CSS নিয়ম পাঁচটি বিভাগের কোনো একটিতে পড়ে, আর প্রতিটি বিভাগের লেখার নিয়ম আলাদা।

বিভাগকী থাকেউদাহরণ
Baseelement-এর মৌলিক চেহারা, কোনো class নয়body, a, h1
Layoutপাতার বড় ভাগ — header, sidebar, grid.l-sidebar, .l-grid
Moduleবারবার ব্যবহারযোগ্য অংশ (আজকের ভাষায় component).card, .search-form
Stateঅবস্থা — খোলা, বন্ধ, সক্রিয়, ভুল.is-open, .is-hidden, .is-active
Themeরং-থিম বদলালে যা বদলায়ঈদের থিম, dark mode

SMACSS-এর সবচেয়ে টেকসই উপহার হলো state class-এর is- উপসর্গ। .is-open দেখলেই বোঝা যায়, এই class JavaScript বা ব্যবহারকারীর কাজে বসে-ওঠে। আজকের অ্যাক্সেসিবল কোডে আরও ভালো পথ প্রায়ই আছে: অবস্থা যদি ARIA-তেও প্রকাশ করতে হয়, তাহলে সরাসরি সেটিকেই selector বানান — [aria-expanded="true"], [aria-current="page"], অথবা :checked, :user-invalid, [open]. তাহলে চেহারা আর screen reader-এর তথ্য কখনো আলাদা হয়ে যায় না (পর্ব ১২ আর ৩৪)।

৫ · ITCSS — উল্টো ত্রিভুজ, আর আজকের @layer

২০১৪-১৫ সালের দিকে Harry Roberts একটি মডেল প্রচার করেন, নাম ITCSS — Inverted Triangle CSS. ধারণাটি একটি ছবিতেই ধরা যায়: একটি উল্টো ত্রিভুজ, ওপরে চওড়া, নিচে সরু। ওপরের নিয়ম পাতার অনেক element-কে ছোঁয় কিন্তু খুব দুর্বল; নিচের নিয়ম অল্প কয়েকটি element-কে ছোঁয় কিন্তু খুব স্পষ্ট ও শক্তিশালী। CSS ফাইল সাজানো হয় ঠিক এই ক্রমে — ওপর থেকে নিচে।

  1. Settings--brandমান, token
  2. Tools@mixinসহায়ক
  3. Generic*reset
  4. Elementsh1, aelement
  5. Objects.o-stacklayout
  6. Components.c-cardনকশা
  7. Utilities.u-hiddenশেষ কথা
Settingsকোনো element ধরে না — শুধু মান
:root {
  --color-brand: #0a7d6e;
  --space-m: 1rem;
}   /* Sass-এর যুগে এখানে $variable থাকত */
Toolspreprocessor-এর mixin আর function
// শুধু Sass-জাতীয় টুলে — নিজে কোনো CSS তৈরি করে না
@mixin focus-ring { outline: 3px solid var(--color-brand); }
Genericসবচেয়ে চওড়া — প্রতিটি element
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
Elementsclass ছাড়া HTML element
body { font-family: "Hind Siliguri", system-ui, sans-serif; line-height: 1.6; }
a    { color: var(--color-brand); }
Objectsচেহারাহীন কাঠামো — OOCSS-এর উত্তরসূরি
.o-stack > * + * { margin-block-start: var(--space-m); }
.o-cluster      { display: flex; flex-wrap: wrap; gap: var(--space-m); }
Componentsনির্দিষ্ট UI অংশ — BEM এখানে
.c-card        { padding: 1rem; border-radius: 12px; }
.c-card__title { font-size: 1.25rem; }
Utilitiesসবচেয়ে সরু, সবচেয়ে শক্ত
.u-visually-hidden { position: absolute; inline-size: 1px; block-size: 1px;
                     overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.u-text-center     { text-align: center !important; }  /* @layer-এর আগের যুগের কৌশল */
  1. Settings — শুধু মান। রং, ফাঁকের মাপ, ফন্ট — নকশার সিদ্ধান্তগুলো এক জায়গায়। আজকের CSS-এ এগুলো custom property, অর্থাৎ design token (পর্ব ৩৩)। নিজে কোনো element-এর চেহারা বদলায় না।
  2. Tools — সহায়ক। Sass-এর mixin বা function. নিজে কোনো CSS লেখে না, শুধু নিচের স্তরকে সাহায্য করে। সাধারণ CSS-এ লিখলে এই স্তর প্রায়ই খালি থাকে (Sass পর্ব ৫০-এ)।
  3. Generic — সবচেয়ে চওড়া নিয়ম। reset আর normalize: * দিয়ে পাতার প্রতিটি element. ত্রিভুজের সবচেয়ে চওড়া অংশ — সবাইকে ছোঁয়, কিন্তু যে কেউ বদলাতে পারে।
  4. Elements — HTML-এর মৌলিক চেহারা। class ছাড়া h1, a, table. SMACSS-এর “base”-এর মতো। এখান থেকে প্রতিটি পাতা একটি ভদ্র ডিফল্ট চেহারা পায়।
  5. Objects — চেহারাহীন কাঠামো। o- উপসর্গের class: stack, cluster, grid, media object. শুধু সাজানো — রং নেই, বর্ডার নেই। এটি OOCSS-এর “structure”-এর সরাসরি উত্তরসূরি।
  6. Components — আসল নকশা। c- উপসর্গ, BEM নাম: card, navbar, modal. প্রজেক্টের বেশিরভাগ CSS এখানে থাকে। ত্রিভুজ এখানে সরু — প্রতিটি নিয়ম পাতার নির্দিষ্ট কয়েকটি element-কে ছোঁয়।
  7. Utilities — শেষ কথা। u- উপসর্গ, একটি কাজ, সবচেয়ে শক্তিশালী — কারণ এদের কাজই অন্য সবকিছুকে নির্দিষ্ট জায়গায় বদলে দেওয়া। @layer-এর আগে এই শক্তি আনতে অনেকে utility-তে !important দিতেন। এখন তার দরকার নেই — পরের editor-এ দেখুন।

ITCSS-এর সমস্যা ছিল একটাই: ক্রম রক্ষা হতো শুধু মানুষের শৃঙ্খলায় — ফাইল ঠিক ক্রমে @import করা, আর কারও selector যেন নিচের স্তরের চেয়ে ভারী না হয়। পর্ব ৩৫-এ দেখা @layer এই ক্রমকে ব্রাউজারের নিয়মে পরিণত করে: পরের layer জেতে, selector যত ভারী হোক। @layer এখন Baseline Widely available, অর্থাৎ সব আধুনিক ব্রাউজারে নিশ্চিন্তে চলে।

@layer settings, generic, elements, objects, components, utilities;

@layer settings {
  :root { --brand: teal; --space: 12px; }
}
@layer generic {
  *, *::before, *::after { box-sizing: border-box; }
}
@layer elements {
  button { font: inherit; padding: 8px 16px; }
}
@layer objects {
  .cluster { display: flex; flex-wrap: wrap; gap: var(--space); }
}
@layer components {
  .nav .btn { background: var(--brand); color: white;
              border: 0; border-radius: 8px; }  /* (0,2,0) */
}
@layer utilities {
  .bg-danger { background: crimson; }            /* (0,1,0) */
}

তিনটি বোতাম teal, কিন্তু “লগআউট” লাল — যদিও .bg-danger-এর ওজন (0,1,0), আর component-এর .nav .btn ভারী (0,2,0). জিতেছে কারণ utilities পরের layer. কোনো !important লাগেনি। চেষ্টা করুন: প্রথম লাইনে utilities-কে components-এর আগে সরিয়ে দিন — লাল রং হারিয়ে যাবে। তারপর ক্রম আগের মতো করে --space-কে 24px করুন — একটি token বদলে পুরো cluster-এর ফাঁক বদলাল।

আরও গভীরে: ITCSS-এর ত্রিভুজ আসলে তিনটি মাপ একসাথে সাজায় — reach (কত element ছোঁয়, ওপর থেকে নিচে কমে), specificity (বাড়ে), আর explicitness (কত নির্দিষ্ট উদ্দেশ্যে লেখা, বাড়ে)। cascade-এর নিয়মে পরে লেখা নিয়ম সমান ওজনে জেতে, তাই ফাইলের ক্রম আর specificity একই দিকে বাড়লে cascade আপনার পক্ষে কাজ করে, বিপক্ষে নয়। @layer এই নকশাকে আরও মজবুত করেছে: Cascade 5 স্পেসিফিকেশনে layer-এর ধাপ specificity-র আগে বিচার হয়, তাই এখন ভুল করে একটি ভারী selector নিচের স্তরে চলে গেলেও ক্রম ভাঙে না। Harry Roberts নিজে মূল মডেলে শেষ স্তরকে “Trumps” নাম দিয়েছিলেন, পরে তা “Utilities” নামেই বেশি প্রচলিত হয়।

৬ · Utility-first বনাম semantic class — সৎ তুলনা

এ পর্যন্ত সব পদ্ধতির ভিত্তি এক: HTML-এ অর্থবহ নাম (.card), চেহারার বিস্তারিত CSS ফাইলে। ২০১০-এর দশকে আরেকটি ধারা জোর পায়, যা প্রায় উল্টো দিকে হাঁটে — utility-first বা atomic CSS. এখানে প্রতিটি class একটি ছোট কাজ করে (.flex, .p-4, .rounded-lg), আর নকশা তৈরি হয় সরাসরি HTML-এ class জুড়ে জুড়ে। ২০১৩ সালে Yahoo-র Thierry Koblentz-এর লেখা “Challenging CSS Best Practices” এই চিন্তাকে আলোচনায় আনে; ২০১৭ সালে Adam Wathan-এর Tailwind CSS একে মূলধারায় নিয়ে আসে। নিচে Tailwind-এর ধাঁচে নিজেদের ছোট্ট একটি সংগ্রহ বানিয়ে দেখুন:

<style>
  /* ছোট্ট একটি utility সংগ্রহ — প্রতিটি class একটি কাজ */
  .flex { display: flex; }        .items-center { align-items: center; }
  .gap-3 { gap: 12px; }           .p-4 { padding: 16px; }
  .border { border: 1px solid #ddd; }  .rounded-lg { border-radius: 12px; }
  .m-0 { margin: 0; }             .font-bold { font-weight: 700; }
  .text-sm { font-size: 14px; }   .text-gray { color: #555; }
  .bg-teal { background: teal; }  .text-white { color: white; }
  .max-w-xs { max-width: 340px; }
</style>

<article class="flex items-center gap-3 p-4 border rounded-lg max-w-xs">
  <div class="p-4 rounded-lg bg-teal text-white font-bold">চা</div>
  <div>
    <h3 class="m-0">ঢাকা টি স্টল</h3>
    <p class="m-0 text-sm text-gray">খোলা: সকাল ৭টা – রাত ১১টা</p>
  </div>
</article>

একটি সুন্দর তালিকা-কার্ড তৈরি হলো, অথচ কার্ডের জন্য আলাদা একটি লাইন CSS-ও লেখা হয়নি — সব নকশা HTML-এর class-এ। চেষ্টা করুন: <style>-এ নতুন utility যোগ করুন .gap-6 { gap: 24px; }, তারপর article-এ gap-3-এর জায়গায় gap-6 লিখুন। এবার একই কার্ড দশটি পাতায় আছে ধরে নিয়ে ভাবুন: কোণ আরও গোল করতে হলে কয়টি জায়গায় বদলাতে হবে? (উত্তর: যদি কার্ডটি একটি component বা template হিসেবে একবার লেখা থাকে, একটিই। না থাকলে দশটি — utility-first কাজ করে component-ভিত্তিক কোডে।)

দিকSemantic class (.card)Utility-first (.p-4 .rounded-lg)
নাম ভাবাপ্রতিটি অংশের জন্য নাম ভাবতে হয় — সময় লাগেনাম ভাবার দরকার প্রায় নেই
CSS ফাইলের আকারনতুন feature-এর সাথে বাড়তেই থাকেutility-র সংখ্যা সীমিত, তাই একসময় প্রায় থেমে যায়
মুছে ফেলাকোনো class আর লাগে কি না বোঝা কঠিনHTML মুছলেই নকশাও গেল; অব্যবহৃত utility টুল নিজেই বাদ দেয়
HTML পড়াপরিষ্কার, অর্থ বোঝা যায়লম্বা class-এর সারি; প্রথমে ভিড় লাগে
নকশার ধারাবাহিকতাযে কেউ যেকোনো মাপ লিখতে পারেননির্দিষ্ট মাপের তালিকা (token) থেকে বাছতে হয় — নকশা আপনাআপনি সামঞ্জস্যপূর্ণ
একই নকশা বহু জায়গায়class একবার বদলালেই সব জায়গায়component বা template না থাকলে প্রতিটি কপি আলাদা বদলাতে হয়
শেখাCSS জানলেই চলেCSS-এর ওপর আরেকটি শব্দভান্ডার; তবু CSS না বুঝলে কাজ আটকায়

সৎ উত্তর হলো — কোনোটিই সব জায়গায় সেরা নয়। React, Vue বা Svelte-এর মতো component-ভিত্তিক প্রজেক্টে, যেখানে একটি কার্ড একবারই লেখা হয়, utility-first খুব দ্রুত আর টেকসই। বহু-পাতার সাধারণ সাইট, CMS-এর লেখা বা যেখানে HTML বারবার হাতে কপি হয়, সেখানে semantic class প্রায়ই ভালো। বাস্তবে বেশিরভাগ দল মেশায়: component-এর জন্য অর্থবহ class, ছোটখাটো ভিন্নতার জন্য অল্প কিছু utility. Tailwind-এর খুঁটিনাটি, build-এর ধাপ আর Bootstrap-এর সাথে তুলনা পর্ব ৫১-এ।

CUBE CSS — মাঝামাঝি একটি পথ

২০২০ সালে Andy Bell এই দুই ধারা মিলিয়ে প্রস্তাব করেন CUBE CSS — Composition, Utility, Block, Exception. ক্রমটিই দর্শন: আগে ব্রাউজারের স্বাভাবিক cascade আর inheritance-কে কাজ করতে দিন; তারপর Composition — চেহারাহীন layout-এর class (.flow, .cluster, .sidebar); Utility — token-ভিত্তিক ছোট class; Block — শুধু যা বাকি থাকে, তার জন্য component; আর Exception — অবস্থা বা ভিন্নতা, যা সাধারণত data- attribute-এ লেখা হয়:

<article class="card flow" data-state="reversed">  <!-- composition + block + exception -->
  <h3 class="card__title text-step-2">…</h3>      <!-- block + utility -->
</article>

<style>
  .flow > * + * { margin-block-start: var(--flow-space, 1em); }
  .card[data-state="reversed"] { background: var(--color-dark); color: var(--color-light); }
</style>

৭ · নামের বদলে scope — CSS Modules, framework আর @scope

BEM সংঘাত এড়ায় নামের শৃঙ্খলা দিয়ে — কিন্তু সবাইকে নিয়মটা মানতে হয়। আরেকটি পথ হলো যন্ত্রকেই বলা: “এই CSS শুধু এই component-এর ভেতরে খাটবে।” এই কাজ তিন জায়গায় হতে পারে — build টুলে, framework-এ, আর এখন ব্রাউজারেই।

CSS Modules (২০১৫ সাল থেকে প্রচলিত) একটি build-এর কৌশল, ব্রাউজারের feature নয়। আপনি সাধারণ নাম লেখেন, টুল প্রতিটি নামকে ফাইল অনুযায়ী অনন্য করে দেয়:

/* Card.module.css — আপনি যা লেখেন */
.title { font-size: 20px; }

/* ব্রাউজার যা পায় (নামটি টুল বানায়) */
.Card_title__x7Gq2 { font-size: 20px; }

JavaScript-এ import styles from "./Card.module.css" লিখে styles.title ব্যবহার করা হয়। দুজন ডেভেলপার দুটি ফাইলে .title লিখলেও আর সংঘাত হয় না। Next.js, Vite-এর মতো টুলে এটি সাধারণত কোনো বাড়তি সেটআপ ছাড়াই চলে।

Scoped style: Vue-র <style scoped>, Svelte-এর component style (ডিফল্টভাবেই scoped), Angular-এর ডিফল্ট “emulated” encapsulation — সবাই প্রায় একই কৌশল নেয়: component-এর প্রতিটি element-এ একটি অনন্য attribute বা class বসায়, আর CSS selector-এ সেটি জুড়ে দেয়।

/* Vue-তে আপনি লেখেন */
.title { color: teal; }

/* তৈরি হয় প্রায় এরকম */
.title[data-v-7ba5bd90] { color: teal; }

আর Web Components-এর Shadow DOM সত্যিকারের ব্রাউজার-স্তরের দেয়াল তোলে — ভেতরের CSS বাইরে যায় না, বাইরের CSS ভেতরে ঢোকে না (পর্ব ৫২)। JavaScript framework-এর দিকটি দেখতে পারেন JavaScript কোর্সের framework পর্বে।

পর্ব ৩৭-এ দেখা @scope একই সুবিধা দেয় কোনো টুল ছাড়াই — আর “ডোনাট” scope দিয়ে ভেতরের আরেকটি component-কে বাদও রাখা যায়:

@scope (.card) to (.card__slot) {
  .title { font-size: 20px; }   /* শুধু কার্ডের ভেতরের .title */
  img    { border-radius: 8px; }
}

@scope সদ্য Baseline Newly available — সব প্রধান ব্রাউজারে এসেছে, কিন্তু পুরোনো ব্রাউজারে পুরো ব্লক বাদ পড়ে। তাই আপাতত শুধু এমন নিয়ম scope-এ রাখুন, যা না খাটলেও পাতা ব্যবহারযোগ্য থাকে, অথবা BEM-এর মতো নামের শৃঙ্খলা পাশাপাশি রাখুন।

খেয়াল করুন, scope নামের সংঘাত দূর করে, কিন্তু অন্য তিনটি সমস্যা (specificity-র যুদ্ধ, dead code, ফাইল সাজানো) নিজে থেকে সমাধান করে না। একটি সুবিধা অবশ্য বড়: CSS যখন component-এর ফাইলের পাশেই থাকে (co-location), component মুছলে তার CSS-ও মুছে যায় — “মুছতে ভয়” অনেকটা কমে।

৮ · ফাইল-ফোল্ডার, token, নথি আর Stylelint

পদ্ধতি বাছাই করলেন; এবার প্রজেক্টের কাঠামো। একটি সাধারণ, ITCSS আর @layer-এর সাথে মেলানো ফোল্ডার-সাজানো:

css/
├── main.css            ← শুধু @layer ক্রম আর @import
├── tokens/
│   ├── colors.css      ← --color-brand, --color-danger …
│   └── spacing.css     ← --space-s, --space-m …
├── base/
│   ├── reset.css
│   └── typography.css  ← body, h1–h6, a
├── layout/
│   ├── stack.css       ← .stack, .cluster, .sidebar-layout
│   └── site-header.css
├── components/
│   ├── card.css        ← .card, .card__title, .card--sale
│   ├── button.css
│   └── product-grid.css
└── utilities/
    └── utilities.css   ← .visually-hidden, .text-center
/* main.css */
@layer tokens, base, layout, components, utilities;

@import url("tokens/colors.css")      layer(tokens);
@import url("tokens/spacing.css")     layer(tokens);
@import url("base/reset.css")         layer(base);
@import url("base/typography.css")    layer(base);
@import url("layout/stack.css")       layer(layout);
@import url("components/card.css")    layer(components);
@import url("utilities/utilities.css") layer(utilities);

নিয়ম: এক component, এক ফাইল, ফাইলের নাম block-এর নামে। card.css-এ শুধু .card-এ শুরু হওয়া class থাকবে। তাহলে কোনো class কোথায় আছে, খুঁজতেই হয় না। একটি সতর্কতা: অনেকগুলো @import ব্রাউজারে একটির পর একটি ফাইল নামায়, তাই প্রকাশের আগে সাধারণত একটি build টুল সব ফাইল জুড়ে একটি ফাইল বানায় — এর বিস্তারিত পর্ব ৪৯ (পারফরম্যান্স) আর ৫০-এ (PostCSS). সব ফাইলের ওপরে থাকে token: পর্ব ৩৩-এ দেখেছেন, রূপ-ভিত্তিক token (--teal-600) থেকে কাজ-ভিত্তিক semantic token (--color-brand) বানালে থিম বদলানো একটি ফাইলের কাজ।

নথি: style guide আর pattern library

নামের নিয়ম মাথায় থাকলে চলবে না, লিখে রাখতে হবে। একটি ছোট style guide — এক পাতার README — বলবে: কোন পদ্ধতি (BEM), কোন উপসর্গ (is-, u-), কোন layer, id-তে স্টাইল নিষেধ, ইত্যাদি। বড় প্রজেক্টে এর সাথে থাকে একটি pattern library: প্রতিটি component-এর সব রূপ (সাধারণ, ছাড়, অক্ষম, লোড হচ্ছে) এক জায়গায় দেখানো জীবন্ত পাতা, পাশে ব্যবহারবিধি। সবচেয়ে প্রচলিত টুল Storybook, যেখানে প্রতিটি component আলাদাভাবে খুলে দেখা ও পরীক্ষা করা যায়। ছোট প্রজেক্টে একটি সাধারণ components.html পাতা — সব component পাশাপাশি — একই কাজ করে।

Stylelint আর Prettier — নিয়ম মানানোর পাহারাদার

মানুষ ভুলে যায়, টুল ভোলে না। Stylelint একটি CSS linter — কোড পড়ে নিয়মভঙ্গ খুঁজে বের করে: id-তে স্টাইল, অতিরিক্ত ভারী selector, !important, BEM-বিরোধী নাম, অচেনা property, দুবার লেখা নিয়ম। আর Prettier একটি formatter — ফাঁকা জায়গা, ইনডেন্ট, লাইন ভাঙা এক নিয়মে বসায়। দুটির কাজ আলাদা: Stylelint বলে “এটা ভুল”, Prettier বলে “এভাবে সাজাও”। Stylelint নিজেই এখন সাজানোর (stylistic) নিয়মগুলো বাদ দিয়ে সেই কাজ Prettier-এর মতো টুলের হাতে ছেড়ে দিয়েছে।

npm install --save-dev stylelint stylelint-config-standard prettier
npx stylelint "css/**/*.css"
npx prettier --write "css/**/*.css"
{
  "extends": ["stylelint-config-standard"],
  "rules": {
    "selector-max-id": 0,
    "selector-max-specificity": "0,3,0",
    "declaration-no-important": true,
    "selector-class-pattern": [
      "^[a-z][a-z0-9]*(-[a-z0-9]+)*(__[a-z0-9]+(-[a-z0-9]+)*)?(--[a-z0-9]+(-[a-z0-9]+)*)?$",
      { "message": "class-এর নাম BEM ধাঁচে, ছোট হাতের ইংরেজিতে লিখুন" }
    ]
  }
}

এই .stylelintrc.json ফাইলটি ঠিক এই পর্বের নীতিগুলোকে নিয়মে বাঁধে: কোনো id নয়, তিন class-এর বেশি ভারী selector নয়, !important নয়, আর class-এর নাম BEM-এর ধাঁচে ASCII অক্ষরে — .দাম বা .cardTitle লিখলেই সতর্কবার্তা। (stylelint-config-standard-এর নিজের নাম-নিয়ম শুধু kebab-case মানে, BEM-এর __ মানে না — তাই আমরা নিয়মটি নিজের মতো বদলে দিয়েছি।) VS Code-এর Stylelint extension লেখার সময়ই লাল দাগ দেখায়, আর অনেক দল Git-এ কোড পাঠানোর আগে এগুলো স্বয়ংক্রিয়ভাবে চালায় (পর্ব ৫৩)। npm আর এই ধরনের টুল নিয়ে আরও জানতে দেখুন JavaScript কোর্সের npm পর্ব।

৯ · একটি এলোমেলো stylesheet ধাপে ধাপে গোছানো

বাস্তবে আপনি প্রায়ই শূন্য থেকে শুরু করবেন না — হাতে আসবে কারও পুরোনো, জট পাকানো CSS. সবকিছু একদিনে নতুন করে লেখার লোভ সামলান: বড় rewrite প্রায়ই নতুন বাগ আনে আর মাঝপথে থেমে যায়। নিরাপদ পথ হলো refactoring — চেহারা একটুও না বদলে, ছোট ছোট ধাপে ভেতরের কোড গোছানো। আগে সমস্যাটি দেখুন — একটি সাইডবারের CSS, যেখানে “active” লিংকটি রঙিন করার চেষ্টা চলছে:

#sidebar .widget h3 { color: #0a7d6e; font-size: 20px; margin: 0 0 8px; }
#sidebar .widget ul { list-style: none; padding: 0; margin: 0; max-width: 240px; }
#sidebar .widget ul li a {
  display: block; padding: 6px 10px;
  color: #333; text-decoration: none;
}
#sidebar .widget ul li a:hover { color: #0a7d6e; }

/* নতুন কাজ: active লিংক রঙিন — রং কাজ করে না! */
.active { background: #0a7d6e; color: white; }

/* তাই কেউ পরে লিখল… */
.active { color: white !important; }

.red-text { color: red; }   /* কোথায় লাগে? কেউ জানে না */

“জামদানি” সবুজ পটভূমিতে সাদা লেখায় এসেছে — কিন্তু কীভাবে, খেয়াল করুন। পটভূমি এসেছে প্রথম .active থেকে, কারণ ভারী selector-টি background লেখেনি। আর লেখার রং .active-এর (0,1,0) দিয়ে #sidebar .widget ul li a-এর (1,1,3)-কে হারাতে পারেনি, তাই !important লাগল। চেষ্টা করুন: দ্বিতীয় .active নিয়মটি মুছে দিন — “জামদানি” আবার কালো হয়ে সবুজের ওপর প্রায় পড়াই যাবে না। এবার ভাবুন, পরের ডেভেলপার যদি active লিংকে hover-এ অন্য রং চান, তাঁকে কী লিখতে হবে? (উত্তর: আরেকটি !important — যুদ্ধ চলতেই থাকে।)

এই কোডে পর্ব শুরুর চারটি সমস্যাই আছে: id আর লম্বা শিকলের ভারী specificity, !important-এর যুদ্ধ, অজানা .red-text (dead code কি না কেউ জানে না), আর HTML-এর গঠনের ওপর নির্ভরতা। এটিকে নিরাপদে গোছানোর ধাপগুলো দেখুন:

ধাপ ১আগে ছবি তুলে রাখুন, তারপর জরিপ
/* জরিপের ফল */
id selector ............ 5
!important ............. 1
একই রং (#0a7d6e) ........ 3 বার
অজানা class ............ .red-text
ধাপ ২বারবার লেখা মানগুলো token-এ
:root {
  --color-brand: #0a7d6e;
  --color-text: #333;
}
#sidebar .widget h3 { color: var(--color-brand); … }
ধাপ ৩HTML-এ নতুন class যোগ — পুরোনোটা এখনো থাকে
<div id="sidebar" class="sidebar">
  <section class="widget">
    <h3 class="widget__title">
    <ul class="widget__list">
      <a class="widget__link widget__link--active" …>
ধাপ ৪এক এক করে selector বদল, প্রতিবার মিলিয়ে দেখা
- #sidebar .widget ul li a { … }
+ .widget__link { … }            (1,1,3) → (0,1,0)
- .active { color: white !important; }
+ .widget__link--active { … }     !important আর লাগে না
ধাপ ৫dead code খুঁজে মোছা — প্রমাণ নিয়ে
$ grep -rn "red-text" --include=*.html --include=*.js .
(কোনো ফল নেই)
DevTools → Coverage: .red-text ব্যবহার হয়নি
→ মুছে ফেলা হলো
ধাপ ৬layer-এ সাজানো আর linter-এ তালা
@layer tokens, components;
/* .stylelintrc.json */
"selector-max-id": 0,
"declaration-no-important": true   ← পুরোনো অভ্যাস আর ফিরতে পারবে না
  1. ছবি আর জরিপ। কিছু বদলানোর আগে প্রতিটি গুরুত্বপূর্ণ পাতার screenshot নিন — পরে মিলিয়ে দেখবেন চেহারা বদলায়নি (বড় দলে এ কাজ স্বয়ংক্রিয় “visual regression test” টুলে হয়)। তারপর গুনে দেখুন: কয়টি id, কয়টি !important, কোন মান বারবার লেখা। Stylelint একবার চালালেই এই তালিকার বড় অংশ পাওয়া যায়।
  2. Token বের করুন। একই রং তিন জায়গায় লেখা — সেটি একটি custom property-তে নিন। এই ধাপে চেহারা একটুও বদলায় না, কিন্তু পরের ধাপগুলো সহজ হয়, আর রং বদলানো একটি লাইনের কাজ হয়ে যায়।
  3. নতুন class যোগ করুন — পুরোনোটা না মুছে। HTML-এ BEM নাম বসান, কিন্তু id="sidebar" এখনই সরাবেন না — হয়তো JavaScript বা কোনো লিংক (#sidebar) তাকে ব্যবহার করে। পুরোনো আর নতুন class কিছুদিন পাশাপাশি থাকতে পারে।
  4. এক এক করে selector বদলান। একটি নিয়ম বদলান, পাতা দেখুন, screenshot-এর সাথে মেলান, তারপর পরেরটি। specificity (1,1,3) থেকে (0,1,0)-এ নামলে !important-এর আর দরকার থাকে না — তখন সেটিও মুছুন।
  5. Dead code — প্রমাণ নিয়ে মুছুন। পুরো প্রজেক্টে class-এর নাম search করুন (HTML, JavaScript, template — সব ফাইলে), আর ব্রাউজারের DevTools-এর Coverage প্যানেলে দেখুন কোন CSS নিয়ম পাতায় ব্যবহার হয়নি (পর্ব ৪৭)। সাবধান: JavaScript-এ "red-" + type লিখে নাম জোড়া হলে search-এ ধরা পড়ে না — তাই কোডে class-এর পুরো নাম লেখার অভ্যাস রাখুন।
  6. Layer আর linter. নতুন কোড @layer-এ সাজান, আর Stylelint-এর নিয়মে তালা দিন — id, !important আর ভুল নামের ধরন যেন আর কখনো ঢুকতে না পারে। গোছানো কোড গোছানো রাখার কাজটা এখন টুলের।

এবার ফলাফল — একই চেহারা, কিন্তু পরিষ্কার ভেতর। HTML-এ BEM নাম, CSS-এ token, layer আর native nesting:

@layer tokens, components;

@layer tokens {
  :root { --color-brand: #0a7d6e; --color-text: #333; }
}

@layer components {
  .widget__title { color: var(--color-brand); font-size: 20px; margin: 0 0 8px; }
  .widget__list  { list-style: none; padding: 0; margin: 0; max-width: 240px; }
  .widget__link {
    display: block; padding: 6px 10px;
    color: var(--color-text); text-decoration: none;
    &:hover { color: var(--color-brand); }
  }
  .widget__link--active,
  .widget__link--active:hover {
    background: var(--color-brand);
    color: white;
  }
}

চেহারা আগের মতোই — কিন্তু এখন কোনো id নেই, কোনো !important নেই, প্রায় সব selector এক class. খেয়াল করুন শেষ নিয়মে :hover-ও জুড়ে দিয়েছি: না দিলে active লিংকে মাউস রাখলে .widget__link:hover (0,2,0) জিতে লেখাকে সবুজ করত — সবুজের ওপর সবুজ, অদৃশ্য! চেষ্টা করুন: .widget__link--active:hover লাইনটি মুছে “জামদানি”-র ওপর মাউস রাখুন — সমস্যাটি নিজের চোখে দেখবেন। তারপর ফিরিয়ে এনে --color-brand-কে #7b2d8e করুন — শিরোনাম, hover আর active, তিন জায়গার রং একসাথে বদলাবে। (সতর্কতা: native nesting-এ &--active লেখা যায় না — ওটা Sass-এর কৌশল, পর্ব ৫০।)

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

  • BEM-এর পূর্ণরূপ আর একটি উদাহরণ? — Block, Element, Modifier; যেমন .card, .card__title, .card--sale.
  • BEM-এ nested selector কেন এড়ানো হয়? — specificity সমান (0,1,0) রাখতে, আর HTML-এর গঠন বদলালে যেন স্টাইল না ভাঙে।
  • ITCSS-এর স্তরগুলোর ক্রম? — Settings, Tools, Generic, Elements, Objects, Components, Utilities; ওপর থেকে নিচে reach কমে, specificity বাড়ে।
  • Utility-first CSS-এর একটি সুবিধা আর একটি অসুবিধা? — নাম ভাবতে হয় না আর CSS-এর আকার সীমিত থাকে; কিন্তু HTML-এ লম্বা class-এর সারি, আর component ছাড়া একই নকশা বহু জায়গায় বদলানো কঠিন।
  • CSS Modules কীভাবে নাম-সংঘাত ঠেকায়? — build-এর সময় প্রতিটি class-এর নাম ফাইল অনুযায়ী অনন্য (hash যুক্ত) করে দেয়।

১০ · AI-এর যুগে — গোছানো CSS-এর জন্য AI-কে নির্দেশ

AI খুব দ্রুত CSS লেখে, কিন্তু প্রতিটি উত্তর আলাদাভাবে — আপনার প্রজেক্টের নামের নিয়ম, token বা layer সে জানে না, যদি না আপনি বলে দেন। ফলে দশটি প্রশ্নে দশ ধাঁচের CSS আসে: কোথাও .productCard, কোথাও .product-card, কোথাও #product; একই রং দশ জায়গায় হাতে লেখা। প্রতিটি উত্তর আলাদাভাবে “চলে”, কিন্তু একসাথে তারা ঠিক সেই জট পাকায়, যা নিয়ে এই পর্ব শুরু হয়েছিল। আর AI প্রায়ই নিজে থেকে div-এর স্তূপ বানায়, অবস্থা বোঝাতে শুধু class দেয় কিন্তু ARIA বা আসল element-এর কথা ভোলে।

AI যা প্রায়ই দেয়সমস্যাযা চাইবেন
প্রতিবার আলাদা ধাঁচের class-এর নামপ্রজেক্টে নামের নিয়ম ভাঙেপ্রম্পটে নিয়ম বলে দিন (BEM, উপসর্গ)
হাতে লেখা hex রং আর px মাপtoken উপেক্ষিত, থিম ভাঙেনিজের token-এর তালিকা দিয়ে দিন
“কাজ করছে না” বললে !importantspecificity-র যুদ্ধ শুরুকারণ ব্যাখ্যা চান, !important নিষেধ করুন
.card .body .title span-এর মতো লম্বা শিকলভারী, গঠন-নির্ভরএক-class-এর selector
সাধারণ .css-এ &__titleএটি Sass; native nesting-এ চলে নাপুরো BEM নাম লিখতে বলুন
বাংলা বা রোমান-বাংলা class-এর নামটুল আর দলের জন্য ঝামেলাইংরেজি ASCII নাম, লেখা বাংলায়

AI-কে কাজ দেওয়ার আগে প্রজেক্টের “নিয়মের খাতা” দিয়ে দিন। একবার লিখে রাখলে প্রতিটি প্রশ্নে কপি করে দিতে পারবেন:

আমার প্রজেক্টের CSS-এর নিয়ম: ১. class-এর নাম BEM ধাঁচে, ছোট হাতের ইংরেজিতে (.block__element--modifier). কোনো id selector দিয়ে স্টাইল দেবে না। ২. সব selector সর্বোচ্চ এক বা দুই class-এর; !important একেবারেই নয়। ৩. রং আর মাপ শুধু এই token থেকে: --color-brand, --color-text, --color-danger, --space-s, --space-m, --space-l. নতুন hex রং লিখবে না। ৪. সব নিয়ম @layer components-এর ভেতরে, শুধু সাধারণ .css-এ চলে এমন আধুনিক CSS (Sass নয়)। ৫. অবস্থা (খোলা/বন্ধ, বর্তমান পাতা) সম্ভব হলে aria-expanded বা aria-current দিয়ে স্টাইল করো। এই নিয়ম মেনে একটি পণ্যের তালিকার কার্ড (নাম, দাম, ছাড়ের দাম, "কার্টে যোগ করুন" বোতাম) এর HTML আর CSS লিখে দাও। শেষে একটি তালিকায় বলো কোন class কোন কাজের, আর কোনো নিয়ম ভাঙতে বাধ্য হলে কেন।

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

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

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Utility class কি “separation of concerns” ভেঙে দেয়? HTML-এ p-4 rounded-lg লেখা কি পুরোনো inline style-এরই নতুন রূপ নয়?

প্রশ্নটি ওয়েব-ডেভেলপারদের মধ্যে বহু বছরের বিতর্ক। ২০০৩ সালে Dave Shea-র CSS Zen Garden দেখিয়েছিল, একই HTML-এ শুধু CSS ফাইল বদলে পুরো সাইটের চেহারা আমূল পাল্টে ফেলা যায়। সেই থেকে প্রচলিত শিক্ষা: HTML-এ অর্থ (class="product-card"), CSS-এ চেহারা — separation of concerns.

২০১৭ সালে Adam Wathan একটি প্রবন্ধে যুক্তি দেন, এই বিভাজন আসলে নির্ভরতার দিক-এর প্রশ্ন। semantic CSS-এ CSS নির্ভর করে HTML-এর ওপর — .product-card-এর CSS জানে HTML-এ কী কী অংশ আছে, তাই HTML নতুন করে সাজাতে পারেন কিন্তু CSS-কে নতুন জায়গায় পুনর্ব্যবহার করা কঠিন। utility CSS-এ উল্টো: HTML নির্ভর করে CSS-এর ওপর, CSS পুরোপুরি পুনর্ব্যবহারযোগ্য। দুটোই বৈধ নকশা — প্রশ্ন হলো আপনার প্রজেক্টে কোনটা বেশি বদলায়।

আর inline style-এর সাথে পার্থক্য? বেশ কয়েকটি বড় পার্থক্য আছে। Utility class নির্দিষ্ট একটি তালিকা (token) থেকে আসে — p-4 মানে সবসময় একই মাপ, আর inline style-এ যে কেউ padding: 17px লিখতে পারেন। inline style-এ media query, :hover বা :focus-visible লেখা যায় না; utility framework-এ md:flex বা hover:bg-teal-এর মতো রূপ থাকে। inline style-এর specificity সব class-কে হারায়; utility সাধারণ class-এর মতো cascade মানে। আর একই class হাজার জায়গায় ব্যবহার হলেও CSS ফাইলে একবারই লেখা থাকে।

তবু সমালোচনাটি পুরোপুরি ভিত্তিহীন নয়: HTML-এ চেহারার বিস্তারিত এসে পড়ে, আর component না থাকলে একই নকশা বহু জায়গায় কপি হয়। এই কারণেই utility-first সবচেয়ে ভালো কাজ করে component-ভিত্তিক কোডে, যেখানে প্রতিটি নকশা একবারই লেখা।

মূল উপলব্ধি: “Separation of concerns” কোনো চিরন্তন ফাইল-বিভাজন নয় — এটি পরিবর্তনকে এক জায়গায় আটকে রাখার নীতি। কোন জিনিস একসাথে বদলায়, তা দেখে ঠিক করুন কোনটা একসাথে রাখবেন।

প্র ০২CSS-এ শুরু থেকেই scope ছিল না কেন? আর ব্রাউজারে scope আনতে এত বছর লাগল কেন?

১৯৯০-এর দশকে CSS নকশা করা হয়েছিল ডকুমেন্টের জন্য — গবেষণাপত্র, খবর, ম্যানুয়াল। একটি ডকুমেন্টে “সব শিরোনাম নীল” — এই ধরনের পাতাজোড়া নিয়মই চাওয়া হতো। Global scope তখন দুর্বলতা নয়, সুবিধা ছিল: অল্প কয়েক লাইনে পুরো ডকুমেন্টের চেহারা ঠিক। ওয়েব যখন ডকুমেন্ট থেকে অ্যাপ্লিকেশনে বদলাল — শত শত component, ডজনখানেক দল — তখনই সেই সুবিধা বোঝা হয়ে দাঁড়াল।

ব্রাউজারে scope আনার প্রথম বড় চেষ্টা ছিল HTML-এর <style scoped> attribute — একটি <style> শুধু তার মা-element-এর ভেতরে খাটবে। HTML5-এর খসড়ায় এটি ছিল, Firefox কিছুদিন সমর্থনও করেছিল, কিন্তু অন্য ব্রাউজারগুলো আগ্রহ দেখায়নি। কারণগুলোর মধ্যে ছিল বাস্তবায়নের জটিলতা আর cascade-এর সাথে এর সম্পর্ক নিয়ে অস্পষ্টতা; শেষে স্পেসিফিকেশন থেকে এটি বাদ পড়ে।

এর মধ্যে ডেভেলপাররা নিজেরাই সমাধান বানালেন: BEM-এর মতো নামের শৃঙ্খলা, তারপর ২০১৫ সালের দিকে CSS Modules, Vue-র scoped style, CSS-in-JS — সবই ব্রাউজারের বাইরে, build টুলে। অন্যদিকে Web Components-এর Shadow DOM পূর্ণ দেয়াল দিল, কিন্তু সেটি এতই শক্ত যে সাইটের সাধারণ থিমও ভেতরে ঢোকানো কঠিন।

নতুন @scope-এর নকশায় এই সব অভিজ্ঞতার ছাপ আছে: দেয়াল নয়, একটি নরম সীমানা — বাইরের CSS এখনো ঢোকে, শুধু scope-এর নিয়মগুলো বাইরে বেরোয় না, আর “ডোনাট”-এর নিচের সীমা দিয়ে nested component-কে বাদ রাখা যায়। Chromium-এ ২০২৩ সালে আসার পর অন্য ব্রাউজারগুলো ধীরে ধীরে যোগ দিয়েছে।

মূল উপলব্ধি: একটি প্রযুক্তির “দুর্বলতা” প্রায়ই তার মূল উদ্দেশ্যের সুবিধা — উদ্দেশ্য বদলালে দুর্বলতা দেখা দেয়। আর ওয়েব প্ল্যাটফর্ম সাধারণত আগে টুলগুলোকে পরীক্ষা করতে দেয়, তারপর সবচেয়ে ভালো ধারণাটি স্থায়ী করে।

প্র ০৩বড় সাইটে CSS ফাইল বছরে বছরে বাড়তেই থাকে কেন? Atomic CSS কীভাবে এই বৃদ্ধি থামায়, আর এর দাম কী?

semantic CSS-এ প্রতিটি নতুন feature সাধারণত নতুন class আনে: নতুন ব্যানার মানে .eid-banner, তার শিরোনাম, তার বোতাম। অথচ এর ভেতরের declaration-গুলো — display: flex, padding: 16px, border-radius: 12px — আগেও শত শত বার লেখা হয়েছে। তাই CSS-এর আকার বাড়ে feature-এর সংখ্যার সাথে প্রায় সমানুপাতে। আর পুরোনো feature মুছলেও তার CSS মোছার সাহস কেউ করেন না (মুছতে ভয়), তাই বৃদ্ধি একমুখী।

Atomic CSS-এ প্রতিটি declaration একবারই লেখা হয়: .p-4 { padding: 16px }. নতুন feature এলেও সাধারণত নতুন CSS লাগে না, পুরোনো class-গুলোর নতুন জোড়া লাগে। একটি নকশা-ব্যবস্থায় মাপ, রং, ফাঁকের সম্ভাব্য মান সীমিত, তাই একসময় CSS-এর আকার প্রায় স্থির হয়ে যায়। ২০২০ সালে নতুন Facebook.com তৈরির সময় Meta-র প্রকৌশলীরা এমনই একটি atomic কৌশলের কথা লেখেন, আর জানান যে এতে হোমপেজের CSS নাটকীয়ভাবে কমেছে (তাদের হিসাবে প্রায় ৮০%). পরে তারা নিজেদের টুল StyleX ওপেন-সোর্স করে।

দাম কী? প্রথমত, HTML ভারী হয় — প্রতিটি element-এ অনেক class. যদিও একই নাম বারবার আসায় gzip বা brotli সংকোচনে এটি অনেকটা ছোট হয়ে যায়। দ্বিতীয়ত, HTML পড়তে কষ্ট হয়, আর DevTools-এ একটি element-এর নকশা বুঝতে দশটি নিয়ম দেখতে হয়। তৃতীয়ত, একটি build ধাপ লাগে, যা ব্যবহার না হওয়া utility ছেঁটে ফেলে — নইলে সব সম্ভাব্য utility মিলে ফাইল বিশাল। আর চতুর্থত, শেখার খরচ: CSS-এর ওপর আরেকটি শব্দভান্ডার।

লক্ষ করুন, Meta-র মতো জায়গায় হাজার হাজার component আর শত শত ডেভেলপার — এই মাপে সমস্যাটি গুরুতর। দশ পাতার একটি ব্যবসার সাইটে CSS-এর বৃদ্ধি হয়তো কখনো সমস্যাই হবে না।

মূল উপলব্ধি: architecture-এর সিদ্ধান্ত আসলে মাপের সিদ্ধান্ত। বড় কোম্পানির সমাধান নিজের ছোট প্রজেক্টে নকল করার আগে জিজ্ঞেস করুন — তাদের সমস্যাটি কি আমারও আছে?

প্র ০৪একটি CSS নিয়ম আর লাগে কি না, তা নিশ্চিতভাবে জানা এত কঠিন কেন? “মুছতে ভয়” কাটানোর বাস্তব উপায় কী?

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

  • অন্য কোনো পাতা, যা আপনি এখন খোলেননি;
  • JavaScript, যে চলার সময় class যোগ করে — কখনো নাম জোড়া দিয়ে ("is-" + state), ফলে search-এ পুরো নামটি পাওয়াই যায় না;
  • CMS বা ডেটাবেসে রাখা পুরোনো ব্লগ পোস্টের HTML;
  • ইমেইল টেমপ্লেট বা অন্য সাইট, যারা আপনার CSS ফাইল ব্যবহার করে।

DevTools-এর Coverage প্যানেল শুধু বলে এই মুহূর্তে এই পাতায় কোন নিয়ম লাগেনি — মেনু খোলা, ভুল ইনপুট দেওয়া বা অন্য পাতার অবস্থা সে দেখে না। PurgeCSS-এর মতো টুল পুরো প্রজেক্টের ফাইল পড়ে অব্যবহৃত class বাদ দেয়, কিন্তু চলার সময় জোড়া নামের জন্য তাদেরও “safelist” দিয়ে হাতে বলে দিতে হয়।

বাস্তব উপায়গুলো তাই মূলত নকশার: (১) co-location — component-এর CSS তার পাশেই, component মুছলে CSS-ও যায়; (২) এক component, এক ফাইল — card.css-এর কোনো class যদি HTML-এ আর না থাকে, পুরো ফাইলটাই মোছা যায়; (৩) পুরো নাম লেখা — JavaScript-এ class-এর নাম জোড়া না দিয়ে পুরোটা লিখুন, যাতে search কাজ করে; (৪) screenshot-ভিত্তিক পরীক্ষা, যাতে মোছার পর কোনো পাতা বদলালে সাথে সাথে ধরা পড়ে; (৫) Git — ভুল করে মুছলেও ফেরত আনা যায়, তাই ভয়টা অনেক কমে (পর্ব ৫৩)।

মূল উপলব্ধি: কোড মোছা যত সহজ, কোডবেস তত সুস্থ। ভালো architecture-এর একটি সহজ পরীক্ষা: “এই feature কাল বাদ দিতে হলে, তার CSS খুঁজে মুছতে কত সময় লাগবে?”

প্র ০৫“নাম রাখা কঠিন” — প্রোগ্রামাররা কেন এ কথা বলেন? BEM-এর অদ্ভুত __ আর --, আর c-, u-, js--এর মতো উপসর্গ কী সমস্যা সমাধান করে?

সফটওয়্যার জগতে একটি বহুল প্রচলিত রসিকতা আছে, যা Phil Karlton-এর নামে চলে: কম্পিউটার বিজ্ঞানে কঠিন কাজ মাত্র দুটি — cache invalidation আর নাম রাখা। নাম কঠিন কারণ একটি নাম একসাথে তিনটি কাজ করে: অর্থ বলে, অন্য নাম থেকে আলাদা করে, আর ভবিষ্যতেও সত্য থাকতে হয়। .left-column নামটি সেদিনই মিথ্যা হয়ে যায়, যেদিন মোবাইলে কলামটি নিচে চলে আসে।

BEM-এর চিহ্নগুলো দেখতে কুৎসিত, কিন্তু সেটাই তাদের শক্তি। সাধারণ hyphen শব্দ জোড়ায় (search-form), তাই block-element-modifier-এর সীমানা বোঝাতে এমন চিহ্ন দরকার ছিল যা স্বাভাবিক নামে আসে না। .search-form__input--large দেখামাত্র একটি parser — মানুষ বা যন্ত্র — বলতে পারে: block “search-form”, element “input”, modifier “large”. এই কারণেই Stylelint-এর মতো টুল নিয়মিত-অভিব্যক্তি দিয়ে BEM নাম যাচাই করতে পারে।

Harry Roberts আরেক ধাপ এগিয়ে namespace উপসর্গ প্রস্তাব করেন: o- (object, চেহারাহীন কাঠামো — বদলালে বহু জায়গায় প্রভাব), c- (component, নির্দিষ্ট — বদলানো তুলনামূলক নিরাপদ), u- (utility, কখনো বদলাবেন না, শুধু ব্যবহার), is-/has- (অবস্থা), আর js- (শুধু JavaScript-এর জন্য, এতে কোনো স্টাইল নেই)। শেষেরটি খুব কাজের: ডিজাইনার .c-modal-এর নাম বদলালেও JavaScript-এর .js-modal-open অক্ষত থাকে — স্টাইল আর আচরণের বাঁধন আলাদা। আজকাল অনেকে এর জন্য data- attribute ব্যবহার করেন, যা একই কাজ আরও পরিষ্কারভাবে করে।

বাংলা প্রজেক্টে এর সাথে যোগ হয় ভাষার প্রশ্ন। ইংরেজিতে সংক্ষিপ্ত, কাজ-ভিত্তিক নাম (price, discount, cart-button) দলের সবার কাছে, টুলের কাছে আর ভবিষ্যতের বিদেশি সহকর্মীর কাছে একই অর্থ বহন করে।

মূল উপলব্ধি: নামের নিয়ম আসলে দলের ভেতরের একটি ভাষা। নিয়মটি কতটা সুন্দর, তার চেয়ে অনেক বেশি গুরুত্বপূর্ণ — সবাই কি একই নিয়ম মানছে, আর টুল কি সেটা পাহারা দিচ্ছে?

অনুশীলন

বেশিরভাগই ভাবা আর দেখার কাজ — একটিতে কোড লিখবেন।

  1. BEM-এ রূপান্তর: একটি মোবাইল ওয়ালেটের পেমেন্ট সারাংশের HTML এরকম: <div class="pay"><h4>…</h4><p>পরিমাণ</p><button>নিশ্চিত করুন</button></div>, আর CSS-এ .pay h4, .pay p, .pay button. ব্যর্থ পেমেন্টের জন্য লাল রূপও লাগবে। BEM নাম দিয়ে HTML আর CSS নতুন করে লিখুন।

    Block payment-summary (পূর্ণ, বোঝা যায় এমন নাম; pay খুব সংক্ষিপ্ত), element __title, __amount, __button, আর ব্যর্থ অবস্থার modifier --failed. নিচে চালিয়ে দেখুন:

    <style>
      .payment-summary { max-width: 280px; padding: 16px; margin-block-end: 10px;
                         border: 2px solid #ddd; border-radius: 12px; }
      .payment-summary__title  { margin: 0 0 6px; font-size: 18px; }
      .payment-summary__amount { margin: 0 0 12px; font-size: 24px; font-weight: bold; }
      .payment-summary__button { padding: 8px 16px; border: 0; border-radius: 8px;
                                 background: #0f766e; color: white; font: inherit; }
      .payment-summary--failed { border-color: crimson; background: #fff1f0; }
    </style>
    
    <div class="payment-summary">
      <h4 class="payment-summary__title">মোবাইল রিচার্জ</h4>
      <p class="payment-summary__amount">৳ ২০০</p>
      <button class="payment-summary__button" type="button">নিশ্চিত করুন</button>
    </div>
    <div class="payment-summary payment-summary--failed" role="alert">
      <h4 class="payment-summary__title">পেমেন্ট ব্যর্থ হয়েছে</h4>
      <p class="payment-summary__amount">৳ ৫০০</p>
      <button class="payment-summary__button" type="button">আবার চেষ্টা করুন</button>
    </div>

    দুটি বাক্সের কাঠামো এক; দ্বিতীয়টিতে লাল বর্ডার আর হালকা লাল পটভূমি — শুধু modifier-এর কারণে। লক্ষ করুন, ব্যর্থতা শুধু রঙে নয়, শিরোনামের লেখায়ও বলা আছে (রং দেখতে না পাওয়া পাঠকের জন্য, পর্ব ৪৫), আর role="alert" screen reader-কে জানায়। চেষ্টা করুন: .payment-summary--success নামে একটি সবুজ modifier বানান।

  2. Coverage দিয়ে dead code খোঁজা: Chrome বা Edge-এ যেকোনো একটি বড় সংবাদ বা ই-কমার্স সাইট খুলুন। DevTools-এ Ctrl+Shift+P চেপে “Coverage” লিখে প্যানেলটি খুলুন, রেকর্ড চালিয়ে পাতা reload করুন। CSS ফাইলগুলোর কত শতাংশ “unused”? এর মানেই কি সেগুলো মুছে ফেলা যায়?

    বড় সাইটে প্রায়ই CSS-এর অর্ধেক বা তারও বেশি লাল (অব্যবহৃত) দেখাবে। কিন্তু না — সরাসরি মোছা যাবে না। Coverage শুধু এই পাতা, এই মুহূর্তের হিসাব দেয়: অন্য পাতা, খোলা মেনু, ভুল ইনপুটের বার্তা, মোবাইলের media query বা dark mode-এর নিয়ম এখানে “অব্যবহৃত” দেখাতে পারে। এটি একটি সূত্র, প্রমাণ নয়। মোছার আগে পুরো প্রজেক্টে নাম search করুন, আর পাতা ছোট রাখতে হলে বড় ফাইলকে পাতা বা component অনুযায়ী ভাগ করার কথা ভাবুন (পর্ব ৪৯)।

  3. ITCSS-এর তাকে বসান: নিচের নিয়মগুলো কোন ITCSS স্তরে (বা কোন @layer-এ) যাবে? (ক) :root { --radius: 12px; } (খ) a { color: var(--color-brand); } (গ) .u-visually-hidden { … } (ঘ) .o-cluster { display: flex; flex-wrap: wrap; gap: 1rem; } (ঙ) .c-navbar__link { … } (চ) *, *::before, *::after { box-sizing: border-box; }

    (ক) Settings — শুধু একটি মান, token. (খ) Elements — class ছাড়া HTML element-এর মৌলিক চেহারা। (গ) Utilities — একটি কাজ, সবচেয়ে শক্তিশালী স্তর। (ঘ) Objects — চেহারাহীন layout-এর কাঠামো। (ঙ) Components — নির্দিষ্ট UI অংশ, BEM নাম। (চ) Generic — reset, সবচেয়ে চওড়া reach. স্তরের ক্রম তাই: ক, চ, খ, ঘ, ঙ, গ — আর @layer settings, generic, elements, objects, components, utilities; লিখলে ব্রাউজারও ঠিক এই ক্রম মেনে চলবে।

  4. Stylelint কী বলবে? উপরের .stylelintrc.json (id নিষেধ, সর্বোচ্চ 0,3,0, !important নিষেধ, BEM নাম) ধরে নিয়ে অনুমান করুন — নিচের কোনগুলো সতর্কবার্তা পাবে, আর কেন? (ক) .card__title--large { } (খ) #cart .total { } (গ) .productCard { } (ঘ) .menu .menu__item .menu__link:hover { } (ঙ) .btn { color: red !important; }

    (ক) ঠিক আছে — বৈধ BEM নাম, এক class. (খ) সতর্কবার্তা — id selector (selector-max-id: 0). (গ) সতর্কবার্তা — camelCase, বড় হাতের অক্ষর নামের ধাঁচে মেলে না। (ঘ) সতর্কবার্তা — তিনটি class আর একটি pseudo-class মিলে (0,4,0), সীমা 0,3,0-এর বেশি; এখানে .menu__link:hover লিখলেই যথেষ্ট। (ঙ) সতর্কবার্তা — !important. নিজে পরীক্ষা করতে চাইলে একটি ফোল্ডারে ফাইল দুটি রেখে npx stylelint "*.css" চালান।

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

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

ক্রমITCSS-এর স্তরগুলো সাজান — সবচেয়ে চওড়া ও দুর্বল থেকে সবচেয়ে সরু ও শক্ত (▲▼ চেপে সরান)

  1. ⚙️ Settings — token আর মান
  2. 🧹 Generic — reset, *
  3. 🔤 Elements — class ছাড়া h1, a
  4. 📐 Objects — চেহারাহীন layout
  5. 🧩 Components — card, navbar
  6. 🏷️ Utilities — এক কাজের class

BEM নাম .search-form__input--large-এ “input” কী?

  • Block
  • Element
  • Modifier
  • Namespace

বড় প্রজেক্টে BEM-এর মতো পদ্ধতি সব selector-কে একটি class-এ রাখতে চায় কেন?

  • কারণ ব্রাউজার এক class-এর selector অনেক দ্রুত পড়ে
  • কারণ পুরোনো ব্রাউজারে descendant selector চলে না
  • যাতে specificity সমান ও কম থাকে, আর HTML-এর গঠন বদলালে স্টাইল না ভাঙে
  • যাতে CSS ফাইলের আকার ছোট হয়

@layer components, utilities; — components-এ .nav .btn { background: teal; } আর utilities-এ .bg-danger { background: crimson; }. একটি বোতামে দুটি class-ই থাকলে পটভূমি কী হবে?

  • teal, কারণ .nav .btn-এর specificity বেশি
  • দুটি রং মিশে একটি নতুন রং
  • কোনোটিই নয়, !important ছাড়া utility কাজ করে না
  • crimson, কারণ utilities পরের layer

SMACSS পদ্ধতিতে .is-open বা .is-hidden class কোন বিভাগে পড়ে?

  • State
  • Base
  • Layout
  • Theme

CSS Modules দুটি আলাদা ফাইলের একই নামের .title class-এর সংঘাত কীভাবে ঠেকায়?

  • ব্রাউজার প্রতিটি ফাইলের জন্য আলাদা Shadow DOM তৈরি করে
  • build-এর সময় টুল প্রতিটি class-এর নাম ফাইল অনুযায়ী অনন্য করে দেয়
  • প্রতিটি selector-এ স্বয়ংক্রিয়ভাবে !important যোগ করে
  • একই নাম দুবার পেলে build ব্যর্থ করে error দেখায়

Stylelint আর Prettier-এর কাজের পার্থক্য কোনটি সঠিকভাবে বলে?

  • Stylelint ব্রাউজারে চলে, Prettier কম্পিউটারে
  • Prettier ভুল ধরে, Stylelint CSS ছোট (minify) করে
  • Stylelint নিয়মভঙ্গ আর সম্ভাব্য ভুল খোঁজে; Prettier কোডের লেখার ধরন একরকম করে সাজায়
  • দুটি একই কাজ করে, তাই একটিই যথেষ্ট

শব্দকোষ

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

মনে রাখুন

  1. বড় প্রজেক্টে CSS-এর চার শত্রু: global scope, specificity-র যুদ্ধ, dead code আর মুছতে ভয়.
  2. তিন নীতি: কম ও সমান specificity, এক class এক কাজ, আর composition — ছোট অংশ জুড়ে বড় নকশা।
  3. BEM: .block__element--modifier; element-এর নাম সবসময় সরাসরি block-এর নামে, modifier-এর পাশে মূল class.
  4. OOCSS (কাঠামো বনাম রূপ), SMACSS (is- অবস্থা), ITCSS-এর উল্টো ত্রিভুজ — আজ @layer দিয়ে ব্রাউজারের নিয়মে বাঁধা যায়; utility-first আর semantic class-এর পছন্দ প্রজেক্টের ধরনের ওপর নির্ভর করে।
  5. এক component এক ফাইল, token সবার ওপরে, Stylelint + Prettier দিয়ে নিয়ম পাহারা, পুরোনো CSS গোছান ছোট ছোট ধাপে — আর class-এর নাম ইংরেজি ASCII-তে।

পরের পর্বে: কোনো নিয়ম কেন জিতল বা হারল, কোন CSS অব্যবহৃত, লেআউট কোথায় ভাঙছে — এসব প্রশ্নের উত্তর খুঁজতে আমরা নামব DevTools-এর ভেতরে: Elements, Styles, Computed, Layout আর Coverage প্যানেল দিয়ে HTML-CSS debugging.

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

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