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

একেবারে নতুন CSS — anchor positioning, @property, @scope আর View Transitions

Cutting-edge CSS — Anchor Positioning, @property, @scope & View Transitions

Taught in Bangla, this lesson surveys cutting-edge CSS: how features reach the web through the CSSWG, Interop and Baseline, typed variables with @property, anchor positioning for tooltips and dropdowns, @scope, View Transitions, form features like field-sizing and customizable select, animating to height: auto and scroll animations, and layering fallbacks with @supports.

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

  • একটি নতুন CSS ফিচার প্রস্তাব থেকে সবার ব্রাউজারে পৌঁছায় কীভাবে — CSSWG, Interop আর Baseline-এর ভূমিকা
  • @property-এর তিনটি অংশ (syntax, inherits, initial-value) আর কেন এটি ছাড়া gradient animate হয় না
  • Anchor positioning দিয়ে tooltip ও dropdown বোতামের সাথে বেঁধে রাখা, popover-এর সাথে জোড়া আর পুরোনো ব্রাউজারের বিকল্প
  • @scope-এর root, limit (ডোনাট) আর proximity; View Transitions-এর ভেতরের pseudo-element আর reduced motion
  • প্রতিটি ফিচারের সৎ সাপোর্ট-অবস্থা, @supports দিয়ে যাচাই, আর AI-এর দেওয়া “নতুন CSS” যাচাইয়ের পদ্ধতি

১ · নতুন CSS কীভাবে ওয়েবে আসে — CSSWG, Interop আর Baseline

এই পর্বের প্রতিটি ফিচার চমৎকার, কিন্তু একটি সতর্কতা দিয়ে শুরু করা দরকার। একটি নতুন মোবাইল ফোন বাজারে এলেই ঢাকার সবার হাতে সেটি পৌঁছে যায় না — কেউ পুরোনো ফোনে আছেন, কেউ আপডেট করেননি। ব্রাউজারের বেলাতেও তাই। আপনার ল্যাপটপের সর্বশেষ Edge বা Chrome-এ যা চলে, একজন ক্রেতার পুরোনো iPhone-এর Safari-তে সেটি না-ও চলতে পারে। তাই নতুন CSS শেখার আসল দক্ষতা দুটি: ফিচারটি বোঝা, আর কে কে এটি দেখতে পাবেন সেটি জানা।

পর্ব ১-এ আপনি Baseline দেখেছেন। এখানে পুরো যাত্রাটি একবার মনে করিয়ে দিই, কারণ এই পর্বের প্রতিটি ফিচার এই পথের কোনো না কোনো ধাপে দাঁড়িয়ে আছে। “পরে” চেপে এগোন:

  1. প্রয়োজনJS libraryডেভেলপাররা নিজেরা জোড়াতালি দেন
  2. CSSWGdraft specনিয়ম লেখা ও তর্ক
  3. প্রথম ব্রাউজারflag / trialপরীক্ষা, মতামত
  4. Interopযৌথ পরীক্ষাসবাই একই আচরণে
  5. Newlyসব ইঞ্জিনেসর্বশেষ সংস্করণে
  6. Widely+৩০ মাসপ্রায় সবার কাছে
  1. ধাপ ১ — একটি পুরোনো যন্ত্রণা। বছরের পর বছর ডেভেলপাররা একই সমস্যা JavaScript দিয়ে সারিয়েছেন — যেমন বোতামের পাশে tooltip বসানো, বা পাতা বদলের সময় অ্যানিমেশন। হাজার হাজার সাইটে একই কাজের লাইব্রেরি মানেই প্ল্যাটফর্মে কিছু একটা নেই।
  2. ধাপ ২ — CSSWG-তে প্রস্তাব। W3C-র CSS Working Group-এ ব্রাউজার-নির্মাতা আর বিশেষজ্ঞরা প্রকাশ্যে (GitHub-এ) আলোচনা করে একটি খসড়া spec লেখেন। এখানে নাম, syntax, এমনকি পুরো নকশা বহুবার বদলায় — তাই খসড়ার সময়ের ব্লগ-পোস্টের কোড পরে ভুল হয়ে যেতে পারে।
  3. ধাপ ৩ — একটি ব্রাউজারে পরীক্ষা। সাধারণত কোনো একটি ইঞ্জিন আগে বানায় — প্রথমে লুকানো flag-এর পেছনে, কখনো origin trial-এ, তারপর সবার জন্য। এই অবস্থায় ফিচারটি not yet Baseline: একটি ব্রাউজারে আছে মানেই সবার কাছে নেই।
  4. ধাপ ৪ — Interop. ২০২১ সাল থেকে (প্রথম বছরের নাম ছিল Compat 2021) প্রধান ব্রাউজার-নির্মাতারা প্রতি বছর একসাথে কিছু ফিচার বেছে নেন, আর যৌথ পরীক্ষা (web-platform-tests) পাস করার লক্ষ্যে কাজ করেন — ২০২২ থেকে এর নাম Interop. এই পর্বের anchor positioning, View Transitions আর @scope — তিনটিই Interop 2025-এর লক্ষ্যের তালিকায় ছিল।
  5. ধাপ ৫ — Baseline Newly available. Chrome, Edge, Firefox আর Safari — চারটি প্রধান ব্রাউজারের সর্বশেষ সংস্করণে (desktop ও mobile) এলে ফিচারটি “Newly available”. মানে: সবার নতুন ব্রাউজারে আছে, কিন্তু যিনি অনেক দিন আপডেট করেননি, তাঁর কাছে নেই।
  6. ধাপ ৬ — Baseline Widely available. Newly হওয়ার প্রায় ৩০ মাস পরে লেবেল হয় “Widely available” — তখন বেশিরভাগ ব্যবহারকারীর কাছেই পৌঁছে যায়। এর আগে পর্যন্ত নতুন ফিচারের পাশে একটি fallback রাখাই পেশাদার অভ্যাস।

এবার এই পর্বের ফিচারগুলোর অবস্থা এক টেবিলে দেখুন। একটি সততার কথা আগে বলি: এই তথ্য ২০২৫–২৬ সময়ের, আর ব্রাউজার প্রতি মাসে বদলায়। তাই ব্যবহারের আগে সবসময় Can I use আর MDN-এর পাতার নিচের Baseline-বাক্সটি নিজে দেখে নিন।

ফিচারঅবস্থা (২০২৫–২৬)কোথায় আছে
@propertyBaseline Newly available (২০২৪ থেকে)চারটি প্রধান ব্রাউজারেই
View Transitions (একই পাতায়)Baseline Newly available (২০২৫-এর শেষ দিকে Firefox যোগ দেওয়ায়)Chromium, Safari, Firefox-এর নতুন সংস্করণ
@scopeসদ্য Newly available হয়েছে বা হওয়ার পথে — Firefox সবার শেষে যোগ দিয়েছেChromium, Safari; Firefox-এর একেবারে নতুন সংস্করণ
Anchor positioningএখনো সব জায়গায় নিশ্চিত নয় — Baseline হওয়ার পথেChromium (২০২৪ থেকে), Safari 26; Firefox-এ সদ্য আসছে — Can I use দেখে নিন
View Transitions (পাতা বদলে)not yet BaselineChromium, Safari; Firefox-এ এখনো নয়
Scroll-driven animationnot yet BaselineChromium, Safari 26; Firefox-এ পরীক্ষামূলক
field-sizingnot yet Baselineমূলত Chromium
appearance: base-selectপরীক্ষামূলক, spec এখনো বদলাচ্ছেশুধু Chromium
interpolate-size, calc-size()not yet Baselineশুধু Chromium
if(), sibling-index()একেবারে নতুন, শুধু নজরে রাখুনমূলত Chromium

টেবিল মুখস্থ করার দরকার নেই — এক বছর পরেই এটি পুরোনো হবে। দরকার পরীক্ষা করার অভ্যাস। নিচের ছোট স্ক্রিপ্টটি আপনার ব্রাউজারকেই জিজ্ঞেস করে সে কোনটি চেনে। CSS.supports() হলো @supports-এর JavaScript রূপ:

// আপনার ব্রাউজার কোন নতুন CSS চেনে?
const checks = {
  '@property':           'CSSPropertyRule' in window,
  'anchor-name':         CSS.supports('anchor-name: --a'),
  'position-area':       CSS.supports('position-area: top'),
  '@scope':              'CSSScopeRule' in window,
  'startViewTransition': 'startViewTransition' in document,
  'field-sizing':        CSS.supports('field-sizing: content'),
  'base-select':         CSS.supports('appearance: base-select'),
  'interpolate-size':    CSS.supports('interpolate-size: allow-keywords'),
  'scroll timeline':     CSS.supports('animation-timeline: scroll()'),
  'sibling-index()':     CSS.supports('order: sibling-index()')
};
for (const k in checks) {
  console.log((checks[k] ? 'YES  ' : 'no   ') + k);
}

প্রতিটি লাইনে YES বা no আর ফিচারের নাম আসবে। এই পর্ব পরীক্ষার সময় Edge-এর (Chromium) একটি নতুন সংস্করণে দশটির সবকটিতেই YES এসেছে — Chromium এখানে সবচেয়ে এগিয়ে। একই পাতা Firefox বা Safari-তে খুলে দেখুন — কয়েকটি no পাবেন, আর ঠিক সেই জায়গাগুলোতেই fallback লাগবে। খেয়াল করুন, @property আর @scope-কে CSS.supports() দিয়ে সরাসরি যাচাই করা যায় না (এগুলো property নয়, at-rule) — তাই সংশ্লিষ্ট JavaScript object আছে কি না দেখা হয়েছে। চেষ্টা করুন: 'display: grid'-এর মতো পুরোনো একটি জিনিস যোগ করুন — সব ব্রাউজারেই YES আসবে।

২ · @property — ধরন জানা CSS variable

পর্ব ৩৩-এ আপনি custom property বা CSS variable দেখেছেন: --brand: #0f766e; লিখে পরে var(--brand). এর একটি লুকানো দুর্বলতা আছে। ব্রাউজার --brand-এর মান কী ধরনের — রং, দৈর্ঘ্য, না কোণ — জানে না। তার কাছে এটি শুধু কয়েকটি অক্ষর, যা var()-এর জায়গায় বসিয়ে দিতে হবে।

বাজারের একজন দোকানদারকে ভাবুন, যাঁকে কেউ একটি সিলমোহর করা খাম দিয়ে বলল “এটি তাকে রাখুন।” খামের ভেতরে চাল না চিনি, তিনি জানেন না — তাই অর্ধেক কেজি মেপে দিতে পারবেন না, পুরো খামই দিতে হবে। সাধারণ CSS variable সেই খাম। @property খামের গায়ে লিখে দেয় “ভেতরে চাল, প্রাথমিক পরিমাণ শূন্য কেজি” — এবার দোকানদার মাঝামাঝি পরিমাণও মাপতে পারেন।

মাঝামাঝি পরিমাণ মাপা — এটিই animation-এর আসল কাজ। 0deg থেকে 360deg-এ যেতে হলে ব্রাউজারকে 90deg, 180deg-এর মতো মাঝের মানগুলো হিসাব করতে হয়। ধরন না জানলে সে তা পারে না; তখন সে discrete animation করে — অর্ধেক সময় পুরোনো মান, তারপর এক লাফে নতুন মান। @property দিয়ে ব্রাউজারকে তিনটি জিনিস জানানো হয়:

@property --angle {
  syntax: '<angle>';     /* মানের ধরন: কোণ */
  inherits: false;        /* parent থেকে উত্তরাধিকার নেবে না */
  initial-value: 0deg;    /* কিছু না দিলে বা ভুল মান দিলে এটি */
}
  • syntax — ধরন। '<color>', '<length>', '<percentage>', '<number>', '<angle>' ইত্যাদি; '*' মানে “যেকোনো কিছু” (তখন আবার animate হয় না)।
  • inherits — সাধারণ variable সবসময় উত্তরাধিকারী (inheritance, পর্ব ১৮)। false দিলে প্রতিটি element নিজের মান রাখে — animation-এ এটি দ্রুতও, কারণ একটি বদলালে পুরো গাছের হিসাব আবার করতে হয় না।
  • initial-value — প্রাথমিক মান। '*' ছাড়া অন্য syntax হলে এটি বাধ্যতামূলক, আর এমন হতে হবে যা অন্য কিছুর ওপর নির্ভর করে না (0deg চলবে, 1em চলবে না)।

এবার আসল উদাহরণ — বাঁয়ের বৃত্তে নিবন্ধিত --angle, ডানের বৃত্তে সাধারণ --plain. দুটির animation হুবহু এক:

<style>
  @property --angle {
    syntax: '<angle>'; inherits: false; initial-value: 0deg;
  }
  .ring {
    display: inline-grid; place-items: center;
    width: 130px; height: 130px; margin: 8px; border-radius: 50%;
    background: conic-gradient(from var(--angle), #16a34a, #facc15, #dc2626, #16a34a);
    animation: spin 3s linear infinite;
  }
  .ring span { display: grid; place-items: center; width: 96px; height: 96px;
    border-radius: 50%; background: #fff; font: 700 14px sans-serif; }
  .plain { --plain: 0deg; animation-name: spin2;
    background: conic-gradient(from var(--plain), #16a34a, #facc15, #dc2626, #16a34a); }
  @keyframes spin  { to { --angle: 360deg; } }
  @keyframes spin2 { to { --plain: 360deg; } }
  @media (prefers-reduced-motion: reduce) { .ring { animation: none; } }
  .note { display: none; color: #b91c1c; }
</style>
<div class="ring"><span>@property</span></div>
<div class="ring plain"><span>সাধারণ var</span></div>
<p class="note" id="note">আপনার ব্রাউজার @property চেনে না — তাই বাঁয়েরটাও ঘুরবে না।</p>
<script>
  if (!window.CSSPropertyRule) document.getElementById('note').style.display = 'block';
</script>

Edge-এ (Chromium) বাঁয়ের রংধনু-বৃত্ত মসৃণভাবে ঘুরতে থাকে, ডানেরটা একদম স্থির — যদিও দুটির keyframe একই। ডানের --plain-এর ধরন অজানা, তাই অর্ধেক সময় সেটি 0deg, তারপর লাফিয়ে 360deg — আর এই দুই কোণ দেখতে হুবহু এক, তাই কিছুই নড়ে না। লাল সতর্কবার্তা শুধু সেই ব্রাউজারে দেখাবে যেখানে @property নেই। আপনার অপারেটিং সিস্টেমে “অ্যানিমেশন কমান” সেটিং চালু থাকলে বৃত্ত ঘুরবে না — এটি ইচ্ছাকৃত। চেষ্টা করুন: spin2-এর 360deg-কে 90deg করুন — ডানের বৃত্ত দেড় সেকেন্ড পরে এক ঝটকায় ঘুরে যাবে, তারপর আবার এক ঝটকায় ফিরবে; এটাই discrete animation. তারপর @property ব্লকে '<angle>'-কে '*' করুন — বাঁয়েরটাও থেমে যাবে।

@property ২০২৪ সালে Firefox যোগ দেওয়ায় চারটি প্রধান ব্রাউজারেই এসেছে — Baseline Newly available. যে পুরোনো ব্রাউজার এটি চেনে না, সে পুরো @property ব্লক উপেক্ষা করে, আর --angle সাধারণ variable হয়ে যায়। ফলে gradient ঠিকই দেখা যায়, শুধু ঘোরে না — এটিই সুন্দর graceful degradation. সতর্কতা: প্রথম মানের জন্য initial-value-এর ওপর ভরসা করলে পুরোনো ব্রাউজারে var(--angle) খালি থাকবে; তাই var(--angle, 0deg)-এর মতো দ্বিতীয় মান (fallback) লিখে রাখা নিরাপদ।

JavaScript থেকেও একই কাজ করা যায়: CSS.registerProperty({ name: '--angle', syntax: '<angle>', inherits: false, initialValue: '0deg' }). এটি CSS Houdini নামের API-গুচ্ছের অংশ। বাস্তবে ব্যবহার: বোতামে hover করলে gradient-এর রং মসৃণভাবে বদলানো ('<color>'), progress-বৃত্তের শতাংশ animate করা ('<percentage>'), বা ভুল মান ঢুকলে নিরাপদ প্রাথমিক মানে ফিরে যাওয়া।

৩ · Anchor positioning — tooltip আর dropdown বোতামের সাথে বাঁধা

পর্ব ২৪-এ দেখেছেন, tooltip বা dropdown-কে বোতামের পাশে রাখতে প্রচলিত কৌশল ছিল wrapper-এ position: relative, ভেতরে position: absolute. এর তিনটি সমস্যা: পূর্বপুরুষের overflow: hidden কেটে ফেলে, z-index-এর দলাদলি, আর পর্দার কিনারায় পৌঁছালে tooltip বাইরে চলে যায়। পর্ব ১১-এর popover প্রথম দুটি সমস্যা মেটায় — সে top layer-এ ওঠে। কিন্তু top layer-এ উঠে সে বোতামের ঠিকানা হারায়, ডিফল্টে বসে পর্দার মাঝখানে।

তৃতীয় সমস্যার সমাধান anchor positioning. ধারণাটি নৌকার নোঙরের মতো সরল: একটি element-কে নোঙর ঘোষণা করুন, আরেকটিকে বলুন “ওই নোঙরের সাপেক্ষে বসো।” দুটি DOM-এ যেখানেই থাকুক, ব্রাউজার জ্যামিতি মিলিয়ে দেবে। ধাপে ধাপে দেখুন:

1.help-btn {2  anchor-name: --help;3}4.tip {5  position: absolute;6  position-anchor: --help;7  position-area: top;8  position-try-fallbacks: flip-block;9}10@supports not (anchor-name: --a) {11  .tip { /* পুরোনো relative + absolute */ }12}
ব্রাউজারের হিসাব
নোঙর
tip কার সাথে
tip কোথায়
  1. নোঙর ঘোষণা। বোতামে anchor-name: --help. নামটি custom property-র মতো দুই হাইফেন দিয়ে শুরু, আর এটি একটি “ড্যাশড আইডেন্ট”, কোনো variable নয়। বোতামের চেহারায় কিছুই বদলায় না — শুধু ব্রাউজার মনে রাখে, “এই বাক্সের নাম --help.”
  2. ভাসমান element বাঁধা। tooltip-কে position: absolute (বা fixed) হতে হবে — anchor positioning কেবল এমন element-এই খাটে। position-anchor: --help বলে দেয় কোন নোঙরের সাপেক্ষে মাপ নেবে। এখনো জায়গা বলা হয়নি, তাই tip বসে তার স্বাভাবিক জায়গায়।
  3. জায়গা বাছাই — position-area. নোঙরকে মাঝখানে রেখে চারপাশকে ৩×৩ ঘরের ছক ভাবুন — টিক-ট্যাক-টো খেলার ঘরের মতো। top মানে ওপরের মাঝের ঘর; ব্রাউজার সেখানে tip বসায়, আর অনুভূমিকভাবে মাঝখানে আনে। আলাদা করে top, left হিসাব করতে হয় না।
  4. জায়গা না পেলে — fallback. বোতাম পর্দার একেবারে ওপরে স্ক্রল হয়ে গেলে tip কাটা পড়ত। position-try-fallbacks: flip-block বলে, “জায়গা না পেলে block দিকে (ওপর-নিচ) উল্টে যাও।” ব্রাউজার নিজেই পরীক্ষা করে, আর tip নিচে নামিয়ে দেয়। আগে এই কাজেই Popper বা Floating UI-এর মতো JavaScript লাইব্রেরি লাগত।
  5. যে ব্রাউজার চেনে না। সে anchor-name, position-area — সব অজানা বলে বাদ দেয়। তাই @supports not (anchor-name: --a)-এর ভেতরে পুরোনো কৌশল লিখে রাখুন: wrapper-এ relative, tip-এ absolute আর নির্দিষ্ট bottom. নিখুঁত না হলেও কাজ চলে — এটাই progressive enhancement.

প্রথমে মূল যন্ত্রটি নিজে চালান। এখানে position-area-র বদলে নিচু-স্তরের anchor() ফাংশন ব্যবহার করা হয়েছে — anchor(top) মানে “নোঙরের ওপরের কিনারা কোথায়”:

.wrap { padding: 70px 24px 24px; font-family: sans-serif; }
.btn  { anchor-name: --send; padding: 10px 16px; font-size: 16px; }
.tip {
  position: absolute;
  position-anchor: --send;
  bottom: anchor(top);   /* tip-এর নিচ = বোতামের ওপর */
  left: anchor(left);    /* tip-এর বাঁ = বোতামের বাঁ */
  margin-bottom: 8px;
  padding: 6px 10px; border-radius: 8px;
  background: #0f172a; color: #fff; font-size: 14px;
}
.note { display: none; color: #b91c1c; }
@supports not (anchor-name: --a) {
  .note { display: block; }
  .wrap { position: relative; }
  .tip  { bottom: auto; top: 20px; left: 24px; }
}

Edge-এ (Chromium) কালো tooltip বসেছে বোতামের ঠিক ওপরে, বাঁ কিনারা বোতামের বাঁ কিনারার সাথে মিলিয়ে, মাঝে 8px ফাঁক। লক্ষ করুন, কোথাও position: relative নেই — tip মাপ নিচ্ছে সরাসরি বোতাম থেকে। লাল নোট শুধু anchor positioning না-চেনা ব্রাউজারে দেখাবে, আর সেখানে tip পুরোনো কৌশলে প্রায় একই জায়গায় বসবে। চেষ্টা করুন: (১) left: anchor(left)-কে left: anchor(center) করুন, সাথে translate: -50% 0; যোগ করুন — tip বোতামের মাঝ বরাবর আসবে। (২) bottom আর left দুটি লাইন মুছে শুধু position-area: top; লিখুন — একই কাজ, অনেক কম হিসাবে। top-কে right করলে tip চলে যাবে বোতামের ডানে।

এবার বাস্তবের জোড়া: popover (পর্ব ১১) + anchor positioning. popover দেয় top layer, light dismiss (বাইরে ক্লিক বা Esc চাপলে বন্ধ) আর কীবোর্ড-সুবিধা; anchor positioning দেয় ঠিকানা। একটি অনলাইন দোকানের অর্ডার-পাতার “অপশন” মেনু:

<style>
  body { font-family: sans-serif; }
  .order { height: 130px; padding: 12px; background: #f1f5f9; border-radius: 10px; }
  .menu-btn { anchor-name: --menu; margin-top: 10px; padding: 8px 14px; }
  #menu { padding: 6px; border: 1px solid #94a3b8; border-radius: 10px; }
  #menu button { display: block; width: 100%; margin: 2px 0; padding: 6px 12px; }
  .spacer { height: 150px; }
  .note { display: none; color: #b91c1c; }
  @supports (anchor-name: --a) {
    #menu {
      position-anchor: --menu;
      position-area: bottom span-right;
      position-try-fallbacks: flip-block;
      inset: auto; margin: 6px 0;
    }
  }
  @supports not (anchor-name: --a) { .note { display: block; } }
</style>
<p class="note">anchor positioning নেই — মেনু পর্দার মাঝখানে খুলবে, তবু কাজ করবে।</p>
<div class="order">অর্ডার #৪৫১২ · জামদানি শাড়ি · ৳ ৮,৫০০</div>
<button class="menu-btn" popovertarget="menu">অপশন ▾</button>
<div id="menu" popover>
  <button popovertarget="menu" popovertargetaction="hide">অর্ডার ট্র্যাক করুন</button>
  <button popovertarget="menu" popovertargetaction="hide">রসিদ দেখুন</button>
</div>
<div class="spacer"></div>

“অপশন ▾” চাপুন। Edge-এ (Chromium) মেনু খোলে বোতামের ঠিক নিচে, বাঁ কিনারা মিলিয়ে, ডানে ছড়িয়ে (span-right). বাইরে কোথাও ক্লিক করুন বা Esc চাপুন — বন্ধ হবে; এটি popover-এর light dismiss. পুরোনো ব্রাউজারে মেনু খোলে ডিফল্টে, পর্দার মাঝখানে — অসুন্দর হলেও ব্যবহারযোগ্য। এখন fallback দেখুন: .spacer-এর height 0 করুন। বোতাম এখন প্রিভিউর একেবারে নিচে, নিচে জায়গা নেই — খুলে দেখুন, মেনু উল্টে বোতামের ওপরে খুলবে (flip-block). তারপর position-try-fallbacks লাইনটি মুছে আবার খুলুন — এবার মেনু নিচে কেটে যাবে।

অ্যাক্সেসিবিলিটি ভুলবেন না। anchor positioning শুধু জায়গা ঠিক করে — অর্থ নয়। tooltip হলে বোতামের সাথে aria-describedby দিয়ে জুড়ুন, শুধু hover নয় focus-এও দেখান, Esc-এ লুকানো যায় এমন রাখুন (WCAG 1.4.13). আর ভিজ্যুয়াল জায়গা বদলালেও DOM-এর ক্রম বদলায় না — screen reader আর Tab-এর ক্রম DOM অনুযায়ী চলে, তাই popover-কে DOM-এ বোতামের কাছাকাছি রাখাই ভালো। Popover-এর বেলায় ব্রাউজার popovertarget বোতাম আর popover-এর সম্পর্ক নিজেই অ্যাক্সেসিবিলিটি API-কে জানায়।

সাপোর্টের সৎ চিত্র: anchor positioning Chromium-এ (Chrome, Edge, Opera, Samsung Internet) ২০২৪ সাল থেকে আছে, Safari 26-এ ২০২৫ সালে এসেছে, আর Firefox-এ এর কাজ Interop 2025-এর অংশ হিসেবে চলছিল। এই লেখার সময় একে নিশ্চিতভাবে “সবখানে আছে” বলা যায় না — আপনি পড়ার সময় হয়তো Baseline Newly available হয়ে গেছে, Can I use-এ দেখে নিন। একটি ইতিহাসও জেনে রাখুন: খসড়ার সময় position-area-র নাম ছিল inset-area, আর position-try-fallbacks-এর নাম ছিল position-try-options. পুরোনো ব্লগ বা AI এই পুরোনো নাম দিলে বর্তমান ব্রাউজারে সেগুলো কাজ করবে না।

৪ · @scope — নিয়মকে একটি এলাকায় আটকে রাখা

বড় সাইটে CSS-এর সবচেয়ে পুরোনো মাথাব্যথা: একটি কার্ডের জন্য লেখা .card a { … } নিয়ম কার্ডের ভেতরে বসানো ব্যবহারকারীর মন্তব্যের লিংকেও লেগে যায়, বা অন্য কোনো component-এর ভেতরে ঢুকে পড়ে। এর সমাধানে মানুষ BEM-এর মতো নামকরণের নিয়ম (পর্ব ৪৬), CSS Modules, বা Shadow DOM ব্যবহার করেছেন। পর্ব ১৮-এ আপনি @scope-এর এক ঝলক দেখেছেন; এবার পুরোটা।

@scope-এর দুটি অংশ। scoping root — কোথা থেকে নিয়ম শুরু; আর ঐচ্ছিক scoping limit — কোথায় থামবে। দুটি একসাথে দিলে মাঝখানে ফুটোওয়ালা একটি এলাকা তৈরি হয়, তাই একে বলে “donut scope”:

@scope (.post) to (.comments) {
  :scope { border: 2px solid green; }  /* .post নিজেই */
  a      { font-weight: 700; }         /* .post-এর লিংক, .comments-এর নয় */
}

একটি বাড়ির রং করার চুক্তির কথা ভাবুন: “পুরো তিনতলা রং করবেন (root), কিন্তু ভাড়াটের ঘর বাদ (limit) — সেটা তাঁর নিজের পছন্দে।” ভাড়াটের ঘরে কী আছে রংমিস্ত্রির জানার দরকার নেই, শুধু দরজাটা চিনলেই হলো। ঠিক এভাবে কার্ড-component জানে না ভেতরে কী মন্তব্য বসবে — শুধু জানে কোথায় তার এলাকা শেষ।

আরেকটি নতুন ধারণা scoping proximity. পর্ব ১৮-এর cascade-এ specificity-র পরে, source order-এর আগে একটি ধাপ আছে: দুটি scope-নিয়মের specificity সমান হলে যে নিয়মের root element-টির সবচেয়ে কাছে, সে জেতে। এতে থিমের ভেতরে থিম (dark-এর ভেতরে light কার্ড) স্বাভাবিকভাবে কাজ করে। দুটিই একসাথে চালিয়ে দেখুন:

<style>
  body { font-family: sans-serif; }
  .note { color: #b91c1c; }
  @scope (body) { .note { display: none; } }

  /* ডোনাট: .post-এর ভেতরে, কিন্তু .comments-এর ভেতরে নয় */
  @scope (.post) to (.comments) {
    :scope { display: block; padding: 4px 12px; border: 2px solid #15803d; border-radius: 8px; }
    a { color: #15803d; font-weight: 700; }
  }
  /* নৈকট্য: কাছের root জেতে, কোডের ক্রম নয় */
  @scope (.light) { p { color: #0f172a; background: #e2e8f0; } }
  @scope (.dark)  { p { color: #fff; background: #1e293b; } }
  .dark, .light { padding: 6px; }
</style>
<p class="note">আপনার ব্রাউজার @scope চেনে না — তাই নিচে কোনো scope-নিয়মই খাটছে না।</p>
<article class="post">
  <p>নতুন পোস্ট: <a href="#r">সরিষা ইলিশের রেসিপি</a></p>
  <div class="comments"><p>মন্তব্য: <a href="#b">আমার ব্লগ দেখুন</a></p></div>
</article>
<div class="dark"><p>dark থিমের লেখা</p>
  <div class="light"><p>dark-এর ভেতরে light কার্ড</p></div>
</div>

Edge-এ (Chromium) পোস্টের চারপাশে সবুজ বর্ডার, “সরিষা ইলিশের রেসিপি” লিংক সবুজ ও মোটা; কিন্তু মন্তব্যের “আমার ব্লগ দেখুন” লিংক ব্রাউজারের সাধারণ নীল — সেটি ডোনাটের ফুটোয়। নিচে dark বাক্সের লেখা গাঢ় পটে সাদা, আর ভেতরের light কার্ডের লেখা হালকা ধূসর পটে কালো — যদিও .dark-এর নিয়ম কোডে পরে লেখা। ভেতরের p-এর কাছের root .light (এক ধাপ), .dark দুই ধাপ দূরে — proximity-তে light জেতে। চেষ্টা করুন: (১) to (.comments) মুছে দিন — মন্তব্যের লিংকও সবুজ হবে। (২) শেষ দুটি @scope-কে সাধারণ .light p { … } আর .dark p { … }-এ বদলান — এবার specificity সমান, proximity নেই, তাই পরে লেখা dark জিতবে আর light কার্ডও গাঢ় হয়ে যাবে।

তিনটি সূক্ষ্ম নিয়ম মনে রাখুন। এক, scoping root-এর selector নিয়মের specificity-তে যোগ হয় না — @scope (#main) { p {…} }-এর specificity শুধু p-এর (0,0,1); তবে নিয়মের ভেতরে :scope লিখলে সেটি একটি pseudo-class হিসেবে (0,1,0) যোগ করে। দুই, limit element নিজেও scope-এর বাইরে — .comments-এর গায়ে নিয়ম লাগে না। তিন, <style>-এর ভেতরে selector ছাড়া @scope { … } লিখলে root হয় সেই <style>-এর parent element — component-এর ভেতরে ছোট ছোট style রাখার সহজ উপায়।

সাপোর্ট: Chromium-এ ২০২৩ সাল থেকে, Safari-তে ২০২৪ সাল থেকে; Firefox সবার শেষে, ২০২৫-এর শেষ দিকে যোগ দিয়েছে বলে আমাদের জানা — অর্থাৎ @scope একেবারে সদ্য Baseline Newly available. Fallback-এর কৌশলটি উপরের নোটে দেখলেন: যে ব্রাউজার @scope চেনে না, সে পুরো ব্লক বাদ দেয়। তাই scope-এর ভেতরে শুধু এমন নিয়ম রাখুন যা না খাটলেও পাতা ব্যবহারযোগ্য থাকে, অথবা পাশে একটি সাধারণ descendant selector-এর সংস্করণও রাখুন।

৫ · View Transitions — দুই অবস্থার মাঝে মসৃণ বদল

মোবাইল অ্যাপে একটি ছবিতে ট্যাপ করলে সেটি বড় হয়ে নতুন পাতায় চলে যায় — চোখ বুঝতে পারে কী কোথায় গেল। ওয়েবে বহু বছর এটি করা খুব কঠিন ছিল: পুরোনো আর নতুন অবস্থা একই সময়ে পর্দায় রাখতে হতো, দুটির মাপ মেপে অ্যানিমেশন হাতে লিখতে হতো। View Transitions এই কঠিন অংশটি ব্রাউজারের হাতে দিয়েছে। ব্রাউজার ভেতরে কী করে, ধাপে ধাপে দেখুন:

  1. ক্লিকstartViewTransition(cb)আপনার কোড ডাকল
  2. পুরোনো ছবিold snapshotবর্তমান অবস্থা
  3. DOM বদলcb()পর্দা স্থির থাকে
  4. অ্যানিমেশন::view-transitionপুরোনো → নতুন
  5. শেষfinishedআসল পাতা দেখা যায়
pseudo-element গাছঅ্যানিমেশনের সময় পাতার ওপরে
::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)   ← পুরোনো ছবি, মিলিয়ে যায়
      └─ ::view-transition-new(root)   ← নতুন অবস্থা, ফুটে ওঠে
  1. আপনার কোড অনুরোধ করে। DOM সরাসরি না বদলে আপনি বলেন document.startViewTransition(update), যেখানে update হলো যে ফাংশন DOM বদলাবে (class যোগ, লেখা বদল ইত্যাদি)।
  2. পুরোনো অবস্থার ছবি। ব্রাউজার বর্তমান পাতার একটি ছবি (snapshot) তোলে — পুরো পাতার নাম root; যেসব element-এ আলাদা view-transition-name দেওয়া, সেগুলোর আলাদা ছবি।
  3. DOM বদল, কিন্তু পর্দা স্থির। এবার ব্রাউজার আপনার update চালায়। এই সময়টুকু রেন্ডারিং থামানো থাকে, তাই ব্যবহারকারী আধা-বদলানো পাতা দেখেন না। তারপর নতুন অবস্থার “জীবন্ত ছবি” প্রস্তুত হয়।
  4. pseudo-element-এর গাছে অ্যানিমেশন। পাতার ওপরে একটি অস্থায়ী গাছ তৈরি হয় (ডান দিকে দেখুন)। ডিফল্টে ::view-transition-old মিলিয়ে যায়, ::view-transition-new ফুটে ওঠে (cross-fade); আর নাম দেওয়া element-এর group পুরোনো জায়গা ও মাপ থেকে নতুনটিতে সরে যায়। এই সব pseudo-element-এ সাধারণ CSS animation দিয়ে নিজের ইচ্ছামতো অ্যানিমেশন বসানো যায়।
  5. শেষ — আসল পাতা ফিরে আসে। অ্যানিমেশন শেষে অস্থায়ী গাছ মুছে যায়, নিচের আসল (নতুন) পাতা দেখা যায়। JavaScript-এ transition.finished promise তখন পূর্ণ হয়। যে ব্রাউজার এটি চেনে না, সেখানে আপনার কোড সরাসরি update() ডাকবে — বদল হবে, শুধু অ্যানিমেশন ছাড়া।

এবার নিজে চালান। তিনটি জেলার কার্ড — বোতাম চাপলে তিন কলাম থেকে এক কলামের তালিকা হয়। প্রতিটি কার্ডে আলাদা view-transition-name, তাই প্রতিটি আলাদা করে নতুন জায়গায় সরে যায়। স্ক্রিপ্টটি মাত্র কয়েক লাইনের, আর পুরোনো ব্রাউজারের জন্য বিকল্পসহ:

<style>
  body { font-family: sans-serif; }
  .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px; }
  .grid.list { grid-template-columns: 1fr; }
  .card { padding: 16px; border-radius: 10px; color: #fff; font-weight: 700; }
  ::view-transition-group(*) { animation-duration: 0.6s; }
  @media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
      animation: none !important;
    }
  }
</style>
<button id="toggle">লেআউট বদলান</button>
<p id="note" hidden>আপনার ব্রাউজারে View Transitions নেই — লেআউট বদলাবে, অ্যানিমেশন ছাড়া।</p>
<div class="grid" id="grid">
  <div class="card" style="background:#0f766e; view-transition-name: c1">ঢাকা</div>
  <div class="card" style="background:#b45309; view-transition-name: c2">চট্টগ্রাম</div>
  <div class="card" style="background:#7c3aed; view-transition-name: c3">সিলেট</div>
</div>
<script>
  const grid = document.getElementById('grid');
  const update = () => grid.classList.toggle('list');
  if (!document.startViewTransition) document.getElementById('note').hidden = false;
  document.getElementById('toggle').addEventListener('click', () => {
    if (!document.startViewTransition) return update();   // fallback
    document.startViewTransition(update);
  });
</script>

শুরুতে তিনটি রঙিন কার্ড এক সারিতে। “লেআউট বদলান” চাপুন — Edge-এ (Chromium) কার্ডগুলো প্রায় আধা সেকেন্ড ধরে নিজের নিজের পথে সরে ও চওড়া হয়ে একটির নিচে আরেকটি বসে; আবার চাপলে ফিরে যায়। আমরা কোথাও কোনো @keyframes লিখিনি — মাপ আর জায়গার অ্যানিমেশন ব্রাউজার নিজেই হিসাব করেছে। অপারেটিং সিস্টেমে অ্যানিমেশন কমানোর সেটিং চালু থাকলে বদল হবে সাথে সাথে। চেষ্টা করুন: (১) তিনটি view-transition-name মুছে দিন — এবার পুরো পাতা শুধু একসাথে cross-fade করবে, কার্ডগুলো আলাদা করে সরবে না। (২) 0.6s-কে 2s করুন — ধীরগতিতে স্পষ্ট দেখবেন group কীভাবে মাপ বদলায়। মনে রাখুন, একই view-transition-name একই সময়ে দুটি element-এ থাকলে transition বাতিল হয়ে যায় — নাম অনন্য হতে হবে।

পাতা থেকে পাতায় — cross-document view transition

উপরেরটি “same-document” — একই পাতার ভেতরে বদল, যেখানে JavaScript লাগে। পর্ব ১৫-এর মতো সাধারণ বহু-পাতার সাইটে (একটি লিংকে ক্লিক করলে পুরো নতুন HTML পাতা আসে) আরও সহজ উপায় আছে: cross-document view transition. দুটি পাতার CSS-এই একটি লাইন লিখলে, একই origin-এর ভেতরে এক পাতা থেকে আরেক পাতায় যাওয়ার সময় ব্রাউজার নিজেই cross-fade করে — JavaScript ছাড়াই:

/* দুই পাতাতেই */
@view-transition {
  navigation: auto;
}
/* লোগোকে দুই পাতাতেই একই নাম দিলে, লোগো জায়গা বদলে সরে যাবে */
.site-logo { view-transition-name: logo; }

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

এটি এখানে চালিয়ে দেখানো যাচ্ছে না, কারণ প্রিভিউ একটিমাত্র ডকুমেন্ট, আর প্রিভিউর ভেতরে অন্য পাতা খোলে না। নিজের কম্পিউটারে দুটি HTML ফাইল বানিয়ে local server দিয়ে খুলে (পর্ব ২) চেষ্টা করতে পারেন। সাপোর্ট: same-document রূপটি Chromium, Safari আর ২০২৫-এর শেষ দিকে Firefox-এ আসায় Baseline Newly available; কিন্তু cross-document রূপটি এখনো Chromium আর Safari-তে, Firefox-এ নয় — not yet Baseline. সৌভাগ্যক্রমে এটি নিখুঁত progressive enhancement: যে ব্রাউজার @view-transition চেনে না, সে সাধারণভাবেই পাতা বদলায়।

নড়াচড়া সবার জন্য আরামদায়ক নয়। ভেস্টিবুলার সমস্যা (ভারসাম্য ও মাথা ঘোরা সংক্রান্ত), মাইগ্রেন বা মনোযোগের সমস্যা আছে এমন অনেক মানুষের কাছে বড় জায়গা-বদলের অ্যানিমেশন শারীরিক অস্বস্তি তৈরি করে। তাঁরা অপারেটিং সিস্টেমে “অ্যানিমেশন কমান” সেটিং চালু রাখেন, যা CSS-এ prefers-reduced-motion: reduce হিসেবে পৌঁছায়। প্রতিটি view transition, @property-ঘোরানো gradient আর বড় অ্যানিমেশনে এই media query-র সম্মান রাখুন — পর্ব ৪৫-এ এটি বিস্তারিত।

৬ · ফর্মের নতুন সুবিধা — field-sizing আর customizable select

ফর্ম (পর্ব ৮–৯) ওয়েবের সবচেয়ে পুরোনো অংশগুলোর একটি, আর এখানেই দুটি অনেক দিনের অভিযোগের সমাধান আসছে। প্রথমটি: মন্তব্যের textarea লেখার সাথে সাথে বড় হোক। এতদিন এর জন্য JavaScript দিয়ে প্রতিটি কি-চাপে উচ্চতা মাপতে হতো। নতুন field-sizing: content বলে, “বাক্সের মাপ ঠিক করবে ভেতরের লেখা”:

body { font-family: sans-serif; }
input, textarea {
  field-sizing: content;   /* লেখা অনুযায়ী মাপ */
  min-width: 6ch;          /* খালি হলেও খুব সরু নয় */
  max-width: 100%;         /* কনটেইনারের বাইরে নয় */
  font: inherit;
  padding: 6px 8px;
}
textarea { min-height: 3em; }
.note { display: none; color: #b91c1c; }
@supports not (field-sizing: content) {
  .note { display: block; }
}

Edge-এ (Chromium) “রহিম” লেখা input-টি ঠিক লেখার সমান চওড়া, আর textarea-র চওড়া ঠিক লেখার লাইনের সমান (উচ্চতা অন্তত 3em, কারণ min-height দেওয়া আছে)। input-এ আরও কিছু লিখুন — বাক্স ডানে বাড়তে থাকবে; textarea-তে Enter চেপে কয়েক লাইন লিখুন — নিচে বাড়বে, কোনো scrollbar আসবে না। max-width: 100% না দিলে খুব লম্বা লেখায় বাক্স পাতার বাইরে চলে যেত — field-sizing-এর সাথে সবসময় একটি সীমা দিন। চেষ্টা করুন: min-width: 6ch মুছে input খালি করুন — বাক্স প্রায় অদৃশ্য সরু হয়ে যাবে, যা ব্যবহারকারীকে বিভ্রান্ত করে। এটি এখনো not yet Baseline — মূলত Chromium-এ — তবে যে ব্রাউজার চেনে না, সেখানে শুধু সাধারণ নির্দিষ্ট মাপের বাক্স থাকে, ক্ষতি নেই।

দ্বিতীয় অভিযোগটি আরও পুরোনো: <select>-এর খোলা তালিকা CSS দিয়ে সাজানো যায় না, কারণ সেটি আঁকে অপারেটিং সিস্টেম। তাই ডিজাইনাররা div দিয়ে নকল dropdown বানাতেন — যা প্রায়ই কীবোর্ড আর screen reader-এ ভাঙত। নতুন customizable select-এ এক লাইনের opt-in: appearance: base-select. তখন বোতাম, তালিকা (::picker(select)), টিকচিহ্ন (::checkmark) — সব CSS দিয়ে সাজানো যায়, আর আসল select-এর কীবোর্ড ও অ্যাক্সেসিবিলিটি অক্ষত থাকে:

body { font-family: sans-serif; padding-bottom: 150px; }
select, ::picker(select) {
  appearance: base-select;     /* নতুন রূপে যোগ দেওয়া */
}
select {
  border: 2px solid #0f766e; border-radius: 10px;
  padding: 6px 12px; background: #f0fdfa;
}
::picker(select) {
  border: 1px solid #0f766e; border-radius: 10px;
}
option:checked { background: #ccfbf1; font-weight: 700; }
option::checkmark { content: '✔'; color: #0f766e; }
.note { display: none; color: #b91c1c; }
@supports not (appearance: base-select) {
  .note { display: block; }
}

Edge-এর নতুন সংস্করণে (Chromium) select-টি সবুজ-বর্ডারের গোলাকার বোতামের মতো; খুললে তালিকাটিও গোল কোণার, আর বাছাই করা অপশনের পাশে সবুজ ✔. Tab দিয়ে এসে ↑↓ আর Enter দিয়ে বাছাই করে দেখুন — কীবোর্ড পুরোপুরি কাজ করে। চেষ্টা করুন: option:checked-এর পটভূমির রং বদলান, বা content: '✔'-কে '→' করুন। প্রথম নিয়মটি মুছে দিলে সাথে সাথে অপারেটিং সিস্টেমের সাধারণ তালিকা ফিরে আসবে। এটি পরীক্ষামূলক — এখন শুধু Chromium-এ, spec-এর খুঁটিনাটি এখনো বদলাতে পারে। কিন্তু নকশাটাই progressive enhancement: অন্য ব্রাউজারে সাধারণ, পুরোপুরি কাজের select দেখাবে।

৭ · height: auto পর্যন্ত animation, স্ক্রল-অ্যানিমেশন আর আগামীর তালিকা

CSS-এর একটি বিখ্যাত অসম্ভব কাজ: height: 0 থেকে height: auto-তে transition. FAQ-এর উত্তর খোলার সময় মসৃণভাবে নামুক — এটুকুই চাওয়া, কিন্তু ব্রাউজার auto শব্দটির মাঝামাঝি মান হিসাব করত না, ফলে এক লাফে খুলত। মানুষ max-height: 1000px-এর মতো কৌশল করতেন, যাতে সময় ভুল হতো, বা JavaScript দিয়ে মেপে নিতেন।

নতুন সমাধান দুটি। interpolate-size: allow-keywords :root-এ দিলে পুরো পাতায় auto, min-content, fit-content-এর মতো keyword-এ animate করার অনুমতি মেলে। আর calc-size(auto, size) একটি নির্দিষ্ট জায়গায় একই কাজ করে, এমনকি হিসাবও করা যায় (calc-size(auto, size + 20px)). opt-in কেন? কারণ পুরোনো সাইট হয়তো ধরে রেখেছে যে auto-তে লাফ দেবে — সবার জন্য হঠাৎ বদলে দিলে সেগুলো ভাঙত।

:root { interpolate-size: allow-keywords; }
body { font-family: sans-serif; }
.faq { max-width: 360px; padding: 10px; border: 1px solid #cbd5e1; border-radius: 10px; }
.answer {
  height: 0;
  overflow: hidden;
  transition: height 0.5s ease;
}
.faq:has(:checked) .answer {
  height: auto;            /* এখন এটিও animate হয় */
}
@media (prefers-reduced-motion: reduce) {
  .answer { transition: none; }
}
.note { display: none; color: #b91c1c; }
@supports not (interpolate-size: allow-keywords) {
  .note { display: block; }
}

প্রশ্নের পাশের checkbox চাপুন — Edge-এ (Chromium) উত্তরটি আধা সেকেন্ড ধরে মসৃণভাবে নেমে আসে, আবার চাপলে উঠে যায়। কোথাও উচ্চতার সংখ্যা লেখা নেই; লেখা বাড়লেও ঠিক থাকবে। চেষ্টা করুন: প্রথম লাইন :root { … } মুছে দিন — এবার উত্তর এক লাফে খুলবে, যা পুরোনো আচরণ। তারপর height: auto-কে height: calc-size(auto, size) করুন — প্রথম লাইন ছাড়াই আবার মসৃণ হবে। এটি not yet Baseline (শুধু Chromium), কিন্তু অন্য ব্রাউজারে শুধু অ্যানিমেশনটুকু বাদ পড়ে — উত্তর ঠিকই খোলে। বাস্তবে এমন খোলা-বন্ধের জন্য আগে <details> (পর্ব ১১) ভাবুন; তার ভেতরের অংশ ::details-content দিয়ে একইভাবে animate করা যায়।

স্ক্রল ধরে অ্যানিমেশন — ঝলকমাত্র

Scroll-driven animation হলো এমন CSS animation, যা সময় ধরে নয়, স্ক্রল ধরে এগোয় — পাতার ওপরে পড়ার অগ্রগতির দাগ, বা পর্দায় ঢোকার সাথে সাথে ফুটে ওঠা ছবি। মূল কৌশল একটি property: animation-timeline: scroll() বা view(). এটি Chromium আর Safari 26-এ আছে, Firefox-এ এখনো পরীক্ষামূলক — not yet Baseline. পুরো আলোচনা, উদাহরণ আর fallback পর্ব ৪২-এ।

নজরে রাখার তালিকা — if() আর sibling-index()

দুটি ফিচার এসেছে মূলত Chromium-এ; অন্য ইঞ্জিনে এগুলো এখনো আলোচনায় বা সবে আসছে। এগুলো দিয়ে আজ প্রোডাকশন সাইট বানাবেন না, তবে দিক বোঝার জন্য চিনে রাখুন:

  • if() — একটি property-র ভেতরেই শর্ত: color: if(style(--theme: dark): white; else: black);. এতদিন এই কাজে আলাদা selector বা media query লাগত।
  • sibling-index() ও sibling-count() — element কত নম্বর child, আর মোট কয়জন। যেমন তালিকার প্রতিটি আইটেম একটু দেরিতে ফুটে উঠুক: transition-delay: calc(sibling-index() * 80ms); — আগে প্রতিটির জন্য আলাদা :nth-child() নিয়ম লিখতে হতো।

৮ · একসাথে ব্যবহার — @supports, স্তরে স্তরে fallback আর পেশাদার সিদ্ধান্ত

এই পর্বের প্রতিটি উদাহরণে একই নকশা ছিল, খেয়াল করেছেন? আগে এমন কোড যা সবখানে চলে, তারপর @supports দিয়ে নতুন ফিচারের স্তর। এর নাম progressive enhancement. বাড়ি বানানোর মতো ভাবুন: আগে ঢালাই আর দেয়াল (সবার জন্য), তারপর রং আর নকশা (যার সামর্থ্য আছে)। নকশা না থাকলে বাড়ি দেখতে সাধারণ, কিন্তু থাকার অযোগ্য নয়।

/* স্তর ১ — সবার জন্য: পুরোনো, নির্ভরযোগ্য কৌশল */
.help { position: relative; }
.help .tip { position: absolute; bottom: 100%; left: 0; }

/* স্তর ২ — যে ব্রাউজার পারে: নতুন কৌশল */
@supports (anchor-name: --a) {
  .help .btn { anchor-name: --help; }
  .help .tip {
    position-anchor: --help;
    inset: auto;                 /* স্তর ১-এর মান মুছে দিই */
    position-area: top;
    position-try-fallbacks: flip-block;
  }
}

এখানে তিনটি সূক্ষ্ম বিষয়। প্রথম, স্তর ২-এ স্তর ১-এর bottom/left মুছে দিতে হয়েছে — নইলে দুটি নিয়ম মিশে অদ্ভুত ফল দিত। দ্বিতীয়, শুধু property-র নাম নয়, একটি পুরো মান যাচাই করুন: @supports (position-area: top) বেশি নিখুঁত, কারণ কোনো ব্রাউজার property চিনলেও সব মান না-ও চিনতে পারে। তৃতীয়, CSS নিজেই ক্ষমাশীল (পর্ব ১৬) — অচেনা property বা at-rule ব্রাউজার চুপচাপ বাদ দেয়। তাই অনেক ক্ষেত্রে, যেমন field-sizing বা @view-transition-এ, @supports ছাড়াই শুধু নতুন লাইন যোগ করলেই চলে; @supports লাগে যখন নতুন কৌশল পুরোনো কৌশলকে বদলে দেয়।

সিদ্ধান্তের নিয়ম

Widely available → নিশ্চিন্তে ব্যবহার করুন। Newly available → ব্যবহার করুন, তবে পুরোনো ব্রাউজারে পাতা ভাঙে না তা যাচাই করুন। not yet Baseline → শুধু বাড়তি সুবিধা হিসেবে, পাশে fallback রেখে। পরীক্ষামূলক বা শুধু এক ইঞ্জিনে → শিখুন, demo বানান, প্রোডাকশনের মূল কাজে নয়। আর যেকোনো ক্ষেত্রে আপনার নিজের দর্শকদের পরিসংখ্যান (analytics) জাতীয় গড়ের চেয়ে বেশি গুরুত্বপূর্ণ।

আরও গভীরে: anchor positioning-এর নকশায় একটি কঠিন সমস্যা ছিল লেআউটের চক্র — tooltip-এর জায়গা নির্ভর করে বোতামের ওপর, আবার tooltip-এর মাপ যদি বোতামের জায়গা বদলায়, তাহলে হিসাব কখনো শেষ হবে না। তাই spec-এ “acceptable anchor element”-এর নিয়ম আছে: নোঙরকে এমন হতে হবে যার লেআউট ভাসমান element-এর আগেই শেষ হয় (যেমন DOM-এ আগে বা বাইরের দিকে থাকা), নইলে ব্রাউজার নোঙরটি উপেক্ষা করে। একইভাবে @property-র initial-value “computationally independent” হতে হয় — 1em বা var() চলে না — কারণ প্রাথমিক মান কোনো element-এর ওপর নির্ভর করলে সেটি আর “প্রাথমিক” থাকে না। নতুন ফিচারের এমন বিধিনিষেধ প্রায়ই ব্রাউজারের ভেতরের পারফরম্যান্স আর নিশ্চয়তার দাম।

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

  • সাধারণ CSS variable দিয়ে gradient-এর কোণ animate হয় না কেন? — ব্রাউজার এর ধরন জানে না, তাই discrete-ভাবে লাফ দেয়; @property দিয়ে '<angle>' ধরন জানালে মসৃণ হয়।
  • Progressive enhancement আর graceful degradation-এর পার্থক্য? — প্রথমটি সাধারণ ভিত্তি থেকে শুরু করে নতুন স্তর যোগ করে; দ্বিতীয়টি পূর্ণ সংস্করণ বানিয়ে পুরোনো ব্রাউজারে কম সুবিধায় নামিয়ে আনে।
  • @supports কী করে, আর এর সীমা কী? — ব্রাউজার একটি property-মান জোড়া চেনে কি না যাচাই করে; চেনা মানেই নির্ভুল বাস্তবায়ন নয়, তাই নিজে পরীক্ষাও করতে হয়।
  • Baseline Newly আর Widely available-এর পার্থক্য? — Newly: সব প্রধান ব্রাউজারের সর্বশেষ সংস্করণে; Widely: তার প্রায় ৩০ মাস পরে, যখন বেশিরভাগ ব্যবহারকারীর কাছে পৌঁছেছে।
  • Tooltip বোতামের পাশে রাখার আধুনিক উপায় কী? — anchor positioning (anchor-name, position-anchor, position-area), প্রায়ই popover-এর সাথে, আর পুরোনো ব্রাউজারের জন্য relative + absolute বিকল্প।

৯ · AI-এর যুগে — “নতুন CSS” নিয়ে AI-এর ভুল ধরা

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

AI যা দিতে পারেসমস্যাঠিক কী
inset-area: top, position-try-optionsখসড়ার পুরোনো নাম; বর্তমান ব্রাউজারে কাজ করে নাposition-area, position-try-fallbacks
tooltip-এর জন্য Popper.js বা স্ক্রিপ্টে মাপজোখঅপ্রয়োজনীয় JavaScriptanchor positioning + fallback
“@scope সব ব্রাউজারে বহুদিন ধরে আছে”ভুল দাবি — Firefox সবে যোগ দিয়েছেCan I use / MDN-এর Baseline দেখে নিশ্চিত হোন
View transition, কিন্তু reduced motion-এর কোনো ব্যবস্থা নেইঅ্যাক্সেসিবিলিটির ঘাটতিprefers-reduced-motion-এ animation বন্ধ
max-height: 9999px দিয়ে খোলা-বন্ধসময় ভুল, পুরোনো কৌশলinterpolate-size / calc-size() + <details>
@supports বা fallback ছাড়াই শুধু Chromium-এর ফিচারঅন্য ব্রাউজারে পাতা ভাঙেস্তরে স্তরে লেখা, আগে সাধারণ কৌশল

AI-কে নতুন CSS-এর কাজ দিলে প্রম্পটেই সাপোর্ট আর fallback-এর দাবি রাখুন, আর বলুন যেখানে নিশ্চিত নয় সেখানে স্বীকার করতে:

একটি অনলাইন দোকানের "সাহায্য" বোতামের পাশে tooltip বানাতে চাই, শুধু HTML আর CSS দিয়ে। ১. CSS anchor positioning ব্যবহার করো — বর্তমান নাম (position-area, position-try-fallbacks), পুরোনো খসড়ার নাম নয়। ২. যে ব্রাউজার anchor positioning চেনে না, তার জন্য @supports দিয়ে relative + absolute fallback দাও। ৩. tooltip যেন hover আর keyboard focus দুটোতেই দেখায়, Esc-এ বন্ধ হয়, আর aria-describedby দিয়ে বোতামের সাথে যুক্ত থাকে। ৪. প্রতিটি নতুন ফিচারের Baseline অবস্থা বলো; নিশ্চিত না হলে "নিশ্চিত নই, Can I use দেখুন" লেখো — অনুমান করবে না।

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

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

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে শুধু syntax নয়, ওয়েব প্ল্যাটফর্মের বিবর্তন বুঝতে সাহায্য করবে।

প্র ০১Tooltip বোতামের পাশে বসানো — এত সাধারণ একটি কাজের জন্য CSS-এ নিজস্ব ব্যবস্থা আসতে এত বছর লাগল কেন?

শুনতে সহজ, কিন্তু কাজটি আসলে তিনটি আলাদা কঠিন সমস্যার জোট। প্রথম, জ্যামিতি: CSS-এর পুরোনো মডেলে একটি absolute বাক্স মাপ নেয় শুধু তার containing block থেকে — কোনো “ভাই” বা দূরের element থেকে নয়। বোতাম আর tooltip DOM-এ দূরে থাকলে CSS-এর কাছে তাদের সম্পর্ক জানানোর কোনো ভাষাই ছিল না। দ্বিতীয়, কাটা পড়া আর স্তর: পূর্বপুরুষের overflow: hidden আর stacking context ভাসমান বাক্সকে কেটে বা ঢেকে ফেলত। তৃতীয়, সংঘর্ষ এড়ানো: পর্দার কিনারায় পৌঁছালে উল্টো দিকে সরানো — এর জন্য বারবার মেপে দেখতে হয়।

তাই এক দশকের বেশি সময় এই কাজ করেছে JavaScript লাইব্রেরি — প্রথমে Tether-এর মতো লাইব্রেরি, পরে Popper, তারপর Floating UI. এগুলো স্ক্রল আর resize-এ বারবার মাপ নিয়ে জায়গা হিসাব করত — এতে কোড বাড়ত, আর ধীর ফোনে tooltip কাঁপত বা পিছিয়ে পড়ত।

প্ল্যাটফর্ম সমস্যাগুলো আলাদা আলাদা ভেঙে সমাধান করেছে। top layer (<dialog>, popover) সমাধান করল কাটা পড়া আর স্তরের সমস্যা। anchor positioning দিল জ্যামিতির ভাষা আর সংঘর্ষ এড়ানোর position-try. কিন্তু spec লিখতেই কয়েক বছর লেগেছে, কারণ লেআউটের চক্র এড়াতে হয়েছে (নোঙর যদি tooltip-এর ওপর নির্ভর করে, হিসাব কখনো শেষ হবে না), আর নামও কয়েকবার বদলেছে (inset-area থেকে position-area). Chromium প্রথমে বাজারে আনার পর Interop 2025-এর লক্ষ্যের তালিকায় এটি জায়গা পায়, যাতে অন্য ইঞ্জিনগুলো একই আচরণে বানায়।

এখানে একটি বড় শিক্ষা আছে: ওয়েবে নতুন ক্ষমতা সাধারণত আসে বাস্তবের লাইব্রেরি থেকে শিখে — হাজার হাজার সাইট যে কাজ বারবার স্ক্রিপ্টে করছে, সেটাই প্ল্যাটফর্মে আনার সবচেয়ে বড় যুক্তি। jQuery-র $('.card a') থেকে querySelectorAll, Sass-এর nesting থেকে CSS nesting — একই গল্প।

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

প্র ০২BEM, CSS Modules, Shadow DOM — এত সমাধান থাকতে @scope-এর দরকার কী? আর HTML-এর পুরোনো <style scoped>-এর কী হয়েছিল?

CSS-এর নকশায় সব নিয়ম global — একটি selector পাতার যেকোনো জায়গায় মিলতে পারে। ছোট সাইটে এটি সুবিধা, বড় সাইটে বিপদ: একজনের লেখা .title অন্যজনের component-এ ঢুকে পড়ে। এর সমাধানগুলো আলাদা আলাদা দাম নিয়ে এসেছে।

  • BEM (পর্ব ৪৬) — নামকরণের শৃঙ্খলা: .card__title. কোনো টুল লাগে না, কিন্তু পুরোটাই মানুষের সতর্কতার ওপর নির্ভর, আর নাম লম্বা হয়।
  • CSS Modules — build টুল প্রতিটি class-এর নাম বদলে অনন্য করে দেয়। নিরাপদ, কিন্তু build ধাপ লাগে, আর HTML-এ আসল নাম থাকে না।
  • Shadow DOM (পর্ব ৫২) — সত্যিকারের দেয়াল: বাইরের CSS ভেতরে ঢোকে না, ভেতরেরটা বাইরে যায় না। শক্তিশালী, কিন্তু অনেক সময় অতিরিক্ত কঠোর — সাইটের সাধারণ টাইপোগ্রাফিও ভেতরে পৌঁছায় না, আর JavaScript লাগে।

@scope মাঝামাঝি পথ: কোনো build টুল বা JavaScript ছাড়া, সাধারণ class-নাম রেখেই নিয়মকে একটি এলাকায় আটকানো — আর দেয়ালটা একমুখী নয়, সাইটের সাধারণ নিয়ম ভেতরেও পৌঁছায়। donut scope এমন একটি সমস্যা মেটায় যা আগের কোনো পদ্ধতি সহজে পারত না: “কার্ডের ভেতরে, কিন্তু কার্ডের ভেতরে বসানো অন্য component-এ নয়।” আর proximity থিমের ভেতরে থিমের পুরোনো সমস্যা সমাধান করে।

ইতিহাসটিও শিক্ষণীয়। এক দশকেরও বেশি আগে HTML spec-এ <style scoped> নামে একটি attribute ছিল — <style> যে element-এর ভেতরে, তার নিয়ম শুধু সেখানেই খাটবে। Firefox এটি চালু করেছিল, Chrome পরীক্ষামূলকভাবে যোগ করে পরে সরিয়ে নেয়, আর শেষে spec থেকেই এটি বাদ পড়ে — কারণ ব্রাউজার-নির্মাতারা একমত হতে পারেননি, আর Shadow DOM-কে তখন সেরা পথ মনে হয়েছিল। বহু বছর পর একই চাহিদা আবার ফিরে এল, এবার CSS-এর at-rule হিসেবে, cascade-এর সাথে সুসংগত নকশায় — আর <style>-এর ভেতরে selector ছাড়া @scope { } লিখে সেই পুরোনো সুবিধাও ফিরে পাওয়া যায়।

মূল উপলব্ধি: প্রকৌশলে একটি সমস্যার একাধিক সমাধান থাকা স্বাভাবিক; প্রতিটি আলাদা দাম নেয় — শৃঙ্খলা, টুল, বা নমনীয়তা। একটি ব্যর্থ প্রস্তাবও হারায় না, সেই অভিজ্ঞতা পরের ভালো নকশার ভিত্তি হয়।

প্র ০৩View Transitions কি “একক-পাতার অ্যাপ” (SPA) আর বহু-পাতার সাইটের (MPA) পুরোনো লড়াই বদলে দিচ্ছে?

বেশ খানিকটা, হ্যাঁ। ২০১০-এর দশকে React, Angular, Vue-এর মতো framework দিয়ে SPA বানানোর একটি বড় কারণ ছিল অনুভূতি: পাতা বদলের সময় সাদা ঝলক নেই, পুরোনো জিনিস মসৃণভাবে নতুন জায়গায় সরে যায় — নেটিভ অ্যাপের মতো। সাধারণ বহু-পাতার সাইটে একটি লিংকে ক্লিক মানে পুরোনো পাতা মুছে নতুন পাতা আঁকা; মাঝে কোনো অ্যানিমেশনের সুযোগই ছিল না, কারণ দুই পাতা কখনো একসাথে থাকে না।

কিন্তু SPA-র দামও বড়: বিশাল JavaScript bundle, ধীর প্রথম লোড (বিশেষ করে কম দামের ফোন আর দুর্বল মোবাইল নেটওয়ার্কে), সার্চ ইঞ্জিনের জন্য বাড়তি কাজ, আর browser-এর Back বোতামের মতো সাধারণ জিনিসও নিজে সামলানো। বাংলাদেশের মতো দেশে, যেখানে অনেক ব্যবহারকারী কম দামের Android ফোনে আর সীমিত ডেটা-প্যাকে ওয়েব দেখেন, এই দাম খুব বাস্তব।

View Transitions-এর ধারণা Chrome দলের প্রস্তাব থেকে আসে (শুরুতে নাম ছিল “shared element transitions”), প্রথমে same-document রূপে, পরে cross-document রূপে। cross-document রূপের অর্থ: সাধারণ HTML পাতা, সাধারণ লিংক, server থেকে আসা পুরো পাতা — আর তবু পাতা বদলের সময় লোগো সরে যায়, ছবি বড় হয়ে নতুন পাতায় বসে। “অনুভূতির জন্য SPA” যুক্তিটি অনেকটাই দুর্বল হয়ে যায়।

মজার ব্যাপার, ধারণাটি একেবারে নতুনও নয়: Internet Explorer-এর পুরোনো কিছু সংস্করণে একটি নিজস্ব meta tag দিয়ে পাতা বদলের সময় fade-এর মতো ইফেক্ট দেওয়া যেত — কিন্তু সেটি কোনো মানদণ্ড ছিল না, অন্য ব্রাউজারে চলত না, আর হারিয়ে যায়। এবারের পার্থক্য: CSSWG-এর spec, Interop-এ যৌথ কাজ, আর অ্যাক্সেসিবিলিটির (reduced motion) কথা মাথায় রেখে নকশা।

তবে SPA মরে যাচ্ছে না। জটিল অবস্থা (state) ধরে রাখা অ্যাপ — যেমন চ্যাট, ড্যাশবোর্ড, ডিজাইন টুল — এখনো SPA-তে স্বাভাবিক। পরিবর্তনটা হলো: কনটেন্ট-প্রধান সাইট (ব্লগ, খবর, দোকানের ক্যাটালগ) এখন সুন্দর অনুভূতির জন্য SPA-তে যেতে বাধ্য নয়।

মূল উপলব্ধি: স্থাপত্যের সিদ্ধান্ত প্রায়ই প্ল্যাটফর্মের কোনো ঘাটতি পূরণের জন্য নেওয়া হয়। প্ল্যাটফর্ম ঘাটতি পূরণ করলে সেই সিদ্ধান্ত আবার ভাবার সময় আসে — অভ্যাসের বশে পুরোনো পথে চলা নয়।

প্র ০৪CSS Houdini-র বড় স্বপ্নের মধ্যে কেবল @property-ই সব ব্রাউজারে পৌঁছাল কেন?

২০১৫-২০১৬ সালের দিকে ব্রাউজার-নির্মাতা আর CSSWG মিলে একটি উচ্চাকাঙ্ক্ষী উদ্যোগ শুরু করেন, নাম CSS Houdini — বিখ্যাত জাদুকর হুডিনির নামে, যিনি যেকোনো তালা খুলে বেরিয়ে আসতেন। লক্ষ্য ছিল CSS ইঞ্জিনের “তালাবন্ধ” ধাপগুলো ডেভেলপারদের জন্য খুলে দেওয়া, যাতে নতুন ফিচারের জন্য বছরের পর বছর spec-এর অপেক্ষা করতে না হয়। পরিকল্পনায় ছিল কয়েকটি API: Properties and Values (custom property-র ধরন), Paint API (JavaScript দিয়ে নিজের background আঁকা), Layout API (নিজের লেআউট পদ্ধতি, যেমন masonry), Typed OM (CSS মানকে লেখা নয়, সংখ্যা-একক হিসেবে পড়া), Animation Worklet ইত্যাদি।

বাস্তবে বেশিরভাগ API একটি ইঞ্জিনেই (মূলত Chromium) থেকে গেছে বা খসড়াতেই আটকে আছে। কারণগুলো প্রকৌশলের: ব্রাউজারের লেআউট বা আঁকার ধাপে বাইরের কোড ঢোকানো মানে নিরাপত্তা, পারফরম্যান্স আর আলাদা thread-এ চালানোর জটিল প্রশ্ন; প্রতিটি ইঞ্জিনের ভেতরের নকশা আলাদা, তাই একটি সাধারণ “ভেতরের দরজা” বানানো কঠিন; আর অগ্রাধিকারের প্রশ্নও ছিল — সব নির্মাতা একসাথে একই জিনিসে বিনিয়োগ করেননি।

@property সফল হলো কারণ এটি ছোট আর সুসংজ্ঞায়িত: এটি ইঞ্জিনের ভেতরে বাইরের কোড ঢোকায় না, শুধু একটি তথ্য দেয় — “এই variable-এর ধরন angle.” ধরন জানা গেলে ব্রাউজার যা আগে থেকেই পারে (রং, দৈর্ঘ্য, কোণের মাঝামাঝি মান হিসাব), সেটাই custom property-তে প্রয়োগ করে। ঝুঁকি কম, লাভ বেশি — gradient animation, থিমের মসৃণ বদল, আর type-checking. JavaScript-এর CSS.registerProperty() আগে এলেও CSS-এর ভেতরে @property লেখার সুযোগ একে সবার জন্য সহজ করেছে।

আরেকটি সূক্ষ্ম লাভ: নিবন্ধিত property-তে ভুল মান দিলে (যেমন --angle: red) ব্রাউজার সেটিকে “invalid at computed-value time” ধরে initial-value-এ ফিরে যায় — অর্থাৎ CSS-এ একধরনের type-safety, যা বড় design system-এ বাগ কমায়।

মূল উপলব্ধি: বড় স্বপ্নের মধ্যে টিকে থাকে সাধারণত সবচেয়ে ছোট, সবচেয়ে কম ঝুঁকির অংশটি। ভালো API নকশা মানে প্রায়ই কম ক্ষমতা দিয়ে বেশি নিশ্চয়তা কেনা।

প্র ০৫একটি নতুন CSS ফিচার “Newly available” হলেই কি বাংলাদেশের একটি ই-কমার্স সাইটে চোখ বুজে ব্যবহার করা যায়?

না — আর কেন নয়, সেটি বোঝাই পেশাদারিত্ব। “Newly available” মানে চারটি প্রধান ব্রাউজারের সর্বশেষ সংস্করণে ফিচারটি আছে। কিন্তু আপনার ক্রেতারা সবাই সর্বশেষ সংস্করণে নেই।

  • iPhone-এর Safari আপডেট হয় মূলত iOS-এর সাথে। যাঁর ফোন পুরোনো, নতুন iOS আর পাচ্ছে না, তাঁর Safari-ও আটকে আছে। ফলে Safari-তে নতুন আসা ফিচার পুরোনো iPhone-এ বহু বছর থাকে না।
  • Android-এ Chrome সাধারণত Play Store দিয়ে আপডেট হয়, কিন্তু কেউ আপডেট বন্ধ রাখেন, কেউ ডেটা বাঁচাতে দেরি করেন। আর Samsung Internet-এর মতো Chromium-ভিত্তিক ব্রাউজার সাধারণত Chrome থেকে কিছুটা পিছিয়ে চলে।
  • অ্যাপের ভেতরের ব্রাউজার — Facebook বা Messenger-এ লিংক খুললে যে ব্রাউজার আসে — বাংলাদেশে ই-কমার্সের অনেক ট্রাফিক এভাবেই আসে, আর এদের আচরণ সাধারণ ব্রাউজার থেকে কিছুটা আলাদা হতে পারে।

এজন্যই Baseline-এ “Widely available” স্তরটি রাখা হয়েছে — Newly হওয়ার প্রায় ৩০ মাস পরে, যখন পুরোনো সংস্করণগুলো অনেকটাই সরে যায়। তবে Widely-র জন্য অপেক্ষাও সবসময় ঠিক নয়। আসল প্রশ্ন: ফিচারটি না থাকলে ক্রেতার কী হবে? View transition না থাকলে পাতা অ্যানিমেশন ছাড়া বদলাবে — কেউ টেরও পাবেন না; এখনই ব্যবহার করুন। কিন্তু “অর্ডার করুন” বোতামের মেনু যদি anchor positioning ছাড়া পর্দার বাইরে চলে যায়, তাহলে বিক্রি হারাবেন — এখানে fallback বাধ্যতামূলক।

পেশাদার দল তাই তিনটি তথ্য মেলান: (১) Baseline বা Can I use-এর বৈশ্বিক অবস্থা, (২) নিজের সাইটের analytics — আসল ক্রেতারা কোন ব্রাউজার ও সংস্করণে আসেন, (৩) ফিচার না থাকলে ক্ষতির মাত্রা। Browserslist-এর মতো টুল দিয়ে দলটি একটি লিখিত “সাপোর্ট নীতি” রাখে, যাতে প্রতিবার তর্ক করতে না হয় — পর্ব ৪৮-এ এ নিয়ে বিস্তারিত।

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

অনুশীলন

বেশিরভাগ কাজ দেখা আর ভাবার; একটিতে ছোট কোড লিখতে হবে।

  1. আজকের সাপোর্ট নিজে দেখুন: MDN-এ anchor-name, @scope আর field-sizing-এর পাতা খুলে নিচের Baseline-বাক্স আর ব্রাউজার-টেবিল দেখুন। প্রতিটির অবস্থা (Widely / Newly / not yet) লিখে রাখুন, আর এই পর্বের টেবিলের সাথে মেলান — কিছু বদলেছে কি?

    উত্তর আপনি কবে পড়ছেন তার ওপর নির্ভর করে — এটাই অনুশীলনের উদ্দেশ্য। এই পর্ব লেখার সময় @scope সদ্য Newly available-এর কাছাকাছি, anchor positioning সব ইঞ্জিনে পৌঁছানোর পথে, আর field-sizing not yet Baseline ছিল। কোনোটি এক ধাপ এগিয়ে গেলে অবাক হবেন না। MDN-এর টেবিলে প্রতিটি ব্রাউজারের কোন সংস্করণ থেকে সাপোর্ট, আর কোথাও “partial support” বা flag-এর টীকা আছে কি না — সেগুলোও খেয়াল করুন। মূল অভ্যাস: AI, ব্লগ বা এই পর্বও নয়, সাম্প্রতিক টেবিলই শেষ কথা।

  2. ডোনাট অনুমান করুন: CSS: @scope (.shop) to (.ad) { img { border: 3px solid red; } }. HTML-এ .shop-এর ভেতরে ৩টি পণ্যের ছবি, আর একটি <div class="ad">, যার ভেতরে ১টি বিজ্ঞাপনের ছবি। পাতার একেবারে বাইরে, ফুটারে আরও ১টি ছবি। কয়টি ছবিতে লাল বর্ডার পড়বে?

    ৩টি — শুধু পণ্যের ছবিগুলোতে। বিজ্ঞাপনের ছবিটি .ad limit-এর ভেতরে, অর্থাৎ ডোনাটের ফুটোয়, তাই বাদ। ফুটারের ছবিটি .shop root-এর বাইরে, তাই সেটিও বাদ। আর .ad div নিজে যদি <img> হতো, সেটিও বাদ পড়ত — limit element নিজেও scope-এর বাইরে। যে ব্রাউজার @scope চেনে না, সেখানে ০টি — পুরো ব্লকটিই উপেক্ষিত।

  3. Fallback-সহ tooltip লিখুন: একটি .help wrapper-এর ভেতরে .btn বোতাম আর .tip span. এমন CSS লিখুন যাতে anchor positioning থাকলে tip বোতামের ডানে বসে আর জায়গা না পেলে বাঁয়ে উল্টায়; না থাকলে পুরোনো কৌশলে বোতামের ডানে বসে।
    /* সবার জন্য */
    .help { position: relative; display: inline-block; }
    .tip  { position: absolute; top: 0; left: 100%; margin-left: 8px; }
    
    /* যে ব্রাউজার পারে */
    @supports (position-area: right) {
      .btn { anchor-name: --help; }
      .tip {
        position-anchor: --help;
        inset: auto;            /* পুরোনো top/left মুছে দিই */
        position-area: right;
        position-try-fallbacks: flip-inline;   /* ডান ↔ বাঁ */
      }
    }

    flip-inline inline দিকে (বাংলা বা ইংরেজির মতো বাঁ-থেকে-ডান লেখায় ডান-বাঁ) উল্টায়, flip-block ওপর-নিচে। inset: auto ভুলে গেলে স্তর ১-এর top/left থেকে যাবে আর ফল গোলমেলে হবে। শেষে hover আর focus-এ দেখানো, আর aria-describedby যোগ করতে ভুলবেন না।

  4. Reduced motion চালু করে দেখুন: Windows-এ Settings → Accessibility → Visual effects → “Animation effects” বন্ধ করুন (Mac-এ System Settings → Accessibility → Display → Reduce motion), অথবা DevTools-এর Rendering প্যানেলে prefers-reduced-motion-কে reduce করুন। তারপর এই পাতার @property বৃত্ত আর View Transitions-এর কার্ড আবার চালান। কী বদলাল, আর কেন?

    বৃত্তটি আর ঘুরবে না, আর “লেআউট বদলান” চাপলে কার্ডগুলো সাথে সাথে নতুন জায়গায় বসবে — অ্যানিমেশন ছাড়া। কারণ দুটি উদাহরণেই @media (prefers-reduced-motion: reduce)-এর ভেতরে animation বন্ধ করা আছে। খেয়াল করুন, কাজ বন্ধ হয়নি — শুধু নড়াচড়া। এটাই সঠিক নকশা: reduced motion মানে “কিছু করবেন না” নয়, “বড় নড়াচড়া ছাড়া একই কাজ করুন।” পরীক্ষা শেষে সেটিংটি আগের অবস্থায় ফিরিয়ে দিন।

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

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

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

  1. 💡 ডেভেলপাররা JavaScript দিয়ে একই সমস্যা বারবার সমাধান করেন
  2. 📝 CSSWG-তে প্রস্তাব আর খসড়া spec
  3. 🧪 একটি ব্রাউজারে flag বা পরীক্ষামূলকভাবে চালু
  4. 🟢 সব প্রধান ব্রাউজারের সর্বশেষ সংস্করণে — Baseline Newly available
  5. ✅ প্রায় ৩০ মাস পরে — Baseline Widely available

@property দিয়ে নিবন্ধন না করে একটি সাধারণ custom property --angle-কে @keyframes-এ 0deg থেকে 360deg-এ animate করলে কী হয়?

  • মসৃণভাবে ঘোরে, @property শুধু গতি বাড়ায়
  • মান মাঝপথে এক লাফে বদলায় — মসৃণ ঘূর্ণন হয় না
  • ব্রাউজার console-এ error দেখায় ও পাতা থামিয়ে দেয়
  • keyframe-এ custom property লেখাই নিষিদ্ধ, তাই নিয়মটি বাদ পড়ে

anchor positioning না-চেনা ব্রাউজারের জন্য পুরোনো tooltip কৌশল রাখতে কোন লেখাটি সঠিক?

  • @media not (anchor-name)
  • @supports -webkit-anchor
  • @supports not (anchor-name: --a) { … }
  • @scope not (anchor-name) { … }

@scope (.card) to (.comments) { a { color: green; } } — কোন লিংকগুলো সবুজ হবে?

  • .card-এর ভেতরের লিংক, কিন্তু .comments-এর ভেতরেরগুলো বাদ
  • .card-এর ভেতরের সব লিংক, .comments-এর ভেতরেরগুলোসহ
  • পাতার সব লিংক
  • শুধু .comments-এর ভেতরের লিংক

একটি .light বাক্স একটি .dark বাক্সের ভেতরে। CSS-এ আগে @scope (.light) { p { color: black; } }, পরে @scope (.dark) { p { color: white; } }. .light-এর ভেতরের p-এর রং কী?

  • সাদা, কারণ .dark-এর নিয়ম পরে লেখা
  • সাদা, কারণ .dark-এর specificity বেশি
  • ব্রাউজারের ডিফল্ট রং, কারণ দুটি নিয়ম সংঘর্ষে বাতিল হয়
  • কালো, কারণ .light root element-টির বেশি কাছে

document.startViewTransition(update) ডাকলে ব্রাউজার কী করে?

  • নতুন একটি HTML পাতা লোড করে
  • পুরোনো অবস্থার ছবি নিয়ে update চালায়, তারপর পুরোনো আর নতুনের মাঝে অ্যানিমেশন করে
  • শুধু আগে থেকে লেখা @keyframes চালায়, DOM বদলায় না
  • স্বয়ংক্রিয়ভাবে সব reduced-motion ব্যবহারকারীর জন্য অ্যানিমেশন বন্ধ রাখে, কিছু লিখতে হয় না

একটি CSS ফিচার “Baseline Newly available” — এর সঠিক অর্থ কোনটি?

  • শুধু Chrome-এ নতুন এসেছে
  • পৃথিবীর প্রায় সব ডিভাইসে বহু বছর ধরে আছে
  • সব প্রধান ব্রাউজারের সর্বশেষ সংস্করণে আছে, কিন্তু পুরোনো সংস্করণে না-ও থাকতে পারে
  • W3C ফিচারটিকে চূড়ান্ত মানদণ্ড (Recommendation) ঘোষণা করেছে

শব্দকোষ

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

মনে রাখুন

  1. নতুন ফিচারের আগে Baseline দেখুন; not yet Baseline হলে @supports আর fallback দিয়ে progressive enhancement হিসেবে যোগ করুন।
  2. @property custom property-র ধরন, উত্তরাধিকার আর প্রাথমিক মান জানায় — তাই gradient-এর কোণের মতো মানও মসৃণভাবে animate হয়।
  3. Anchor positioning: anchor-name → position-anchor → position-area → position-try-fallbacks; popover-এর সাথে জোড়ায় সবচেয়ে কাজের।
  4. @scope (root) to (limit) নিয়মকে ডোনাট-এলাকায় আটকায়; specificity সমান হলে কাছের root জেতে।
  5. View Transitions পুরোনো-নতুন অবস্থার মাঝে মসৃণ বদল দেয়; সবসময় prefers-reduced-motion-এর সম্মান রাখুন। field-sizing, base-select, interpolate-size এখনো পরীক্ষামূলক বা এক ইঞ্জিনে।

পরের পর্বে: এই পর্বে বারবার “মসৃণ বদল” এসেছে। এবার তার ভিত্তিতে নামব — transition কীভাবে কাজ করে, timing function আসলে কী, আর কোন property animate করলে পাতা দ্রুত থাকে।

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

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