এই পর্বে যা শিখবেন
- Pseudo-class আর pseudo-element-এর আসল পার্থক্য, লেখার নিয়ম আর specificity
- মাউস, কি-বোর্ড, লিংক আর ফর্মের অবস্থা দেখে স্টাইল — সাথে
:visited-এর গোপনীয়তার সীমা :nth-child(an+b)-এর সূত্র হাতে হিসাব করা,of S,:nth-of-type,:empty,:root:has()দিয়ে parent, আগের ভাই-বোন আর “কতগুলো আছে” দেখে বাছাই::before,::after,::first-letter,::marker,::placeholderআর বন্ধুরা — অ্যাক্সেসিবিলিটি মাথায় রেখে
১ · অবস্থা আর অংশ — pseudo-class বনাম pseudo-element
পর্ব ১৭-এ (Selector) দেখেছেন, selector element চেনে তার নাম, class বা attribute দিয়ে — অর্থাৎ HTML-এ যা লেখা আছে তা দিয়ে। কিন্তু অনেক জরুরি তথ্য HTML-এ লেখা থাকে না। মাউস এখন কোন বোতামের ওপর? কোন ঘরে ভুল নম্বর লেখা হয়েছে? এই li তালিকার কততম? অনুচ্ছেদের প্রথম লাইন কোথায় শেষ — সেটি তো স্ক্রিনের চওড়ার ওপর নির্ভর করে!
এই দুই ধরনের প্রশ্নের জন্য CSS-এর দুটি আলাদা হাতিয়ার আছে। Pseudo-class (“সিউডো” মানে ছদ্ম — এমন class যা আপনি লেখেননি, ব্রাউজার নিজে বসায়) element-এর অবস্থা বা অবস্থান দেখে বাছে। আর pseudo-element element-এর এমন একটি অংশ ধরে, যার নিজের কোনো ট্যাগ নেই।
bKash অ্যাপের “Send Money” বোতামের কথা ভাবুন। আঙুল রাখলে রং গাঢ় হয়, টাকা না লিখলে বোতাম ঝাপসা থাকে — বোতাম একই, শুধু অবস্থা বদলায়। এটি pseudo-class. আবার পাড়ার মিষ্টির দোকানের সাইনবোর্ডে প্রথম অক্ষরটি বড় আর লাল করে আঁকা — অক্ষরটি আলাদা কোনো বোর্ড নয়, একই লেখার একটি অংশ। এটি pseudo-element.
এক কোলন : — pseudo-class
অন্য selector-এর গায়ে লেগে শর্ত যোগ করে: button:hover, li:nth-child(2), input:checked. একটি selector-এ যতগুলো খুশি জোড়া যায়: a:hover:focus-visible.
দুই কোলন :: — pseudo-element
একটি নতুন “বাক্স” বা অংশ তৈরি করে: p::first-line, .card::before. Selector-এর একদম শেষে বসে, আর সাধারণত একটিই: a:hover::after চলে, a::after:hover চলে না।
১৯৯৬ সালের CSS1-এ ছিল মাত্র কয়েকটি: :link, :visited, :active আর :first-line, :first-letter. ১৯৯৮ সালের CSS2 আনে :hover, :focus, :first-child, :lang() আর :before/:after — তখন সবই এক কোলনে। Selectors Level 3 পার্থক্য স্পষ্ট করতে pseudo-element-এর জন্য দুই কোলন চালু করে। পুরোনো সাইট যাতে না ভাঙে, তাই সেই চারটি পুরোনো pseudo-element আজও এক কোলনে চলে; নতুন কোড লিখুন দুই কোলনে। আজকের :is(), :has(), :focus-visible এসেছে Selectors Level 4 থেকে।
২ · ব্যবহারকারীর কাজে বদলানো অবস্থা — :hover, :active, :focus, :focus-visible
পাঁচটি pseudo-class ব্যবহারকারীর হাতের কাজের সাথে সাথে চালু-বন্ধ হয়:
:hover— মাউসের তীর element-এর ওপর। ফোন আর ট্যাবে মাউস নেই; সেখানে ছোঁয়ার পর hover “আটকে” থাকতে পারে, আবার না-ও আসতে পারে। তাই জরুরি তথ্য বা মেনু কখনো শুধু hover-এ লুকাবেন না। শুধু মাউসওয়ালা যন্ত্রে hover-সাজ দিতে চাইলে@media (hover: hover)(পর্ব ৩১-এর media query) ব্যবহার করুন।:active— চাপ দিয়ে ধরে রাখার মুহূর্তটুকু: মাউসের বোতাম চেপে আছেন, অথবা কি-বোর্ডে Space চেপে ধরেছেন।:focus— element-টি এখন focus পেয়েছে, অর্থাৎ কি-বোর্ডের চাপ এখানেই যাবে। কারণ যা-ই হোক — Tab, মাউস-ক্লিক বা JavaScript.:focus-visible— focus পেয়েছে এবং ব্রাউজার মনে করছে দাগ দেখানো দরকার।:focus-within— নিজে বা ভেতরের যেকোনো বংশধর focus পেয়েছে। পুরো ফর্ম বা কার্ডকে “সক্রিয়” দেখাতে।
:focus থাকতে :focus-visible কেন? কারণ মাউসে ক্লিক করা মানুষ জানেন তিনি কোথায় চাপলেন — বোতামের চারপাশে মোটা নীল দাগ তাঁর কাছে “বাগ” মনে হয়। ডিজাইনাররা তাই বছরের পর বছর outline: none লিখে দাগ মুছে দিতেন, আর কি-বোর্ডে চলা মানুষ (যাঁদের হাতে সমস্যা, screen reader ব্যবহারকারী, দ্রুত কাজ করা অফিসকর্মী) হারিয়ে যেতেন। :focus-visible-এ ব্রাউজার একটি হিসাব (heuristic) চালায়: focus কি-বোর্ড থেকে এলে দাগ দেখায়; মাউসে বোতাম চাপলে দেখায় না; আর লেখার ঘরে (text input) সবসময় দেখায়, কারণ সেখানে কার্সর কোথায় তা জানা দরকার। আজকের সব প্রধান ব্রাউজার নিজেদের ডিফল্ট দাগও :focus-visible দিয়েই দেখায় (Baseline Widely available).
নিচের ডায়াগ্রামে একটি বোতামের জীবনের কয়েক মুহূর্ত দেখুন — কোন মুহূর্তে কোন pseudo-class সত্য, আর তাই কোন CSS নিয়ম খাটে (সবুজ লাইন)।
1button { background: white; }2button:hover { background: #ccfbf1; }3button:active { transform: translateY(2px); }4button:focus { /* কিছু লেখা নেই */ }5button:focus-visible { outline: 3px solid blue; }
| :hover | |
| :active | |
| :focus | |
| :focus-visible |
- পাতা খুলল। কোনো অবস্থাই সত্য নয়, তাই শুধু প্রথম নিয়মটি খাটে — সাদা বোতাম।
- মাউস ওপরে এল।
:hoverসত্য হলো, পটভূমি হালকা সবুজ। কোনো ক্লিক হয়নি, তাই focus এখনো অন্য কোথাও। - মাউসের বোতাম চেপে ধরলেন। এখন
:active— বোতাম ২px নিচে নামে, যেন সত্যিই দাবানো হলো। Windows আর Android-এর ব্রাউজারে এই মুহূর্তেই বোতাম focus পায় (macOS-এর Safari-তে বোতাম ক্লিকে সাধারণত focus পায় না)। কিন্তু:focus-visibleমিথ্যা — মাউসে চাপা হয়েছে, দাগের দরকার নেই। - ছেড়ে দিলেন।
:activeশেষ, ক্লিক ঘটল। focus রয়ে গেল বোতামেই। যদি লাইন ৪-এoutlineলিখতেন, এখন মাউস ব্যবহারকারীও দাগ দেখতেন — পুরোনো দিনের সেই অভিযোগ। - মাউস সরিয়ে নিলেন।
:hoverগেল, কিন্তু focus আছে। কেউ এখন Enter চাপলে এই বোতামই চলবে — অথচ দেখে বোঝার উপায় নেই। এটাই মাউস-জগতে মেনে নেওয়া যায়, কি-বোর্ড-জগতে নয়। - কি-বোর্ড দিয়ে ফিরলেন। Tab চেপে পরের বোতামে গিয়ে Shift+Tab চেপে এখানে ফিরলেন। focus এবার কি-বোর্ড থেকে এসেছে, তাই
:focus-visibleসত্য — নীল মোটা দাগ। কি-বোর্ড ব্যবহারকারী ঠিক জানেন তিনি কোথায়। - Space চেপে ধরলেন। কি-বোর্ডে চাপলেও
:activeসত্য হয় — বোতাম নিচে নামে, দাগও থাকে। Space ছাড়লে ক্লিক ঘটবে। খেয়াল করুন, একটি element-এ একসাথে চারটি অবস্থাও সত্য হতে পারে; তখন সবগুলো নিয়মই খাটে, আর বিরোধ হলে cascade (পর্ব ১৮) ঠিক করে কে জিতবে।
এবার নিজে পরীক্ষা করুন। প্রথমে মাউস দিয়ে দুটি বোতামে ক্লিক করুন, তারপর প্রিভিউর ভেতরে একবার ক্লিক করে Tab চাপতে থাকুন:
button, .pay { font: inherit; padding: 8px 14px; margin: 4px;
border: 2px solid #0f766e; border-radius: 8px; background: #fff; }
.pay { display: inline-block; color: #0f766e; text-decoration: none; }
button:hover, .pay:hover { background: #ccfbf1; }
button:active, .pay:active { transform: translateY(2px); }
/* পুরোনো উপায় — মাউস-ক্লিকেও দাগ */
.a:focus { outline: 3px solid crimson; outline-offset: 2px; }
/* আধুনিক উপায় — শুধু যখন দরকার */
.b:focus-visible, .pay:focus-visible {
outline: 3px solid #2563eb; outline-offset: 2px; }
/* ভেতরের কেউ focus পেলে পুরো ফর্ম */
.search { margin-top: 10px; padding: 8px; border: 2px dashed #94a3b8; }
.search:focus-within { border-color: #2563eb; background: #eff6ff; }মাউস ওপরে আনলে তিনটি বোতাম-লিংক হালকা সবুজ, চেপে ধরলে একটু নিচে নামে। মাউসে ক্লিক করলে “:focus বোতাম”-এ লাল দাগ বসে যায়, কিন্তু “:focus-visible বোতাম”-এ কিছু দেখায় না। Tab চাপলে দুটিতেই দাগ আসে — একটি লাল, একটি নীল। Tab চেপে খোঁজার ঘরে পৌঁছালে পুরো ফর্মের বাক্স নীল হয় (:focus-within); খোঁজার ঘরে মাউসে ক্লিক করলেও ঘরটিতে ব্রাউজারের নিজের দাগ দেখায়, কারণ লেখার ঘরে :focus-visible সবসময় সত্য। চেষ্টা করুন: .b:focus-visible-এর নিয়ম মুছে দিন — ব্রাউজারের ডিফল্ট দাগ ফিরে আসে কি না দেখুন। তারপর outline-offset-কে -5px করুন।
outline: none লিখলে তার বদলে কিছু দিন। পুরোনো টেমপ্লেট আর AI-এর কোডে প্রায়ই থাকে *:focus { outline: none; }. এটি WCAG 2.2-এর ২.৪.৭ (Focus Visible) নিয়ম ভাঙে। নিরাপদ নিয়ম: ডিফল্ট দাগ রাখুন, অথবা :focus-visible-এ নিজের স্পষ্ট দাগ দিন — অন্তত ২px, পটভূমির সাথে যথেষ্ট contrast. খুব শখ করে রং-বদল দিয়ে focus দেখাতে চাইলেও Windows-এর High Contrast mode-এ রং মুছে যায়; outline থাকে। বিস্তারিত পর্ব ৪৫-এ।
৩ · লিংকের অবস্থা — :link, :visited, :any-link আর গোপনীয়তা
লিংকের নিজস্ব দুটি অবস্থা: :link — যে লিংকে এখনো যাননি, আর :visited — যেটি আপনার ব্রাউজারের ইতিহাসে আছে। দুটি মিলিয়ে :any-link — href আছে এমন যেকোনো লিংক। খেয়াল করুন, href ছাড়া <a> কোনোটিই মানে না; সেটি আসলে লিংকই নয়, শুধু একটি জায়গা-ধরা ট্যাগ (পর্ব ৫)।
/* LVHA ক্রম — চারটির specificity সমান, তাই পরে লেখাটি জেতে */
a:link { color: #1d4ed8; }
a:visited { color: #7c3aed; }
a:hover { color: #0f766e; text-decoration-thickness: 2px; }
a:focus-visible { outline: 3px solid currentColor; outline-offset: 2px; }
a:active { color: #b91c1c; }ক্রমটা মনে রাখার কৌশল “LoVe HAte” — Link, Visited, Hover, Active. কারণ চারটির ওজন সমান (প্রতিটি একটি type আর একটি pseudo-class); a:hover-কে যদি a:visited-এর আগে লেখেন, তাহলে আগে দেখা লিংকে মাউস আনলে রং আর বদলাবে না — পরে লেখা :visited জিতে যাবে।
:visited-এ কেন প্রায় কিছুই করা যায় না? চেষ্টা করে দেখুন a:visited { font-size: 30px; } — কিছুই হবে না। ব্রাউজার :visited-এ শুধু রং-জাতীয় property মানে: color, background-color, border-color, outline-color, text-decoration-color আর SVG-র fill/stroke. তার ওপর JavaScript দিয়ে লিংকের রং জিজ্ঞেস করলে ব্রাউজার ইচ্ছা করে “না-দেখা” লিংকের রং বলে। কারণ — history sniffing: একসময় যেকোনো সাইট হাজারটা লিংক লুকিয়ে বসিয়ে মেপে দেখত কোনগুলো :visited, আর এভাবে জেনে যেত আপনি কোন ব্যাংক, কোন হাসপাতাল বা কোন সংবাদ সাইটে যান। পুরো গল্প ভাবনার প্রশ্ন ১-এ।
৪ · ফর্মের অবস্থা — :checked থেকে :user-invalid
পর্ব ৮ আর ৯-এ ফর্মের ঘর, required, pattern আর ব্রাউজারের নিজস্ব যাচাই দেখেছেন। প্রতিটি ঘরের অবস্থা ব্রাউজার জানে, আর CSS সেটি পড়তে পারে:
| Pseudo-class | কখন সত্য | সাবধানতা |
|---|---|---|
:checked | checkbox/radio টিক দেওয়া, অথবা <option> বাছাই করা | আধা-টিকের জন্য আলাদা :indeterminate |
:disabled / :enabled | disabled attribute আছে/নেই | disabled ঘর Tab-এ আসে না, ফর্মের সাথে জমাও হয় না |
:required / :optional | required আছে/নেই | শুধু চিহ্ন দেখায়; লেবেলেও লিখুন “আবশ্যক” |
:valid / :invalid | যাচাইয়ে পাস/ফেল — এই মুহূর্তে | পাতা খুলতেই খালি required ঘর invalid! |
:user-valid / :user-invalid | একই, কিন্তু ব্যবহারকারী ঘরটি বদলানোর পর বা জমা দিতে চাইলে | Baseline ২০২৩ সালের শেষ থেকে, এখন Widely available |
:placeholder-shown | ঘর খালি, তাই placeholder দেখা যাচ্ছে | “ভাসমান লেবেল” নকশায় কাজে লাগে |
:read-only / :read-write | লেখা যায় না / যায় | শুধু readonly ঘর নয় — p, div, এমনকি checkbox-ও :read-only |
নিচের পাঠানোর ফর্মে সবগুলো একসাথে দেখুন। শুরুতেই খেয়াল করুন কোন ঘরের পটভূমি কমলা — এটাই :invalid-এর সমস্যা:
<style>
label { display: block; margin: 8px 0 2px; }
input { font: inherit; padding: 6px; border: 2px solid #94a3b8; border-radius: 6px; }
input:required { border-left-width: 8px; }
input:invalid { background: #fff7ed; } /* পাতা খুলতেই! */
input:user-invalid { border-color: #dc2626; } /* কিছু করার পরে */
input:user-valid { border-color: #16a34a; }
input:placeholder-shown { font-style: italic; }
input[readonly] { background: #f1f5f9; color: #475569; }
:checked + span { font-weight: bold; color: #15803d; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
<form>
<label for="ph">মোবাইল নম্বর (আবশ্যক)</label>
<input id="ph" name="phone" required pattern="01[3-9][0-9]{8}" placeholder="01XXXXXXXXX" inputmode="numeric">
<label for="amt">পরিমাণ (টাকা)</label>
<input id="amt" name="amount" type="number" min="10" max="25000" placeholder="যেমন 500">
<label for="fee">চার্জ</label>
<input id="fee" name="fee" value="পাঠানোর আগে দেখাবে" readonly>
<p><label><input type="checkbox" name="save"> <span>নম্বরটি মনে রাখুন</span></label></p>
<button>পাঠান</button>
<button type="button" disabled>নির্ধারিত সময়ে পাঠান</button>
</form>পাতা খুলতেই মোবাইল নম্বরের ঘর কমলা — কিছু না লিখতেই “ভুল” বলা হচ্ছে! এটাই :invalid-এর দোষ। ঘরে 0171 লিখে বাইরে ক্লিক করুন — এবার লাল সীমানা (:user-invalid); পুরো ১১ অঙ্ক (01712345678) দিলে সবুজ। খালি ঘরে placeholder তেরছা, লিখলেই সোজা। চার্জের ঘর ধূসর, লেখা যায় না। টিক দিলে “নম্বরটি মনে রাখুন” মোটা-সবুজ। ঘর খালি রেখে “পাঠান” চাপলে ব্রাউজার নিজের বার্তা দেখায় আর ঘরটি লাল হয়। চেষ্টা করুন: input:invalid-এর লাইনটি মুছে দেখুন — অভিজ্ঞতা কতটা ভদ্র হলো? তারপর input[readonly]-কে input:read-only করে দেখুন checkbox-ও এখন এই নিয়মে পড়ে কি না (DevTools-এ দেখা যাবে)।
:disabled নিয়ে একটি বাস্তব কথা: disabled বোতাম Tab-এ আসে না, তাই screen reader ব্যবহারকারী অনেক সময় জানতেই পারেন না যে বোতামটি আছে, আর কেন চাপা যাচ্ছে না। অনেক দল তাই বোতাম সক্রিয় রেখে চাপলে ভুলগুলো দেখায়, অথবা aria-disabled="true" দিয়ে focus রেখে দেয় (তখন স্টাইল দিতে হয় [aria-disabled="true"] দিয়ে, কারণ :disabled মেলে না)। ফর্ম সাজানোর পুরো আলোচনা পর্ব ৪৩-এ।
৫ · গঠন দেখে বাছাই — :nth-child(an+b) আর বন্ধুরা
Structural pseudo-class element-কে চেনে DOM গাছে তার অবস্থান দিয়ে — ক্লাসে রোল নম্বরের মতো। সবচেয়ে সহজগুলো: :first-child (parent-এর প্রথম সন্তান), :last-child (শেষ সন্তান), :only-child (একমাত্র সন্তান)। আর সবচেয়ে শক্তিশালীটি :nth-child(), যার ভেতরে একটি সূত্র বসে: an+b.
নিয়মটা এরকম: ব্রাউজার n-এর জায়গায় ০, ১, ২, ৩… বসায়। প্রতিবার যে সংখ্যা আসে, সেই নম্বরের সন্তান মেলে। সংখ্যা ১-এর কম হলে বাদ (গোনা শুরু ১ থেকে)। 2n+1 = ১, ৩, ৫… (একই কথা odd), 2n = ২, ৪, ৬… (even). নিচে একটি সূত্র হাতে হিসাব করে দেখুন:
1<li>রহিম2<li>করিম3<li>সুমি4<li>রাজু5<li>মিতা6<li>জামাল7<li>নীলা8<li>তানিয়া9<li>সাকিব10<li>রুমা
| সূত্র | |
| n | |
| ফল |
- সূত্র পড়ুন।
3n+2-এ a = ৩ (কত ঘর পরপর), b = ২ (কোথা থেকে শুরু)। মানে “দ্বিতীয়জন থেকে শুরু করে প্রতি তিনজনে একজন”। এবার n-এ ০ থেকে বসাই। - n = ০. ৩×০+২ = ২ — করিম মিলল। n শুরু হয় ০ থেকে, তাই b নিজেই প্রথম মিল।
- n = ১. ৩×১+২ = ৫ — মিতা।
- n = ২. ৩×২+২ = ৮ — তানিয়া। প্রতিবার ৩ করে লাফ।
- n = ৩. ফল ১১, কিন্তু তালিকায় ১০ জন। আর কেউ মিলবে না — উত্তর ২, ৫, ৮. ক্রিকেট দলে “দ্বিতীয় থেকে প্রতি তৃতীয় খেলোয়াড় ফিল্ডিং করবে”-র মতো।
- ঋণাত্মক a:
-n+3. n = ০, ১, ২, ৩ বসালে ৩, ২, ১, ০ — শূন্য বা তার কম বাদ। ফল প্রথম তিনজন! “প্রথম N-টি” বাছার কৌশল এটিই। একইভাবে:nth-last-child(-n+3)= শেষ তিনজন। - পরিসর: দুটি জোড়া।
n+4= চতুর্থ থেকে শেষ পর্যন্ত,-n+7= প্রথম সাতজন। দুটি একসাথে লিখলেli:nth-child(n+4):nth-child(-n+7)— দুই শর্তই মানতে হবে, তাই ৪ থেকে ৭. “পাতার ৪ থেকে ৭ নম্বর ফল হাইলাইট” এভাবেই হয়।
আরও কয়েকটি দরকারি structural pseudo-class:
:nth-child(an+b of S)— গোনার আগে শুধু S মানে এমন ভাই-বোনদের আলাদা করে।li.x:nth-child(odd)মানে “সবার মধ্যে বিজোড় অবস্থানে আছে এমন .x”; আরli:nth-child(odd of .x)মানে “শুধু .x-দের মধ্যে ১ম, ৩য়, ৫ম…”। লুকানো সারি বাদ দিয়ে zebra stripe বানাতে খুব কাজের:tr:nth-child(even of :not([hidden])). Safari বহু বছর আগেই এটি এনেছিল; Chrome আর Firefox ২০২৩ সালে যোগ দেয়, তাই এখন এটি Baseline Widely available.:nth-of-type(),:first-of-type,:last-of-type— একই ট্যাগের ভাই-বোনদের মধ্যে গোনে।p:nth-of-type(2)= দ্বিতীয় p, মাঝে যত span বা h2-ই থাকুক।:empty— একদম ফাঁকা element: কোনো সন্তান নেই, কোনো লেখা নেই। HTML comment চলে, কিন্তু একটি ফাঁকা জায়গা বা নতুন লাইন থাকলেও আজকের ব্রাউজারে আর:emptyনয়।:root— ডকুমেন্টের সবচেয়ে ওপরের element, HTML-এ যা<html>. ওজন একটি class-এর সমান (htmlলিখলে type-এর সমান) — এজন্যই পর্ব ৩৩-এর CSS variable সাধারণত:root-এ রাখা হয়।
:root { --mark: #2563eb; }
li:nth-child(3n+2) { background: #fef3c7; } /* ২, ৫, ৮ */
li.x:nth-child(odd) { outline: 2px dashed crimson; } /* বিজোড় অবস্থানের x */
li:nth-child(odd of .x) { color: var(--mark); font-weight: bold; }
li:last-child { border-bottom: 3px solid #16a34a; }
.box :first-child { font-style: italic; }
.box p:nth-of-type(2) { background: #dcfce7; }
.solo b:only-child { background: #e0e7ff; padding: 2px 6px; }
.note { min-height: 16px; border: 1px solid #94a3b8; margin: 6px 0; }
.note:empty {
background: repeating-linear-gradient(45deg, #fecaca 0 6px, #fff 6px 12px); }হলুদ পটভূমি করিম, মিতা আর তানিয়ার (২, ৫, ৮)। লাল ভাঙা দাগ সুমি, মিতা আর নীলার — এরা x, আর সবার মধ্যে বিজোড় অবস্থানে (৩, ৫, ৭)। নীল মোটা লেখা করিম আর মিতার — শুধু x-দের মধ্যে প্রথম আর তৃতীয়। একই “odd”, দুই রকম ফল! বাক্সে span তেরছা (প্রথম সন্তান), “দ্বিতীয় p” সবুজ। নিচের দুটি বাক্সের প্রথমটিতে ডোরা আছে, দ্বিতীয়টিতে নেই — তার ভেতরে শুধু একটি ফাঁকা জায়গা। চেষ্টা করুন: .box p:nth-of-type(2)-কে .box p:nth-child(2) করুন — এবার কোন p সবুজ? তারপর 3n+2-কে -n+3 করুন।
৬ · :target, :lang() আর :not(), :is(), :where()-এর পুনরালোচনা
:target সেই element-কে ধরে, যার id এখন URL-এর #-এর পরের অংশের সাথে মেলে (পর্ব ৫-এর fragment). প্রশ্নোত্তর পাতায় কোনো প্রশ্নের লিংকে চাপলে সেই উত্তরটি হাইলাইট করা, অথবা footnote-এ লাফ দিলে সেটি রাঙানো — দুটোই এর কাজ। :lang() ভাষা দেখে বাছে। এটি [lang] attribute selector-এর চেয়ে বুদ্ধিমান: :lang(bn) মেলে lang="bn" আর lang="bn-BD" দুটোতেই, আর ভাষা ওপর থেকে উত্তরাধিকারে আসে বলে নিজের গায়ে lang না থাকলেও ভেতরের element মেলে (পর্ব ১৪)।
<style>
.faq { padding: 4px 12px; border-left: 4px solid #cbd5e1; margin: 8px 0; }
.faq:target { border-left-color: #2563eb; background: #eff6ff; }
.faq h3 { margin: 4px 0; font-size: 1.05em; }
:lang(en) { font-style: italic; color: #7c3aed; }
nav a:not(:last-child) { margin-right: 14px; }
</style>
<nav>
<a href="#delivery">ডেলিভারি কবে?</a>
<a href="#pay">কীভাবে দাম দেব?</a>
<a href="#return">ফেরত দেওয়া যায়?</a>
</nav>
<section class="faq" id="delivery"><h3>ডেলিভারি কবে পাব?</h3>
<p>ঢাকার ভেতরে সাধারণত দুই থেকে তিন দিনে।</p></section>
<section class="faq" id="pay"><h3>কীভাবে দাম দেব?</h3>
<p>মোবাইল ওয়ালেটে, অথবা <span lang="en">Cash on Delivery</span>-তে।</p></section>
<section class="faq" id="return"><h3>ফেরত দেওয়া যায়?</h3>
<p>হ্যাঁ, সাত দিনের মধ্যে, প্যাকেট না খুলে।</p></section>শুরুতে তিনটি উত্তরই ধূসর দাগে। ওপরের যেকোনো প্রশ্নে চাপুন — সেই উত্তরে লাফ দেয় আর নীল হয় (:target); অন্য প্রশ্নে চাপলে আগেরটি স্বাভাবিক হয়ে নতুনটি নীল হয়, কারণ URL-এ একসাথে একটিই fragment থাকে। “Cash on Delivery” বেগুনি তেরছা — :lang(en). শেষ লিংকের ডানে কোনো ফাঁক নেই (:not(:last-child)). চেষ্টা করুন: .faq:target-এর নিয়মে scroll-margin-top: 40px; যোগ করুন — লাফের পর ওপরে একটু জায়গা থাকে, sticky header থাকলে এটি জরুরি।
অনেকে :target দিয়ে JavaScript ছাড়াই ট্যাব বানান (.panel { display: none }, .panel:target { display: block })। দেখতে চলে, কিন্তু প্রতিটি ক্লিক ব্রাউজারের ইতিহাসে নতুন ধাপ যোগ করে, Back চাপলে ট্যাব বদলায়, আর screen reader ট্যাবের ভূমিকা বোঝে না। প্রশ্নোত্তরের জন্য পর্ব ১১-এর <details> ভালো সমাধান।
পর্ব ১৭ আর ১৮-এ দেখা তিনটি “তালিকা নেওয়া” pseudo-class মনে করে নিন — এরা এই পর্বের বাকিদের সাথে মিলে আরও কাজের হয়:
| লেখা | অর্থ | Specificity |
|---|---|---|
:is(h2, h3):hover | h2 বা h3, যার ওপর মাউস | তালিকার সবচেয়ে শক্ত সদস্য + pseudo-class |
:where(ul, ol) li:first-child | যেকোনো তালিকার প্রথম আইটেম | :where() অংশ শূন্য |
input:not(:placeholder-shown):invalid | কিছু লেখা হয়েছে এবং ভুল | সবচেয়ে শক্ত সদস্যের সমান |
৭ · :has() — অবশেষে “parent selector”
বহু বছর ধরে CSS শুধু নিচের দিকে আর সামনের দিকে দেখতে পারত। “যে কার্ডের ভেতরে ছবি আছে, সেই কার্ডটি পাশাপাশি সাজাও” — এই সহজ কাজের জন্য JavaScript লিখে কার্ডে class বসাতে হতো। :has() সেই দরজা খুলেছে। .card:has(img) মানে “এমন .card, যার ভেতরে একটি img আছে” — স্টাইল পায় কার্ড, ছবি নয়।
বন্ধনীর ভেতরে থাকে relative selector — যার শুরুটা ধরা হয় বাইরের element থেকে। :has(img) = যেকোনো গভীরতায় img; :has(> img) = সরাসরি সন্তান img; আর h2:has(+ p) = যে h2-এর ঠিক পরে একটি p আছে — অর্থাৎ আগের ভাই-বোন বাছাই, যা আগে অসম্ভব ছিল। ব্রাউজার ভেতরে কীভাবে মেলায়, দেখুন:
1<section class="shop">2 <article class="card">3 <img alt="ইলিশ">4 <h3>ইলিশ</h3>5 <article class="card">6 <h3>মধু</h3>7 <p>৳ ৮৫০</p>
| selector | |
| হাতে আছে | |
| নিচে খোঁজা |
- Subject কে?
:has()-এর বাইরের অংশ —.card— হলো যে স্টাইল পাবে। তাই আগের মতোই প্রার্থী দুটি কার্ড (লাইন ২ আর ৫)। বন্ধনীর ভেতরেরটি শুধু একটি শর্ত। - প্রথম কার্ড: নিচে খোঁজা। সাধারণ selector ওপরের দিকে হাঁটে (পর্ব ১৭-এর ডান-থেকে-বাঁয়ে), কিন্তু
:has()-এর শর্ত যাচাইয়ে ব্রাউজার কার্ডের ভেতরে নামে। লাইন ৩-এ img পাওয়া গেল — শর্ত পূরণ। - দ্বিতীয় কার্ড। ভেতরে শুধু h3 আর p. কোনো img নেই — এই কার্ড নিয়মটি পাবে না।
- ফল। শুধু ইলিশের কার্ড পাশাপাশি সাজবে। কোনো class লাগেনি, JavaScript-ও না — HTML-এর গঠন দেখেই সিদ্ধান্ত।
- পাতা বদলালে কী হয়? ধরুন বিক্রেতা মধুর ছবি আপলোড করলেন, JavaScript দ্বিতীয় কার্ডে একটি img যোগ করল। ব্রাউজার পুরো পাতা আবার যাচাই করে না; নতুন img-এর ওপরের পথের যারা
:has()-নিয়মের subject হতে পারে, শুধু তাদের আবার দেখে। দ্বিতীয় কার্ড এখন মিলে যায় — সঙ্গে সঙ্গে নতুন সাজ। - আগের ভাই-বোন বাছাই।
h3:has(+ p)মানে “যে h3-এর ঠিক পরে p”. ইলিশের h3-এর পরে কেউ নেই, মধুর h3-এর পরে p আছে — তাই মধুর h3 স্টাইল পায়। আগেh3 + pদিয়ে শুধু p-কে ধরা যেত, h3-কে নয়।
বাস্তবে সবচেয়ে বেশি কাজে লাগে এমন চারটি নকশা নিচের এডিটরে। শেষেরটির নাম quantity query — “কতগুলো সন্তান আছে” দেখে সাজ বদলানো। :has(> li:nth-child(5)) সত্য মানে পঞ্চম li আছে, অর্থাৎ কমপক্ষে ৫টি। (২০১৫ সালের দিকে Heydon Pickering এটি :nth-last-child আর ~ জুড়ে করার কৌশল জনপ্রিয় করেন, কিন্তু তখন শুধু li-গুলোকেই সাজানো যেত — তালিকাকে নয়।)
.card { border: 2px solid #cbd5e1; border-radius: 10px; padding: 8px; margin: 6px 0; }
.card h3, .card p { margin: 0; }
/* ১. ছবি আছে এমন কার্ড — পাশাপাশি সাজাও */
.card:has(img) { display: flex; gap: 10px; align-items: center; border-color: #2563eb; }
/* ছবি নেই এমন কার্ড */
.card:not(:has(img)) { border-style: dashed; background: #f8fafc; }
/* ২. কোনো ঘর ভুল হলে পুরো ফর্ম লাল */
.f:has(:user-invalid) { outline: 3px solid #dc2626; outline-offset: 4px; }
/* ৩. টিক দেওয়া checkbox-এর label */
label:has(:checked) { font-weight: bold; color: #15803d; }
/* ৪. quantity query — ৫টি বা বেশি li থাকলে */
.tags:has(> li:nth-child(5)) { columns: 2; font-size: 0.85em; color: #7c3aed; }ইলিশের কার্ডে ছবি আর লেখা পাশাপাশি, নীল সীমানা; মধুর কার্ডে ভাঙা দাগ। ইমেইলের ঘরে abc লিখে বাইরে ক্লিক করুন — পুরো ফর্মের চারপাশে লাল দাগ, অথচ ভুলটা শুধু একটি ঘরে। checkbox-এ টিক দিলে পুরো লেবেল মোটা-সবুজ। তিন আইটেমের তালিকা স্বাভাবিক, ছয় আইটেমেরটি দুই কলামে ছোট বেগুনি লেখায়। চেষ্টা করুন: দ্বিতীয় তালিকার দুটি li মুছে দিন (data-html বদলানো যায় না, তাই নিয়মে nth-child(5)-কে nth-child(7) করুন) — সাজ চলে যায়। তারপর লিখুন h3:has(+ p) { color: crimson; }.
সাপোর্ট ও সাবধানতা: Safari আর Chrome ২০২২ সালে :has() আনে, Firefox যোগ দেয় ২০২৩ সালের ডিসেম্বরে — তখন থেকে Baseline, আর এখন Widely available. খুব পুরোনো ব্রাউজারের জন্য @supports selector(:has(*)) { … } দিয়ে নিয়মটি ঘিরে রাখতে পারেন (পর্ব ৪৮), যাতে সেখানে সাধারণ নকশাটি থাকে। দুটি নিয়ম মনে রাখুন: :has()-এর ভেতরে আরেকটি :has() চলে না, আর ভেতরে pseudo-element (::before) চলে না। আর :has()-কে সবসময় কোনো element-এর সাথে বাঁধুন — .card:has(…); একা :has(…) বা body:has(…) পুরো পাতার প্রতিটি পরিবর্তনে বেশি যাচাইয়ের কারণ হতে পারে।
আরও গভীরে: :has()-কে দ্রুত রাখতে ইঞ্জিনগুলো দুই দিকে কাজ করে। মেলানোর সময় ফল জমা রাখে (cache), যাতে একই subtree বারবার নামতে না হয়। আর DOM বদলালে style invalidation-এর বিশেষ ব্যবস্থা ব্যবহার করে — CSS পড়ার সময়ই হিসাব করে রাখে কোন class, ট্যাগ বা attribute-এর বদল কোনো :has()-এর ফল বদলাতে পারে, আর বদল এলে শুধু সেই element-এর ancestor বা আগের ভাই-বোনদের দিকে উঠে প্রার্থী খোঁজে। এজন্যই :has(> img) (এক ধাপ) সাধারণত :has(img) (যেকোনো গভীরতা)-এর চেয়ে সস্তা। Specificity-তে :has() নেয় ভেতরের সবচেয়ে শক্ত argument-এর ওজন, ঠিক :is()-এর মতো।
৮ · ::before আর ::after — CSS দিয়ে বানানো বাক্স
::before আর ::after element-এর ভেতরে, একদম শুরুতে আর একদম শেষে একটি নতুন বাক্স তৈরি করে — element-এর আগে-পরে নয়, এটা অনেকে ভুল বোঝেন। বাক্স তৈরি হয় শুধু তখন, যখন content property দেওয়া থাকে; শুধু সাজের জন্য হলে content: "". এর নাম generated content. বাক্সটি DOM-এ নেই — JavaScript খুঁজে পায় না, মাউসে টেনে লেখা বাছা যায় না, কপি করলে আসে না — কিন্তু render tree-এ আছে, তাই দেখা যায়। img, input-এর মতো replaced element-এ সাধারণত কাজ করে না, কারণ তাদের ভেতরের অংশ ব্রাউজার নিজে আঁকে।
অ্যাক্সেসিবিলিটির বড় কথা: অনেকে ভাবেন CSS-এর লেখা screen reader-এর কাছে অদৃশ্য। ভুল। accessible name হিসাবের নিয়মে ::before/::after-এর লেখাও যোগ হয়, তাই বেশিরভাগ screen reader “★” পেলে “black star” পড়ে, “→” পেলে “right arrow”. সমাধান: content-এ স্ল্যাশ দিয়ে বিকল্প লেখা (alt text) দেওয়া যায় — content: "★" / ""; মানে চোখে তারা, screen reader-এ কিছু না। এই লেখার ধরন ২০২৪ সাল থেকে Baseline Newly available; পুরোনো ব্রাউজার পুরো ঘোষণাটি বাদ দেয়, তাই আগে স্ল্যাশ ছাড়া একটি লাইন লিখে রাখুন (fallback).
/* আগে fallback, তারপর alt text-সহ — "" মানে screen reader চুপ */
.req::after { content: " *"; content: " *" / ""; color: #dc2626; }
.ext::after { content: " ↗"; content: " ↗" / " (বাইরের সাইট)"; }
.q { quotes: "“" "”"; font-size: 1.2em; }
.q::before { content: open-quote; color: #94a3b8; }
.q::after { content: close-quote; color: #94a3b8; }
/* শুধু সাজ — content খালি, বাক্সটি absolute */
.card { position: relative; padding: 12px 12px 12px 24px;
border: 2px solid #cbd5e1; border-radius: 10px; }
.card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0;
width: 8px; background: #16a34a; border-radius: 8px 0 0 8px; }
/* ভুল ব্যবহার: আসল তথ্য CSS-এ রাখা */
.price::before { content: "দাম: ৳ "; }নামের লেবেলের পরে লাল তারকা, লিংকের পরে ↗ তীর, প্রবাদের দুই পাশে ধূসর উদ্ধৃতিচিহ্ন, অফারের কার্ডের বাঁ পাশে সবুজ ফিতা — এগুলোর একটিও HTML-এ নেই। নিচে “দাম: ৳ ১,২০০” দেখাচ্ছে — এখন মাউসে টেনে পুরো লাইনটি বাছতে চেষ্টা করুন: শুধু “১,২০০” বাছাই হয়। কপি করে কাউকে পাঠালে “দাম” আর “৳” হারিয়ে যাবে, অনুবাদ টুলও হয়তো দেখবে না — তাই আসল তথ্য CSS-এ নয়। চেষ্টা করুন: .card::before থেকে content: ""; মুছে দিন — ফিতাটি উধাও, কারণ content ছাড়া বাক্সই তৈরি হয় না। তারপর .ext::after-এর alt text-কে "" করুন, আর ভাবুন কোনটি ঠিক।
তারকাটির alt text কেন খালি? কারণ ঘরে required আছে, screen reader নিজেই “required” বলবে — “star” বললে শুধু বাড়তি শব্দ। বাইরের লিংকের তীরের alt text কিন্তু তথ্য বহন করে: “(বাইরের সাইট)”. প্রতিটি চিহ্নের জন্য একটি প্রশ্ন করুন — “চোখে না দেখলে কি এই তথ্য হারাত?” হ্যাঁ হলে alt text দিন (বা তথ্যটি HTML-এ লিখুন), না হলে খালি রাখুন। ::before/::after-এর আরও কাজ: counter() দিয়ে স্বয়ংক্রিয় নম্বর, attr(data-x) দিয়ে attribute-এর মান দেখানো, tooltip-এর ছোট ত্রিভুজ, আর পর্ব ৩৮-৩৯-এর অ্যানিমেশনের বাড়তি স্তর।
৯ · লেখা আর ফর্মের অংশ — ::first-letter থেকে ::file-selector-button
বাকি pseudo-element-গুলো এমন অংশ ধরে, যা ব্রাউজার নিজে তৈরি করে বা আঁকে:
::first-letter— প্রথম অক্ষর; পত্রিকার মতো বড় drop cap বানাতে।float,font-size,marginচলে। নতুনinitial-letterproperty সরাসরি “তিন লাইন জুড়ে বড় অক্ষর” বানায়, কিন্তু Firefox-এ এখনো নেই — not yet Baseline.::first-line— স্ক্রিনে প্রথম যে লাইনটি দেখা যাচ্ছে। জানালা ছোট-বড় করলে লাইনটির শেষ বদলায়, তবু নিয়ম ঠিক থাকে। শুধু লেখা-সংক্রান্ত property (রং, font,letter-spacing) চলে।::marker— তালিকার বুলেট বা নম্বর।color,font-*আরcontent-এর মতো কয়েকটি property-ই শুধু চলে, আর ব্রাউজারভেদে সমর্থন কিছুটা আলাদা। নম্বরের ধরন বদলাতেlist-style-type: bengali(পর্ব ৭-এর counter style).::placeholder— ফাঁকা ঘরের ইঙ্গিত-লেখা। হালকা ধূসর অনেক সময় contrast-এ ফেল করে; আর placeholder কখনো label-এর বদলি নয় (পর্ব ৮)।::selection— মাউসে টেনে বাছাই করা অংশ। শুধুcolor,background-color,text-decoration,text-shadow-এর মতো কয়েকটি চলে।::backdrop— পর্ব ১১-এ দেখেছেন: modal<dialog>, popover বা fullscreen element-এর ঠিক পেছনের, পুরো পর্দা-জোড়া স্তর (top layer-এ থাকে)। যেমনdialog::backdrop { background: rgb(0 0 0 / 0.5); }.::file-selector-button—<input type="file">-এর “Choose file” বোতাম। আগে এটি সাজানোর কোনো মানসম্মত উপায় ছিল না, ঘরটি লুকিয়ে নকল বোতাম বানাতে হতো; এখন Baseline Widely available.
.en { display: flow-root; } /* float যেন পরের অনুচ্ছেদে না ঢোকে */
.en::first-letter { float: left; font-size: 3.2em; line-height: 1;
margin: 2px 6px 0 0; color: #b91c1c; }
.en::first-line { font-variant: small-caps; }
/* বাংলায়: যুক্তাক্ষর আর কার-চিহ্ন কী হয়? */
.bn::first-letter { font-size: 2em; color: #b91c1c; }
.steps { list-style-type: bengali; }
.steps li::marker { color: #2563eb; font-weight: bold; }
input::placeholder { color: #6b7280; } /* যথেষ্ট গাঢ় */
::selection { background: #fde68a; color: #1e293b; }
input::file-selector-button { font: inherit; border: 0; border-radius: 6px;
padding: 6px 12px; background: #0f766e; color: #fff; cursor: pointer; }ইংরেজি অনুচ্ছেদের “D” বড় লাল অক্ষরে দুই লাইন জুড়ে বসেছে, আর প্রথম লাইনটি ছোট বড়-হাতের অক্ষরে (small caps) — প্রিভিউর চওড়া বদলালে প্রথম লাইনের শেষও বদলায়। বাংলা দুটি লাইনের প্রথম “অক্ষর” কতটুকু বড় হলো খেয়াল করুন: “ক্ষ” আর “স্ব” যুক্তাক্ষর পুরোটা বড় হলো, নাকি শুধু “ক” বা “স”? Edge-এ পরীক্ষায় পুরো যুক্তাক্ষর, এমনকি সাথের উ-কারও (“ক্ষু”) বড় হয়েছে; অন্য ব্রাউজার আর ফন্টে উত্তর আলাদা হতে পারে। তালিকার নম্বর বাংলা অঙ্কে, নীল মোটা। লেখা মাউসে বাছলে হলুদ পটভূমি; ফাইলের বোতাম সবুজ, গোল কোণা। চেষ্টা করুন: .en::first-letter-এ float মুছে দিয়ে দেখুন, তারপর ::marker-এ content: "✔ "; যোগ করুন।
বাংলায় ::first-letter: বাংলা “অক্ষর” প্রায়ই কয়েকটি Unicode code point মিলে তৈরি — ব্যঞ্জন + হসন্ত + ব্যঞ্জন (যুক্তাক্ষর, পর্ব ১৪-এর conjunct), বা ব্যঞ্জন + কার-চিহ্ন। Spec বলে ব্রাউজার পুরো “typographic letter unit” ধরবে, কিন্তু যুক্তাক্ষর কোথায় শেষ — এর হিসাব ব্রাউজার ও ফন্টভেদে মেলে না; কখনো অক্ষর ভেঙে কার-চিহ্ন আলাদা হয়ে যায়, বা আকার মেলে না। তা ছাড়া বাংলা ছাপায় drop cap প্রচলিত রীতিও নয়। বাংলা পাতায় এটি ব্যবহার করলে কয়েকটি ব্রাউজারে আর নানা শুরু-অক্ষরে পরীক্ষা করে নিন; নিশ্চিত ফল চাইলে প্রথম শব্দটি <span>-এ রেখে সেটি সাজান।
১০ · AI-এর যুগে — pseudo-class লেখার কাজ AI-কে দিলে
“বোতামে সুন্দর hover দাও”, “required ঘরে লাল তারা দাও” — AI মুহূর্তে CSS দেবে, আর দেখতেও ঠিক লাগবে। কিন্তু pseudo-class আর pseudo-element-এর ভুলগুলো প্রায়ই চোখে ধরা পড়ে না: মাউসে সব ঠিক, ভুলটা কি-বোর্ডে, screen reader-এ বা ফোনে। AI অনেক পুরোনো টিউটোরিয়াল থেকে শিখেছে, তাই সেই যুগের অভ্যাসও ফিরিয়ে আনে।
AI সাধারণত যেসব ভুল করে
| AI-এর ভুল | কেন সমস্যা | ঠিক উপায় |
|---|---|---|
button:focus { outline: none; } | কি-বোর্ড ব্যবহারকারী পথ হারান, WCAG ২.৪.৭ ভাঙে | :focus-visible-এ স্পষ্ট দাগ |
মেনু খোলে শুধু :hover-এ | ফোনে আর কি-বোর্ডে খোলেই না | বোতাম + popover/<details>, সাথে :focus-within |
input:invalid { border: red } | পাতা খুলতেই সব খালি ঘর লাল | :user-invalid |
দাম, “নতুন”, ধাপের নম্বরের মতো তথ্য ::before-এ | কপি হয় না, অনুবাদ হয় না, screen reader-এ অনিশ্চিত | তথ্য HTML-এ, CSS-এ শুধু সাজ |
আইকন content: "\f007" (icon font) | screen reader অদ্ভুত কিছু পড়ে বা কিছুই না; ফন্ট না এলে খালি বাক্স | inline SVG আর aria-hidden (পর্ব ৪৪) |
| “parent selector নেই”, তাই JavaScript দিয়ে class বসানো | পুরোনো তথ্য — বাড়তি কোড | :has(), এখন Baseline Widely available |
a:hover লেখা a:visited-এর আগে | দেখা লিংকে hover কাজ করে না | LVHA ক্রম |
AI-কে কাজ দেওয়ার সময় শুধু “দেখতে কেমন হবে” নয়, প্রতিটি অবস্থা আর প্রতিটি ব্যবহারকারীর কথা বলে দিন। শেষ শর্তটি খেয়াল করুন — AI-কে নিজের কোডের দুর্বলতা নিজে বলতে বাধ্য করা হচ্ছে:
AI-এর দেওয়া CSS হাতে পেলে — যাচাইয়ের চেকলিস্ট
পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:
- Pseudo-class আর pseudo-element-এর পার্থক্য? — প্রথমটি অবস্থা বা অবস্থান দেখে পুরো element বাছে (
:hover), দ্বিতীয়টি element-এর একটি অংশ বা নতুন বাক্স ধরে (::before); এক বনাম দুই কোলন। :focusআর:focus-visible-এর পার্থক্য? —:focusযেকোনো উপায়ে focus পেলেই;:focus-visibleকেবল ব্রাউজার যখন দাগ দেখানো দরকার মনে করে, যেমন কি-বোর্ডে।li:nth-child(-n+3)কাকে ধরে? — প্রথম তিনটি li.:nth-child(2)আর:nth-of-type(2)? — প্রথমটি সব সন্তানের মধ্যে দ্বিতীয়, দ্বিতীয়টি একই ট্যাগের মধ্যে দ্বিতীয়।::beforeকাজ না করলে প্রথমে কী দেখবেন? —contentদেওয়া আছে কি না, আর element-টিimg/input-এর মতো replaced element কি না।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে শুধু নিয়ম জানা থেকে নিয়মের পেছনের কারণ বোঝার দিকে নিয়ে যাবে।
প্র ০১:visited-এ কেন শুধু রং বদলানো যায়, আর JavaScript কেন লিংকের আসল রং বলে না? একটি নিরীহ ফিচার কীভাবে গোপনীয়তার ঝুঁকি হলো?
শুরুতে :visited ছিল একেবারে সাধারণ একটি সুবিধা — দেখা লিংক বেগুনি, যাতে পাঠক বোঝেন কোথায় আগে গেছেন। সমস্যা ধরা পড়ে ২০০০-এর দশকের শুরুতে: CSS দিয়ে লিংকের চেহারা বদলানো যায়, আর JavaScript দিয়ে চেহারা মাপা যায়। তাহলে একটি সাইট চুপিচুপি হাজারটা লিংক বসাতে পারে — বিভিন্ন ব্যাংক, হাসপাতাল, চাকরির সাইট, রাজনৈতিক দল — তারপর মেপে দেখে কোনগুলো :visited-এর রং পেয়েছে। কোনো অনুমতি ছাড়াই আপনার ব্রাউজিং ইতিহাসের একটি ছবি তৈরি! একে বলা হয় history sniffing. গবেষকেরা দেখিয়েছিলেন, কিছু বড় সাইট এই কৌশল সত্যিই ব্যবহার করছিল।
সমাধান সহজ ছিল না, কারণ বৈশিষ্ট্যটি পুরো বাদ দিলে কোটি পাঠকের একটি দরকারি সুবিধা যেত। ২০১০ সালের দিকে Mozilla-র প্রকৌশলী David Baron একটি মাঝামাঝি পথ প্রস্তাব করেন, যা পরে সব বড় ব্রাউজার গ্রহণ করে: (১) :visited-এ শুধু রং-জাতীয় property চলবে — আকার, পটভূমির ছবি বা অবস্থান বদলানো যাবে না, যাতে লেআউট মেপে কিছু জানা না যায়; (২) রঙের স্বচ্ছতা (alpha) আসল লিংকের মতো রাখা হবে; (৩) getComputedStyle() সবসময় এমন উত্তর দেবে যেন লিংকটি দেখা হয়নি। অর্থাৎ পর্দায় রং আলাদা, কিন্তু কোড তা জানতে পারে না।
তবু গল্প শেষ হয়নি। পরে গবেষকেরা দেখান, পাতা আঁকতে কত সময় লাগছে মেপে (timing attack) এখনো কিছুটা আন্দাজ করা যায়। তাই ব্রাউজারগুলো আরও এগোচ্ছে — যেমন Chrome সম্প্রতি এমন ব্যবস্থা এনেছে যাতে একটি লিংক “দেখা” দেখায় কেবল তখন, যখন আপনি সেটিতে সেই একই সাইট থেকে গিয়েছিলেন (visited link partitioning).
মূল উপলব্ধি: ওয়েবে প্রতিটি ফিচার একটি সম্ভাব্য তথ্য-ফাঁসের পথ। ভালো নকশা মানে সুবিধা আর গোপনীয়তার মাঝে সচেতন আপস — আর CSS-এর মতো “নিরীহ” ভাষাও নিরাপত্তার বাইরে নয়।
প্র ০২ব্রাউজার তো নিজেই focus-এর দাগ দেখাত — তাহলে :focus-visible বানানোর দরকার পড়ল কেন? এটি না থাকলে কাদের সবচেয়ে ক্ষতি হতো?
সমস্যাটা ছিল প্রযুক্তির নয়, মানুষের আচরণের। পুরোনো ব্রাউজারে মাউসে বোতাম চাপলেই চারপাশে একটি দাগ বসে যেত। ক্লায়েন্ট বা ডিজাইনার বলতেন, “এই কুৎসিত বাক্সটা সরান।” ডেভেলপার লিখতেন :focus { outline: none; } — সমস্যা মিটে গেল। কিন্তু তাতে মুছে গেল কি-বোর্ড ব্যবহারকারীর একমাত্র দিকনির্দেশ। যাঁরা হাত কাঁপার কারণে মাউস ধরতে পারেন না, যাঁরা screen reader-এর সাথে কি-বোর্ড চালান, এমনকি যে অফিসকর্মী দ্রুত ফর্ম পূরণে Tab চাপেন — সবাই অন্ধকারে। পুরোনো অনেক CSS reset আর জনপ্রিয় টেমপ্লেটেও এই লাইন ছিল, ফলে লাখো সাইটে ছড়িয়ে পড়ে।
অ্যাক্সেসিবিলিটি কর্মীরা বছরের পর বছর বোঝাতে চেষ্টা করেন “দাগ মুছবেন না”, কিন্তু ডিজাইনারদের অভিযোগও অযৌক্তিক ছিল না। তাই সমাধান হিসেবে ভাবা হলো: দুই পক্ষকেই খুশি রাখা যায় কি? Firefox-এর নিজস্ব :-moz-focusring এই ধারণার পূর্বসূরি। পরে Selectors Level 4-এ একটি মানসম্মত pseudo-class প্রস্তাব করা হয় — প্রথমে নাম ছিল :focus-ring, পরে :focus-visible. ব্রাউজার আসার আগেই WICG একটি JavaScript polyfill বানায়, যা অনেক সাইট ব্যবহার করে। ২০২২ সালে Safari যোগ দেওয়ার পর সব প্রধান ব্রাউজারে এটি চলে, আর ব্রাউজারগুলো নিজেদের ডিফল্ট দাগও :focus-visible-এ সরিয়ে নেয় — অর্থাৎ মাউস-ক্লিকে দাগ আর আসে না, তাই মুছে ফেলার প্রলোভনও কমে।
আইনি ও মানদণ্ডের দিকেও গুরুত্ব বেড়েছে। WCAG ২.২-এ ২.৪.৭ (Focus Visible) AA স্তরে আছে, নতুন যোগ হয়েছে ২.৪.১১ (Focus Not Obscured — sticky header যেন focus-এর element ঢেকে না দেয়), আর AAA স্তরে ২.৪.১৩ (Focus Appearance — দাগ কতটা মোটা ও স্পষ্ট হবে)।
মূল উপলব্ধি: ভালো মানদণ্ড শুধু “এটা করো” বলে না — যে কারণে মানুষ ভুল করে, সেই কারণটাই দূর করে। :focus-visible সৌন্দর্য আর অ্যাক্সেসিবিলিটির লড়াই থামিয়েছে দুই পক্ষের কথা শুনে।
প্র ০৩একসময় প্রায় সব সাইটের আইকন বসত ::before-এ, icon font দিয়ে। আজ বিশেষজ্ঞরা কেন তা নিরুৎসাহিত করেন? CSS-এ লেখা কন্টেন্টের আসল সমস্যা কোথায়?
২০১০-এর দশকে Font Awesome-এর মতো icon font খুব জনপ্রিয় হয়। কৌশলটি চতুর: একটি ফন্ট বানানো হয় যার অক্ষরগুলো আসলে আইকন, আর সেগুলো রাখা হয় Unicode-এর “Private Use Area”-তে — যে কোড কোনো আসল অক্ষরের জন্য বরাদ্দ নয়। তারপর .icon-user::before { content: "\f007"; } লিখলেই আইকন। একটি ফাইল, শত আইকন, রং-আকার CSS দিয়ে বদলানো যায় — সেই সময়ের জন্য দারুণ।
সমস্যাগুলো ধীরে ধীরে সামনে আসে। প্রথমত, screen reader ::before-এর লেখা পড়ে — কিন্তু Private Use Area-র অক্ষরের কোনো নাম নেই, তাই কোনোটি কিছুই বলে না, কোনোটি অদ্ভুত শব্দ বলে। আইকনই যদি বোতামের একমাত্র লেবেল হয় (যেমন শুধু একটি 🔍), বোতামটি নামহীন হয়ে যায়। দ্বিতীয়ত, ফন্ট লোড না হলে — ধীর ইন্টারনেট, অফিসের firewall, বা যে মানুষ dyslexia-র কারণে নিজের পছন্দের ফন্ট জোর করে চালান — আইকনের জায়গায় খালি বাক্স (tofu) বা ভুল অক্ষর আসে। তৃতীয়ত, Windows-এর High Contrast বা forced colors মোডে ফন্ট-আইকন অনেক সময় হারিয়ে যায়।
এর পেছনে একটি বড় নীতি আছে: CSS হলো উপস্থাপনা, HTML হলো অর্থ। content-এ লেখা কপি-পেস্টে আসে না, অনেক অনুবাদ টুল ও সার্চ ইঞ্জিন একে মূল লেখা ধরে না, আর CSS বন্ধ থাকলে (বা reader mode-এ) পুরো উধাও। তাই আজকের পরামর্শ: আইকনের জন্য inline SVG (পর্ব ৪৪), সাজের চিহ্ন হলে aria-hidden="true", আর তথ্য বহনকারী আইকনের পাশে আসল লেখা বা aria-label. CSS-এ চিহ্ন বসালে নতুন alt text লেখার ধরন — content: "→" / ""; — দিয়ে screen reader-কে চুপ করান বা অর্থ বলুন।
মূল উপলব্ধি: একটি কৌশল “দেখতে কাজ করছে” মানেই সবার জন্য কাজ করছে না। কন্টেন্ট কোথায় থাকবে — HTML না CSS — সেই সিদ্ধান্তই ঠিক করে দেয় কে সেটি পাবে আর কে হারাবে।
প্র ০৪:checked আর :has() দিয়ে এখন JavaScript ছাড়াই মেনু, ট্যাব, থিম-বদল বানানো যায়। তাহলে কি CSS-ই যথেষ্ট? কোথায় এর সীমা টানবেন?
২০১০-এর দশকের শুরু থেকে “checkbox hack” নামের একটি কৌশল চলত: একটি লুকানো checkbox, তার সাথে জোড়া label, আর #menu:checked ~ nav { display: block; }. label-এ চাপলে checkbox টিক পায়, পাশের ভাই-বোন nav খোলে — JavaScript ছাড়াই মোবাইল মেনু! কিন্তু এটি শুধু পরের ভাই-বোনদের বদলাতে পারত, তাই HTML-এর ক্রম কৌশলের দাস হয়ে যেত। :has() সেই বাঁধন খুলেছে: html:has(#dark:checked) { color-scheme: dark; } লিখলে একটি checkbox পুরো পাতার থিম বদলায়, HTML যেখানেই থাকুক।
তবু প্রশ্ন হলো, “করা যায়” আর “করা উচিত” এক নয়। একটি মেনু-বোতামকে screen reader-এর কাছে বলতে হয় সে “বোতাম”, আর এখন “খোলা না বন্ধ” (aria-expanded). লুকানো checkbox বলে “checkbox, not checked” — ব্যবহারকারী বিভ্রান্ত। Esc চাপলে বন্ধ হওয়া, বাইরে চাপলে বন্ধ হওয়া, focus ঠিক জায়গায় ফেরত — এগুলো CSS একা পারে না। আর পাতা নতুন করে খুললে CSS-এর অবস্থা হারিয়ে যায়; থিমের পছন্দ মনে রাখতে কোথাও সংরক্ষণ করতে হয়।
আজকের ভালো পথ তিন স্তরের। প্রথমে দেখুন HTML-এ তৈরি জিনিস আছে কি না — <details>, <dialog>, popover (পর্ব ১১); এরা অ্যাক্সেসিবিলিটি আর কি-বোর্ড আচরণ নিজেরাই দেয়। তারপর CSS দিয়ে অবস্থা অনুযায়ী সাজান — [aria-expanded="true"], :has(), :popover-open. JavaScript আনুন কেবল সত্যিকারের যুক্তি আর তথ্য সংরক্ষণের জন্য। এটাই progressive enhancement: সবচেয়ে সরল স্তর আগে, প্রতিটি স্তর আগেরটির ওপর দাঁড়িয়ে।
মূল উপলব্ধি: অবস্থা (state) থাকা উচিত HTML-এ, যেখানে ব্রাউজার আর সহায়ক প্রযুক্তি পড়তে পারে; CSS সেই অবস্থা দেখায়। :has() এই নীতিকে সহজ করেছে — অবস্থা HTML-এ থাকলে CSS এখন পাতার যেকোনো জায়গা থেকে তা পড়তে পারে।
প্র ০৫Spec-এ লেখা থাকলেই কি ব্রাউজারে থাকে? :empty-এর ফাঁকা জায়গা আর :nth-child(… of S)-এর দীর্ঘ অপেক্ষা থেকে কী শেখার আছে?
দুটি ছোট গল্প। প্রথমটি :nth-child(an+b of S). Selectors Level 4-এ এটি এসেছিল অনেক আগে, আর Safari ২০১৫ সালেই চালু করে দেয়। কিন্তু Chrome আর Firefox তখন আনেনি — অগ্রাধিকার আর জটিলতার কারণে। ফলে প্রায় আট বছর ধরে ফিচারটি “spec-এ আছে, একটি ব্রাউজারে আছে” অবস্থায় ছিল, যা ডেভেলপারদের কাছে প্রায় অকেজো — কারণ ব্যবহারকারীদের বড় অংশের ব্রাউজারে কাজ করে না। ২০২৩ সালে বাকি দুটি ব্রাউজার যোগ দেওয়ার পরই এটি Baseline হয়।
দ্বিতীয়টি :empty. সবাই চায় <p> </p> বা নতুন লাইনওয়ালা ফাঁকা div-কেও “খালি” ধরা হোক — বাস্তবে টেমপ্লেট থেকে আসা HTML-এ এমন ফাঁকা জায়গা প্রায় সবসময় থাকে। Selectors Level 4-এর খসড়ায় একসময় এমন প্রস্তাবও ছিল যে শুধু ফাঁকা জায়গাওয়ালা element-ও :empty মানবে। কিন্তু কোনো বড় ব্রাউজার তা চালু করেনি। কারণ ওয়েবের পুরোনো নিয়ম “don't break the web”: লাখো সাইট হয়তো বর্তমান আচরণের ওপর নির্ভর করে, হঠাৎ অর্থ বদলালে কোথাও কিছু লুকিয়ে যেতে পারে। ব্রাউজারগুলো তাই আচরণ বদলানোর আগে বাস্তব সাইটে মেপে দেখে কতগুলো ভাঙবে।
এ ধরনের অভিজ্ঞতাই “Baseline” ধারণার পেছনের যুক্তি — spec-এর মর্যাদা নয়, প্রধান সব ব্রাউজারে বাস্তবে চলে কি না, সেটাই আসল মাপকাঠি। প্রতি বছর ব্রাউজার নির্মাতারা “Interop” নামের যৌথ উদ্যোগে ঠিক করেন কোন ফিচারগুলো সবাই মিলে একসাথে ঠিক করবেন; :has() ছিল এমনই একটি অগ্রাধিকার। কোনো নতুন selector ব্যবহারের আগে তাই তিনটি প্রশ্ন: সব প্রধান ব্রাউজারে আছে কি (Can I use, MDN-এর Baseline চিহ্ন)? না থাকলে পুরোনো ব্রাউজারে পাতা কেমন দেখায়? আর @supports selector(…) দিয়ে আলাদা করা যায় কি?
মূল উপলব্ধি: ওয়েব একটি জীবন্ত চুক্তি — spec লেখা হয় আলোচনা করে, চালু হয় ধাপে ধাপে, আর পুরোনো আচরণ বদলানো সবচেয়ে কঠিন। পেশাদার ডেভেলপার তাই “নতুন” দেখে নয়, “সবার কাছে পৌঁছাবে কি না” দেখে সিদ্ধান্ত নেন।
অনুশীলন
আগে উত্তর ভাবুন বা কাগজে লিখুন, তারপর মিলিয়ে দেখুন। ওপরের লাইভ এডিটরগুলোতেই যাচাই করা যাবে।
-
সূত্র হিসাব করুন: ১২টি li-এর একটি তালিকায় নিচের প্রতিটি কোন কোন নম্বর ধরবে? (ক)
:nth-child(4n)(খ):nth-child(3n-1)(গ):nth-last-child(-n+2)(ঘ):nth-child(n+3):nth-child(odd)(ক) n = ০ থেকে: ০ (বাদ), ৪, ৮, ১২ → ৪, ৮, ১২.
(খ) n = ০ → −১ (বাদ), n = ১ → ২, তারপর ৫, ৮, ১১ → ২, ৫, ৮, ১১. লক্ষ করুন,
3n-1আর3n+2একই ফল দেয়।(গ) শেষ থেকে গোনা, প্রথম দুটি → ১১, ১২.
(ঘ) দুটি শর্তই: তৃতীয় থেকে, আর বিজোড় → ৩, ৫, ৭, ৯, ১১.
-
“odd” দুই রকম: একটি তালিকায় class আছে এভাবে — ১: a, ২: b, ৩: a, ৪: a, ৫: b, ৬: a.
li.a:nth-child(odd)আরli:nth-child(odd of .a)কোন কোন নম্বর ধরবে?li.a:nth-child(odd): সবার মধ্যে বিজোড় অবস্থান ১, ৩, ৫ — এদের মধ্যে .a হলো ১ আর ৩ → ১, ৩.li:nth-child(odd of .a): আগে শুধু .a-দের আলাদা করি — ১, ৩, ৪, ৬. এদের মধ্যে ১ম আর ৩য় → ১, ৪.এক কথায়: প্রথমটি “গুনে তারপর ছাঁকো”, দ্বিতীয়টি “ছেঁকে তারপর গোনো”। ফিল্টার করা টেবিলে zebra stripe-এর জন্য দ্বিতীয়টিই ঠিক।
-
ভুল ধরুন: AI নিচের CSS দিয়েছে। তিনটি সমস্যা খুঁজে ঠিক করুন —
*:focus { outline: none; },.price::before { content: "৳ 500"; },input:invalid { border: 2px solid red; }১. focus-এর দাগ মুছে দেওয়া — কি-বোর্ড ব্যবহারকারী পথ হারান। বদলে:
:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }(আরoutline: noneলাইনটি মুছে দিন)।২. দামের মতো আসল তথ্য CSS-এ — কপি হয় না, অনুবাদ বা সার্চে না-ও আসতে পারে, CSS না এলে উধাও। দাম HTML-এ লিখুন:
<p class="price">৳ ৫০০</p>.৩.
:invalidপাতা খুলতেই খালি required ঘর লাল করে। বদলেinput:user-invalid; সাথে ভুলের কারণ লেখায় জানান, শুধু রঙে নয়। -
নিজে বানান (
:has()): একটি অর্ডার-টেবিলে প্রতিটি সারিতে একটি checkbox. টিক দেওয়া সারি সবুজ হবে, বাকি সারিতে zebra stripe থাকবে, আর কোনো টিক না থাকলে টেবিলের নিচের “মুছুন” বোতাম ঝাপসা দেখাবে।একটি সমাধান — চালিয়ে দেখুন, টিক দিয়ে-তুলে পরীক্ষা করুন:
<style> td, th { padding: 4px 10px; text-align: left; } tbody tr:nth-child(even) { background: #f1f5f9; } tr:has(:checked) { background: #dcfce7; } .tbl:not(:has(:checked)) + .del { opacity: 0.45; } :focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; } </style> <table class="tbl"> <thead><tr><th>বাছাই</th><th>অর্ডার</th></tr></thead> <tbody> <tr><td><input type="checkbox" aria-label="অর্ডার ১০১"></td><td>#১০১ — চাল ৫ কেজি</td></tr> <tr><td><input type="checkbox" aria-label="অর্ডার ১০২"></td><td>#১০২ — মসুর ডাল ১ কেজি</td></tr> <tr><td><input type="checkbox" aria-label="অর্ডার ১০৩"></td><td>#১০৩ — সয়াবিন তেল ২ লিটার</td></tr> <tr><td><input type="checkbox" aria-label="অর্ডার ১০৪"></td><td>#১০৪ — চিনি ১ কেজি</td></tr> </tbody> </table> <button class="del" type="button">বাছাই করা অর্ডার মুছুন</button>শুরুতে দ্বিতীয় আর চতুর্থ সারি হালকা ধূসর, বোতাম ঝাপসা। যেকোনো সারিতে টিক দিলে সেটি সবুজ হয় —
tr:has(:checked)পরে লেখা, তাই zebra-কে হারায় — আর বোতাম পুরো উজ্জ্বল। সব টিক তুলে দিলে বোতাম আবার ঝাপসা। মনে রাখুন, ঝাপসা দেখানো মানে বোতাম বন্ধ নয়; সত্যিই বন্ধ করতে JavaScript লাগবে। চেষ্টা করুন:tbody tr:nth-child(even)-কেtbody tr:nth-child(even of :not(:has(:checked)))করুন — টিক দেওয়া সারি বাদ দিয়ে ডোরা আবার সাজে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমলিংকের চারটি নিয়ম এমন ক্রমে সাজান, যাতে প্রতিটি অবস্থা ঠিকমতো দেখা যায় (▲▼ চেপে সরান)
a:link { … }— না-দেখা লিংকa:visited { … }— দেখা লিংকa:hover { … }— মাউস ওপরেa:active { … }— চেপে ধরা অবস্থায়
নিচের কোনটি pseudo-element?
:hover:first-child::first-line:focus-within
১০টি li-এর তালিকায় li:nth-child(-n+3) কোনগুলো ধরে?
- শেষ তিনটি li
- প্রথম তিনটি li
- ৩, ৬, ৯ নম্বর li
- কোনোটিই না — ঋণাত্মক সূত্র চলে না
:focus আর :focus-visible-এর পার্থক্য কোনটি?
:focus-visibleশুধু লিংকে কাজ করে,:focusসব element-এ:focus-visibleশুধু মাউস-ক্লিকে সত্য হয়:focus-visibleএখনো শুধু পরীক্ষামূলক:focusযেকোনো উপায়ে focus পেলেই সত্য;:focus-visibleকেবল যখন ব্রাউজার দাগ দেখানো দরকার মনে করে
.card:has(img) { border-color: blue; } — নীল সীমানা কে পায়?
- যে .card-এর ভেতরে একটি img আছে, সেই কার্ড
- কার্ডের ভেতরের img
- পাতার সব .card
- কেউ না — CSS-এ parent selector নেই
a:visited-এ কোন property ব্রাউজার মানবে?
font-sizebackground-imagecolordisplay
.icon::before { content: "★" / ""; } — এর অর্থ কী?
- তারাটি দেখা যাবে না, শুধু screen reader পড়বে
- তারাটি চোখে দেখা যাবে, কিন্তু screen reader এটি পড়বে না
- এটি ভুল লেখা, পুরো CSS ফাইল কাজ বন্ধ করবে
- তারাটির আকার অর্ধেক হবে
ফর্মের খালি required ঘর পাতা খোলার সঙ্গে সঙ্গে লাল না দেখিয়ে, ব্যবহারকারী কিছু করার পরেই লাল দেখাতে কোনটি ঠিক?
input:invalidinput:requiredinput:placeholder-showninput:user-invalid
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Pseudo-class (
:) = অবস্থা বা অবস্থান, ওজন class-এর সমান; pseudo-element (::) = অংশ, ওজন type-এর সমান, selector-এর শেষে বসে। - দাগ মুছবেন না —
:focus-visible-এ স্পষ্ট দাগ দিন; লিংকে LVHA ক্রম;:visited-এ শুধু রং; ফর্মে:user-invalid. :nth-child(an+b): n = ০, ১, ২… বসিয়ে হিসাব;-n+3প্রথম তিনটি;of Sআগে ছাঁকে, তারপর গোনে;:empty-এ ফাঁকা জায়গাও “কিছু”।:has()দিয়ে parent, আগের ভাই-বোন আর quantity query — Baseline Widely available; subject সবসময় বাইরের অংশ, আর একে কোনো element-এর সাথে বেঁধে রাখুন।::before/::after-এcontentছাড়া কিছু হয় না; এগুলো সাজের জন্য, তথ্যের জন্য নয় — দরকারেcontent: "★" / ""দিয়ে alt text.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৩৫ · আধুনিক CSS — nesting, clamp(), logical property আর @layerপরবর্তী পর্বনতুন CSS লেখার ধরন, যা বড় স্টাইলশিট ছোট আর গোছানো রাখে।
- পর্ব ১৭ · Selector — element, class, id, attribute আর combinatorভিত্তিcombinator আর ডান-থেকে-বাঁয়ে মেলানো —
:has()বোঝার পূর্বশর্ত। - পর্ব ৪৫ · অ্যাক্সেসিবল CSS — focus, contrast, reduced motion আর বড় লেখাগভীরে যানসুন্দর ও মানসম্মত focus স্টাইল, contrast আর ব্যবহারকারীর পছন্দ মানা।
- HTML ও CSS · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — প্রথম পেজ থেকে আধুনিক রেসপন্সিভ ডিজাইন।
- JavaScript · Eventপাশের ট্র্যাকCSS যেখানে থামে — ক্লিক, কি-বোর্ড আর focus-এর ঘটনা JavaScript দিয়ে ধরা।