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

CSS variable (custom properties) — একবার লিখে বারবার ব্যবহার

CSS Custom Properties — var(), Fallbacks, Scope, IACVT, Design Tokens, Theming & @property

Taught in Bangla, this lesson covers CSS custom properties: declaring --name values and using var() with fallbacks, scope and inheritance, live variables that change with hover and media queries, the invalid-at-computed-value-time behaviour, design tokens, naming, themes and component variants, calc() with unitless numbers, typed variables with @property, and reading or writing them from JavaScript.

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

  • custom property ঘোষণা আর var() দিয়ে ব্যবহার, fallback আর নেস্টেড fallback
  • scope আর inheritance — :root বনাম component, child-এ মান বদলানো, ব্রাউজার কীভাবে মান খুঁজে পায়
  • “invalid at computed-value time” — ভুল মান কেন আগের নিয়মে ফেরে না
  • design token, নামকরণের নিয়ম, থিম আর component variant বানানো; calc() দিয়ে মাপের সিঁড়ি
  • @property-এর পরিচয়, JavaScript থেকে পড়া-লেখা আর DevTools-এ variable খুঁজে বের করা

১ · একই রং ৪০ জায়গায় — সমস্যাটা কোথায়

ধরুন আপনি “হাটবাজার” নামে একটি অনলাইন দোকানের সাইট বানাচ্ছেন। দোকানের রং গাঢ় সবুজ-নীল, হেক্স কোডে #0f766e. লোগোতে এই রং, “কার্টে যোগ করুন” বাটনে এই রং, লিংকে এই রং, দামের পাশের দাগে এই রং, ফর্মের focus-এ এই রং। ছয় মাস পর দেখা গেল CSS ফাইলে এই একটি কোড আছে চল্লিশ জায়গায়।

তারপর একদিন মালিক বললেন, “ঈদের জন্য দোকানের রং একটু উজ্জ্বল করুন।” এবার আপনার কাজ: চল্লিশটি জায়গা খুঁজে বদলানো। “Find and Replace” চাপলেই হবে? সমস্যা হলো, কোথাও লেখা #0f766e, কোথাও #0F766E, কোথাও rgb(15, 118, 110), আর কোথাও কেউ চোখের আন্দাজে লিখেছেন #10766f. দুটি বাদ পড়ে গেল। সাইটে এখন দুই রকম সবুজ। এই ছোট্ট গল্পটিই বড় প্রজেক্টের প্রতিদিনের বাস্তবতা।

বাড়ি রং করার কথা ভাবুন। রংমিস্ত্রি প্রতিটি ঘরের জন্য আলাদা করে রং মেশালে কোনো দুটি ঘরের রং হুবহু এক হবে না। অভিজ্ঞ মিস্ত্রি তাই একটি বড় বালতিতে একবার রং মিশিয়ে, বালতির গায়ে লিখে রাখেন “বসার ঘর”। যত ঘরে লাগবে, সবাই সেই বালতি থেকে নেবে। রং বদলাতে হলে বদলাবেন বালতির রং — একবার। CSS variable হলো সেই নাম লেখা বালতি।

CSS-এর এই বালতির আসল নাম custom property — নিজের বানানো property. সবাই একে ডাকে “CSS variable”. দুটি অংশ: নাম দিয়ে মান রাখা, আর নাম ধরে মান তুলে আনা। নিচের এডিটরে দোকানের রং রাখা আছে মাত্র এক জায়গায়, আর ব্যবহার হয়েছে চার জায়গায়:

:root {
  --brand: #0f766e;   /* দোকানের রং — শুধু এখানে লেখা */
}

body { font-family: system-ui, sans-serif; margin: 12px; }

.logo  { color: var(--brand); font-size: 26px; font-weight: 800; }
.price { border-left: 6px solid var(--brand); padding-left: 10px; }
.btn {
  background: var(--brand);
  color: white; border: 0; border-radius: 8px;
  padding: 10px 18px; font-size: 16px; margin-right: 10px;
}
a { color: var(--brand); }

লোগো, দামের পাশের মোটা দাগ, বাটন আর লিংক — চারটিই একই সবুজ-নীল। অথচ রঙের কোডটি লেখা মাত্র একবার, :root-এর ভেতরে। চেষ্টা করুন: দ্বিতীয় লাইনে #0f766e-কে বদলে লিখুন #c2410c (কমলা) — চারটি জায়গা একসাথে বদলে যাবে। তারপর --brand-এর বানান একটি জায়গায় ভুল করে --brnd লিখে দেখুন — শুধু সেই জায়গার রং হারিয়ে যাবে। কেন হারায় আর তখন কী রং আসে, অংশ ২-এ দেখবেন।

এই পদ্ধতির পেছনের নীতিটির নাম DRY — Don't Repeat Yourself, একই তথ্য দুই জায়গায় না রাখা। কিন্তু CSS variable শুধু টাইপ কমানোর কৌশল নয়। এর আসল শক্তি হলো, মানটি ব্রাউজারের ভেতরে বেঁচে থাকে — পাতা চলার সময় বদলানো যায়, পাতার এক অংশে এক মান আর অন্য অংশে অন্য মান রাখা যায়। এই পর্বের বাকিটা সেই শক্তির গল্প।

CSS-এ variable-এর দাবি বহু পুরোনো। ২০০০-এর দশকে মানুষ এই অভাব মেটাত Sass আর Less-এর মতো preprocessor দিয়ে — আলাদা ভাষায় লিখে CSS-এ রূপান্তর। W3C-তে custom property-র প্রথম খসড়া আসে ২০১২ সালে, Google-এর Tab Atkins Jr.-সহ কয়েকজন সম্পাদকের হাতে। প্রথম ব্রাউজার হিসেবে Firefox এটি চালু করে ২০১৪ সালে, আর ২০১৬-১৭ সালের মধ্যে Chrome, Safari আর Edge-এ চলে আসে। আজ এটি Baseline Widely available — নির্ভয়ে ব্যবহার করুন, কোনো fallback বা prefix লাগে না।

২ · ঘোষণা আর ব্যবহার — --name, var() আর fallback

ঘোষণা: দুটি ড্যাশ দিয়ে শুরু

custom property ঘোষণা করা হয় সাধারণ declaration-এর মতোই — selector-এর ভেতরে, নাম, কোলন, মান। পার্থক্য শুধু নামে: নামের শুরুতে থাকতে হবে দুটি ড্যাশ (--). এর পরে যেকোনো নাম দিতে পারেন।

:root {
  --brand: #0f766e;
  --space-m: 16px;
  --radius: 10px;
  --font-bangla: "Noto Sans Bengali", "Hind Siliguri", sans-serif;
  --shadow-card: 0 2px 6px rgb(0 0 0 / 0.15);
}

কয়েকটি নিয়ম মনে রাখুন:

  • বড়-ছোট হাতের অক্ষর আলাদা। সাধারণ CSS property-র নাম (COLOR আর color) একই, কিন্তু --Brand আর --brand দুটি আলাদা variable. তাই দলে একটি নিয়ম ঠিক করুন — সাধারণত সব ছোট হাতের, শব্দের মাঝে হাইফেন, অর্থাৎ kebab-case.
  • মান প্রায় যেকোনো কিছু। ব্রাউজার ঘোষণার সময় মানটি যাচাই করে না — একটি রং, একটি মাপ, কয়েকটি শব্দ, এমনকি অর্ধেক একটি shadow-ও রাখা যায়। মানটি শুধু “কয়েকটি টুকরো লেখা” হিসেবে জমা থাকে। এই স্বাধীনতার একটি দাম আছে, যা অংশ ৫-এ দেখবেন।
  • শুধু মানের জায়গায় ব্যবহার। var() বসে কোনো property-র মানের ভেতরে। property-র নাম, selector বা media query-র শর্তে var() চলে না: @media (min-width: var(--bp)) কাজ করে না (কেন, ভাবনার প্রশ্ন ৪-এ)।

ব্যবহার: var()

মান তুলে আনার function-এর নাম var(). প্রথম জিনিসটি variable-এর নাম, দুটি ড্যাশসহ: color: var(--brand);. ব্রাউজার var(--brand)-এর জায়গায় variable-এর মানটি হুবহু বসিয়ে দেয়, তারপর পুরো declaration পড়ে। একটি মানের ভেতরে একাধিক var()-ও চলে: border: 2px solid var(--brand); বা padding: var(--space-s) var(--space-m);.

Fallback — variable না পেলে বিকল্প

variable কোথাও ঘোষণা করা না থাকলে কী হবে? var()-এর ভেতরে কমা দিয়ে একটি বিকল্প মান দেওয়া যায়, যাকে বলে fallback: var(--accent, crimson) মানে “--accent থাকলে সেটি, না থাকলে crimson.” fallback-এর ভেতরে আরেকটি var()-ও রাখা যায় — এভাবে একটি শিকল তৈরি হয়। নিচে ছয়টি অবস্থা একসাথে দেখুন:

:root { --brand: #7c3aed; }   /* বেগুনি; --accent কোথাও নেই */

.box {
  color: white; padding: 8px 12px; margin: 6px 0;
  border-radius: 6px; font: 600 17px system-ui, sans-serif;
}
.a { background: var(--brand); }
.b { background: var(--accent, crimson); }
.c { background: var(--accent, var(--brand, gray)); }
.d { background: var(--x, var(--y, #475569)); }
.e {
  background: #0e7490;
  font-family: var(--font, Georgia, "Times New Roman", serif);
}
.f { background: var(--Brand, darkorange); }   /* বড় হাতের B */

a বেগুনি — সরাসরি --brand. b লাল, কারণ --accent নেই। c বেগুনি: --accent নেই, তাই fallback-এর ভেতরের var(--brand) কাজে লাগল। d ধূসর-নীল — দুটি variable-ই নেই, শিকলের শেষ মান। e-এর লেখা Georgia-র মতো serif ফন্টে: প্রথম কমার পরের সবকিছু, অন্য কমাসহ, একটিই fallback. আর f কমলা — --Brand আর --brand আলাদা variable! চেষ্টা করুন: প্রথম লাইনে --accent: seagreen; যোগ করুন — b আর c দুটোই সবুজ হবে। তারপর .b থেকে , crimson মুছে দিন আর --accent-ও সরিয়ে দিন — b-এর পেছনের রং পুরো হারিয়ে যাবে, সাদা লেখা সাদা পাতায় অদৃশ্য।

শেষ পরীক্ষাটি জরুরি। variable না থাকলে, আর fallback-ও না থাকলে, ব্রাউজার property-টিকে এমনভাবে ধরে যেন সেখানে unset লেখা আছে (পর্ব ১৮-এর CSS-wide keyword) — উত্তরাধিকারী property হলে parent-এর মান, না হলে initial value. background-এর initial value স্বচ্ছ, তাই রং হারিয়ে গেল। ব্রাউজারের ভাষায়, যে variable ঘোষণা করা নেই, তার মান একটি বিশেষ “শূন্য” — guaranteed-invalid value. fallback ঠিক এই অবস্থার জন্যই। নিচের ডায়াগ্রামে একই শিকল পাঁচটি ভিন্ন অবস্থায় দেখুন — শেষ ধাপটি অনেক অভিজ্ঞ ডেভেলপারকেও চমকে দেয়:

1:root  { --brand: ??? }2.badge { --accent: ??? }34.badge {5  background: var(--accent, var(--brand, gray));6}
variable-এর অবস্থা
--accent
--brand
  1. কোনো variable নেই। ব্রাউজার আগে --accent খোঁজে — নেই। তাই প্রথম কমার পরের অংশ, অর্থাৎ var(--brand, gray). এখানেও --brand নেই, তাই শেষ মান gray. শিকল বাঁ থেকে ডানে পড়া হয়, প্রথম যে ধাপে মান পাওয়া যায় সেখানেই থামে।
  2. শুধু --brand আছে। :root-এ --brand: teal ঘোষণা হলো। --accent এখনো নেই, তাই দ্বিতীয় ধাপে গিয়ে teal পাওয়া গেল। gray আর দেখাই হলো না।
  3. --accent এলো। এবার প্রথম ধাপেই মান পাওয়া গেল — crimson. --brand থাকলেও তা বিবেচনায় আসে না। এই কৌশলেই “component-এর নিজের রং থাকলে সেটি, না থাকলে সাইটের রং” লেখা যায়।
  4. --accent: initial. custom property-তে initial লিখলে তার মান হয় guaranteed-invalid — অর্থাৎ “ঘোষণাই করা হয়নি”-র সমান। তাই আবার fallback, আবার teal. কোনো variable-কে ইচ্ছা করে “মুছে” দিতে এটি কাজের।
  5. --accent: 20px — ফাঁদ! variable এখন আছে, মান 20px. ব্রাউজারের কাছে variable থাকা মানেই fallback-এর প্রশ্ন শেষ। তাই সে বসায় background: 20px — যা অর্থহীন। ফল gray নয়, teal নয়, স্বচ্ছ। fallback ভুল মান থেকে বাঁচায় না, শুধু না-থাকা variable থেকে বাঁচায়। কেন এমন হয়, অংশ ৫-এ পুরো ব্যাখ্যা।

একটি পরিচিত ভুল: fallback-এর ভেতরে কমা থাকলে নতুনরা ভাবেন দুটি আলাদা fallback দিলেন। var(--font, "Hind Siliguri", sans-serif)-এর fallback আসলে পুরো "Hind Siliguri", sans-serif — যা font-family-এর জন্য ঠিক এটাই চাই। কিন্তু var(--a, --b) লিখলে fallback হলো লেখা --b, variable --b-এর মান নয়। শিকল চাইলে লিখতে হবে var(--a, var(--b)).

৩ · Scope আর inheritance — কোথায় রাখলে কে পায়

এখানেই CSS variable অন্য প্রোগ্রামিং ভাষার variable থেকে আলাদা। JavaScript বা C-তে variable থাকে কোডের কোনো “block”-এর ভেতরে। CSS-এ variable থাকে element-এর গায়ে — ঠিক color বা font-size-এর মতো। আর অন্য property-র মতোই এটি cascade মানে, আর inheritance-এ parent থেকে child-এ নামে। সব custom property ডিফল্টভাবে উত্তরাধিকারী।

তাই “variable-এর scope” মানে আসলে: কোন element-এ ঘোষণা করলেন। সেই element আর তার ভেতরের সবাই মানটি পায়; বাইরের কেউ পায় না।

🌐 :root-এ ঘোষণা

:root মানে ডকুমেন্টের root element — HTML পাতায় <html>. সবাই এর ভেতরে, তাই এখানে রাখা variable সারা পাতায় পাওয়া যায়। সাইটের রং, ফন্ট, মাপের সিঁড়ি এখানে থাকে।

সারা সাইটের ডিফল্ট

📦 component-এ ঘোষণা

.card { --card-pad: 12px; } লিখলে মানটি শুধু কার্ড আর তার ভেতরের element-গুলো দেখে। পাতার অন্য অংশ এর কথা জানেই না — নামের সংঘাতের ভয় কম।

শুধু ওই অংশের ভেতরে

🔁 child-এ নতুন মান

ভেতরের কোনো element-এ একই নামের variable আবার ঘোষণা করলে, সেখান থেকে নিচের সবাই নতুন মান পায়। বাইরের মান বদলায় না — শুধু ঢেকে যায়।

override

কেন সবাই html না লিখে :root লেখেন? ফল একই element, কিন্তু :root একটি pseudo-class, তাই এর specificity (0,1,0), html-এর (0,0,1). আর SVG বা অন্য ধরনের ডকুমেন্টেও :root তার নিজের মূল element-কে বোঝায়। এটি মূলত প্রচলিত রীতি — দুটোর যেকোনোটিই কাজ করে।

এবার দেখুন একই নাম, তিন জায়গায় তিন মান:

:root      { --brand: #0f766e; }   /* সারা পাতার ডিফল্ট */
.sale      { --brand: #dc2626; }   /* অফার কার্ড আর তার ভেতরে */
.sale .vip { --brand: #a16207; }   /* তার ভেতরে আরও নির্দিষ্ট */

.card {
  border: 3px solid var(--brand);
  border-radius: 10px; padding: 10px; margin: 8px 0;
  font-family: system-ui, sans-serif;
}
.card h3 { margin: 0 0 6px; color: var(--brand); }
.tag {
  background: var(--brand); color: white;
  padding: 2px 10px; border-radius: 99px; font-size: 14px;
}

প্রথম কার্ড সবুজ-নীল (:root-এর মান)। দ্বিতীয়টি লাল — .sale নিজের মান ঘোষণা করেছে। আর তার ভেতরের VIP কার্ড সোনালি-বাদামি। খেয়াল করুন, .card-এর CSS একবারই লেখা, কোথাও “লাল কার্ড” বা “সোনালি কার্ড” আলাদা করে নেই — বদলেছে শুধু variable. চেষ্টা করুন: তৃতীয় লাইনটি মুছে দিন — VIP কার্ড লাল হবে, কারণ সে তখন parent .sale থেকে উত্তরাধিকারে পায়। তারপর HTML না বদলে প্রথম লাইনে --brand-এর মান বদলান — শুধু প্রথম কার্ড বদলাবে।

ব্রাউজার একটি element-এর জন্য var(--brand)-এর মান কীভাবে খুঁজে পায়, ধাপে ধাপে দেখুন। মনে রাখবেন: প্রতিটি element-এর জন্য ব্রাউজার আগে --brand-এর নিজের মান ঠিক করে (cascade, তারপর না পেলে উত্তরাধিকার), তারপর সেই মান var()-এ বসায়।

  1. html:rootঘোষণা আছে
  2. body—উত্তরাধিকার
  3. div.sale.saleনিজের মান
  4. div.vip.sale .vipআবার নতুন
  5. span.tagvar(--brand)ব্যবহার
htmlযে rule মেলে
:root { --brand: #0f766e; }

--brand = #0f766e   ← নিজের declaration জিতেছে
bodyনিজের কোনো --brand নেই
/* কোনো rule body-তে --brand বসায় না */

--brand = #0f766e   ← parent html থেকে উত্তরাধিকার
div.card.saleনিজের rule আছে
.sale { --brand: #dc2626; }

--brand = #dc2626   ← সরাসরি লাগা rule উত্তরাধিকারকে হারায়
html-এর --brand এখনো #0f766e — বদলায়নি, শুধু এখানে ঢাকা
div.card.vipদুটি rule প্রতিযোগী নয় — একটিই মেলে
.sale      { --brand: #dc2626; }  ← এই div-এ মেলে না (class নেই)
.sale .vip { --brand: #a16207; }  ← মেলে

--brand = #a16207
span.tagএবার ব্যবহার
.tag { background: var(--brand); }

--brand = #a16207     ← parent .vip থেকে উত্তরাধিকার
background = #a16207  ← var() বসানোর পর
  1. শুরু html-এ। :root rule-টি html element-এ মেলে, তাই এখানে --brand-এর মান #0f766e. এই মুহূর্তে কেউ এটি ব্যবহার করছে না — variable শুধু জমা আছে।
  2. body — কিছু লেখা নেই। কোনো rule body-তে --brand বসায় না। custom property উত্তরাধিকারী, তাই body পায় parent html-এর মান। এভাবেই একটি :root-এর variable পাতার প্রতিটি element-এ পৌঁছে যায়।
  3. div.sale — নিজের মান। এখানে .sale rule সরাসরি লাগে। পর্ব ১৮-এর নিয়ম: সরাসরি লাগা rule, যত দুর্বলই হোক, উত্তরাধিকারকে হারায়। তাই এই div আর তার ভেতরের সবার জন্য --brand এখন লাল। html-এর মান একটুও বদলায়নি — পাশের সাধারণ কার্ড এখনো সবুজ-নীল।
  4. div.vip — আবার নতুন মান। .sale .vip শুধু .sale-এর ভেতরের .vip-এ মেলে। তাই এখানে মান সোনালি-বাদামি। .sale rule এই div-এ মেলেই না (এর class শুধু card vip), তাই এখানে কোনো লড়াই হয়নি।
  5. span.tag — ব্যবহার। span-এর নিজের --brand নেই, তাই parent .vip থেকে পায় #a16207. এবার background: var(--brand)-এ মানটি বসে চূড়ান্ত পেছনের রং হয়। মনে রাখুন, var() সবসময় যে element-এ লেখা, সেই element-এর variable দেখে — যেখানে variable ঘোষণা হয়েছিল সেখানকার নয়।
মূল ধারণা

CSS variable “ঘোষণার জায়গা” থেকে নয়, “ব্যবহারের জায়গা” থেকে মান পায়। .tag-এর CSS একটিই, কিন্তু তিন কার্ডে সে তিনটি আলাদা রং দেখায় — কারণ তিনটি span তিন ভিন্ন parent-এর সন্তান। এই একটি ধারণাই থিম, component variant আর dark mode-এর ভিত্তি।

৪ · জীবন্ত variable — cascade, hover আর media query

যেহেতু variable একটি সাধারণ property, তাই যেখানে যেখানে একটি সাধারণ property বদলানো যায়, সেখানে variable-ও বদলানো যায়: :hover-এ, :focus-visible-এ, media query-র ভেতরে, container query-র ভেতরে (পর্ব ৩১), JavaScript দিয়ে। আর বদলানোর সাথে সাথে যত জায়গায় var() দিয়ে ব্যবহার হয়েছে, সব নতুন করে হিসাব হয়।

body { font-family: system-ui, sans-serif; margin: 12px; }

.btn {
  --bg: #2563eb;
  --lift: 0px;
  background: var(--bg); color: white; border: 0;
  padding: 10px 18px; border-radius: 8px; font-size: 16px;
  transform: translateY(var(--lift));
  transition: background 0.2s, transform 0.2s;
}
.btn:hover, .btn:focus-visible { --bg: #1e3a8a; --lift: -4px; }

.grid {
  --cols: 1;
  display: grid; gap: 8px; margin-top: 14px;
  grid-template-columns: repeat(var(--cols), 1fr);
}
.grid div { background: #e0f2fe; padding: 14px; border-radius: 6px; }
@media (min-width: 420px) {
  .grid { --cols: 3; }
}

বাটনে মাউস রাখলে (বা Tab চেপে focus দিলে) রং গাঢ় হয় আর বাটন একটু ওপরে ওঠে — :hover-এ শুধু দুটি variable বদলানো হয়েছে, background বা transform আবার লেখা হয়নি। নিচের তিনটি বাক্স পাশাপাশি, কারণ প্রিভিউর চওড়া 420px-এর বেশি, তাই media query --cols-কে 3 করেছে। চেষ্টা করুন: 420px-কে 2000px করুন — বাক্সগুলো ওপর-নিচে চলে আসবে (ফোনের মাপের মতো)। লক্ষ করুন, transition লেখা হয়েছে আসল property-তে (background, transform), variable-এ নয় — সাধারণ variable নিজে মসৃণভাবে বদলায় না। কেন, অংশ ৮-এ।

Sass variable বনাম CSS variable

Sass-এর মতো preprocessor-এর (পর্ব ৫০-এ বিস্তারিত) নিজের variable আছে: $brand: #0f766e;. দেখতে একই রকম, কিন্তু কাজ সম্পূর্ণ আলাদা। Sass আপনার কম্পিউটারে, পাতা প্রকাশের আগে চলে; প্রতিটি $brand-এর জায়গায় রঙের কোডটি বসিয়ে একটি সাধারণ CSS ফাইল বানায়। ব্রাউজার কোনোদিন $brand দেখে না। অর্থাৎ Sass variable কারখানার ছাঁচের মতো — জিনিস তৈরি হয়ে গেলে ছাঁচ আর জিনিসের সাথে যায় না। CSS variable জিনিসের সাথেই যায়, আর ব্যবহারকারীর ফোনে বসেও বদলাতে পারে।

বিষয়Sass $varCSS --var
কখন মান বসেbuild-এর সময়, একবারব্রাউজারে, পাতা চলার সময়, যতবার দরকার
ব্রাউজার দেখে?না — শুধু ফল দেখেহ্যাঁ — DevTools-এ দেখা ও বদলানো যায়
scopeSass ফাইলের block ({ }) অনুযায়ীDOM অনুযায়ী — কোন element-এর ভেতরে
:hover/media query-তে বদলনা (আগেই সব হিসাব শেষ)হ্যাঁ
JavaScript দিয়ে পড়া-লেখানাহ্যাঁ
media query-র শর্তে ব্যবহারহ্যাঁ — @media (min-width: $bp)না
রং গাঢ় করার মতো হিসাবনিজের function (যেমন color.adjust())এখন CSS-এর নিজের color-mix(), calc()

তাই দুটি শত্রু নয় — কোনটি কখন, ভাবনার প্রশ্ন ২-এ।

৫ · ভুল মানের রহস্য — invalid at computed-value time

এই অংশটি একটু কঠিন, কিন্তু এটি না বুঝলে একদিন এমন একটি বাগে আটকাবেন যার কোনো ব্যাখ্যা খুঁজে পাবেন না। প্রথমে পর্ব ১৬-এর একটি পুরোনো নিয়ম মনে করুন: ব্রাউজার কোনো declaration বুঝতে না পারলে সেটি চুপচাপ ফেলে দেয়, আর আগের declaration কাজ করে। এই কারণেই পুরোনো কৌশলটি চলে:

.box {
  background: #0f766e;                     /* পুরোনো ব্রাউজারের জন্য */
  background: color-mix(in oklch, #0f766e, white 20%);   /* নতুনদের জন্য */
}

যে ব্রাউজার color-mix() চেনে না, সে দ্বিতীয় লাইনটি ফাইল পড়ার সময়েই (parse time) বাতিল করে, প্রথম লাইনটি থেকে যায়। কিন্তু var() থাকলে ফাইল পড়ার সময় ব্রাউজার জানেই না শেষে মান কী হবে — variable-এর মান তো element অনুযায়ী আলাদা হতে পারে, পরে বদলাতেও পারে। তাই var() থাকা যেকোনো declaration-কে ব্রাউজার আপাতত বৈধ ধরে নেয়। আসল যাচাই হয় অনেক পরে, প্রতিটি element-এর মান হিসাব করার সময় — computed value-এর সময়। ততক্ষণে cascade শেষ, আগের declaration-গুলো অনেক আগেই হেরে গেছে।

তখন যদি দেখা যায় বসানো মানটি অর্থহীন (যেমন color: 20px), সেই অবস্থার নাম invalid at computed-value time, সংক্ষেপে IACVT. ফল: property-টি এমন হয় যেন সেখানে unset লেখা — উত্তরাধিকারী হলে parent-এর মান, না হলে initial value. আগের declaration-এ ফেরার আর পথ নেই।

:root { --size: 20px; }

.box { color: purple; font: 600 17px system-ui, sans-serif; }
p    { background: #fde68a; padding: 6px 10px; margin: 6px 0; }

.one   { color: green; }
.one   { color: 20px; }               /* পার্সের সময়েই বাতিল */

.two   { color: green; }
.two   { color: var(--size); }        /* পরে ধরা পড়ে */

.three { color: green; }
.three { color: var(--size, blue); }  /* --size আছে, তাই fallback নয় */

.four  { background: green; }
.four  { background: var(--size); }   /* উত্তরাধিকারী নয় */

প্রথম লাইন সবুজ — ভুল declaration ফেলে দেওয়া হয়েছে, আগেরটি টিকে আছে। দ্বিতীয় আর তৃতীয় লাইন বেগুনি: সবুজও নয়, নীলও নয়। color উত্তরাধিকারী, তাই unset মানে parent .box-এর বেগুনি। চতুর্থ লাইনের পেছনের হলুদ রং পুরো উধাও — background উত্তরাধিকারী নয়, তাই initial value স্বচ্ছ। খেয়াল করুন, সে p-এর হলুদেও ফেরেনি। চেষ্টা করুন: প্রথম লাইনে --size-এর মান crimson করুন — এবার ২, ৩ আর ৪ সব লাল, কারণ মানটি এখন রং হিসেবে বৈধ। তারপর :root-এর লাইনটি পুরো মুছে দিন — শুধু তৃতীয়টি নীল হবে, কারণ এখন variable সত্যিই নেই।

  1. পার্সparseফাইল পড়া
  2. Cascadeবিজয়ীকে জেতে
  3. বসানোvar() → মানsubstitution
  4. যাচাইcolor: 20px?অর্থ আছে কি
  5. ফলunsetচূড়ান্ত মান
পার্সের সময়ফাইল পড়তে পড়তে যাচাই
.one { color: 20px; }          ← color-এর ব্যাকরণে px চলে না
                               → এখনই বাতিল, যেন লেখাই হয়নি

.two { color: var(--size); }   ← var() আছে, মান এখনো অজানা
                               → আপাতত বৈধ ধরে রাখা হলো
Cascadeপ্রতিযোগী declaration
.one-এর জন্য:  color: green          ← একমাত্র প্রতিযোগী, জিতল

.two-এর জন্য:  color: green          ← হারল (আগে লেখা)
               color: var(--size)    ← জিতল (পরে লেখা)
Substitutionএই element-এর --size কত?
--size = 20px   ← :root থেকে উত্তরাধিকার

color: var(--size)
    ↓
color: 20px
যাচাইএবার ব্যাকরণ দেখা
color: 20px    ✗ রং নয়

হেরে যাওয়া "color: green" কি আবার আনা যায়?
না — cascade-এর ফল আগেই পাকা হয়ে গেছে
ফলproperty আচরণ করে unset-এর মতো
color      (উত্তরাধিকারী)  → inherit → parent-এর purple
background (উত্তরাধিকারী নয়) → initial → transparent

fallback দেওয়া থাকলেও এই পথ বদলায় না,
কারণ --size "নেই" নয় — আছে, শুধু ভুল।
  1. পার্স — ফাইল পড়া। ব্রাউজার CSS ফাইল পড়ার সময় প্রতিটি declaration property-র নিয়মে মেলায়। color: 20px মেলে না, তাই সাথে সাথে বাতিল। কিন্তু color: var(--size)-এর মান এখনো কেউ জানে না — একই rule একশটি element-এ একশ রকম মান দিতে পারে। তাই একে বাতিল করা যায় না।
  2. Cascade — কে জেতে। প্রথম ক্ষেত্রে ভুল লাইনটি তো আর নেই, তাই green একাই প্রতিযোগী। দ্বিতীয় ক্ষেত্রে দুটি প্রতিযোগী, specificity সমান, তাই পরে লেখা var(--size) জিতল। green এখানেই বাদ পড়ল।
  3. Substitution — মান বসানো। এবার ব্রাউজার এই element-এর --size-এর মান খোঁজে (অংশ ৩-এর মতো) — পায় 20px, আর var(--size)-এর জায়গায় বসায়। এই ধাপকে বলে substitution, বাংলায় “প্রতিস্থাপন”।
  4. যাচাই — অর্থ আছে কি না। এখন ব্রাউজার দেখে color: 20px — রং নয়। কিন্তু cascade অনেক আগেই শেষ, ব্রাউজার প্রতিটি element-এর হেরে যাওয়া সব declaration মনে রাখে না। তাই green-এ ফেরার উপায় নেই। declaration-টি এখন “invalid at computed-value time”.
  5. ফল — unset. property আচরণ করে unset-এর মতো: color উত্তরাধিকারী, তাই parent-এর বেগুনি; background নয়, তাই স্বচ্ছ। এজন্যই ভুল variable-এ “রং চলে গেল” বা “অন্য কোথাও থেকে রং এলো” মনে হয়। DevTools-এ এমন declaration-এ সাধারণত কোনো কাটা দাগ থাকে না — সে জিতেছে, শুধু তার মান অর্থহীন।

এর একটি ব্যবহারিক ফল: var()-ওয়ালা লাইনের আগে একটি সাধারণ মান লিখে রাখার পুরোনো fallback কৌশল শুধু সেই ব্রাউজারে কাজ করে যা custom property-ই চেনে না। যে ব্রাউজার চেনে, সেখানে variable-এ ভুল মান থাকলে আগের লাইন বাঁচাতে পারবে না। নিরাপত্তা চাইলে variable-এর মান ঠিক রাখুন, অথবা @property দিয়ে ধরন ঘোষণা করুন (অংশ ৮)।

৬ · Design token, নামকরণ, থিম আর component variant

Design token — নকশার সিদ্ধান্তগুলোর তালিকা

বড় দল এক-দুটি রং নয়, পুরো নকশার ভাষাটিই variable-এ রাখে: রঙের তালিকা, ফাঁকা জায়গার একটি সিঁড়ি (৪, ৮, ১২, ১৬, ২৪ পিক্সেল), কোণার গোলাকার মাপ, লেখার আকারের সিঁড়ি, ছায়া। নকশার এই প্রতিটি নামকরা সিদ্ধান্তকে বলে design token. ডিজাইনার Figma-য় যে নাম দেন, ডেভেলপার CSS-এ সেই নাম ব্যবহার করেন — দুজনের ভাষা এক হয়।

দর্জির দোকানের মাপের খাতার মতো। প্রতিবার গ্রাহকের গায়ে ফিতা না ধরে, খাতায় লেখা থাকে “বুক ৩৮, হাতা ২৪”। কারিগর খাতা দেখে কাটেন। মাপ বদলালে বদলায় খাতা — কারিগরকে নতুন করে কিছু শেখাতে হয় না। design token হলো সাইটের মাপের খাতা।

অভিজ্ঞ দলগুলো token-কে সাধারণত তিন স্তরে সাজায়:

স্তরকী বলেউদাহরণ
১. মূল (primitive)কাঁচা মান — “কোন রং”--teal-700: #0f766e; --space-4: 16px;
২. অর্থবহ (semantic)কাজ — “কী জন্য”--color-primary: var(--teal-700); --color-danger, --color-text
৩. componentএকটি অংশের নিজস্ব--btn-bg: var(--color-primary);

নামকরণের কয়েকটি পরীক্ষিত নিয়ম:

  • রূপ নয়, কাজ দিয়ে নাম দিন। --red নামের variable-এ একদিন কেউ নীল রং বসাবেই — তখন নামটি মিথ্যা বলবে। --color-danger সবসময় সত্য।
  • বিভাগ আগে, তারপর বিবরণ। --color-text-muted, --space-m, --radius-l, --font-size-xl — DevTools আর code editor-এর auto-complete-এ একই বিভাগের সব নাম পাশাপাশি আসে।
  • সিঁড়ির জন্য সংখ্যা বা মাপের নাম। --space-1 … --space-8 বা --text-s, m, l, xl. “একটু বড়”, “আরও বড়” — এমন নাম কয়েক মাসেই গোলমাল হয়।
  • library বা design system হলে prefix. Bootstrap 5-এর সব variable শুরু হয় --bs- দিয়ে — অন্যের variable-এর সাথে নামের সংঘাত হয় না (পর্ব ৫১)।

থিম আর variant — শুধু token বদলান

token থাকলে থিম বানানো মানে কয়েকটি অর্থবহ token-এর মান বদলানো — কোনো component-এর CSS ছুঁতেই হয় না। একইভাবে component variant (যেমন সাধারণ, বিপদ, হালকা বাটন) মানে শুধু component token বদলানো। নিচের এডিটরে দুটি প্যানেল — একটি সাধারণ, একটি .dark class-এর:

:root {
  --teal-700: #0f766e;  --red-600: #dc2626;          /* মূল */
  --color-bg: #f8fafc;  --color-text: #0f172a;       /* অর্থবহ */
  --color-primary: var(--teal-700);
  --color-danger: var(--red-600);
  --space-2: 8px; --space-4: 16px; --radius-m: 10px; --text-m: 16px;
}
.dark {                                  /* থিম = অর্থবহ token বদল */
  --color-bg: #0f172a; --color-text: #e2e8f0; --color-primary: #2dd4bf;
}
.panel {
  background: var(--color-bg); color: var(--color-text);
  padding: var(--space-4); border-radius: var(--radius-m);
  margin-bottom: var(--space-2); font: var(--text-m) system-ui, sans-serif;
}
.btn {                                   /* component token */
  --btn-bg: var(--color-primary);
  --btn-fg: var(--color-bg);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-m);
  font-size: var(--text-m);
}
.btn-danger { --btn-bg: var(--color-danger); --btn-fg: white; }
.btn-ghost  { --btn-bg: transparent; --btn-fg: var(--color-primary);
              box-shadow: inset 0 0 0 2px var(--color-primary); }

দুটি প্যানেলের HTML হুবহু এক। নিচের প্যানেলে .dark তিনটি অর্থবহ token বদলেছে, আর পটভূমি, লেখা, বাটন, এমনকি বাটনের লেখার রং পর্যন্ত বদলে গেছে — কারণ --btn-fg নিজেই var(--color-bg). variant-গুলোতে .btn-এর CSS আবার লেখা হয়নি, শুধু --btn-bg আর --btn-fg. চেষ্টা করুন: .dark-এ --color-danger: #fb7185; যোগ করুন — শুধু অন্ধকার প্যানেলের “বাতিল” বাটন হালকা হবে। তারপর .dark-এ --color-primary-এর বদলে --teal-700: #2dd4bf; লিখে দেখুন — কিছুই বদলাবে না! কারণটা নিচে।

শেষ পরীক্ষার রহস্য: --color-primary: var(--teal-700) লেখা আছে :root-এ, তাই var()-টি বসানো হয়ে যায় html element-এই — --color-primary-এর মান সেখানেই #0f766e হয়ে পাকা হয়, তারপর নিচে উত্তরাধিকারে নামে। পরে .dark-এ --teal-700 বদলালেও --color-primary নতুন করে হিসাব হয় না, কারণ .dark-এ --color-primary-এর কোনো declaration নেই। নিয়মটি: যে token অন্য token থেকে তৈরি, থিমে সেটিকেই সরাসরি বদলান — অথবা দুটোকেই একই selector-এ আবার ঘোষণা করুন। অংশ ৩-এর মূল কথাটিই এখানে: variable উত্তরাধিকারে যায় তার হিসাব-করা মান হিসেবে, সূত্র হিসেবে নয়।

বাস্তবে থিম বদলের জন্য সাধারণত <html>-এ একটি attribute ব্যবহার হয়, যেমন [data-theme="dark"] { … }, আর ব্যবহারকারীর কম্পিউটারের পছন্দ বুঝতে @media (prefers-color-scheme: dark). ব্রাউজারের নিজের অংশ (scrollbar, ফর্ম) মেলাতে color-scheme আর নতুন light-dark() function — এসবের পূর্ণ কৌশল আসছে পর্ব ৩৬-এ। এই পর্বের মূল শিক্ষা: থিম মানে “একই component, ভিন্ন token”.

৭ · calc() আর একক ছাড়া সংখ্যা — মাপের সিঁড়ি

পর্ব ২০-এ calc() দেখেছেন। variable আর calc() একসাথে হলে CSS প্রায় একটি ছোট হিসাবের খাতা হয়ে যায়: padding: calc(var(--space) * 2);, width: calc(100% - var(--sidebar));. সবচেয়ে কাজের কৌশলটি হলো variable-এ একক ছাড়া সংখ্যা রাখা, আর একক যোগ করা ব্যবহারের সময়।

কেন? কারণ একটি সংখ্যা দিয়ে অনেক কিছু করা যায় — গুণ, ভাগ, শতাংশ, ডিগ্রি — কিন্তু একবার 16px লিখে ফেললে সেটি থেকে আর 16% বা 16deg বানানো যায় না। আর একটি ভুল প্রায় সবাই একবার করেন: var(--n)px. এতে 16px হয় না, হয় “16” আর “px” — দুটি আলাদা টুকরো, যা অর্থহীন, ফলে IACVT. সঠিক উপায়: calc(var(--n) * 1px).

:root {
  --base: 16;      /* একক নেই — শুধু সংখ্যা */
  --ratio: 1.25;   /* প্রতিটি ধাপ আগেরটির ১.২৫ গুণ */
}
body { font-family: system-ui, sans-serif; margin: 10px; }
p { margin: 0 0 6px; }

.h3 { font-size: calc(var(--base) * 1px * var(--ratio)); }
.h2 { font-size: calc(var(--base) * 1px * var(--ratio) * var(--ratio)); }
.h1 { font-size: calc(var(--base) * 1px * var(--ratio) * var(--ratio) * var(--ratio)); }

.bar {
  height: 14px; margin: 6px 0; border-radius: 7px;
  background: #0ea5e9;
  width: calc(var(--pct) * 1%);
}
.wrong { background: #fecaca; margin-left: var(--base)px; }

তিনটি শিরোনাম 20px, 25px আর প্রায় 31px — একটি type scale, যা শুধু --ratio দিয়ে নিয়ন্ত্রিত। দুটি নীল দাগের চওড়া 30% আর 75%: মান এসেছে HTML-এর style="--pct: 30" থেকে — ডেটা HTML-এ, নকশা CSS-এ। শেষ লাইনটি বাঁ দিকে একটুও সরেনি: var(--base)px অর্থহীন, তাই margin-left initial value 0. চেষ্টা করুন: --ratio-কে 1.5 করুন — পুরো সিঁড়ি একসাথে খাড়া হবে। তারপর শেষ লাইনে লিখুন margin-left: calc(var(--base) * 1px); — এবার 16px সরে যাবে।

HTML-এর style attribute-এ শুধু একটি variable বসানো inline style-এর একটি সম্মানজনক ব্যবহার — নকশা stylesheet-এ, প্রতিটি element দেয় শুধু নিজের সংখ্যা। তবে স্ক্রিন রিডারের জন্য আসল সংখ্যাটি লেখায় বা <progress>/<meter>-এ রাখুন — CSS-এর দাগ তারা “দেখে” না।

ফাঁকা জায়গার সিঁড়িও একই কৌশলে বানানো যায়: --space-unit: 4px; রেখে --space-2: calc(var(--space-unit) * 2);, --space-4: calc(var(--space-unit) * 4);. আর লেখার আকার স্ক্রিনের সাথে মসৃণভাবে বাড়াতে চাইলে clamp()-এর সাথে variable — পর্ব ৩৫-এ দেখবেন। calc(), min(), max(), clamp() সবই Baseline Widely available.

৮ · @property — variable-কে ধরন দেওয়া

এতক্ষণের variable-গুলো ব্রাউজারের কাছে শুধু “কয়েক টুকরো লেখা”। --angle: 45deg লিখলেও সে জানে না এটি একটি কোণ। এর তিনটি অসুবিধা আপনি দেখেছেন: ভুল মান যাচাই হয় না (অংশ ৫), উত্তরাধিকার বন্ধ করা যায় না, আর animation বা transition-এ মাঝের মান হিসাব করা যায় না — লেখার টুকরো “৪৫ আর ৯০-এর মাঝামাঝি” কী, ব্রাউজার কীভাবে জানবে?

@property rule দিয়ে একটি variable-কে নিবন্ধন (register) করা যায় — তিনটি তথ্য দিয়ে:

@property --angle {
  syntax: "<angle>";      /* ধরন: কোণ */
  inherits: false;         /* parent থেকে নামবে না */
  initial-value: 0deg;     /* কিছু না দিলে বা ভুল দিলে এই মান */
}

এখন ব্রাউজার জানে --angle একটি কোণ, তাই 0deg থেকে 360deg-এর মাঝের প্রতিটি মান হিসাব করতে পারে। নিচে দুটি একই রকম বৃত্ত, একই animation — শুধু বাঁ দিকেরটির variable নিবন্ধিত:

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.row { display: flex; gap: 20px; font: 600 14px system-ui, sans-serif; }
.ring {
  width: 140px; aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center; text-align: center; color: white;
  animation: spin 3s linear infinite;
}
.typed { background: conic-gradient(from var(--angle), #0ea5e9, #a855f7, #f43f5e, #0ea5e9); }
.plain { background: conic-gradient(from var(--turn, 0deg), #0ea5e9, #a855f7, #f43f5e, #0ea5e9); }

@keyframes spin { to { --angle: 360deg; --turn: 360deg; } }

@media (prefers-reduced-motion: reduce) {
  .ring { animation: none; }
}

বাঁ দিকের বৃত্তের রং মসৃণভাবে ঘুরছে — ব্রাউজার প্রতিটি ফ্রেমে --angle-এর মাঝের মান (1deg, 2deg…) বের করছে। ডান দিকেরটি স্থির! নিবন্ধনহীন --turn-এর মাঝামাঝি মান হয় না, সে শুধু অর্ধেক সময়ে লাফ দিয়ে 0deg থেকে 360deg-এ যায় — আর দুটি দেখতে একই, তাই কোনো নড়াচড়াই চোখে পড়ে না। (আপনার কম্পিউটারে “কম নড়াচড়া” সেটিং চালু থাকলে দুটোই থেমে থাকবে — শেষের media query-র কাজ, পর্ব ৪৫-এ।) চেষ্টা করুন: @keyframes-এ --turn-এর মান 180deg করুন — ডান দিকেরটি এবার প্রতি দেড় সেকেন্ডে হঠাৎ লাফ দেবে। তারপর @property block পুরো মুছে দিন — বাঁ দিকেরটিও থেমে যাবে।

@property-এর আরও দুটি উপহার: inherits: false দিলে variable শুধু যে element-এ ঘোষণা, সেখানেই থাকে — ভেতরের component-গুলোতে অজান্তে নেমে যায় না। আর ধরনের সাথে না মেলা মান দিলে ব্রাউজার initial-value ব্যবহার করে, ফলে অংশ ৫-এর “রং উধাও” সমস্যা অনেকটা নিয়ন্ত্রণে আসে। syntax-এ "<color>", "<length>", "<number>", "<percentage>" ইত্যাদি দেওয়া যায়; "*" মানে যেকোনো কিছু।

সাপোর্ট: @property ২০২৪ সালে সব প্রধান ব্রাউজারে এসেছে (Firefox সবার শেষে) — তাই এটি Baseline Newly available. পুরোনো ব্রাউজারে rule-টি উপেক্ষিত হয়, variable সাধারণ variable-এর মতোই কাজ করে — শুধু animation মসৃণ হয় না। অর্থাৎ এটি নিরাপদ progressive enhancement, যদি নকশা মসৃণ animation-এর ওপর নির্ভর না করে। JavaScript-এর CSS.registerProperty() দিয়েও একই নিবন্ধন করা যায়। gradient-এর animation, সংখ্যা গোনার কাউন্টার আর আরও নতুন CSS — পর্ব ৩৭-এ; transition আর animation-এর মূল নিয়ম পর্ব ৩৮-৩৯-এ।

৯ · JavaScript থেকে পড়া-লেখা, DevTools আর পারফরম্যান্স

JavaScript আর CSS-এর সেতু

custom property JavaScript আর CSS-এর মধ্যে সবচেয়ে পরিষ্কার সেতু। JavaScript দিয়ে সরাসরি পাঁচটি element-এর রং, মাপ, ছায়া আলাদা করে বদলানোর বদলে, JavaScript শুধু একটি variable বদলায় — আর বাকিটা CSS নিজেই করে। দুটি method মনে রাখুন:

  • পড়া: getComputedStyle(el).getPropertyValue('--brand') — element-টির জন্য ব্রাউজার যে চূড়ান্ত মান ঠিক করেছে (নিজের বা উত্তরাধিকারে পাওয়া)।
  • লেখা: el.style.setProperty('--brand', 'navy') — element-এর inline style-এ variable বসানো। el.style.brand বা el.style['--brand'] কাজ করে না; ড্যাশওয়ালা নামের জন্য setProperty() লাগে।
const rootEl = document.documentElement;
const card = document.querySelector('.card');

// পড়া: চূড়ান্ত মান
const read = (el, n) => getComputedStyle(el).getPropertyValue(n).trim();
console.log('html --brand =', read(rootEl, '--brand'));
console.log('card --brand =', read(card, '--brand'));
console.log('card padding =', getComputedStyle(card).paddingTop);

// লেখা: html-এ --gap বদলালে কার্ডও বদলায়
rootEl.style.setProperty('--gap', '24px');
console.log('padding এখন =', getComputedStyle(card).paddingTop);

// ভুল উপায়
console.log("style['--gap'] =", rootEl.style['--gap']);
console.log("getPropertyValue =", rootEl.style.getPropertyValue('--gap'));

card.style.setProperty('--brand', 'navy');
console.log('card --brand এখন =', read(card, '--brand'));

প্রথম দুই লাইনে দুটি আলাদা মান — html-এর #0f766e, কার্ডের #dc2626 (কার্ডের নিজের ঘোষণা)। padding প্রথমে 12px, html-এ --gap বদলানোর পর 24px — কার্ডের CSS-এ কোনো হাত দেওয়া হয়নি। style['--gap'] দেয় undefined, কিন্তু style.getPropertyValue('--gap') দেয় 24px. শেষ লাইনে কার্ডের মান navy — inline style সবচেয়ে শক্ত (পর্ব ১৮)। .trim() দেওয়া হয়েছে, কারণ কিছু ব্রাউজার আগে মানের শুরুর ফাঁকা জায়গাটুকুও ফেরত দিত। চেষ্টা করুন: read(card, '--gap') লগ করুন — কার্ডে কখনো ঘোষণা না হলেও উত্তরাধিকারে 24px পাবেন।

সাধারণ ব্যবহার: থিম বদলের বোতাম, মাউসের অবস্থান (--x, --y), স্লাইডার দিয়ে লেখার আকার। DOM নিয়ে JavaScript-এর পুরো কাজ দেখবেন JavaScript কোর্সের DOM-এর পর্বে আর এই কোর্সের পর্ব ৫২-এ।

DevTools-এ variable খোঁজা

“এই রং কোথা থেকে এলো?” — variable-ওয়ালা সাইটে এই প্রশ্নের উত্তর DevTools-এ খুব সহজ (পুরো debugging পর্ব ৪৭-এ):

  • Styles প্যানেলে ঘোষণা: element Inspect করলে নিচের দিকে “Inherited from html” অংশে :root-এর সব variable দেখা যায়। কোনো মানে ক্লিক করে বদলালে সাথে সাথে পুরো পাতায় প্রভাব দেখবেন — থিম নিয়ে পরীক্ষার সবচেয়ে দ্রুত উপায়।
  • var()-এর ওপর মাউস: Chrome, Edge আর Firefox-এ var(--brand)-এর ওপর মাউস রাখলে এই element-এ variable-টির আসল মান দেখায়; রং হলে পাশে ছোট রঙের বর্গও থাকে। variable না থাকলে সাধারণত জানিয়ে দেয় যে এটি সংজ্ঞায়িত নয়। নামে ক্লিক করলে অনেক সংস্করণে সরাসরি ঘোষণার জায়গায় নিয়ে যায়।
  • Computed ট্যাব: চূড়ান্ত মান দেখায় — IACVT হলে এখানেই ধরা পড়ে যে color-এর মান parent-এর রং, অথচ Styles-এ var(--size) “জিতেছে” দেখাচ্ছে। custom property দেখতে অনেক সময় “Show all” চালু করতে হয়।

পারফরম্যান্স — কোথায় সাবধান

variable নিজে ধীর নয় — শত শত variable থাকলেও সাধারণ সাইটে পার্থক্য টের পাবেন না। সাবধান হতে হয় বদলানোর সময়। :root-এর একটি variable বদলানো মানে পাতার প্রতিটি element-এর জন্য উত্তরাধিকার নতুন করে হিসাব — ব্রাউজারের ভাষায় style recalculation. থিম বদলে এটি কোনো সমস্যা নয়, কিন্তু মাউস নড়ার বা স্ক্রলের প্রতিটি ফ্রেমে :root-এ variable বসালে বড় পাতায় হোঁচট দেখা দিতে পারে। নিয়ম দুটি: variable বসান যত কাছে সম্ভব (যে কার্ডটি নড়ছে, তার গায়ে); আর যাকে নিচে নামানোর দরকার নেই, তাকে @property দিয়ে inherits: false করুন। সন্দেহ হলে DevTools-এর Performance প্যানেলে “Recalculate Style” মেপে দেখুন।

আরও গভীরে: নিবন্ধনহীন custom property-র computed value হলো তার মানের টুকরোগুলো (token), যেখানে ভেতরের var()-গুলো আগেই বসানো — রং বা মাপ হিসেবে নয়, লেখা হিসেবে। তাই --x: 1em উত্তরাধিকারে 1em হিসেবেই নামে, আর প্রতিটি child নিজের ফন্টের মাপে সেটিকে আলাদা px-এ পরিণত করে; কিন্তু @property-তে <length> হিসেবে নিবন্ধিত হলে ঘোষণার element-এই px-এ পাকা হয়ে নামে। একটি variable নিজেকে বা একে অপরকে ডাকলে (--a: var(--b); --b: var(--a);) ব্রাউজার এই চক্র ধরে ফেলে, আর দুটিকেই guaranteed-invalid করে দেয়। আর Shadow DOM-এর ভেতরেও উত্তরাধিকারে custom property পৌঁছায় — এজন্যই Web Components (পর্ব ৫২) বাইরে থেকে সাজানোর প্রধান পথ এই variable.

১০ · AI-এর যুগে CSS variable

AI টুলকে “আমার সাইটের জন্য একটি color theme বানাও” বললে সে সাধারণত দ্রুত, সুন্দর দেখতে একগাদা variable দেয়। সমস্যা হলো, AI আপনার আসল HTML আর cascade দেখতে পায় না, আর তার শেখার ডেটায় পুরোনো Sass-যুগের কোড অনেক বেশি। ফলে কয়েকটি ভুল বারবার ফিরে আসে:

AI যা করেসমস্যাযা করা উচিত
margin: var(--gap)pxদুটি আলাদা টুকরো — IACVT, margin 0calc(var(--gap) * 1px), বা variable-এ একক রাখুন
.css ফাইলে $primarySass-এর লেখা; ব্রাউজার বোঝে না, পুরো declaration বাতিল--primary আর var(--primary)
--red, --blue-text-এর মতো রূপ-নির্ভর নামথিম বদলালে নাম মিথ্যা হয়ে যায়কাজ-নির্ভর নাম: --color-danger
dark mode-এ প্রতিটি component-এর CSS আবার লেখাদ্বিগুণ কোড, একটি বাদ পড়লেই অমিলশুধু অর্থবহ token বদলান
@media (max-width: var(--bp))media query-তে var() চলে নাসরাসরি মান, বা Sass/PostCSS
“fallback দিলাম, তাই নিরাপদ”ভুল মানে fallback কাজ করে নামান ঠিক রাখুন, প্রয়োজনে @property
সাধারণ variable-এ transition: --x 0.3sনিবন্ধনহীন variable মসৃণভাবে বদলায় নাআসল property-তে transition, বা @property

AI-কে কাজ দেওয়ার সময় token-এর কাঠামো আর শর্তগুলো আগেই বলে দিন, আর ব্যাখ্যা চান — তাতে আপনি যাচাই করতে পারবেন:

আমার একটি অনলাইন দোকানের সাইট আছে (শুধু HTML আর সাধারণ CSS, কোনো Sass বা framework নেই)। নিচের CSS-এ একই রং আর মাপ বারবার লেখা আছে: [এখানে আপনার CSS পেস্ট করুন] ১. এটিকে CSS custom property দিয়ে সাজাও — :root-এ মূল (primitive) token, তারপর কাজ-নির্ভর নামের অর্থবহ token (--color-primary, --space-m ধরনের), আর বাটনের জন্য component token. ২. [data-theme="dark"] দিয়ে একটি dark থিম দাও, যেখানে শুধু অর্থবহ token বদলাবে — কোনো component-এর CSS আবার লিখবে না। ৩. কোথাও var(--x)px লিখবে না, .css ফাইলে $ variable দেবে না, media query-র শর্তে var() দেবে না। ৪. শেষে একটি তালিকা দাও: কোন পুরোনো মান কোন token-এ গেল, আর কোন token অন্য token থেকে তৈরি — যাতে আমি থিমে ভুল token বদলে না ফেলি।

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

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

  • CSS variable কীভাবে ঘোষণা আর ব্যবহার করে? — দুটি ড্যাশ দিয়ে নাম, যেমন --main: teal;, আর ব্যবহার var(--main); সারা পাতার জন্য সাধারণত :root-এ।
  • Sass variable আর CSS custom property-র মূল পার্থক্য? — Sass-এরটি build-এর সময় মুছে গিয়ে স্থির মান হয়; CSS-এরটি ব্রাউজারে থাকে, cascade ও উত্তরাধিকার মানে, পাতা চলার সময় বদলানো যায়।
  • var(--x, red)-এ red কখন ব্যবহার হয়? — শুধু --x ঘোষিত না থাকলে (বা initial হলে); --x-এ ভুল মান থাকলে নয়।
  • IACVT কী? — var() বসানোর পর মান অর্থহীন হলে declaration-টি computed-value-এর সময় অবৈধ হয়, আর property unset-এর মতো আচরণ করে — আগের declaration-এ ফেরে না।
  • JavaScript দিয়ে CSS variable বদলায় কীভাবে? — el.style.setProperty('--x', '10px'); পড়তে getComputedStyle(el).getPropertyValue('--x').

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

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

প্র ০১CSS variable-এর নাম কেন দুটি ড্যাশ (--) দিয়ে শুরু? $ বা @-এর মতো সহজ চিহ্ন নয় কেন?

প্রথমে মনে রাখুন, CSS Working Group এটিকে “variable” নয়, “custom property” বলে — আর নামের রহস্য এই শব্দেই। এটি আসলে একটি নতুন ধরনের property, যা অন্য property-র মতোই একটি selector-এর ভেতরে declaration হিসেবে বসে, cascade মানে আর উত্তরাধিকারে নামে। তাই নামটিও property-র নামের নিয়মে থাকতে হয়েছে।

২০১২ সালের প্রথম খসড়ায় লেখার ধরন ছিল অন্যরকম: var-brand: teal; দিয়ে ঘোষণা, আর var(brand) দিয়ে ব্যবহার। আলোচনার পর ২০১৪ সালের দিকে এটি বদলে আজকের --brand আর var(--brand) হয়। দুটি ড্যাশের পেছনে একটি সুন্দর যুক্তি আছে। CSS-এ ব্রাউজার কোম্পানির নিজস্ব পরীক্ষামূলক property-র নাম শুরু হতো একটি ড্যাশ, কোম্পানির নাম আর আরেকটি ড্যাশ দিয়ে — -webkit-, -moz-. স্পেসিফিকেশন নিশ্চয়তা দেয় যে CSS-এর নিজের কোনো property কখনো -- দিয়ে শুরু হবে না। তাই -- যেন “নাম-না-থাকা কোম্পানির prefix” — অর্থাৎ আপনার নিজের। ভবিষ্যতে CSS-এ যত নতুন property আসুক, আপনার --gap-এর সাথে নামের সংঘাত হবে না।

কেন $ নয়? প্রথমত, $brand লিখলে Sass আর Less ব্যবহারকারীদের ফাইলে বিশাল বিভ্রান্তি হতো — preprocessor ভাবত এটি তার নিজের variable, আর build-এর সময়ই বদলে ফেলত। দ্বিতীয়ত, Working Group-এর আলোচনায় এমন মতও এসেছিল যে Sass-এর মতো build-এর-সময়ের, cascade-বিহীন “আসল” variable যদি কোনোদিন CSS-এ আসে, তার জন্য একটি ছোট চিহ্ন খালি রাখা ভালো। আর @ CSS-এ আগে থেকেই @media, @import-এর মতো at-rule-এর চিহ্ন।

দুটি ড্যাশ টাইপ করতে একটু বিরক্তিকর, কিন্তু নামটি দেখলেই বোঝা যায় এটি লেখকের বানানো, আর property হিসেবে এটি DevTools, setProperty() আর @property — সব জায়গায় একই নিয়মে খাপ খায়।

মূল উপলব্ধি: ভাষার নকশায় একটি চিহ্ন বাছাইও দীর্ঘমেয়াদি চুক্তি। -- তিনটি সমস্যা একসাথে সমাধান করেছে — ভবিষ্যতের CSS property-র সাথে সংঘাত নেই, preprocessor-এর সাথে বিভ্রান্তি নেই, আর “এটি একটি property” ধারণাটি নামেই স্পষ্ট।

প্র ০২CSS-এ এখন নিজের variable আছে — তাহলে Sass কি অপ্রয়োজনীয়? কোন কাজে কোনটি বেছে নেবেন?

Sass-এর জন্ম ২০০৬ সালে, Hampton Catlin-এর ভাবনা আর Natalie Weizenbaum-এর হাতে; পরে Chris Eppstein যোগ দেন। ২০০৯ সালে Alexis Sellier আনেন Less. তখন CSS-এ variable ছিল না, nesting ছিল না, রং গাঢ়-হালকা করার function ছিল না, এক ফাইলকে টুকরো করার ভালো উপায় ছিল না। preprocessor এই সব অভাব একসাথে মিটিয়েছিল, আর এক দশক ধরে পেশাদার CSS প্রায় মানেই ছিল Sass.

আজ ছবিটা বদলেছে। custom property (২০১৬-১৭ থেকে সব ব্রাউজারে), nesting (২০২৩ থেকে), color-mix(), calc()-এর সাথে clamp(), @layer — Sass-এর জনপ্রিয় ফিচারগুলোর বড় অংশ এখন ব্রাউজার নিজেই দেয়। আর একটি জায়গায় CSS variable Sass-কে সরাসরি হারায়: পাতা চলার সময় বদলানো। Sass variable দিয়ে dark mode বানাতে হলে দুটি আলাদা CSS ফাইল বা প্রতিটি component-এর দুই সেট নিয়ম লাগত; CSS variable-এ কয়েকটি token বদলালেই হয়। ব্যবহারকারীর লেখার আকার, স্লাইডার, মাউসের অবস্থান — এসব Sass কখনো জানতেই পারবে না, কারণ সে চলে ব্যবহারকারী আসার আগেই।

তবু Sass-এর জায়গা আছে। loop আর mixin দিয়ে একসাথে বিশটি utility class বানানো, breakpoint media query-তে ব্যবহার (CSS variable সেখানে চলে না), বড় কোডবেসকে module-এ ভাগ করা আর build-এর সময় ভুল ধরা — এসবে Sass এখনো কাজের। অনেক পরিণত দল তাই দুটোই চালায়: Sass-এ token-এর মূল তালিকা লিখে build-এর সময় সেগুলোকেই CSS custom property হিসেবে বের করে দেয়। Bootstrap 5 ঠিক এভাবে কাজ করে — সোর্স Sass-এ, কিন্তু ফলাফলের CSS-এ --bs- দিয়ে শুরু শত শত variable, যাতে ব্যবহারকারী Sass ছাড়াই রং বদলাতে পারেন।

সিদ্ধান্তের একটি সহজ প্রশ্ন: “এই মান কি ব্যবহারকারীর ব্রাউজারে কখনো বদলাতে পারে, বা পাতার এক অংশে আলাদা হতে পারে?” হ্যাঁ হলে CSS variable. মানটি শুধু কোড লেখার সুবিধার জন্য, আর কখনো বদলাবে না — তাহলে দুটোই চলে, তবে নতুন ছোট প্রজেক্টে build step না রাখাই সহজ।

মূল উপলব্ধি: টুলের প্রশ্ন আসলে সময়ের প্রশ্ন — কাজটি build-এর সময় হবে, না ব্রাউজারে চলার সময়। কোনটি কখন জানা যায়, সেটা বুঝলে টুল বাছাই সহজ হয়ে যায়।

প্র ০৩ভুল variable-এ ব্রাউজার আগের ঠিক declaration-এ ফিরে যায় না কেন? স্পেসিফিকেশন কি এটা ঠিক করতে পারত না?

প্রথম দেখায় IACVT একটি “বাগ”-এর মতো লাগে — ব্রাউজার তো জানত আগে একটি সুন্দর color: green ছিল! কিন্তু ব্রাউজারের কাজের ক্রম দেখলে সিদ্ধান্তটি বোঝা যায়।

একটি সাধারণ পাতায় কয়েক হাজার element, প্রতিটিতে কয়েকশ property. প্রতিটি element-এর প্রতিটি property-র জন্য cascade একজন বিজয়ী বেছে নেয়, আর বাকি প্রতিযোগীদের ফেলে দেয়। যদি নিয়ম হতো “var() বসানোর পর ভুল হলে আগের প্রতিযোগীতে ফিরে যাও”, তাহলে ব্রাউজারকে প্রতিটি element-এর প্রতিটি var()-ওয়ালা property-র জন্য হেরে যাওয়া সব declaration-এর একটি ক্রমবদ্ধ তালিকা মনে রাখতে হতো — আর সেই আগেরটিতেও var() থাকলে আবার আগেরটি… মেমরি আর সময় দুটোরই খরচ বহু গুণ বাড়ত, বিশেষ করে মোবাইলে। তাছাড়া ব্রাউজারগুলো একই rule-এর ফল বহু element-এ ভাগ করে নেয় (style sharing); element-অনুযায়ী আলাদা “পিছিয়ে যাওয়া” সেই অপ্টিমাইজেশন ভেঙে দিত।

দ্বিতীয় কারণ পূর্বানুমেয়তা। variable-এর মান element অনুযায়ী বদলায়; একই rule এক কার্ডে বৈধ, আরেক কার্ডে অবৈধ হতে পারে। “কোথাও আগের rule, কোথাও এই rule” — এমন আচরণ debug করা আরও কঠিন হতো। তুলনায় “অবৈধ হলে unset” নিয়মটি সরল: সবসময় একই ফল, আর DevTools-এর Computed ট্যাবে সহজে ধরা যায়।

স্পেসিফিকেশন তাই সমাধান দিয়েছে অন্য দিক থেকে। @property দিয়ে variable-এর ধরন ঘোষণা করলে ধরনের সাথে না মেলা মান variable-এর নিজের স্তরেই আটকে যায়, আর ব্যবহার হয় নিবন্ধনের initial-value — একটি নিশ্চিত, আপনার বেছে নেওয়া মান। আর ব্রাউজার আসলে custom property চেনে কি না জানতে @supports (--x: 0)-এর মতো feature query আছে (পর্ব ৪৮)।

মূল উপলব্ধি: নিয়মের অদ্ভুত কোণাগুলো প্রায়ই কোনো trade-off-এর চিহ্ন। “কেন এমন?” জিজ্ঞেস করলে প্রায়ই উত্তর আসে: “কারণ অন্যটির দাম সবাইকে দিতে হতো।”

প্র ০৪media query-র শর্তে var() চলে না কেন — @media (min-width: var(--bp))? তাহলে breakpoint এক জায়গায় রাখব কীভাবে?

উত্তরটি লুকিয়ে আছে অংশ ৩-এর মূল কথায়: custom property থাকে element-এর গায়ে। var(--bp)-এর মান জানতে হলে প্রশ্ন করতে হয়, “কোন element-এর --bp?” একটি div-এর ভেতরে একটি color: var(--brand)-এর উত্তর আছে — সেই div-এর --brand. কিন্তু @media-র শর্ত কোনো element-এর নয়; এটি পুরো ডকুমেন্ট আর ব্রাউজারের জানালা নিয়ে প্রশ্ন। কোন element-এর variable নেবে, প্রশ্নটারই কোনো উত্তর নেই।

আরেকটি সমস্যা চক্র। media query ঠিক করে কোন rule খাটবে, আর rule-ই ঠিক করে variable-এর মান। যদি @media (min-width: var(--bp))-এর ভেতরেই কেউ :root { --bp: 900px; } লেখেন? শর্ত variable-এর ওপর নির্ভর করে, variable শর্তের ওপর — চক্র। একই কারণে var() selector-এও চলে না।

তাহলে উপায়? কয়েকটি পথ আছে:

  • env(): CSS-এ ব্রাউজারের দেওয়া কিছু “পরিবেশ মান” আছে, যা element-নির্ভর নয় — যেমন ফোনের notch-এর জন্য env(safe-area-inset-top). এগুলো ডকুমেন্টের, তাই নীতিগতভাবে media query-তে চলতে পারে; নিজের বানানো env মানের প্রস্তাব আলোচনায় এসেছে, তবে ব্রাউজারে আসেনি।
  • @custom-media: Media Queries Level 5 খসড়ায় @custom-media --tablet (min-width: 768px); লিখে @media (--tablet) ব্যবহারের প্রস্তাব আছে। প্রধান ব্রাউজারে এখনো সাধারণভাবে চালু নয়, কিন্তু PostCSS-এর plugin দিয়ে আজই লেখা যায় (পর্ব ৫০)।
  • preprocessor: Sass-এর $bp build-এর সময় বসে, তাই media query-তে চলে — এখানে Sass এখনো সুবিধাজনক।
  • container style query: @container style(--theme: dark) দিয়ে parent-এর variable-এর মান দেখে নিয়ম বদলানো যায় — এখানে একটি নির্দিষ্ট container element আছে বলেই সম্ভব। এটি দীর্ঘদিন সব ব্রাউজারে ছিল না, তাই এখনো Baseline ধরে নেবেন না — ব্যবহারের আগে Can I use দেখে নিন।

আর অনেক ক্ষেত্রে breakpoint-এর দরকারই কমে যায়: clamp(), grid-এর auto-fit আর container query দিয়ে লেআউট নিজের জায়গা দেখে মানিয়ে নেয় (পর্ব ২৮, ৩১)।

মূল উপলব্ধি: কোনো ফিচার “কোথায় কাজ করে” বুঝতে জিজ্ঞেস করুন “এর মান কার?” — custom property-র মান element-এর, তাই যেখানে কোনো element নেই, সেখানে সে নেই।

প্র ০৫“Design token” কথাটি কোথা থেকে এলো, আর শুধু CSS variable থাকলেই কি একটি design system হয়ে যায়?

“Design token” কথাটি জনপ্রিয় হয় ২০১৪ সালের দিকে, Salesforce-এর Lightning Design System তৈরির সময় — Jina Anne আর Jon Levine-এর দল এই নামটি ব্যবহার করে। তাঁদের সমস্যা ছিল বাস্তব: একই কোম্পানির ওয়েবসাইট, iPhone অ্যাপ, Android অ্যাপ আর ইমেইল — সব জায়গায় একই নীল, একই ফাঁকা জায়গা চাই। কিন্তু প্রতিটি প্ল্যাটফর্মের ভাষা আলাদা: ওয়েবে CSS, iOS-এ Swift, Android-এ XML বা Kotlin. সমাধান: নকশার সিদ্ধান্তগুলো একটি নিরপেক্ষ ফাইলে (সাধারণত JSON) লেখা, তারপর একটি টুল দিয়ে প্রতিটি প্ল্যাটফর্মের জন্য আলাদা কোড বানানো। Amazon-এর Style Dictionary-র মতো টুল এই কাজই করে, আর W3C-র একটি Community Group (Design Tokens Community Group) সবার জন্য একটি সাধারণ ফাইল-ফরম্যাট দাঁড় করাতে কাজ করছে।

অর্থাৎ token CSS variable-এর চেয়ে বড় ধারণা। CSS variable হলো ওয়েবে token প্রকাশের সবচেয়ে ভালো রূপ — তবে token-এর আসল মূল্য তার সিদ্ধান্তে, সিনট্যাক্সে নয়। একটি CSS ফাইলের ওপরে পঞ্চাশটি -- লিখে দিলেই design system হয় না। design system মানে:

  • সীমিত, ভেবেচিন্তে বাছা পছন্দ: চল্লিশ রকম ধূসর নয়, আট-দশটি; ফাঁকা জায়গার একটি সিঁড়ি, যার বাইরে কেউ যায় না।
  • অর্থবহ স্তর: “কোন রং” আর “কোন কাজের রং” আলাদা রাখা, যাতে থিম, ব্র্যান্ড বদল বা high-contrast মোড সহজ হয়।
  • নথি আর নিয়ম: কোন token কখন ব্যবহার হবে, কোন রঙের জোড়া WCAG-এর contrast (সাধারণ লেখায় অন্তত ৪.৫:১) মানে।
  • component: বাটন, কার্ড, ফর্ম — যারা token ব্যবহার করে, আর নিজের component token দিয়ে variant দেয়।
  • শাসন: নতুন token কে যোগ করবেন, পুরোনো কীভাবে বাদ যাবে — না হলে দুই বছরে আবার চল্লিশটি ধূসর।

মূল উপলব্ধি: variable একটি হাতিয়ার; design system একটি চুক্তি — ডিজাইনার, ডেভেলপার আর ভবিষ্যতের সহকর্মীদের মধ্যে। বড় প্রজেক্টে CSS গোছানোর বাকি কৌশল পর্ব ৪৬-এ।

অনুশীলন

বেশিরভাগ কাজে কোড লেখার দরকার নেই — অনুমান, পর্যবেক্ষণ আর ভাবাই আসল কাজ।

  1. ফল অনুমান করুন: .box { color: navy; }-এর ভেতরে একটি p, আর CSS-এ আছে :root { --c: 1.5rem; }, p { color: orange; }, তার পরে p { color: var(--c, red); }. অনুচ্ছেদের রং কী হবে? আর :root-এর লাইনটি মুছলে?

    প্রথম অবস্থায় navy. শেষ declaration-টি cascade-এ জেতে (পরে লেখা), --c ঘোষিত আছে তাই fallback red প্রশ্নই ওঠে না, বসানোর পর color: 1.5rem অর্থহীন — IACVT. color উত্তরাধিকারী, তাই parent .box-এর navy. orange-এ ফেরে না। :root-এর লাইন মুছলে --c আর নেই, তাই fallback কাজ করে — red.

  2. Token-এ রূপান্তর: একটি CSS-এ #1d4ed8 আছে বাটন, লিংক আর শিরোনামে; #dc2626 আছে ভুলের বার্তা আর “মুছুন” বাটনে; আর 8px, 16px, 24px ছড়িয়ে আছে padding-margin-এ। কাগজে মূল, অর্থবহ আর component — তিন স্তরের token-এর একটি তালিকা লিখুন।

    একটি সম্ভাব্য উত্তর:

    :root {
      /* মূল */
      --blue-700: #1d4ed8;  --red-600: #dc2626;
      --space-2: 8px; --space-4: 16px; --space-6: 24px;
      /* অর্থবহ */
      --color-primary: var(--blue-700);
      --color-danger: var(--red-600);
      --color-link: var(--color-primary);
    }
    .btn        { --btn-bg: var(--color-primary); padding: var(--space-2) var(--space-4); }
    .btn-delete { --btn-bg: var(--color-danger); }
    .error      { color: var(--color-danger); margin-block: var(--space-4); }

    মূল বিষয়: “লাল” রং নয়, “বিপদের” রং — --color-danger. ফাঁকা জায়গার নামে সংখ্যা ৪-এর গুণিতক অনুযায়ী (--space-2 = ২ × 4px), যাতে পরে 12px দরকার হলে --space-3 মাঝখানে বসানো যায়। আর থিমে শুধু --color-… বদলাবেন — অংশ ৬-এর সতর্কতা মনে রেখে, যেমন --color-link যেহেতু --color-primary থেকে তৈরি, থিমে দুটোকেই আবার ঘোষণা করতে হবে, বা লিংকের রং সরাসরি দিতে হবে।

  3. বড় সাইটের token খুঁজুন: যেকোনো একটি পরিচিত বড় সাইট (সংবাদ, কেনাকাটা বা ডেভেলপারদের সাইট) খুলে DevTools-এ <html> element নির্বাচন করুন। Styles প্যানেলে কয়টি -- variable দেখছেন? তাদের নামকরণে কোনো নিয়ম চোখে পড়ে? একটি রঙের মান বদলে দেখুন পাতায় কী বদলায়।

    অনেক আধুনিক সাইটে কয়েক ডজন থেকে কয়েকশ variable পাবেন। সাধারণ নমুনা: একটি prefix (কোম্পানি বা design system-এর নামের সংক্ষেপ), তারপর বিভাগ — color, space, font, radius — তারপর মাপ বা কাজ। Bootstrap ব্যবহার করা সাইটে --bs-primary, --bs-body-font-size-এর মতো নাম দেখবেন। একটি অর্থবহ রঙের token (যেমন primary বা link) বদলালে একসাথে অনেক বাটন-লিংক বদলাবে — সেটাই প্রমাণ যে সাইটটি token দিয়ে সাজানো। কোনো বদলই না হলে সম্ভবত সেই নামটি অন্য token থেকে তৈরি নয়, বা component-গুলো সরাসরি মান ব্যবহার করছে।

  4. AI অডিট: যেকোনো AI-কে বলুন: “একটি কার্ড component-এর CSS দাও যার light আর dark দুটি থিম আছে, CSS variable দিয়ে।” উত্তরে খুঁজুন: (ক) var(--x)px বা Sass-এর $, (খ) dark থিমে component-এর CSS আবার লেখা হয়েছে কি না, (গ) নামগুলো রূপ-নির্ভর (--white) না কাজ-নির্ভর।

    প্রায়ই দেখা যায়: --white, --dark-gray-এর মতো নাম, যা dark থিমে উল্টো অর্থ দেয় (“white” variable-এ কালো রং!)। কখনো dark থিমের জন্য .dark .card { background: …; color: …; } — অর্থাৎ component আবার লেখা, token-এর সুবিধা হারানো। কখনো prefers-color-scheme-এর সাথে color-scheme নেই, ফলে scrollbar আর ফর্ম সাদাই থাকে। AI-কে বলুন: “নামগুলো কাজ-নির্ভর করো, আর dark থিমে শুধু অর্থবহ token বদলাও।” তারপর DevTools-এ দুই থিমে contrast যাচাই করুন — AI প্রায়ই dark থিমে হালকা ধূসর লেখায় যথেষ্ট contrast রাখে না।

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

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

ক্রমcolor: var(--brand) লেখা একটি declaration-এর চূড়ান্ত মান পেতে ব্রাউজার যে ধাপগুলো পার হয়, আগে থেকে পরে সাজান (▲▼ চেপে সরান)

  1. 📖 ফাইল পড়ার সময় declaration-টি আপাতত বৈধ ধরে রাখা
  2. ⚖️ Cascade — একই property-র প্রতিযোগীদের মধ্যে বিজয়ী বাছাই
  3. 🌳 এই element-এর --brand-এর মান ঠিক করা — নিজের rule, না হলে উত্তরাধিকার
  4. 🔁 var(--brand)-এর জায়গায় মান বসানো (না থাকলে fallback)
  5. 🔍 ফল যাচাই — অর্থহীন হলে property আচরণ করে unset-এর মতো

নিচের কোনটি একটি বৈধ CSS custom property ঘোষণা?

  • $main-color: #0f766e;
  • :root { --main-color: #0f766e; }
  • :root { var-main-color: #0f766e; }
  • @main-color: #0f766e;

CSS-এ .badge { background: var(--accent, crimson); } লেখা, আর পাতার কোথাও --accent ঘোষণা করা নেই। badge-এর পেছনের রং কী হবে?

  • স্বচ্ছ (transparent)
  • কালো
  • crimson
  • পুরো rule বাতিল হয়ে ব্রাউজারের ডিফল্ট রং

parent-এ color: purple; CSS-এ :root { --size: 20px; }, তারপর p { color: green; } আর তার পরে p { color: var(--size, blue); }. parent-এর ভেতরের p-এর লেখার রং কী?

  • green — কারণ ভুল declaration বাতিল হয়
  • blue — কারণ fallback কাজ করে
  • ব্রাউজারের ডিফল্ট কালো
  • purple — parent-এর রং

নিচের কোন কাজটি CSS custom property দিয়ে করা যায়, কিন্তু Sass-এর $variable দিয়ে করা যায় না?

  • :hover-এর সময় একটি variable-এর মান বদলে একসাথে কয়েকটি property বদলানো
  • একটি রং এক জায়গায় রেখে অনেক জায়গায় ব্যবহার
  • media query-র শর্তে breakpoint হিসেবে ব্যবহার
  • loop দিয়ে একসাথে দশটি utility class তৈরি

CSS-এ :root { --gap: 8; } আছে। কোন লেখাটি সঠিকভাবে 8px margin দেবে?

  • margin: var(--gap)px;
  • margin: calc(var(--gap) * 1px);
  • margin: var(--gap);
  • margin: px(var(--gap));

JavaScript দিয়ে একটি element el-এর CSS variable --gap-এর মান 24px করার সঠিক উপায় কোনটি?

  • el.style.--gap = '24px';
  • el.style.gap = '24px'; লিখলেই --gap বদলায়
  • el.style.setProperty('--gap', '24px');
  • getComputedStyle(el).setProperty('--gap', '24px');

শব্দকোষ

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

মনে রাখুন

  1. ঘোষণা --name: value, ব্যবহার var(--name); নাম বড়-ছোট হাতের অক্ষর আলাদা ধরে, আর var() বসে শুধু মানের জায়গায়।
  2. custom property একটি সাধারণ property-র মতো cascade মানে আর উত্তরাধিকারে নামে। :root-এ সারা পাতার জন্য, component-এ শুধু তার ভেতরে; var() মান নেয় যে element-এ লেখা, সেখান থেকে।
  3. fallback (var(--x, red)) কাজ করে শুধু variable না থাকলে। ভুল মান থাকলে IACVT — property unset, আগের declaration-এ ফেরে না।
  4. token-কে তিন স্তরে সাজান — মূল, অর্থবহ, component; নাম দিন কাজ দিয়ে। থিম আর variant মানে শুধু token বদল; একক ছাড়া সংখ্যা + calc() দিয়ে মাপের সিঁড়ি।
  5. @property দিয়ে ধরন দিলে animate করা যায় (Baseline Newly available). JavaScript-এ পড়া getPropertyValue(), লেখা style.setProperty(); ঘন ঘন বদলালে variable যত কাছে সম্ভব বসান।

পরের পর্বে: variable বদলাতে এই পর্বে :hover আর :focus-visible ব্যবহার করেছেন। এবার সেই pseudo-class-গুলোর পুরো জগৎ — :has() দিয়ে “parent selector”, আর ::before/::after দিয়ে HTML না ছুঁয়েই নতুন সাজ।

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

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