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

ব্রাউজার সাপোর্ট — Can I use, fallback, @supports আর progressive enhancement

Browser Support — Can I use, Baseline, Fallbacks, @supports, Vendor Prefixes & Progressive Enhancement

Taught in Bangla, this lesson explains why browsers behave differently across engines and update cycles, how to read Can I use, MDN tables, Baseline and Interop, progressive enhancement versus graceful degradation, CSS's built-in fallbacks, the full @supports syntax and its traps, vendor prefixes with Autoprefixer and browserslist, the limits of polyfills, known cross-browser bugs, testing and writing a support policy.

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

  • ব্রাউজারগুলো কেন আলাদা আচরণ করে — ইঞ্জিন, আপডেটের চক্র আর বাংলাদেশের পুরোনো ফোনের বাস্তবতা
  • Can I use, MDN-এর সাপোর্ট-টেবিল, Baseline আর Interop পড়ে একটি ফিচার ব্যবহারের সিদ্ধান্ত নেওয়া
  • progressive enhancement বনাম graceful degradation, আর CSS-এর নিজস্ব fallback কৌশল
  • @supports-এর পুরো ব্যাকরণ — not, and, or, selector() — আর এর ফাঁদগুলো
  • vendor prefix-এর ইতিহাস, Autoprefixer আর browserslist, polyfill-এর সীমা, ক্রস-ব্রাউজার বাগ আর একটি support policy লেখা

১ · একই কোড, ভিন্ন ব্রাউজার — পার্থক্য আসে কোথা থেকে

একটি পরিচিত গল্প দিয়ে শুরু করি। নাদিয়া একটি অনলাইন শাড়ির দোকানের পাতা বানিয়েছেন। নিজের ল্যাপটপে Chrome-এ সব নিখুঁত। কিন্তু দোকানের মালিক বললেন, “আমার iPhone-এ নিচের বোতামটা অর্ধেক কাটা।” আর এক ক্রেতা ফেসবুকে লিংক চেপে পাতা খুলে দেখলেন, পণ্যের কার্ডগুলো এলোমেলো। কোড একটাই — তবু তিন জায়গায় তিন রকম ফল। কেন?

পর্ব ০১ আর ০২-এ দেখেছেন, ব্রাউজারের যে অংশ HTML-CSS পড়ে পাতা আঁকে, তার নাম rendering engine. আজকের ওয়েবে প্রধান ইঞ্জিন মাত্র তিনটি, কিন্তু ব্রাউজারের নাম অনেক:

ইঞ্জিনকোন ব্রাউজারেআপডেট কীভাবে আসে
Blink (Chromium প্রজেক্ট)Chrome, Edge, Opera, Samsung Internet, Brave; Android-এর অ্যাপের ভেতরের ব্রাউজার (WebView)নতুন বড় সংস্করণ প্রায় প্রতি চার সপ্তাহে, নিজে থেকে আপডেট
WebKitSafari (Mac, iPhone, iPad); iPhone-এ Chrome, Firefox, Edge — সবাই ভেতরে WebKitবছরে একটি বড় সংস্করণ, মাঝে কয়েকটি ছোট আপডেট; iPhone-এ সাধারণত iOS আপডেটের সাথে
GeckoFirefox (কম্পিউটার আর Android)প্রায় প্রতি চার সপ্তাহে, নিজে থেকে আপডেট

এখানেই প্রথম শিক্ষা: ব্রাউজারের নাম নয়, ইঞ্জিন আর তার সংস্করণই ঠিক করে আপনার CSS কেমন দেখাবে। নাদিয়ার মালিকের iPhone-এ Chrome থাকলেও পাতা আঁকছে WebKit. আবার যে ব্রাউজার নিজে থেকে নিয়মিত আপডেট হয়, তাকে বলে evergreen browser — কিন্তু “নিজে থেকে” মানে সবসময় নয়। পুরোনো iPhone নতুন iOS না পেলে Safari-ও পুরোনো সংস্করণে আটকে থাকে।

বোর্ড পরীক্ষার খাতার কথা ভাবুন। সিলেবাস আর নম্বর দেওয়ার নিয়ম (spec) সবার জন্য এক, কিন্তু খাতা দেখেন আলাদা আলাদা পরীক্ষক (ইঞ্জিন)। বেশিরভাগ উত্তরে সবাই একই নম্বর দেন। কিন্তু একেবারে নতুন ধরনের প্রশ্নে কোনো পরীক্ষক হয়তো এখনো নতুন নির্দেশিকা হাতে পাননি — তিনি পুরোনো নিয়মে দেখবেন। ব্রাউজার সাপোর্টের পুরো বিষয়টি এই “নতুন নির্দেশিকা কে কবে পেলেন” প্রশ্ন।

বাংলাদেশের বাস্তবতা — পুরোনো ফোন আর অ্যাপের ভেতরের ব্রাউজার

ঢাকার অফিসের ডেভেলপার সাধারণত নতুন ল্যাপটপে সর্বশেষ Chrome চালান। কিন্তু আপনার পাতার পাঠকদের বড় একটি অংশ কম দামি বা কয়েক বছরের পুরোনো Android ফোনে, অনেক সময় দুর্বল নেটওয়ার্কে পাতা খোলেন। তাঁদের অনেকে ব্রাউজার আপডেট করেন না, ফোনের জায়গা কম বলে আপডেট বন্ধ রাখেন, বা এমন ফোন ব্যবহার করেন যা আর নতুন সিস্টেম আপডেট পায় না। সংখ্যা প্রতি বছর বদলায়, তাই নির্দিষ্ট হিসাব নয় — শুধু মনে রাখুন, “সবার ব্রাউজার আমার মতো নতুন” ধরে নেওয়া এখানে সবচেয়ে বড় ভুল।

আরেকটি বড় উৎস হলো in-app browser: ফেসবুক, মেসেঞ্জার বা অন্য অ্যাপে লিংক চাপলে পাতা খোলে অ্যাপের ভেতরেই। সেখানে পাতা আঁকে WebView — Android-এ Chromium-ভিত্তিক, iPhone-এ WebKit-ভিত্তিক। বাংলাদেশে অনলাইন দোকানের বড় অংশ ক্রেতা আসে ঠিক এই পথে — ফেসবুক পেজের পোস্ট থেকে।

এক দশক আগে বাংলাদেশসহ দক্ষিণ এশিয়ায় খুব জনপ্রিয় ছিল Opera Mini আর UC Browser. Opera Mini ছিল একটি proxy browser: পাতাটি আসলে Opera-র সার্ভারে আঁকা হতো, তারপর ছোট করে ফোনে পাঠানো হতো — ধীর 2G আর দামি ডেটায় এটি ছিল আশীর্বাদ, কিন্তু JavaScript আর আধুনিক CSS-এর বড় অংশ সেখানে চলত না বা সীমিতভাবে চলত। UC Browser-ও ডেটা বাঁচানোর জন্য সার্ভারের মাধ্যমে পাতা ছোট করার ব্যবস্থা রাখত, আর তার ইঞ্জিন অন্যদের চেয়ে পিছিয়ে থাকত। সস্তা স্মার্টফোন আর 4G ছড়ানোর পর এদের ব্যবহার অনেক কমেছে। কিন্তু শিক্ষাটি রয়ে গেছে: যে পাতা শুধু HTML পেলেও পড়া যায়, সে-ই সব পরিস্থিতিতে টেকে।

২ · সাপোর্টের তথ্য পড়া — Can I use, MDN, Baseline আর Interop

“এই CSS কি সব ব্রাউজারে চলবে?” — প্রশ্নটির উত্তর অনুমানে নয়, তথ্যে খুঁজতে হয়। পেশাদাররা চারটি জায়গা দেখেন।

Can I use

caniuse.com-এ কোনো ফিচারের নাম লিখলে প্রতিটি ব্রাউজারের প্রতিটি সংস্করণের রঙিন ছক আসে — সবুজ মানে আছে, লাল মানে নেই, হলুদ মানে আংশিক। পাশে বিশ্বজুড়ে কত শতাংশ ব্যবহারকারী এটি পান, আর নিচে “Notes” ও “Known issues” — আসল মূল্যবান অংশ।

দ্রুত এক নজরে

MDN-এর টেবিল

MDN-এর প্রতিটি property-র পাতার নিচে “Browser compatibility” টেবিল, আর ওপরে একটি Baseline-বাক্স। এই তথ্য আসে browser-compat-data নামের একটি উন্মুক্ত তথ্যভান্ডার থেকে, যা অনেক টুল আর editor-ও ব্যবহার করে।

বিস্তারিত, value ধরে ধরে

Baseline

Baseline হলো এক শব্দের রায়। ব্রাউজার-নির্মাতারা মিলে ২০২৩ সালে এটি চালু করেন, যাতে প্রতিটি টেবিল খুঁটিয়ে না দেখেও বোঝা যায় একটি ফিচার কতটা নিরাপদ।

এই কোর্সের লেবেল

Interop

Interop হলো প্রধান ব্রাউজার-নির্মাতাদের বার্ষিক যৌথ উদ্যোগ — কোন ফিচারগুলো এ বছর সবাই মিলে ঠিক করবেন। এর তালিকা দেখে বোঝা যায় কোন ফিচার শিগগিরই সবখানে আসছে।

ভবিষ্যতের আভাস

Baseline-এর তিন অবস্থা — আর এই কোর্স কীভাবে লেবেল দেয়

Baseline একটি নির্দিষ্ট “মূল ব্রাউজার-গুচ্ছ” দেখে: কম্পিউটারের Chrome, Edge, Firefox ও Safari, আর মোবাইলের Chrome (Android), Firefox (Android) ও Safari (iOS).

  • Limited availability (not yet Baseline): এক বা একাধিক মূল ব্রাউজারে এখনো নেই। এই কোর্সে লিখি “not yet Baseline”. ব্যবহার করলে বাধ্যতামূলকভাবে fallback.
  • Newly available: সব মূল ব্রাউজারের সর্বশেষ সংস্করণে এসেছে। কিন্তু যিনি কয়েক মাস আপডেট করেননি, তাঁর কাছে নেই। সাথে সাধারণত একটি বছর লেখা থাকে — “Baseline 2024” মানে সেই বছরে Newly হয়েছে।
  • Widely available: Newly হওয়ার ৩০ মাস পরে এই লেবেল আসে। ধরে নেওয়া হয়, ততদিনে প্রায় সব ব্যবহারকারীর ব্রাউজার আপডেট হয়ে গেছে। এই কোর্সে কোনো ফিচারকে “Baseline Widely available” বললে মানে — সাধারণ প্রজেক্টে নিশ্চিন্তে ব্যবহার করুন।

উদাহরণ: পর্ব ৩৪-এর :has() ২০২৩ সালের শেষে Firefox যোগ দেওয়ায় Newly available হয়; ৩০ মাস পরে, ২০২৬ সালের মাঝামাঝি, Widely available. অন্যদিকে পর্ব ২৫-এর Flexbox বা পর্ব ২৭-এর Grid বহু বছর ধরেই Widely available. নতুন একটি ফিচার ব্যবহার করবেন কি না, সিদ্ধান্তটি ধাপে ধাপে দেখুন:

  1. খুঁজুনMDN · caniuseতথ্য আগে
  2. Widely?ব্যবহার করুননিশ্চিন্তে
  3. Newly?কারা পাঠক?fallback ভাবুন
  4. Limited?@supportsবাড়তি সুবিধা মাত্র
  5. NotesKnown issuesছোট লেখা পড়ুন
  1. প্রশ্নটি স্পষ্ট করুন, তারপর খুঁজুন। “:has() চলবে?” না জিজ্ঞেস করে জিজ্ঞেস করুন “আমার এই কাজে কোন ফিচার লাগবে, আর তা কোথায় আছে?” MDN-এর পাতার ওপরের Baseline-বাক্স এক নজরে উত্তর দেয়; Can I use-এ সংস্করণ ধরে ধরে ছক দেখেন। Can I use-এ চাইলে দেশ বেছে নিয়ে সেই দেশের ব্যবহারকারীর হিসাবও দেখা যায়।
  2. Widely available — সবুজ সংকেত। সব মূল ব্রাউজারে আড়াই বছরের বেশি সময় ধরে আছে। এই কোর্সের বেশিরভাগ ফিচার এই দলে। fallback লেখার দরকার নেই।
  3. Newly available — পাঠক দেখে সিদ্ধান্ত। শহরের তরুণদের জন্য বানানো অ্যাপে হয়তো সমস্যা নেই; জেলা পর্যায়ের সরকারি সেবার পাতায় অনেক পুরোনো ফোন আসবে। সহজ নিয়ম: ফিচারটি না থাকলে পাতা একটু কম সুন্দর হলে চলবে, কিন্তু কাজ যেন বন্ধ না হয়।
  4. Limited availability — শুধু বাড়তি সুবিধা। ফিচারটি যেখানে আছে সেখানে চমৎকার অভিজ্ঞতা দিন, কিন্তু মূল পথ (পড়া, ফর্ম জমা, কেনা) কখনো এর ওপর দাঁড় করাবেন না। এখানেই কাজে আসে @supports (অংশ ৫)।
  5. ছোট লেখাগুলো পড়ুন। Can I use-এর Notes আর Known issues-এ লেখা থাকে “আংশিক সাপোর্ট”, “অমুক অবস্থায় বাগ”। Flexbox-এর gap এর বিখ্যাত উদাহরণ: পুরোনো Safari gap property চিনত (Grid-এর জন্য), কিন্তু Flexbox-এ তা কাজ করত না — শুধু property-র নাম দেখলে এ ফাঁদ ধরা পড়ে না।

Baseline-এর সীমা জানুন: Baseline শুধু মূল ব্রাউজার-গুচ্ছ দেখে, আর প্রতিটি ব্রাউজারের সর্বশেষ সংস্করণ ধরে হিসাব করে। Samsung Internet, পুরোনো Android WebView বা অ্যাপের ভেতরের ব্রাউজার সরাসরি এ হিসাবে নেই। তাই Baseline হলো শুরু, শেষ কথা নয় — নিজের সাইটের analytics দেখাই চূড়ান্ত।

৩ · Progressive enhancement বনাম graceful degradation

সব ব্রাউজারের জন্য পাতা বানানোর দুটি দর্শন আছে। প্রথমটি পুরোনো: সবচেয়ে নতুন ব্রাউজারের জন্য পূর্ণ নকশা বানাও, তারপর দেখো পুরোনোতে কতটা ভাঙছে, আর জোড়াতালি দাও। একে বলে graceful degradation. দ্বিতীয়টি উল্টো দিক থেকে শুরু করে: আগে এমন ভিত্তি বানাও যা সবখানে চলে, তারপর যে ব্রাউজার যত বেশি পারে, তার জন্য তত স্তর যোগ করো। এটিই progressive enhancement.

বাড়ি বানানোর কথা ভাবুন। আগে ভিত, পিলার আর ছাদ — বৃষ্টি-রোদে থাকা যায় (HTML). তারপর প্লাস্টার আর রং — দেখতে সুন্দর (সাধারণ CSS). তারপর টাইলস, কাচের রেলিং আর আলোর নকশা — যার সামর্থ্য আছে (আধুনিক CSS). শেষে লিফট আর স্বয়ংক্রিয় গেট (JavaScript). লিফট নষ্ট হলে সিঁড়ি তো আছেই। graceful degradation হলো আগে লিফটসহ বিলাসবহুল নকশা, পরে ভাবা “লিফট না চললে সিঁড়ি কোথায় বসাব?”

একটি পণ্য-কার্ড দিয়ে চারটি স্তর ধাপে ধাপে দেখুন:

  1. HTMLform + buttonসবখানে চলে
  2. সাধারণ CSSরং, ফাঁক, flexপ্রায় সবখানে
  3. আধুনিক CSS@supportsযে পারে
  4. JavaScriptfetch()পাতা না বদলে
index.htmlশুধু HTML — Opera Mini-তেও কেনা যায়
<article class="card">
  <h3>জামদানি শাড়ি</h3>
  <p>৳৪,৫০০</p>
  <form action="/cart" method="post">
    <input type="hidden" name="id" value="j12">
    <button>কার্টে নিন</button>
  </form>
</article>
style.cssবহু বছরের পুরোনো, সবাই চেনে
.card { border: 1px solid #d4d4d8; border-radius: 8px;
        padding: 12px; max-width: 20em; }
.card button { background: #15803d; color: #fff;
               padding: 8px 14px; border: 0; }
.cards { display: flex; flex-wrap: wrap; }
style.cssচিনলে খাটে, না চিনলে আগের স্তর
@supports (display: grid) {
  .cards { display: grid; gap: 1rem;
    grid-template-columns: repeat(auto-fill, minmax(14em, 1fr)); }
}
.card:has(.sold-out) { opacity: .6; }  /* না চিনলে নিয়মটিই বাদ */
cart.jsJS চললে পাতা না বদলে কার্টে যোগ
if ('fetch' in window) {
  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    await fetch(form.action, { method: 'post', body: new FormData(form) });
    // কার্টের সংখ্যা বাড়াও
  });
}
// JS না চললে? form নিজেই জমা হয় — স্তর ১
  1. স্তর ১ — HTML-এ পুরো কাজ। একটি সত্যিকারের <form> আর <button>. CSS না এলেও, JavaScript বন্ধ থাকলেও, proxy browser বা screen reader-এও “কার্টে নিন” চাপলে সার্ভারে অনুরোধ যায় (পর্ব ০৮)। এটিই সেই সিঁড়ি, যা কখনো নষ্ট হয় না।
  2. স্তর ২ — সাধারণ CSS. সীমানা, রং, ফাঁক আর flex-wrap — এগুলো এত পুরোনো যে কার্যত সব ব্রাউজার চেনে। এই স্তরেই পাতা “ঠিকঠাক সুন্দর”।
  3. স্তর ৩ — আধুনিক CSS, শর্তসাপেক্ষে। Grid-এর সমান মাপের কলাম @supports-এর ভেতরে; :has() দিয়ে বিক্রি-শেষ কার্ড ফিকে। যে ব্রাউজার এগুলো চেনে না, সে স্তর ২-এর নকশাই দেখায় — কিছু ভাঙে না, শুধু একটু কম সাজানো।
  4. স্তর ৪ — JavaScript দিয়ে আরাম। পাতা নতুন করে না খুলেই কার্টে যোগ। কিন্তু খেয়াল করুন: JavaScript নষ্ট হলে, দেরিতে এলে বা আটকে গেলে form নিজেই জমা হয়। এটিই progressive enhancement-এর আসল শক্তি।
প্রশ্নProgressive enhancementGraceful degradation
শুরু কোথা থেকেসবচেয়ে দুর্বল ব্রাউজারে চলে এমন ভিত্তিসবচেয়ে নতুন ব্রাউজারের পূর্ণ নকশা
নতুন ব্রাউজার না পেলেআগে থেকেই পরিকল্পিত সরল রূপপরে জোড়াতালি, প্রায়ই ভুলে যাওয়া
JavaScript না চললেমূল কাজ চলেপ্রায়ই সাদা পাতা বা অচল বোতাম
অ্যাক্সেসিবিলিটিস্বাভাবিকভাবে ভালো — semantic HTML ভিত্তিআলাদা করে মেরামত করতে হয়
কখন যুক্তিসংগতসাধারণ ওয়েবসাইট, দোকান, সরকারি সেবা, খবরভেতরের অফিস-টুল, যেখানে ব্রাউজার নিশ্চিত জানা

“Progressive enhancement” কথাটি ২০০৩ সালে Steve Champeon একটি সম্মেলনের বক্তৃতায় জনপ্রিয় করেন — ঠিক যখন ব্রাউজারের লড়াইয়ে ডেভেলপাররা “এই সাইট Internet Explorer-এ সবচেয়ে ভালো দেখায়” লিখে বাকিদের বাদ দিচ্ছিলেন। তাঁর যুক্তি ছিল সরল: বিষয়বস্তু আর মূল কাজ সবার; সাজসজ্জা আর বাড়তি সুবিধা যার যতটা সম্ভব।

৪ · CSS-এর নিজস্ব fallback — অচেনা জিনিস বাদ দেওয়ার নিয়ম

পর্ব ১৬-এ দেখেছেন, CSS ভুল পেলে error দেখিয়ে থেমে যায় না। একে বলে forgiving error handling: ব্রাউজার যে declaration বোঝে না — অচেনা property, বা চেনা property-র অচেনা মান — সেটি চুপচাপ বাদ দেয়, আর বাকিগুলো চালিয়ে যায়। এই একটি নিয়মই CSS-এর সবচেয়ে পুরোনো আর সহজ fallback কৌশলের ভিত্তি: একই property দুইবার লিখুন — আগে পুরোনো মান, পরে নতুন মান। নতুন ব্রাউজার দুটিই বোঝে, আর cascade-এ পরেরটি জেতে; পুরোনো ব্রাউজার দ্বিতীয়টি বাদ দেয়, আর প্রথমটি থেকে যায়।

  1. পড়াprop: value;parse
  2. property?চেনা কি নানা চিনলে বাদ
  3. value?ব্যাকরণ ঠিক?না হলে বাদ
  4. cascadeশেষেরটি জেতেবাকি দুইটির মধ্যে
  5. var()ফাঁদপরে ধরা পড়ে
  1. পড়া শুরু। ব্রাউজার একটি rule-এর ভেতরের declaration-গুলো একটি একটি করে পড়ে — প্রতিটি সেমিকোলনে শেষ। প্রতিটির ভাগ্য আলাদাভাবে ঠিক হয়।
  2. property-র নাম চেনা? না চিনলে পুরো declaration বাদ। Console-এ কোনো লাল লেখা আসে না — DevTools-এর Styles প্যানেলে শুধু সেটির ওপর একটি কাটা দাগ আর হলুদ সতর্কচিহ্ন দেখবেন (পর্ব ৪৭)।
  3. মানের ব্যাকরণ ঠিক? নাম চেনা হলেও মানটি অচেনা হলে (নতুন রং-ফাংশন, নতুন একক, নতুন keyword) সেই declaration-ও বাদ। এখানেই পুরোনো আর নতুন ব্রাউজারের পথ আলাদা হয়।
  4. যারা টিকে রইল, তাদের মধ্যে cascade. পর্ব ১৮-এর নিয়মে একই specificity-তে পরে লেখাটি জেতে। তাই ক্রম গুরুত্বপূর্ণ — নতুনটি আগে লিখলে নতুন ব্রাউজারও পুরোনো মানে আটকে যাবে।
  5. var()-এর ফাঁদ। var() থাকলে ব্রাউজার পড়ার সময় মান যাচাই করতে পারে না, তাই declaration-টি সবসময় “বৈধ” ধরে রাখে আর আগেরটিকে হারিয়ে দেয়। পরে variable বসিয়ে মান অবৈধ হলে (IACVT, পর্ব ৩৩) property চলে যায় inherited বা initial মানে — fallback আর ফেরে না। সমাধান: var(--brand, #1d4ed8)-এর মতো var()-এর নিজের fallback, বা @supports.

এবার নিজে দেখুন। নিচের বোতামে আগে নীল, পরে oklch()-এর সবুজ; বাক্সে আগে লাল সীমানা, পরে একটি বানানো রঙের নাম যা কোনো ব্রাউজারই চেনে না — পুরোনো ব্রাউজারের চোখে নতুন ফিচার ঠিক এমনই অচেনা দেখায়:

body { font-family: sans-serif; }
.btn {
  display: inline-block;
  padding: 10px 16px;
  border-radius: 8px;
  color: white;
  background: #1d4ed8;                /* ১ · সবাই চেনে — fallback */
  background: oklch(0.55 0.17 145);   /* ২ · চিনলে এটি জেতে */
}
.box {
  padding: 10px;
  border: 3px solid #b91c1c;          /* ১ · fallback */
  border: 3px solid shapla-red;       /* ২ · অচেনা মান — বাদ পড়বে */
  width: 220px;                       /* ১ · পুরোনো ব্রাউজারের জন্য */
  width: fit-content;                 /* ২ · লেখার মাপে বাক্স */
}

যেকোনো আধুনিক ব্রাউজারে বোতামটি সবুজ — oklch() Baseline Widely available, তাই দ্বিতীয় declaration জিতেছে। বাক্সের সীমানা লাল — shapla-red কোনো রঙের নাম নয়, তাই সেই লাইন বাদ পড়েছে, আর আগের লাইন রয়ে গেছে। বাক্সটি লেখার মাপে ছোট, কারণ fit-content চেনে। চেষ্টা করুন: (১) বোতামের দুটি background লাইনের ক্রম উল্টে দিন — এখন আধুনিক ব্রাউজারও নীল দেখাবে, কারণ শেষেরটি জেতে। (২) shapla-red-কে green করুন — সীমানা সবুজ হবে।

সাবধান: selector-এর ভুল পুরো rule ফেলে দেয়

declaration-এর ক্ষমাশীলতা selector-এ নেই। একটি rule-এর selector তালিকায় কমা দিয়ে কয়েকটি selector থাকলে, আর তার একটিও অচেনা হলে, ব্রাউজার পুরো rule বাদ দেয় — চেনা selector-গুলোসহ। পুরোনো দিনে ::-moz-selection, ::selection একসাথে লিখে অনেকে এই ফাঁদে পড়তেন। ব্যতিক্রম পর্ব ১৭-এর forgiving selector list: :is() আর :where()-এর ভেতরে অচেনা অংশটি একা বাদ পড়ে।

p { padding: 8px; font-family: sans-serif; border-radius: 6px; }

/* ক · একটি selector অচেনা → পুরো নিয়মই বাদ */
.a, .a:shapla { background: #fde68a; }

/* খ · :is() ক্ষমাশীল → শুধু অচেনা অংশটি বাদ */
:is(.b, .b:shapla) { background: #bbf7d0; }

/* গ · আলাদা rule → একটির ভুল অন্যটিকে ছোঁয় না */
.c { background: #bfdbfe; }
.c:shapla { background: red; }

প্রথম লাইনে কোনো রং নেই — :shapla নামের pseudo-class নেই, তাই চেনা .a-সহ পুরো rule বাদ। দ্বিতীয় লাইন সবুজ, কারণ :is() অচেনা অংশটি ক্ষমা করেছে। তৃতীয়টি নীল, কারণ ভুল rule-টি আলাদা। চেষ্টা করুন: প্রথম rule থেকে , .a:shapla মুছে দিন — হলুদ ফিরে আসবে। বাস্তবে নতুন pseudo-class (যেমন :user-invalid) পুরোনো selector-এর সাথে এক তালিকায় লিখতে চাইলে আলাদা rule বা :is() ব্যবহার করুন।

আরও গভীরে: এই আচরণ এলোমেলো নয় — CSS Syntax spec-এ ভুল থেকে সেরে ওঠার নিয়ম ধাপে ধাপে লেখা আছে। parser বন্ধনী আর উদ্ধৃতি গুনে গুনে পরের সেমিকোলন বা বন্ধ } পর্যন্ত লাফ দেয়, তাই একটি ভুল পাশের নিয়মকে নষ্ট করে না। @supports-এর শর্তেও অচেনা কোনো ভবিষ্যৎ ব্যাকরণ (যেমন অজানা কোনো ফাংশন) error নয়, শুধু “মিথ্যা” ধরা হয় — যাতে আজকের ব্রাউজার আগামী দিনের শর্ত দেখেও না ভাঙে। CSS-এর নকশাকারেরা শুরু থেকেই ধরে নিয়েছিলেন যে পুরোনো ব্রাউজার চিরকাল নতুন stylesheet পড়বে; এই “সামনের দিকে সামঞ্জস্য” (forward compatibility) ওয়েবের টিকে থাকার একটি বড় কারণ।

৫ · @supports — ব্রাউজারকে সরাসরি জিজ্ঞেস করা

দুইবার declaration লেখার কৌশল একটি property-র জন্য চমৎকার। কিন্তু নতুন লেআউটে প্রায়ই অনেকগুলো property একসাথে বদলাতে হয় — Grid চালু করলে পুরোনো margin সরাতে হয়, কলামের মাপ বদলাতে হয়। তখন লাগে @supports — একে বলে feature query. এটি ব্রাউজারকে জিজ্ঞেস করে, এই property আর মানের জোড়াটি সে চেনে কি না। চিনলে ভেতরের সব নিয়ম খাটে।

@supports (display: grid) { … }                 /* চেনে কি না */
@supports not (display: grid) { … }             /* চেনে না */
@supports (display: grid) and (gap: 1rem) { … } /* দুটিই চেনে */
@supports (height: 100dvh) or (height: 100svh) { … } /* যেকোনো একটি */
@supports selector(:has(img)) { … }             /* selector চেনে কি না */

একটি জিনিস খেয়াল করুন: শর্তে শুধু property-র নাম নয়, নাম আর মান দুটিই লাগে, বন্ধনীর ভেতরে। and, or একসাথে মেশাতে হলে বাড়তি বন্ধনী দিন — (a) and ((b) or (c)). এবার একটি সত্যিকারের ফিচার আর একটি বানানো ফিচারের জন্য একই প্রশ্ন করে দেখি:

<style>
  body { font-family: sans-serif; }
  .msg { padding: 8px 12px; border-radius: 8px; margin: 6px 0; }
  .yes { display: none; background: #dcfce7; }
  .no  { background: #fee2e2; }
  /* ডিফল্টে "না" দেখায়; চিনলে "হ্যাঁ"-তে বদলায় */
  @supports (display: grid) {
    .grid .yes { display: block; }  .grid .no { display: none; }
  }
  @supports (display: shapla) {
    .fake .yes { display: block; }  .fake .no { display: none; }
  }
</style>
<div class="grid">
  <p class="msg yes">✔ display: grid চেনে — নতুন নকশা পাবে</p>
  <p class="msg no">✘ display: grid চেনে না — সরল নকশা পাবে</p>
</div>
<div class="fake">
  <p class="msg yes">✔ display: shapla চেনে</p>
  <p class="msg no">✘ display: shapla চেনে না — fallback দেখাচ্ছে</p>
</div>

আধুনিক ব্রাউজারে প্রথম সারিতে সবুজ “✔ চেনে” — Grid বহু বছর ধরে Widely available. দ্বিতীয় সারিতে লাল “✘ চেনে না”, কারণ shapla নামের কোনো display মান নেই। এখানে বানানো নামটি দিয়ে আমরা অনুকরণ করছি পুরোনো একটি ব্রাউজার নতুন ফিচার দেখলে কী করে — আসল পুরোনো ফোনে display: grid-এর সারিটিও লাল দেখাত। খেয়াল করুন নকশাটি: ডিফল্টে “না”, @supports মিললে “হ্যাঁ”। এতে যে ব্রাউজার @supports-ই চেনে না, সে-ও সঠিক (সরল) রূপটি দেখে। চেষ্টা করুন: shapla-কে flow-root করুন — দ্বিতীয় সারিও সবুজ হবে।

সবচেয়ে সাধারণ ভুল — fallback-কে @supports not-এর ভেতরে রাখা। যে খুব পুরোনো ব্রাউজার @supports নিয়মটিই চেনে না (যেমন Internet Explorer), সে @supports আর @supports not — দুটি ব্লকই পুরো বাদ দেয়। তখন fallback-ও হারায়। নিরাপদ নিয়ম: fallback থাকুক বাইরে, সাধারণ CSS হিসেবে; নতুনটি থাকুক @supports (…)-এর ভেতরে। @supports not রাখুন শুধু তখন, যখন জানেন সব লক্ষ্য-ব্রাউজার @supports চেনে (আজকের সব evergreen ব্রাউজার চেনে)।

selector() — :has()-এর মতো নতুন selector যাচাই

নতুন selector-এর ক্ষেত্রে আগের অংশেই দেখেছেন, অচেনা selector থাকলে rule নিজেই বাদ পড়ে — তাই @supports ছাড়াও “চিনলে খাটে, না চিনলে নয়” ঘটে। তবু @supports selector() কাজে লাগে, যখন selector চিনলে অন্য কিছুও বদলাতে হয় — যেমন :has() চিনলে একটি সাহায্য-লেখা লুকানো, বা পুরোনো কৌশলটি বন্ধ করা:

body { font-family: sans-serif; }
.opt { display: block; padding: 10px; margin: 6px 0;
       border: 2px solid #d4d4d8; border-radius: 8px; }
input { accent-color: #15803d; }   /* সবার জন্য: টিকের রং */

.note::after { content: "এই ব্রাউজার :has() চেনে না — বাক্স একই থাকবে, টিক ঠিকই কাজ করবে।"; }

@supports selector(:has(*)) {
  .opt:has(:checked) { border-color: #15803d; background: #f0fdf4; }
  .note::after { content: "এই ব্রাউজার :has() চেনে — টিক দিলে পুরো বাক্স সবুজ।"; }
}

আধুনিক ব্রাউজারে দ্বিতীয় বাক্সটি সবুজ সীমানা আর হালকা সবুজ পটভূমিতে, আর নিচের লেখা বলছে “:has() চেনে”। প্রথম বাক্সে টিক দিন — সেটিও সবুজ হবে। :has() ২০২৩-এর শেষে Newly available হয়েছে, আর ২০২৬-এর মাঝামাঝি থেকে Widely available; তবু পুরোনো ফোনে লেখাটি প্রথম বাক্যটি দেখাত, আর বাক্সের রং বদলাত না — কেনাকাটার কাজ কিন্তু ঠিকই চলত। চেষ্টা করুন: selector(:has(*))-কে selector(:shapla(*)) করুন — দেখবেন পুরোনো ব্রাউজারের চেহারা। (বাস্তবে ::after-এ গুরুত্বপূর্ণ লেখা রাখবেন না; screen reader সবসময় তা পড়ে না — এখানে শুধু দেখানোর জন্য।)

JavaScript থেকে একই প্রশ্ন — CSS.supports()

@supports-এর JavaScript রূপ হলো CSS.supports(). এটি feature detection-এর সবচেয়ে পরিষ্কার উদাহরণ — ব্রাউজারের নাম নয়, সরাসরি ক্ষমতা যাচাই। নিচের কোডে and, or, not আর selector(), সবগুলো চালিয়ে দেখুন:

const tests = [
  'display: grid',
  'display: shapla',
  '(display: grid) and (gap: 1rem)',
  '(display: shapla) or (display: flex)',
  'not (display: shapla)',
  'selector(:has(img))',
  'height: 100dvh',
  'color: oklch(0.6 0.2 145)',
  'gap: 1rem'
];
for (const t of tests) {
  console.log((CSS.supports(t) ? 'true ' : 'false') + '  ' + t);
}

আধুনিক ব্রাউজারে শুধু দ্বিতীয় লাইন false — বাকি সব true. or-এর শর্তে একটি মিললেই হয়, আর not অচেনা জিনিসকে উল্টে true বানায়। কিন্তু শেষ লাইনটি খেয়াল করুন: gap: 1rem-এর উত্তর true এসেছে পুরোনো Safari-তেও, যেখানে Flexbox-এ gap কাজই করত না — কারণ Grid-এর জন্য property-টি সে চিনত। @supports শুধু বলে “এই লেখাটি আমি পড়তে পারি”, বলে না “সব লেআউটে সঠিকভাবে আঁকতে পারি”। চেষ্টা করুন: তালিকায় 'anchor-name: --tip' যোগ করে দেখুন আপনার ব্রাউজার anchor positioning চেনে কি না (পর্ব ৩৭)।

ব্রাউজারের নাম দেখে সিদ্ধান্ত নয়। পুরোনো দিনে JavaScript-এ navigator.userAgent-এ “Safari” লেখা আছে কি না দেখে আলাদা CSS দেওয়া হতো — একে বলে user-agent sniffing. সমস্যা: Chrome-এর user-agent-এও “Safari” লেখা থাকে, Edge-এর লেখায় “Chrome”, আর ব্রাউজার আপডেট হলে পুরোনো হিসাব ভুল হয়ে যায়। ফিচার আছে কি না সরাসরি যাচাই করুন — @supports বা CSS.supports() দিয়ে।

একটি পূর্ণ উদাহরণ — flex থেকে grid

অংশ ৩-এর স্তর ২ আর স্তর ৩ এবার হাতে-কলমে। পণ্যের তালিকা সবার জন্য flex-wrap, আর Grid চিনলে সমান মাপের কলাম:

.products { list-style: none; padding: 0; margin: 0;
            font-family: sans-serif; }
.products li { padding: 10px; border: 1px solid #d4d4d8;
               border-radius: 8px; }

/* স্তর ২ — সবার জন্য: flex-wrap আর margin দিয়ে ফাঁক */
.products { display: flex; flex-wrap: wrap; }
.products li { flex: 1 1 140px; margin: 0 8px 8px 0; }

/* স্তর ৩ — Grid চিনলে: সমান কলাম, gap দিয়ে ফাঁক */
@supports (display: grid) {
  .products { display: grid; gap: 8px;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .products li { margin: 0; }
}

আধুনিক ব্রাউজারে Grid চালু: সব কার্ড সমান চওড়া, শেষ সারির কার্ডগুলোও বাকিদের মাপে। এবার পুরোনো ব্রাউজারের চেহারা দেখুন: @supports (display: grid)-এ grid-কে ভুল বানানে gird লিখুন। এখন flex-এর নকশা — শেষ সারির কার্ডগুলো টেনে চওড়া হয়ে পুরো জায়গা নিয়েছে, ডান পাশে margin-এর ফাঁক। খারাপ নয়, শুধু একটু কম গোছানো — এটাই progressive enhancement. চেষ্টা করুন: minmax(140px, 1fr)-কে minmax(200px, 1fr) করে কলামের সংখ্যা বদলান।

৬ · Vendor prefix, Autoprefixer আর browserslist

২০১০-এর দশকের শুরুর দিকের CSS দেখলে প্রায়ই এমন দৃশ্য পাবেন:

/* ইতিহাস — আজ এভাবে লিখবেন না */
.box {
  -webkit-border-radius: 8px;   /* Safari, পুরোনো Chrome */
     -moz-border-radius: 8px;   /* Firefox */
          border-radius: 8px;   /* মানদণ্ড — সবসময় শেষে */
  -webkit-transition: opacity .3s;
      -ms-transition: opacity .3s;  /* Internet Explorer */
          transition: opacity .3s;
}

এগুলো vendor prefix: -webkit- (Safari, পরে Chrome-ও), -moz- (Firefox), -ms- (Microsoft), -o- (পুরোনো Opera). ধারণাটি ছিল ভালো — একটি ফিচার পরীক্ষামূলক অবস্থায় prefix-সহ চালু হবে, spec স্থির হলে prefix ছাড়া। আর prefix-ছাড়া রূপ সবসময় শেষে, যাতে চূড়ান্ত মানটি জেতে।

কিন্তু বাস্তবে ডেভেলপাররা পরীক্ষামূলক ফিচার আসল সাইটে ব্যবহার করতে শুরু করেন, আর অনেকে শুধু -webkit- লিখতেন — iPhone-ই তখন মোবাইল ওয়েবের প্রধান। ফল: অন্য ব্রাউজারে সাইট ভাঙত, আর তারা বাধ্য হয়ে নিজেরাই -webkit- নামগুলো চিনতে শুরু করে। আজও Firefox অনেক -webkit- property বোঝে শুধু পুরোনো সাইট বাঁচাতে। এই অভিজ্ঞতার পর ব্রাউজারগুলো নীতি বদলায়: নতুন পরীক্ষামূলক ফিচার এখন আসে লুকানো flag বা origin trial-এর মাধ্যমে, prefix দিয়ে নয়।

তাহলে আজ কি prefix একেবারে নেই? কয়েকটি পুরোনো জিনিস ইতিহাসের কারণে prefix-সহই টিকে আছে, আর সবাই সেই রূপটিই চেনে। সবচেয়ে পরিচিত উদাহরণ পর্ব ২৩-এর line-clamp — লেখা কয়েক লাইনে কেটে শেষে “…”:

.card { font-family: sans-serif; max-width: 22em;
        border: 1px solid #d4d4d8; border-radius: 8px; padding: 0 12px; }
.desc {
  display: -webkit-box;          /* পুরোনো flexbox-এর নাম, শুধু এ কাজে */
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;         /* দুই লাইন পর “…” */
  overflow: hidden;
}

বর্ণনাটি ঠিক দুই লাইনে থেমে শেষে “…” দেখাচ্ছে। এই তিনটি -webkit- লাইন Chromium, Safari আর Firefox — সবাই বোঝে; spec-ও এখন এই পুরোনো রূপটিকে স্বীকার করে নিয়েছে। চেষ্টা করুন: (১) 2-কে 3 করুন। (২) -webkit-box-orient লাইনটি মুছে দিন — কাটা বন্ধ হয়ে পুরো লেখা আসবে। তিনটি লাইন একসাথে লাগে, এটাই এর পুরোনো ব্যাকরণের অদ্ভুততা। মনে রাখবেন, “…”-এর পরের লেখা পাতায় থেকেই যায়; জরুরি তথ্য এভাবে লুকাবেন না।

হাতে নয়, টুলে — Autoprefixer আর browserslist

কোন property-তে কোন ব্রাউজারের জন্য prefix লাগে, তা মনে রাখা মানুষের কাজ নয়। পেশাদার প্রজেক্টে আপনি শুধু মানদণ্ডের CSS লেখেন, আর PostCSS-এর একটি plugin, Autoprefixer, build-এর সময় যেখানে দরকার সেখানে prefix বসায় — আবার যেখানে আর দরকার নেই, পুরোনো prefix মুছেও দেয়। কোন ব্রাউজারকে সমর্থন করবেন, সেটি সে জানে browserslist নামের একটি ছোট তালিকা থেকে, যা Can I use-এর তথ্যের সাথে মিলিয়ে দেখা হয়:

# .browserslistrc — প্রজেক্টের মূল ফোল্ডারে
> 0.5% in BD       # বাংলাদেশে ০.৫%-এর বেশি ব্যবহৃত সব ব্রাউজার
last 2 versions    # প্রতিটি ব্রাউজারের শেষ দুই সংস্করণ
not dead           # যাদের আর আপডেট আসে না, তাদের বাদ

# কোন কোন ব্রাউজার তালিকায় এলো, দেখতে:
npx browserslist

একই তালিকা পড়ে আরও অনেক টুল — যেমন JavaScript-কে পুরোনো রূপে অনুবাদ করার টুল। ফলে পুরো প্রজেক্টে “আমরা কাদের সমর্থন করি” কথাটি এক জায়গায় লেখা থাকে। browserslist-এর সাম্প্রতিক সংস্করণে Baseline-ভিত্তিক তালিকা লেখার সুযোগও যোগ হয়েছে। PostCSS আর Autoprefixer কীভাবে প্রজেক্টে বসাবেন, তা দেখবেন পর্ব ৫০-এ।

৭ · Polyfill — কখন কাজ করে, আর CSS-এ কেন প্রায় অসম্ভব

Polyfill হলো এমন কোড, যা পুরোনো ব্রাউজারে একটি নতুন ফিচারের আচরণ নকল করে। JavaScript-এ এটি খুব কাজের: নতুন কোনো ফাংশন না থাকলে, একই নামে একটি ফাংশন বানিয়ে দেওয়া যায় — আপনার কোড বুঝতেই পারে না পার্থক্য। পর্ব ১১-এর <dialog> যখন নতুন ছিল, তখন এর polyfill অনেক সাইটে ব্যবহার হতো।

CSS-এ সমস্যা মৌলিক। অংশ ৪-এ দেখেছেন, ব্রাউজার অচেনা CSS পড়ার সময়েই ফেলে দেয়। JavaScript যখন চলে, তখন সেই নিয়মটি ব্রাউজারের তৈরি CSS-এর কাঠামো (CSSOM) থেকে আগেই উধাও। তাই একটি CSS polyfill-কে stylesheet ফাইলটি আবার নিজে ডাউনলোড করে, নিজে পড়ে, নিজের মতো হিসাব করে প্রতিটি element-এ inline style বসাতে হয় — আর পাতা স্ক্রল বা মাপ বদলালে বারবার সেটা করতে হয়। এটি ধীর, ভঙ্গুর, আর প্রায়ই আসল আচরণের সাথে মেলে না। container query-র মতো কিছু ফিচারের জন্য এমন polyfill তৈরি হয়েছিল, কিন্তু সেগুলো সাময়িক সেতু মাত্র।

মূল ধারণা

CSS-এ polyfill খোঁজার আগে প্রশ্ন করুন: এটি না থাকলে কি পাতা চলবে? প্রায় সব ক্ষেত্রে উত্তর হ্যাঁ — তখন polyfill নয়, সাধারণ fallback আর @supports. polyfill-এর কথা ভাবুন শুধু JavaScript API-র ক্ষেত্রে, যেখানে ফিচারটি ছাড়া মূল কাজই বন্ধ হয়ে যায়। আর প্রতিটি polyfill-এর একটি মেয়াদ আছে — ফিচারটি Widely available হলে মুছে ফেলুন, নইলে পুরোনো কোড সবাইকে ধীর করবে।

৮ · পরিচিত ক্রস-ব্রাউজার বাগ আর পরীক্ষার পদ্ধতি

সব মানদণ্ড মেনে লিখলেও কিছু জায়গায় ব্রাউজারভেদে পার্থক্য প্রায় নিশ্চিত। সবচেয়ে চোখে পড়ে ফর্মের কন্ট্রোলে, কারণ এগুলো native widget — অনেক সময় অপারেটিং সিস্টেমের নিজের চেহারায় আঁকা। নিচের এডিটরে আপনার ব্রাউজার যা আঁকছে, তা শুধু তার নিজের রূপ:

<style>
  body { font-family: sans-serif; }
  label { display: block; margin: 8px 0; }
</style>
<label>ডেলিভারির তারিখ <input type="date" value="2026-10-25"></label>
<label>সময় <input type="time" value="15:30"></label>
<label>জেলা
  <select><option>ঢাকা</option><option>রাজশাহী</option></select>
</label>
<label>পরিমাণ <input type="range" min="1" max="10" value="3"></label>
<label><input type="checkbox" checked> শর্তে রাজি</label>
<label>আপলোড <progress value="60" max="100"></progress></label>

Chrome বা Edge-এ তারিখের ঘরের ডান পাশে ছোট ক্যালেন্ডার-আইকন, তারিখের ক্রম আপনার কম্পিউটারের ভাষা-সেটিং অনুযায়ী (মাস আগে না দিন আগে), আর select, slider, checkbox ও progress-এর নীল রং Chromium-এর নিজস্ব। একই কোড Firefox বা Safari-তে খুললে আকার, রং, ক্যালেন্ডারের নকশা — সবই একটু আলাদা; ফোনে খুললে তারিখ বাছার পুরো পর্দাই অপারেটিং সিস্টেমের। কিন্তু খেয়াল করুন, পাঠানো মান সবখানে একই রূপে যায় — 2026-10-25. চেষ্টা করুন: <style>-এর body-তে accent-color: #b91c1c; যোগ করুন — checkbox, slider আর progress লাল হবে; এটি সব আধুনিক ব্রাউজারে কাজ করে। তারপর input, select { font: inherit; } যোগ করুন — ব্রাউজারের ছোট ডিফল্ট লেখার বদলে পাতার লেখার মাপ আসবে।

যেসব জায়গায় পার্থক্য প্রায় নিশ্চিত

সমস্যাকী ঘটেআধুনিক সমাধান
তারিখ-সময়ের ঘরক্যালেন্ডারের চেহারা, ক্রম আর ভাষা ব্রাউজার ও OS ঠিক করে; বেশ কিছুদিন আগ পর্যন্ত desktop Safari-তে এটি ছিল সাধারণ লেখার ঘরচেহারা মেনে নিন; লেবেলে উদাহরণ দিন; মান যাচাই সার্ভারেও করুন (পর্ব ০৯)
ফর্ম কন্ট্রোলের আকার ও লেখাডিফল্ট font, উচ্চতা, padding ব্রাউজারভেদে আলাদাfont: inherit, accent-color, দরকারে appearance: none দিয়ে নিজে আঁকা (পর্ব ৪৩)
লেখার রেন্ডারিংWindows, Mac আর Android ভিন্নভাবে অক্ষর মসৃণ করে; বাংলা যুক্তাক্ষর ফন্ট আর ইঞ্জিনভেদে ভিন্ন দেখাতে পারেভালো বাংলা web font, যথেষ্ট line-height (পর্ব ১৪, ২১); শুধু Mac-এর জন্য বানানো smoothing property-র ওপর ভরসা নয়
মোবাইলে 100vhঠিকানার বার লুকানো অবস্থার উচ্চতা ধরা হয়, তাই বার দেখা গেলে নিচের অংশ কেটে যায় — নাদিয়ার কাটা বোতামheight: 100vh; তারপর height: 100svh; — নতুন একক svh, dvh (পর্ব ২০)
Flexbox-এ gapপুরোনো Safari-তে কাজ করত না, অথচ @supports “হ্যাঁ” বলতএখন Widely available; খুব পুরোনো ফোন লক্ষ্য হলে margin-এর fallback
/* নাদিয়ার কাটা বোতামের সমাধান — fallback আগে, নতুন একক পরে */
.hero {
  min-height: 100vh;    /* পুরোনো ব্রাউজার */
  min-height: 100svh;   /* ঠিকানার বার দেখা অবস্থাতেও পুরোটা দেখা যায় */
}

কীভাবে পরীক্ষা করবেন

Cross-browser testing মানে প্রতিটি ব্রাউজারে পিক্সেল মিলিয়ে দেখা নয় — মূল কাজগুলো (পড়া, খোঁজা, ফর্ম জমা, কেনা) প্রতিটি লক্ষ্য-ব্রাউজারে চলে কি না, আর চেহারা গ্রহণযোগ্য কি না, সেটা দেখা।

  1. অন্তত তিন ইঞ্জিন: কম্পিউটারে Chrome বা Edge (Blink), Firefox (Gecko), আর WebKit-এর একটি রূপ। Mac না থাকলে Linux-এ WebKit-ভিত্তিক GNOME Web, বা স্বয়ংক্রিয় পরীক্ষার টুল Playwright-এর WebKit — যদিও এগুলো iPhone-এর Safari-র হুবহু অনুলিপি নয়।
  2. আসল ফোন: DevTools-এর device mode শুধু পর্দার মাপ বদলায়, ইঞ্জিন নয় — Edge-এ “iPhone” বাছলেও আঁকে Blink. পরিবার বা বন্ধুদের একটি পুরোনো Android আর একটি iPhone-এ পাতা খুলুন; ফেসবুকের লিংক থেকেও একবার খুলুন।
  3. ফোনকে কম্পিউটারের সাথে জুড়ে: Android ফোন USB-তে জুড়ে Chrome-এর chrome://inspect থেকে ফোনের পাতা DevTools-এ দেখা যায়; iPhone-এর পাতা দেখা যায় Mac-এর Safari Web Inspector-এ।
  4. ক্লাউড ডিভাইস ল্যাব: BrowserStack-এর মতো সেবায় ব্রাউজারে বসেই শত শত আসল ফোন আর ব্রাউজার ভাড়ায় চালানো যায় — বড় প্রজেক্ট বা নির্দিষ্ট পুরোনো ডিভাইসের বাগ খুঁজতে কাজের।
  5. ধীর নেটওয়ার্ক আর JavaScript বন্ধ করে: DevTools-এ নেটওয়ার্ক ধীর করে, আর JavaScript বন্ধ করে একবার দেখুন — progressive enhancement সত্যিই কাজ করছে কি না।

৯ · প্রজেক্টের support policy ঠিক করা

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

স্তরকোন ব্রাউজারপ্রতিশ্রুতি
ক · পূর্ণChrome, Edge, Firefox, Safari আর Samsung Internet-এর শেষ দুই সংস্করণনকশা আর সব সুবিধা পুরোপুরি; প্রতিটি রিলিজের আগে পরীক্ষা
খ · মূল কাজপুরোনো Android ফোন, ফেসবুক-মেসেঞ্জারের ভেতরের ব্রাউজার, কয়েক বছর পুরোনো Safariপণ্য দেখা, কার্ট, অর্ডার আর পেমেন্ট চলবে; নকশা সরল হতে পারে
গ · পড়া যাবেখুব পুরোনো বা proxy browserলেখা আর দাম পড়া যাবে; বাকি কিছুর নিশ্চয়তা নেই

আর CSS লেখার নিয়ম হিসেবে এক লাইনে: “last 2 versions + Baseline Widely available স্বাধীনভাবে; Newly available-এর সাথে fallback; not yet Baseline শুধু @supports-এর ভেতরে।” সংখ্যাগুলো আপনার সাইটের analytics দেখে বছরে অন্তত একবার নতুন করে ঠিক করুন — পাঠকদের ফোন বদলায়, নীতিও বদলাবে।

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

  • Progressive enhancement আর graceful degradation-এর পার্থক্য? — প্রথমটি সবখানে চলা ভিত্তি থেকে স্তর যোগ করে; দ্বিতীয়টি পূর্ণ নকশা থেকে শুরু করে পুরোনো ব্রাউজারে কম ভাঙার ব্যবস্থা করে।
  • ব্রাউজার অচেনা CSS property পেলে কী করে? — শুধু সেই declaration বাদ দেয়, error দেখায় না; তাই fallback আগে, নতুনটি পরে।
  • @supports দিয়ে কী যাচাই হয়, আর কী হয় না? — property-মান বা selector পড়তে পারে কি না; সব লেআউটে সঠিকভাবে কাজ করে কি না নয় (যেমন পুরোনো Safari-তে flex gap).
  • Baseline Widely available মানে? — সব মূল ব্রাউজারে আসার (Newly available) পর ৩০ মাস পেরিয়েছে।
  • Feature detection কেন user-agent sniffing-এর চেয়ে ভালো? — ব্রাউজারের নাম মিথ্যা বা পুরোনো হতে পারে; ফিচার আছে কি না সরাসরি যাচাই সবসময় সঠিক থাকে।

১০ · AI-এর যুগে — ব্রাউজার সাপোর্টের প্রশ্ন AI-কে করলে

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

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

AI-এর ভুলআপনি যা দেখবেনঠিক উপায়
অপ্রয়োজনীয় -webkit-, -moz- prefix হাতে লেখালম্বা, পুরোনো ধাঁচের CSSমানদণ্ডের CSS লিখে Autoprefixer-কে দায়িত্ব দিন
not yet Baseline ফিচার fallback ছাড়াChrome-এ নিখুঁত, Safari বা Firefox-এ ভাঙাMDN-এ Baseline দেখে @supports আর fallback
fallback @supports not-এর ভেতরেখুব পুরোনো ব্রাউজারে কোনো নকশাই নেইfallback বাইরে, নতুনটি @supports-এর ভেতরে
navigator.userAgent দেখে আলাদা CSSনতুন সংস্করণে ভুল ব্রাউজারে ভুল নকশাCSS.supports() বা @supports
Internet Explorer-এর hack, float-লেআউট, বিশাল polyfillঅকারণ ভারী, ধীর পাতানিজের support policy অনুযায়ী বাদ দিন
“CSS polyfill” যোগ করে নতুন লেআউট চালানোস্ক্রলে ঝাঁকুনি, ভুল মাপসরল fallback; polyfill শুধু জরুরি JavaScript API-তে

AI-কে কাজ দেওয়ার সময় আপনার support policy বলে দিন, আর প্রতিটি নতুন ফিচারের সাপোর্ট-অবস্থা আলাদা করে লিখতে বলুন — তাহলে যাচাই করা সহজ হয়:

তুমি একজন অভিজ্ঞ ফ্রন্ট-এন্ড ডেভেলপার। আমার বাংলাদেশি অনলাইন দোকানের পণ্য-তালিকার CSS লিখে দাও। আমার support policy: Chrome, Edge, Firefox, Safari আর Samsung Internet-এর শেষ দুই সংস্করণে পূর্ণ নকশা; পুরোনো Android ফোন আর ফেসবুকের ভেতরের ব্রাউজারে অন্তত পণ্য দেখা ও "কার্টে নিন" চলবে। শর্ত: ১. progressive enhancement — আগে সবখানে চলা সাধারণ CSS, তারপর নতুন ফিচার @supports-এর ভেতরে। fallback কখনো @supports not-এর ভেতরে রাখবে না। ২. যে প্রতিটি নতুন ফিচার ব্যবহার করবে, মন্তব্যে তার Baseline অবস্থা লেখো (Widely / Newly / not yet), আর নিশ্চিত না হলে স্পষ্ট বলো "নিশ্চিত নই, MDN-এ যাচাই করো"। ৩. হাতে কোনো vendor prefix লিখবে না, শুধু যেখানে prefix-ছাড়া কোনো রূপই নেই (যেমন -webkit-line-clamp). ৪. user-agent দেখে কিছু করবে না। শেষে বলো, কোন কোন ব্রাউজারে আমি নিজে পরীক্ষা করব আর কী দেখব।

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

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

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

প্র ০১প্রায় সবাই তো Chrome-জাতীয় ব্রাউজার ব্যবহার করেন — তাহলে Firefox আর Safari নিয়ে মাথা ঘামানোর দরকার কী?

প্রশ্নটি যৌক্তিক, কারণ Blink-ভিত্তিক ব্রাউজারগুলো মিলে বিশ্বের বেশিরভাগ ব্যবহারকারীকে ধরে। কিন্তু তিনটি কারণে উত্তরটি “না, শুধু Chrome যথেষ্ট নয়”।

  • iPhone-এ Chrome নেই: বাংলাদেশে iPhone ব্যবহারকারী সংখ্যায় কম হলেও প্রায়ই ঠিক তাঁরাই অনলাইনে বেশি কেনাকাটা করেন। আর তাঁদের ফোনে যেকোনো ব্রাউজার খুললেই পাতা আঁকে WebKit. “Chrome-এ পরীক্ষা করেছি” মানে তাঁদের জন্য কিছুই পরীক্ষা করা হয়নি।
  • ইতিহাসের শিক্ষা — Internet Explorer 6: ২০০০-এর দশকের শুরুতে একটি ব্রাউজার প্রায় পুরো বাজার দখল করেছিল। প্রতিযোগিতা না থাকায় তার উন্নয়ন বছরের পর বছর প্রায় থেমে যায়, আর ডেভেলপাররা মানদণ্ড নয়, সেই ব্রাউজারের বাগ মেনে সাইট বানাতেন। “Best viewed in Internet Explorer” লেখা সাইটগুলো পরে অন্য সব ব্রাউজারে ভাঙে, আর সেই পুরোনো ব্রাউজার ছাড়তে ব্যাংক আর সরকারি অফিসের এক দশকেরও বেশি লেগেছিল। একটি ইঞ্জিনের একচেটিয়া মানে, তার বাগই হয়ে যায় “মানদণ্ড”।
  • শুধু এক ইঞ্জিনে পরীক্ষা মানে বাগ রপ্তানি: Chromium-এ কোনো কিছু ভুলভাবেও কাজ করলে আপনি ভাববেন সেটাই সঠিক। পরে অন্য ইঞ্জিন spec মেনে আঁকলে মনে হবে তারা ভুল। আসলে তিন ইঞ্জিনে পরীক্ষাই spec-এর বিরুদ্ধে আপনার কোড যাচাইয়ের সবচেয়ে সস্তা উপায়।

এর সাথে আছে Samsung Internet (Blink হলেও আলাদা সংস্করণ-চক্র), অ্যাপের ভেতরের WebView, আর Firefox-এর মতো স্বাধীন ইঞ্জিন, যা গোপনীয়তা ও উন্মুক্ত ওয়েবের প্রশ্নে বহু মানুষের পছন্দ। Interop প্রজেক্টের মূল কারণও এটি: একাধিক স্বাধীন ইঞ্জিন একই spec মানলেই spec আসল অর্থ পায়।

মূল উপলব্ধি: আপনি ব্রাউজারের জন্য নয়, মানদণ্ডের জন্য কোড লেখেন; একাধিক ইঞ্জিনে পরীক্ষা হলো সেই মানদণ্ডের কাছে সৎ থাকার উপায়।

প্র ০২Vendor prefix-এর ধারণাটি তো ভালোই ছিল — তাহলে এটি ব্যর্থ হলো কেন, আর তার বদলে এখন কী হয়?

ধারণাটি ছিল নিরীক্ষার জন্য একটি আলাদা ঘর: “-webkit- দিয়ে লেখা জিনিস পরীক্ষামূলক, এর ওপর ভরসা কোরো না; চূড়ান্ত হলে নামের আগের অংশটি সরে যাবে।” সমস্যা হলো, ডেভেলপাররা অপেক্ষা করেন না। গোল কোণা, ছায়া, gradient, অ্যানিমেশন — সবই prefix-সহ বছরের পর বছর আসল সাইটে চলেছে।

২০১০-এর দশকের শুরুর দিকে মোবাইল ওয়েব মানেই ছিল iPhone আর Android-এর WebKit. অনেক সাইট শুধু -webkit- লিখত, prefix-ছাড়া রূপ বা অন্যদের prefix বাদ দিয়ে। ফল দাঁড়াল এক বিদ্রূপে: অন্য ব্রাউজার spec মেনে চললেও ব্যবহারকারী দেখতেন ভাঙা পাতা, আর দোষ পড়ত ব্রাউজারের ঘাড়ে। তখন Opera, Microsoft আর পরে Firefox — সবাই নিজেদের ইঞ্জিনে -webkit- নামগুলো চেনার ব্যবস্থা করতে বাধ্য হয়। WHATWG-এর Compatibility Standard নামের একটি দলিলে আজও লেখা আছে, ওয়েব না ভাঙাতে কোন কোন -webkit- জিনিস সব ব্রাউজারকে চিনতে হয়। অর্থাৎ যে prefix “অস্থায়ী” হওয়ার কথা ছিল, তা স্থায়ী হয়ে গেছে।

এই অভিজ্ঞতা থেকে ব্রাউজারগুলোর নীতি বদলায়। Chrome ২০১৩ সালে WebKit থেকে আলাদা হয়ে Blink বানানোর সময়ই ঘোষণা করে, নতুন ফিচার আর prefix দিয়ে নয়। এখন নতুন ফিচার আসে তিন পথে: (১) লুকানো flag — শুধু ডেভেলপার নিজের ব্রাউজারে চালু করতে পারেন, সাধারণ ব্যবহারকারীর কাছে বন্ধ; (২) origin trial — নির্দিষ্ট সাইট সীমিত সময়ের জন্য আসল ব্যবহারকারীদের ওপর পরীক্ষা করতে পারে, মেয়াদ শেষে বন্ধ হয়ে যায়; (৩) Safari Technology Preview বা Firefox Nightly-এর মতো আলাদা পরীক্ষামূলক সংস্করণ। কোনো পথেই “পরীক্ষামূলক জিনিস সবার কাছে চিরকাল” থেকে যায় না।

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

প্র ০৩JavaScript-এর polyfill এত সহজ হলে CSS-এর polyfill এত কঠিন কেন? আর এর ফল কী হয়েছে?

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

CSS-এর ক্ষেত্রে ব্রাউজার stylesheet পড়ার মুহূর্তেই অচেনা property, মান বা selector ফেলে দেয়। এরপর JavaScript দিয়ে CSSOM খুঁজলে সেগুলোর চিহ্নই পাওয়া যায় না। তাই একটি CSS polyfill-কে যা করতে হয়:

  • stylesheet ফাইলটি আবার নিজে ডাউনলোড করে লেখা হিসেবে পড়া — অর্থাৎ JavaScript-এ একটি ছোট CSS parser লেখা;
  • নতুন নিয়ম বুঝে প্রতিটি element-এর মাপ নিজে হিসাব করা;
  • ফল inline style বা সাধারণ class হিসেবে বসানো, আর পাতার মাপ, স্ক্রল, নতুন element যোগ — প্রতিটি পরিবর্তনে আবার পুরো হিসাব।

এতে পাতা ধীর হয়, প্রথমে ভুল নকশা দেখিয়ে পরে লাফ দেয় (layout shift), আর ব্রাউজারের নিজের হিসাবের সাথে খুঁটিনাটিতে মেলে না। container query, :has() বা বিভিন্ন নতুন লেআউটের জন্য এমন চেষ্টা হয়েছে; কাজও করেছে, কিন্তু সবসময় “শেষ উপায়” হিসেবে।

এই সমস্যা মেটাতে Houdini প্রকল্প চেয়েছিল ব্রাউজারের CSS ইঞ্জিনের ভেতরের ধাপগুলোতে (parse, layout, paint) ডেভেলপারকে সরাসরি হুক দিতে। এর কিছু অংশ, যেমন @property, সব ব্রাউজারে পৌঁছেছে; কিন্তু নিজের লেআউট-নিয়ম বানানোর মতো বড় অংশগুলো সব ইঞ্জিনে আসেনি। ফলে CSS জগৎ বেছে নিয়েছে অন্য পথ — @supports আর fallback, আর Interop-এর মাধ্যমে নতুন ফিচার দ্রুত সব ইঞ্জিনে আনা।

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

প্র ০৪Baseline Widely available-এর জন্য ঠিক ৩০ মাস কেন? আর Baseline দেখে সব সিদ্ধান্ত নিলে কোথায় ভুল হতে পারে?

Baseline আসার আগে প্রতিটি ডেভেলপারকে নিজে Can I use-এর ছক দেখে ঠিক করতে হতো “যথেষ্ট সাপোর্ট” মানে কী — কেউ বলতেন ৯০%, কেউ ৯৫%, কেউ “IE বাদে সবাই”। ২০২৩ সালে W3C-র একটি কমিউনিটি দলে ব্রাউজার-নির্মাতা ও অন্যরা মিলে একটি সাধারণ ভাষা তৈরি করেন: একটি নির্দিষ্ট মূল ব্রাউজার-গুচ্ছ, আর দুটি সহজ রায়।

৩০ মাস সংখ্যাটি একটি ব্যবহারিক আপস। সব মূল ব্রাউজারের নতুন সংস্করণে আসা মাত্রই সবাই তা পান না — কেউ ফোন আপডেট করেন দেরিতে, কারো পুরোনো iPhone নতুন iOS পায় না, কোনো অফিস নিয়ন্ত্রিত সংস্করণ চালায়। আড়াই বছরে এই পুরোনো সংস্করণগুলোর বেশিরভাগ ধীরে ধীরে বদলে যায়। লক্ষ্য ছিল এমন একটি সময়, যার পরে বেশিরভাগ সাইট fallback ছাড়াই নিরাপদে ফিচারটি ব্যবহার করতে পারে, কিন্তু এত দীর্ঘ নয় যে নতুন ফিচার বছরের পর বছর অব্যবহৃত পড়ে থাকে।

তবে Baseline-এর সীমাগুলোও জানতে হবে:

  • শুধু মূল গুচ্ছ: Samsung Internet, Opera, Android WebView বা অ্যাপের ভেতরের ব্রাউজার সরাসরি হিসাবে নেই, যদিও Blink-ভিত্তিক হওয়ায় সাধারণত কাছাকাছি থাকে।
  • ব্যবহারকারীর হিসাব নয়: Baseline বলে ফিচারটি কতদিন ধরে আছে, বলে না আপনার পাঠকদের কতজন পুরোনো ফোনে আছেন। গ্রামের স্কুলের তথ্য-সাইট আর ঢাকার একটি ডিজাইন স্টুডিওর পোর্টফোলিওর পাঠক এক নয়।
  • বাগ ধরে না: সব জায়গায় “আছে” মানে সব জায়গায় একইভাবে “নির্ভুল” নয়। Known issues আর নিজের পরীক্ষা তাই এখনো দরকার।
  • আংশিক ফিচার: একটি property-র কিছু মান Widely, কিছু মান নতুন হতে পারে — তাই MDN-এ value ধরে ধরে দেখা জরুরি।

মূল উপলব্ধি: Baseline হলো সবার বোঝার মতো একটি সাধারণ মাপকাঠি — সিদ্ধান্ত দ্রুত হয়, তর্ক কমে। কিন্তু চূড়ান্ত সিদ্ধান্ত নেয় আপনার পাঠক, তাঁদের ফোন আর আপনার analytics.

প্র ০৫iPhone-এ Chrome বা Firefox নামালেও ভেতরে WebKit চলে কেন? এই নিয়ম ওয়েব ডেভেলপারদের জন্য কী অর্থ বহন করে?

কারণটি প্রযুক্তিগত নয়, নীতিগত। Apple-এর App Store-এর নিয়মে বহু বছর ধরে লেখা ছিল: iPhone আর iPad-এ ওয়েব পাতা দেখায় এমন প্রতিটি অ্যাপকে Apple-এর WebKit ব্যবহার করতে হবে। তাই iPhone-এর Chrome আসলে Google-এর নিজস্ব নকশা, bookmark আর sync সুবিধা, কিন্তু পাতা আঁকে WebKit. Firefox, Edge, এমনকি ফেসবুকের ভেতরের ব্রাউজার — সবাই একই। Apple-এর যুক্তি ছিল নিরাপত্তা, ব্যাটারি আর একরকম অভিজ্ঞতা; সমালোচকেরা বলেন, এতে প্রতিযোগিতা থেমে যায়, আর iPhone-এ ওয়েবের অগ্রগতি পুরোপুরি একটি কোম্পানির গতির ওপর নির্ভর করে।

২০২৪ সালে ইউরোপীয় ইউনিয়নের ডিজিটাল বাজারের নতুন আইনের চাপে Apple শুধু ইউরোপের ব্যবহারকারীদের জন্য অন্য ইঞ্জিনের ব্রাউজার আনার সুযোগ খুলে দেয়। কিন্তু শর্ত আর ঝামেলার কারণে বড় ব্রাউজারগুলো সেখানে নিজস্ব ইঞ্জিন নিয়ে ব্যাপকভাবে আসেনি বলে অভিযোগ রয়েছে, আর বাংলাদেশসহ ইউরোপের বাইরে নিয়মটি আগের মতোই।

ওয়েব ডেভেলপারের জন্য এর অর্থ:

  • iPhone-এ পরীক্ষা মানে Safari-র ইঞ্জিনে পরীক্ষা — ব্যবহারকারী যে ব্রাউজারই বলুন।
  • iPhone-এ ইঞ্জিন আপডেট হয় iOS-এর সাথে। যে পুরোনো iPhone নতুন iOS পায় না, তার সব ব্রাউজার একসাথে পুরোনো সংস্করণে আটকে থাকে। তাই iPhone-এ “পুরোনো ব্রাউজার” সমস্যা Android-এর চেয়ে ভিন্ন ধরনের।
  • নতুন ফিচারের অপেক্ষা: কোনো ফিচার Chromium-এ এলেও যতদিন WebKit-এ না আসে, ততদিন iPhone-এর কোনো ব্যবহারকারী তা পান না — এজন্যই Baseline-এর হিসাবে Safari iOS আলাদা করে আছে।

মূল উপলব্ধি: ব্রাউজার সাপোর্ট শুধু প্রযুক্তির প্রশ্ন নয় — ব্যবসা, আইন আর প্ল্যাটফর্মের নীতিও ঠিক করে আপনার CSS কোথায় কীভাবে চলবে।

অনুশীলন

বেশিরভাগ কাজে খোঁজা আর ভাবাই আসল — একটিতে ছোট CSS লিখবেন।

  1. তথ্য খুঁজুন: MDN আর caniuse.com-এ এই চারটির Baseline অবস্থা খুঁজে লিখুন: gap (Flexbox-এ), :has(), svh একক, আর field-sizing. প্রতিটির পাশে লিখুন — আপনার নিজের প্রজেক্টে fallback লাগবে কি না।

    ২০২৬ সালের হিসাবে মোটামুটি: Flexbox-এর gap বহু আগে থেকে Widely available — fallback লাগে না, শুধু খুব পুরোনো ফোন লক্ষ্য হলে margin. :has() ২০২৩-এর শেষে Newly, ২০২৬-এর মাঝামাঝি থেকে Widely — সাধারণ প্রজেক্টে নিশ্চিন্তে, তবে মূল কাজ এর ওপর না রাখাই ভালো। svh/dvh ২০২২-এর শেষে Newly, ২০২৫ থেকে Widely — তবু 100vh-এর fallback লাইনটি রাখা সস্তা বিমা। field-sizing not yet Baseline — শুধু বাড়তি সুবিধা হিসেবে, না থাকলে স্বাভাবিক মাপের ঘর। আপনার পাওয়া তথ্য এর থেকে আলাদা হলে, সাইটের তথ্যই ঠিক — ব্রাউজার প্রতি মাসে বদলায়, আর সেটাই এই অনুশীলনের শিক্ষা।

  2. ভুল ধরুন: একজন সহকর্মী লিখেছেন — .hero { min-height: 100svh; min-height: 100vh; } আর @supports not (display: grid) { .list { display: flex; } } সাথে @supports (display: grid) { .list { display: grid; } }. দুটি সমস্যা কী, আর ঠিক রূপ কী?

    প্রথমটি: ক্রম উল্টো। নতুন ব্রাউজার দুটিই বোঝে, আর শেষেরটি (100vh) জেতে — ফলে নতুন ব্রাউজারও পুরোনো সমস্যায় পড়ে। ঠিক রূপ: আগে min-height: 100vh; পরে min-height: 100svh;.

    দ্বিতীয়টি: fallback আছে @supports not-এর ভেতরে। যে ব্রাউজার @supports-ই চেনে না, সে দুটি ব্লকই বাদ দেবে — তালিকা কোনো নকশাই পাবে না। ঠিক রূপ: .list { display: flex; flex-wrap: wrap; } বাইরে সাধারণ নিয়ম হিসেবে, আর @supports (display: grid) { .list { display: grid; … } }.

  3. নিজে লিখুন: একটি বোতামের CSS লিখুন, যার পটভূমি পুরোনো ব্রাউজারে #15803d, আর color-mix() চিনলে hover-এ সেই রঙের সাথে ২০% কালো মেশানো গাঢ় রং; না চিনলে hover-এ #166534. তারপর ইচ্ছাকৃত ভুল বানানে পুরোনো ব্রাউজারের অনুকরণ করুন।

    একই property দুইবার — আগে স্থির রং, পরে নতুন ফাংশন। কোনো @supports লাগে না:

    .buy {
      font: inherit; color: white; border: 0;
      padding: 10px 18px; border-radius: 8px;
      background: #15803d;
    }
    .buy:hover, .buy:focus-visible {
      background: #166534;                              /* fallback */
      background: color-mix(in srgb, #15803d 80%, black); /* নতুন */
    }

    কোডটি অংশ ৪-এর প্রথম এডিটরে বসিয়ে দেখুন — সেখানকার বোতামের class btn, তাই .buy-এর জায়গায় .btn লিখুন। আধুনিক ব্রাউজারে hover বা Tab-এর focus-এ color-mix() জেতে। color-mix-কে colour-mix লিখলে সেই লাইন বাদ পড়ে, আর fallback-এর #166534 খাটে — দেখতে প্রায় একই, ঠিক যেমনটা চাই। color-mix() নিজে এখন Baseline Widely available, তাই বাস্তবে fallback-টি শুধু খুব পুরোনো ফোনের জন্য।

  4. Support policy লিখুন: আপনার এলাকার একটি কোচিং সেন্টারের ভর্তির ফর্মওয়ালা ওয়েবসাইটের জন্য তিন স্তরের support policy আর একটি .browserslistrc লিখুন। কাদের কথা মাথায় রাখবেন?

    পাঠক হবেন শিক্ষার্থী আর অভিভাবক — অনেকে কম দামি বা পুরোনো Android ফোনে, ফেসবুক পেজের লিংক থেকে। একটি যুক্তিসংগত নীতি:

    • পূর্ণ: Chrome, Samsung Internet, Edge, Firefox, Safari-র শেষ দুই সংস্করণ।
    • মূল কাজ: পুরোনো Android, ফেসবুক-মেসেঞ্জারের ভেতরের ব্রাউজার — কোর্সের তথ্য পড়া আর ভর্তির ফর্ম জমা অবশ্যই চলবে; ফর্মটি সাধারণ HTML <form>, JavaScript ছাড়াও জমা হয়।
    • পড়া যাবে: বাকি সব — ফোন নম্বর আর ঠিকানা লেখা হিসেবে আছে, ছবিতে নয়।

    .browserslistrc: > 0.5% in BD, last 2 versions, not dead. আর CSS-এর নিয়ম: Widely available স্বাধীনভাবে, Newly-র সাথে fallback, not yet Baseline শুধু @supports-এ।

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

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

ক্রমProgressive enhancement-এ একটি পাতার স্তরগুলো নিচ থেকে ওপরে সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📄 বিষয়বস্তু — লেখা, দাম, ফোন নম্বর
  2. 🧱 semantic HTML — heading, link, সত্যিকারের form আর button
  3. 🎨 সবাই চেনে এমন সাধারণ CSS — রং, ফাঁক, flex-wrap
  4. ✨ আধুনিক CSS — @supports-এর ভেতরে Grid, :has()
  5. ⚡ JavaScript — পাতা না বদলে কার্টে যোগের মতো বাড়তি আরাম

একটি rule-এ লেখা আছে color: #1d4ed8; color: oklch(0.55 0.2 260);. যে ব্রাউজার oklch() চেনে না, সে লেখার কোন রং দেখাবে?

  • #1d4ed8 — প্রথম declaration
  • oklch()-এর রং, কারণ শেষেরটি জেতে
  • ব্রাউজারের ডিফল্ট কালো, কারণ পুরো rule বাদ
  • পাতা লোড হবে না, error দেখাবে

কোনো ওয়েব ফিচারকে “Baseline Widely available” বলা হয় কখন?

  • যখন Chrome-এ প্রথম চালু হয়
  • যখন সব মূল ব্রাউজারের সর্বশেষ সংস্করণে আসে
  • সব মূল ব্রাউজারে আসার (Newly available) পর ৩০ মাস পেরোলে
  • যখন বিশ্বের ৫০% মানুষ ফিচারটি ব্যবহার করেন

পুরোনো ব্রাউজারের জন্য fallback কোথায় লেখা সবচেয়ে নিরাপদ?

  • @supports not (…)-এর ভেতরে
  • বাইরে সাধারণ CSS হিসেবে, আর নতুন নিয়ম @supports (…)-এর ভেতরে
  • JavaScript-এ navigator.userAgent দেখে আলাদা ফাইলে
  • নতুন declaration-এর পরে, একই rule-এ

@supports selector(:has(img)) { … } লিখে ব্রাউজারকে কী জিজ্ঞেস করা হয়?

  • পাতায় কোনো <img> আছে কি না
  • ছবিগুলো ঠিকমতো লোড হয়েছে কি না
  • img property-টি ব্রাউজার চেনে কি না
  • ব্রাউজার :has() selector-টি চেনে কি না

বাংলাদেশে বিক্রি হওয়া একটি iPhone-এ Chrome অ্যাপ দিয়ে ওয়েব পাতা খুললে পাতাটি কোন rendering engine আঁকে?

  • Blink
  • Gecko
  • WebKit
  • Trident

পুরোনো একটি Safari-তে @supports (gap: 1rem) সত্য হতো, অথচ Flexbox-এ gap কাজ করত না। এ থেকে @supports সম্পর্কে কী বোঝা যায়?

  • Safari-তে @supports ভুলভাবে বানানো ছিল
  • এটি শুধু বলে property-মান পড়া যায় কি না, সব প্রসঙ্গে কাজ করে কি না নয়
  • @supports কখনো ব্যবহার করা উচিত নয়
  • @supports শুধু display property যাচাই করতে পারে

শব্দকোষ

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

মনে রাখুন

  1. ইঞ্জিন আর সংস্করণই আসল: Blink, WebKit, Gecko; iPhone-এ সব ব্রাউজার WebKit; পুরোনো ফোন আর অ্যাপের ভেতরের ব্রাউজার বাংলাদেশে খুব সাধারণ।
  2. তথ্য দেখে সিদ্ধান্ত: MDN-এর Baseline-বাক্স আর Can I use-এর Notes; Widely available নিশ্চিন্তে, Newly-র সাথে fallback, not yet Baseline শুধু বাড়তি সুবিধা।
  3. Progressive enhancement: HTML-এ মূল কাজ → সাধারণ CSS → @supports-এ আধুনিক CSS → JavaScript; কোনো স্তর না চললেও নিচেরটি চলে।
  4. CSS-এর fallback: অচেনা declaration বাদ পড়ে — পুরোনোটি আগে, নতুনটি পরে; selector অচেনা হলে পুরো rule বাদ; var()-এ এই কৌশল চলে না; fallback থাকুক @supports-এর বাইরে।
  5. আধুনিক অভ্যাস: prefix হাতে নয়, Autoprefixer আর browserslist; CSS polyfill নয়, fallback; user-agent নয়, feature detection; তিন ইঞ্জিন আর আসল ফোনে পরীক্ষা; লিখিত support policy.

পরের পর্বে: পাতা সব ব্রাউজারে চলছে — কিন্তু পুরোনো ফোনে আর ধীর নেটওয়ার্কে কত দ্রুত খুলছে? এবার পারফরম্যান্স: Core Web Vitals কী মাপে, CSS কীভাবে পাতা আঁকা আটকে রাখে, আর critical CSS দিয়ে প্রথম পর্দা কীভাবে চোখের পলকে দেখানো যায়।

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

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