এই পর্বে যা শিখবেন
- ব্রাউজার ফর্ম কন্ট্রোল কীভাবে আঁকে,
appearanceআসলে কী মুছে দেয়, আর কোন কন্ট্রোল কতটা সাজানো যায় - সব ঘরের ফন্ট, প্রস্থ, উচ্চতা মিলিয়ে একটি পরিষ্কার ভিত্তি; label, ফাঁকা জায়গা আর দলে সাজানো
- দৃশ্যমান focus, accent-color, নিজের checkbox-radio-toggle, select, file আর range — সবই আসল element রেখে
:user-invalid,:has(),aria-describedbyদিয়ে ভুল দেখানো, placeholder-এর contrast, disabled বনাম readonly- ফোন থেকে ডেস্কটপ, লাইট থেকে ডার্ক — সব জায়গায় চলা একটি পূর্ণ পেমেন্ট ফর্ম, আর AI-এর বানানো ফর্ম যাচাই
১ · ফর্ম সাজানো কেন কঠিন — কন্ট্রোল আঁকে ব্রাউজার আর অপারেটিং সিস্টেম
সুমি একটি অনলাইন শাড়ির দোকানের চেকআউট পাতা বানাচ্ছেন। ডিজাইনারের ছবিতে সব ঘর গোল কোণার, বেগুনি টিকচিহ্ন, সুন্দর ড্রপডাউন। নিজের ল্যাপটপে মোটামুটি ঠিক দেখাল, কিন্তু ফোনে ড্রপডাউন চাপতেই Android-এর নিজস্ব তালিকা উঠে এলো, ঘরের লেখা ছোট, আর checkbox-এর রং কিছুতেই বদলায় না। সুমির CSS ভুল নয় — সমস্যাটা ফর্ম কন্ট্রোলের প্রকৃতিতে।
পর্ব ৮ আর ৯-এ আপনি ফর্মের HTML আর নানা কন্ট্রোল ও built-in validation শিখেছেন। এই পর্বে সেই ফর্মকে সুন্দর, পরিষ্কার আর সবার জন্য ব্যবহারযোগ্য করব। শুরু করি মূল প্রশ্ন দিয়ে: একটি <p> বা <div> যত সহজে সাজানো যায়, একটি checkbox কেন তত সহজে যায় না?
কারণ form control-গুলো সাধারণ বাক্স নয়। এরা native widget — ব্রাউজার এদের আঁকে অপারেটিং সিস্টেমের চেনা চেহারায়, যাতে Windows ব্যবহারকারী Windows-এর মতো, Mac ব্যবহারকারী Mac-এর মতো বোতাম দেখেন। এদের ভেতরের অনেক অংশ — select-এর তীরচিহ্ন, খোলা তালিকা, range-এর গোল হাতল, file-এর বোতাম — আপনার DOM-এ নেই; ব্রাউজার ভেতরে লুকিয়ে রাখে, তাই সাধারণ selector সেখানে পৌঁছায় না।
ব্যাংকের ছাপানো জমা-স্লিপের কথা ভাবুন। স্লিপের ঘর, বাক্স, টিকচিহ্নের জায়গা ব্যাংক আগেই ছাপিয়ে দিয়েছে। আপনি কলমের রং বেছে নিতে পারেন, বড় করে লিখতে পারেন — কিন্তু ছাপা বাক্সের আকার বদলাতে চাইলে নতুন করে নিজের স্লিপ ছাপাতে হবে, আর তখন ব্যাংকের সব নিয়ম (কোথায় সই, কোথায় অঙ্ক) নিজেকেই মেনে চলতে হবে। Native কন্ট্রোল হলো সেই ছাপা স্লিপ; appearance: none হলো “ছাপা নকশা বাদ, আমি নিজে আঁকব।”
এই “ছাপা নকশা” নিয়ন্ত্রণ করে appearance property. এর ডিফল্ট মান auto — ব্রাউজার যেমন খুশি native চেহারায় আঁকবে। appearance: none দিলে native চেহারা মুছে যায়, কন্ট্রোলটি একটি সাধারণ বাক্সের মতো আচরণ করে, যাকে আপনি border, background, pseudo-element দিয়ে নিজে আঁকবেন। কিন্তু খেয়াল করুন — মুছে যায় শুধু চেহারা। Checkbox তখনো checkbox: Space চাপলে টিক পড়ে, label-এ ক্লিক করলে বদলায়, ফর্ম জমা দিলে মান যায়, screen reader “checkbox, checked” বলে। নিচের ডায়াগ্রামে দেখুন, একটি কন্ট্রোল আঁকার সময় ব্রাউজার কোন স্তরগুলো পার হয়:
- HTML
type="checkbox"অর্থ আর আচরণ - ব্রাউজারের CSS
font: 13.33pxuser agent stylesheet - Native চেহারা
appearance: autoOS-এর ধাঁচে আঁকা - আপনার সিদ্ধান্ত
appearance: noneচেহারা মুছে দেওয়া - আপনার নকশা
border, ::beforeআচরণ অক্ষত
- অর্থ আসে HTML থেকে।
<input type="checkbox">লেখামাত্র ব্রাউজার জানে এটি কী, কীভাবে চলবে, আর accessibility tree-এ এর পরিচয় কী। এই অংশ CSS কখনো বদলায় না। - ব্রাউজারের নিজের CSS. প্রতিটি ব্রাউজারের একটি user agent stylesheet আছে। সেখানে ফর্ম কন্ট্রোলকে ছোট system ফন্ট, নিজস্ব padding-margin দেওয়া থাকে — Chromium-এ input-এর লেখা ডিফল্টে প্রায় 13.33px. এজন্যই ঘরের লেখা পাতার লেখার সাথে মেলে না।
- Native চেহারা।
appearance: autoঅবস্থায় ব্রাউজার কন্ট্রোলটি অপারেটিং সিস্টেম বা নিজের থিমের ধাঁচে আঁকে। এ অবস্থায় checkbox-এbackgroundবাborder-radiusদিলে বেশিরভাগ ব্রাউজারে কিছুই হয় না — native ছবি সবকিছু ঢেকে রাখে। - আপনি মুছে দিলেন।
appearance: nonenative ছবিটি সরিয়ে দেয়। এখন বাক্সটি খালি — আকার না দিলে প্রায় অদৃশ্যও হতে পারে। কিন্তু role, কীবোর্ড,:checked, ফর্মে মান পাঠানো — সব আগের মতোই কাজ করে। - আপনার নকশা, আসল আচরণ। এবার width, border, background আর
:checked::beforeদিয়ে নিজের টিক আঁকুন। চেহারা পুরোপুরি আপনার, অথচ আচরণ ব্রাউজারের — এটাই আধুনিক ফর্ম স্টাইলিংয়ের মূল কৌশল।
চেহারা বদলান, আচরণ নয়। সবসময় আসল <input>, <select>, <button> রাখুন, শুধু তার চেহারা বদলান। <div> দিয়ে নকল checkbox বা dropdown বানালে কীবোর্ড, label, ফর্মে মান পাঠানো, validation, screen reader — সব আপনাকে JavaScript দিয়ে আবার বানাতে হবে, আর প্রায় সবসময় কিছু না কিছু বাদ পড়ে।
appearance এখন সব প্রধান ব্রাউজারে prefix ছাড়াই কাজ করে — Baseline Widely available. পুরোনো টিউটোরিয়ালে -webkit-appearance: none; -moz-appearance: none; দেখবেন; সেগুলো এখন ইতিহাস, আলাদা vendor prefix আর দরকার নেই। কোন কন্ট্রোল কতটা সাজানো যায়, এক নজরে:
| কন্ট্রোল | CSS কতটা পারে | সেরা পথ |
|---|---|---|
| লেখার ঘর, textarea, button | প্রায় পুরোটা | সাধারণ CSS; font: inherit ভুলবেন না |
| checkbox, radio | শুধু রং, অথবা পুরো নিজের নকশা | accent-color, নইলে appearance: none + ::before |
| select | বন্ধ অবস্থার বাক্স; খোলা তালিকা প্রায় নয় | appearance: none + নিজের তীর; নতুন base-select পরীক্ষামূলক |
| file | বোতাম অংশটি | ::file-selector-button |
| range, progress | রং সহজে; পুরো নকশা prefix-ওয়ালা অংশে | accent-color, নইলে ব্রাউজারভেদে আলাদা pseudo-element |
| date, time, color | খুব কম | native-ই রাখুন — ফোনের নিজের picker সবচেয়ে কাজের |
২ · ভিত্তি ঠিক করা — ফন্ট, box-sizing, সমান উচ্চতা আর ওপরে label
সুন্দর ফর্মের ৮০ ভাগ কাজ খুব সাধারণ চারটি জিনিসে। প্রথমটি ফন্ট। ফর্ম কন্ট্রোল পাতার ফন্ট inherit করে না — ব্রাউজারের CSS এদের আলাদা ছোট system ফন্ট দিয়ে রাখে। ফলে আপনার সুন্দর বাংলা ফন্ট label-এ দেখা যায়, কিন্তু ঘরের ভেতরে লেখা ছোট আর অন্য ফন্টে। সমাধান এক লাইনে: font: inherit — “পাতার যা ফন্ট, আকার, ওজন, তাই নাও।”
দ্বিতীয়টি প্রস্থ। পর্ব ১৯-এর box-sizing মনে আছে? অনেক ব্রাউজারে input ডিফল্টে content-box (width-এর বাইরে padding আর border যোগ হয়), অথচ select আর button ডিফল্টে border-box. তাই দুটোকেই width: 100% দিলেও input একটু চওড়া হয়ে বেরিয়ে যায়। সবাইকে border-box দিলেই মিলে যায়।
তৃতীয়টি উচ্চতা। input, select, button — সবাইকে একই min-height দিন, যাতে পাশাপাশি সমান দেখায় আর চাপতে সহজ হয়। WCAG 2.2-এর ন্যূনতম touch target ২৪×২৪ CSS pixel; আরামদায়ক মাপ হিসেবে অনেক ডিজাইন নির্দেশিকা ৪৪–৪৮ পিক্সেলের কাছাকাছি সুপারিশ করে। আর চতুর্থটি label-এর জায়গা — ঘরের ওপরে। চোখ সোজা নিচে নামে, আর ফোনের সরু পর্দায় লম্বা বাংলা label-ও জায়গা পায়।
.f {
display: grid; gap: 4px; max-width: 340px;
font-family: sans-serif; font-size: 18px;
}
input, select, textarea, button {
font: inherit; /* ১ · পাতার ফন্ট আর আকার */
box-sizing: border-box; /* ২ · প্রস্থে padding-border ধরা */
width: 100%;
min-height: 44px; /* ৩ · সবার সমান উচ্চতা */
padding: 8px 12px;
border: 1px solid #6b7280;
border-radius: 8px;
}
label { margin-top: 12px; font-weight: 600; } /* ৪ · ওপরে label */
button {
margin-top: 18px; border: 0;
background: #0f766e; color: #fff; font-weight: 700;
}একটি পরিষ্কার এক-কলামের ফর্ম দেখছেন: প্রতিটি label মোটা অক্ষরে ঘরের ঠিক ওপরে, সব ঘর একই প্রস্থ আর উচ্চতার, ভেতরের লেখা label-এর সমান বড়। label আর তার ঘরের মাঝে ফাঁক ছোট (gap: 4px), কিন্তু এক জোড়া থেকে পরের জোড়ায় বড় (margin-top: 12px) — তাই চোখ নিজেই বোঝে কোন label কোন ঘরের। চেষ্টা করুন: (১) font: inherit; লাইনটি মুছে দিন — ঘরের ভেতরের লেখা হঠাৎ ছোট আর অন্য ফন্টে। (২) এবার box-sizing লাইনটি মুছুন — input আর textarea ডানদিকে একটু বেরিয়ে যাবে, select যাবে না; কারণ ডিফল্ট box-sizing আলাদা।
ফাঁকা জায়গা আর দলে সাজানো
উপরের ফর্মে একটি নীরব নিয়ম কাজ করছে — নৈকট্য (proximity): যা একসাথে, তা কাছাকাছি; যা আলাদা, তার মাঝে বেশি ফাঁক। label থেকে তার ঘর, ঘর থেকে তার নিচের সাহায্য-লেখা বা ভুলের বার্তা — এরা খুব কাছে। এক প্রশ্ন থেকে পরের প্রশ্ন একটু দূরে। আর পুরো একটি বিষয় (যেমন “ডেলিভারির ঠিকানা” বনাম “পেমেন্ট”) আরও দূরে, পর্ব ৮-এর fieldset আর legend দিয়ে আলাদা দলে। fieldset-এর ডিফল্ট ভাঙা-বর্ডারটি বেশিরভাগ নকশায় মানায় না; চেহারা বদলান, কিন্তু element রাখুন — screen reader দলের নাম (legend) পড়ে শোনায়।
fieldset { border: 0; padding: 0; margin: 0 0 28px; min-inline-size: 0; }
legend { font-size: 1.15em; font-weight: 700; padding: 0; margin-bottom: 8px; }min-inline-size: 0 একটি পুরোনো ফাঁদের সমাধান: fieldset-এর ডিফল্ট ন্যূনতম প্রস্থ ভেতরের সবচেয়ে চওড়া জিনিসের সমান, তাই ভেতরে চওড়া টেবিল বা লম্বা লেখা থাকলে ফোনে পুরো fieldset পাতার বাইরে চলে যেত।
৩ · Focus — যে দাগটি কখনো মুছবেন না
এখন যা পড়ছেন, সেটি মাউস বা আঙুল দিয়ে স্ক্রল করছেন। কিন্তু অনেক মানুষ পাতা চালান শুধু কীবোর্ডে — হাতের নড়াচড়ায় সমস্যা আছে এমন মানুষ, screen reader ব্যবহারকারী, দ্রুত কাজ করা অফিসকর্মী যিনি Tab চেপে চেপে ফর্ম পূরণ করেন, কিংবা যার ল্যাপটপের touchpad নষ্ট। তাঁরা কোন ঘরে আছেন, তা বোঝার একমাত্র উপায় focus indicator — focus পাওয়া element-এর চারপাশের দাগ।
অথচ ওয়েবে সবচেয়ে বেশি কপি হওয়া CSS লাইনগুলোর একটি হলো outline: none. ডিজাইনার মাউসে বোতাম চেপে নীল দাগ দেখে বিরক্ত হন, কেউ সেটি মুছে দেন, আর কীবোর্ড ব্যবহারকারী অন্ধকারে হাতড়ান। পর্ব ৩৪-এ দেখা :focus-visible এই দ্বন্দ্বের সমাধান: ব্রাউজার নিজেই ঠিক করে কখন দাগ দরকার — কীবোর্ড দিয়ে এলে, বা লেখার ঘরে ঢুকলে (সেখানে মাউস দিয়ে এলেও দাগ দেখায়, কারণ লেখার ঘরে কোথায় লিখছেন তা জানা জরুরি)। মাউসে বোতাম চাপলে সাধারণত দেখায় না।
body { font-family: sans-serif; font-size: 17px; }
button, input {
font: inherit; padding: 8px 14px;
border: 1px solid #6b7280; border-radius: 8px;
}
.bad:focus { outline: none; } /* ✗ কীবোর্ড ব্যবহারকারী হারিয়ে যান */
:focus-visible {
outline: 3px solid #1d4ed8; /* মোটা, স্পষ্ট রং */
outline-offset: 3px; /* element থেকে একটু দূরে */
}
input:focus-visible { outline-offset: 1px; }প্রিভিউতে ক্লিক করে Tab চাপতে থাকুন। লিংকে, “ভালো” বোতামে আর খোঁজার ঘরে মোটা নীল দাগ আসে, element থেকে একটু ফাঁক রেখে — গোল কোণার সাথে বাঁকও মেলায়। কিন্তু “খারাপ” বোতামে পৌঁছালে কিছুই দেখা যায় না: focus সেখানে আছে (Enter চাপলে বোতাম চাপা হবে), অথচ চোখে বোঝার উপায় নেই। .bad:focus-এর specificity বেশি বলে সে জিতে গেছে। এবার মাউস দিয়ে “ভালো” বোতামে ক্লিক করুন — সাধারণত দাগ আসে না; খোঁজার ঘরে ক্লিক করলে আসে। চেষ্টা করুন: outline-offset-কে -3px করুন — দাগ ভেতরে ঢুকে যাবে; রং বদলে #93c5fd করুন — সাদা পটভূমিতে প্রায় দেখাই যায় না, কেন contrast জরুরি তা নিজেই দেখবেন।
খেয়াল করুন, পর্ব ১৯-এর মতোই outline কোনো জায়গা নেয় না — দাগ এলে-গেলে পাশের কিছু নড়ে না। আধুনিক সব প্রধান ব্রাউজারে outline এখন border-radius-এর বাঁকও মেনে চলে, তাই গোল বোতামে গোল দাগ হয়। অনেকে outline-এর বদলে box-shadow দিয়ে focus আঁকেন। দেখতে সুন্দর, কিন্তু একটি বিপদ আছে: Windows-এর contrast theme-এর মতো forced colors মোডে ব্রাউজার box-shadow পুরো সরিয়ে দেয়, তখন দাগই থাকে না। তাই box-shadow দিলে সাথে outline: 2px solid transparent রাখুন — সাধারণ মোডে স্বচ্ছ বলে দেখা যায় না, forced colors মোডে ব্রাউজার সেটিকে দৃশ্যমান রঙে আঁকে।
নিয়মগুলো WCAG 2.2-এ স্পষ্ট লেখা আছে। প্রতিটি success criterion একটি নম্বর দিয়ে চেনা যায়:
| নিয়ম | স্তর | সহজ ভাষায় |
|---|---|---|
| 2.4.7 Focus Visible | AA | কীবোর্ডে চালানো যায় এমন সবকিছুর focus চোখে দেখা যেতে হবে |
| 2.4.11 Focus Not Obscured (Minimum) | AA (2.2-তে নতুন) | sticky header, cookie-র ব্যানার বা chat বোতাম যেন focus পাওয়া element-কে পুরো ঢেকে না ফেলে |
| 1.4.11 Non-text Contrast | AA | ঘরের বর্ডার আর focus-এর দাগ পাশের রঙের সাথে অন্তত ৩:১ contrast |
| 2.4.13 Focus Appearance | AAA (2.2-তে নতুন) | দাগ যথেষ্ট বড় (মোটামুটি ২ পিক্সেল পুরু ঘের) আর আগের-পরের অবস্থায় ৩:১ পার্থক্য |
2.4.11-এর একটি সহজ CSS সমাধান আছে: পাতার ওপরে sticky header থাকলে html { scroll-padding-top: 5rem; } দিন। তখন Tab চেপে নিচের ঘরে গেলে ব্রাউজার এমনভাবে স্ক্রল করে যে ঘরটি header-এর নিচে চাপা পড়ে না।
এই লাইনটি কখনো লিখবেন না: *:focus { outline: none; }. কোনো “CSS reset” বা AI এটি দিলে মুছে দিন। দাগ বদলাতে চাইলে বদলান — রং, পুরুত্ব, offset — কিন্তু বিকল্প না দিয়ে মুছবেন না। নিয়ম মনে রাখার সহজ উপায়: outline: none লিখলে একই নিয়মের ভেতরে আরেকটি দৃশ্যমান focus-চিহ্ন থাকতেই হবে।
৪ · Checkbox, radio আর toggle — accent-color থেকে পুরো নিজের নকশা
Checkbox আর radio সাজানোর দুটি স্তর আছে, আর প্রথমটি দিয়েই বেশিরভাগ কাজ হয়ে যায়। পর্ব ৩৫-এ দেখা accent-color এক লাইনে checkbox, radio, range আর progress-এর মূল রং বদলে দেয়। ব্রাউজার নিজেই টিকচিহ্নের রং (সাদা না কালো) বেছে নেয়, যাতে পড়া যায়; native আকার, কীবোর্ড, ডার্ক মোড — কিছুই ভাঙে না। এটিও Baseline Widely available.
কিন্তু ডিজাইনার যদি চান গোল কোণার বড় বাক্স, নিজের টিকচিহ্ন, চাপলে ছোট্ট অ্যানিমেশন? তখন দ্বিতীয় স্তর: আসল input-এ appearance: none, তারপর নিজের বাক্স আর ::before দিয়ে টিক। বাম দিকে accent-color, ডান দিকে পুরো নিজের নকশা — দুটো পাশাপাশি:
body { font-family: sans-serif; font-size: 17px; }
fieldset { display: inline-block; vertical-align: top; margin: 4px; }
label { display: flex; align-items: center; gap: 8px; margin: 8px 0; }
.a { accent-color: #c026d3; } /* এক লাইনেই শেষ */
.c input {
appearance: none; margin: 0; /* native ছবি মুছল */
width: 1.4em; height: 1.4em;
border: 2px solid #475569; border-radius: 6px;
display: grid; place-content: center;
}
.c input::before {
content: ""; width: .8em; height: .8em; background: #fff;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%);
transform: scale(0); transition: transform .15s;
}
.c input:checked { background: #0f766e; border-color: #0f766e; }
.c input:checked::before { transform: scale(1); }
.c input:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 2px; }
@media (forced-colors: active) {
.c input:checked::before { background: CanvasText; }
}বাম দলে checkbox, radio আর progress বার — সবই বেগুনি, অথচ আকার আর চেহারা ব্রাউজারের নিজের। ডান দলে গোল কোণার বড় বাক্স; টিক দেওয়াটি সবুজ, ভেতরে সাদা টিকচিহ্ন। লেখায় বা বাক্সে ক্লিক করুন — টিকটি ছোট থেকে বড় হয়ে ফোটে। Tab দিয়ে ডান দলে এসে Space চাপুন — কীবোর্ডেও চলে, নীল focus-দাগসহ। চেষ্টা করুন: (১) .a-এর রং বদলে #facc15 (হলুদ) করুন — ব্রাউজার নিজেই টিকচিহ্ন কালো করে দেয়, যাতে পড়া যায়। (২) clip-path-এর লাইন মুছে দিন — টিকের জায়গায় সাদা বর্গ আসবে; টিকের আকারটি এসেছে ওই বহুভুজ থেকে।
শেষের @media (forced-colors: active) অংশটি ছোট, কিন্তু জরুরি। forced colors মোডে ব্রাউজার আপনার background রং সরিয়ে দেয়, ফলে সবুজ বাক্স আর সাদা টিক — দুটোই হারিয়ে যেত। CanvasText একটি system color; ব্যবহারকারীর বেছে নেওয়া লেখার রঙে টিকটি আঁকা হয়, তাই সব থিমে দেখা যায়। নিজের নকশা বানানো মানে এই দায়িত্বগুলোও নিজের কাঁধে নেওয়া — নিচের ডায়াগ্রাম সেই হিসাব:
- আসল element দিয়ে শুরু।
<input type="checkbox">-এর সাথে বিনা খরচে আসে role, checked অবস্থা, Space-এ বদল, ফর্মে মান পাঠানো,requiredvalidation আর:checkedpseudo-class. - label দিয়ে জোড়া। input-কে
<label>-এর ভেতরে রাখলে (বাfor/idদিয়ে জুড়লে) পুরো লেখাটিই চাপার জায়গা হয়ে যায়। ছোট বাক্সে আঙুল লাগানোর ঝামেলা নেই — touch target নিজে থেকেই বড়। - চেহারা মুছে নিজের বাক্স।
appearance: noneশুধু native ছবি সরায়। তারপর width, height, border, border-radius দিয়ে নিজের বাক্স আঁকুন।emএককে মাপ দিলে লেখা বড় করলে বাক্সও বড় হয়। - অবস্থা থেকে নকশা। টিকচিহ্ন আঁকা হয়
:checked::beforeদিয়ে — অবস্থার মালিক ব্রাউজার, CSS শুধু সেই অবস্থা দেখে আঁকে। তাই নিজের JavaScript দিয়ে class বসানোর দরকার নেই, আর অবস্থা কখনো চেহারার সাথে অমিল হয় না। - যা ভুলে যাওয়া সহজ। native চেহারা মুছলে ব্রাউজারের focus-দাগও প্রায়ই চলে যায় —
:focus-visibleনিজে দিন। আর forced colors মোডে background হারিয়ে যায় — system color দিয়ে টিক আঁকুন। এই দুটি ছাড়া নকশাটি অসম্পূর্ণ।
পুরোনো একটি পদ্ধতিও প্রচলিত — visually-hidden কৌশল: আসল input চোখের আড়ালে (কিন্তু পাতায়) রেখে পাশের <span>-কে বাক্সের মতো আঁকা, আর input:checked + span দিয়ে টিক দেখানো। এখনো চলে, শুধু input-কে কখনো display: none বা visibility: hidden দেবেন না (কীবোর্ড আর screen reader দুটোই একে হারায়), আর focus-দাগ দিন input:focus-visible + span-এ।
Toggle switch — দেখতে নতুন, ভেতরে সেই checkbox
ফোনের সেটিংসে যে “অন-অফ” সুইচ দেখেন, ওয়েবে সেটিও আসলে একটি checkbox. পার্থক্য অর্থে: toggle switch চাপামাত্র কাজ হয় (নোটিফিকেশন চালু, ডার্ক থিম চালু); আর checkbox সাধারণত ফর্ম জমা দেওয়ার পরে কাজ করে (“শর্তাবলি মেনে নিচ্ছি”)। সুইচ বানাতে checkbox-এ role="switch" দিন — screen reader তখন “switch, on” বা “off” বলে। native element রেখে শুধু নির্দিষ্ট ভূমিকা জানানো হচ্ছে, তাই এটি ARIA-র প্রথম নিয়মের বিরোধী নয়।
body { font-family: sans-serif; font-size: 17px; }
.sw { display: flex; align-items: center; gap: 12px; margin: 12px 0; }
.sw input {
appearance: none; margin: 0; flex: none;
inline-size: 3rem; block-size: 1.75rem;
border-radius: 999px; background: #64748b;
position: relative; transition: background .2s;
}
.sw input::before { /* গোল হাতল */
content: ""; position: absolute;
inset-block: 3px; inset-inline-start: 3px;
inline-size: calc(1.75rem - 6px);
border-radius: 50%; background: #fff;
transition: translate .2s;
}
.sw input:checked { background: #0f766e; }
.sw input:checked::before { translate: 1.25rem 0; }
.sw input:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
.sw input, .sw input::before { transition: none; }
}প্রথম সুইচটি সবুজ, হাতল ডানে — চালু; দ্বিতীয়টি ধূসর, হাতল বামে — বন্ধ। লেখায় বা সুইচে ক্লিক করুন — হাতল মসৃণভাবে সরে যায়, রং বদলায়। Tab দিয়ে এসে Space চাপলেও একই কাজ। খেয়াল করুন, label-এর লেখা অবস্থার সাথে বদলায় না (“চালু/বন্ধ” লেখা নেই) — অবস্থা জানায় role আর checked, তাই screen reader নিজেই “on/off” বলে। চেষ্টা করুন: (১) translate: 1.25rem 0-কে 2rem করুন — হাতল ট্র্যাকের বাইরে চলে যাবে; মাপের হিসাব: ট্র্যাক ৪৮px − হাতল ২২px − দুই পাশের ৩px করে ফাঁক = ২০px, অর্থাৎ 1.25rem. (২) বন্ধ অবস্থার রং #cbd5e1 করুন — সাদা পটভূমিতে ট্র্যাকের সীমানা প্রায় হারিয়ে যায়; ৩:১ non-text contrast কেন লাগে, দেখতে পাবেন।
শুধু রঙে চালু-বন্ধ বোঝানো ঝুঁকিপূর্ণ; এখানে হাতলের অবস্থান (বাম বনাম ডান) দ্বিতীয় সংকেত। আর prefers-reduced-motion অংশটি পর্ব ৪৫-এর বিষয় — অ্যানিমেশনে অস্বস্তি বোধ করেন যাঁরা, তাঁদের জন্য নড়াচড়া বন্ধ।
৫ · select, file আর range — জেদি কন্ট্রোলগুলোকে বশে আনা
<select>-এর দুটি অংশ: বন্ধ অবস্থার বাক্সটি, আর খুললে যে তালিকা আসে। প্রথমটি appearance: none দিয়ে পুরো সাজানো যায় — কিন্তু তখন ব্রাউজারের তীরচিহ্নও মুছে যায়, তাই নিজের তীর background ছবি হিসেবে বসাতে হয়। খোলা তালিকাটি এতদিন আঁকত অপারেটিং সিস্টেম; ফোনে সেটি আসলে সুবিধা — বড় আঙুল-বান্ধব তালিকা বা চাকা। File input-এর “ফাইল বাছুন” বোতামটির জন্য আছে মানসম্মত pseudo-element ::file-selector-button (Baseline Widely available). আগে এর জন্য ঘরটি লুকিয়ে নকল বোতাম বানাতে হতো।
body {
font-family: sans-serif; font-size: 17px;
display: grid; gap: 6px; max-width: 340px;
}
label { font-weight: 600; margin-top: 10px; }
select {
appearance: none; font: inherit;
min-height: 44px; padding: 8px 40px 8px 12px;
border: 1px solid #6b7280; border-radius: 8px;
background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23334155' stroke-width='2'/%3E%3C/svg%3E") no-repeat right 14px center / 12px 8px;
}
input[type="file"] { font: inherit; }
::file-selector-button {
font: inherit; margin-inline-end: 12px; padding: 8px 14px;
border: 0; border-radius: 8px; cursor: pointer;
background: #0f766e; color: #fff;
}
::file-selector-button:hover { background: #115e59; }
select:focus-visible, input:focus-visible {
outline: 3px solid #1d4ed8; outline-offset: 2px;
}select-টি এখন অন্য ঘরগুলোর মতো গোল কোণার, ডানে নিজের আঁকা সরু তীর; নিচে file input-এর বোতামটি সবুজ, গোল কোণার। select খুললে তালিকাটি এখনো ব্রাউজারের নিজের, আর Tab দিয়ে এসে ↑↓ চাপলে মান বদলায়। চেষ্টা করুন: (১) appearance: none; মুছে দিন — ব্রাউজারের তীর ফিরে আসবে, আপনার তীরও থাকবে; দুটো তীর! (২) ডান দিকের padding 40px-কে 12px করে লম্বা নাম বাছুন — লেখা তীরের নিচে ঢুকে যাবে।
খোলা তালিকা সাজানোর গল্প বদলাচ্ছে। পর্ব ৩৭-এ দেখা customizable select-এ appearance: base-select দিলে বোতাম, খোলা তালিকা (::picker(select)), টিকচিহ্ন (::checkmark) — সবই CSS দিয়ে সাজানো যায়, আর আসল select-এর কীবোর্ড ও অ্যাক্সেসিবিলিটি থাকে। এটি এখনো পরীক্ষামূলক, not yet Baseline — মূলত Chromium-এ। তাই একে progressive enhancement হিসেবে দিন: @supports (appearance: base-select)-এর ভেতরে নতুন নকশা, বাইরে উপরের মতো সাধারণ সাজানো select.
Range slider — মানসম্মত পথ নেই, তাই সাবধানে
<input type="range">-এর দুটি অংশ — লম্বা ট্র্যাক আর টেনে সরানোর হাতল (thumb). শুধু রং বদলাতে accent-color-ই যথেষ্ট। পুরো নকশার জন্য কোনো মানসম্মত pseudo-element এখনো নেই; আছে ব্রাউজার-নির্দিষ্ট, non-standard নাম — Chromium আর Safari-তে ::-webkit-slider-thumb আর ::-webkit-slider-runnable-track, Firefox-এ ::-moz-range-thumb, ::-moz-range-track আর ভরাট অংশের জন্য ::-moz-range-progress. এগুলো spec-এর অংশ নয়, তবে বহু বছর ধরে স্থির, আর এখনো এগুলোই একমাত্র পথ।
body { font-family: sans-serif; font-size: 17px; }
input[type="range"] {
appearance: none; background: transparent;
display: block; inline-size: 100%; max-inline-size: 340px;
margin-block: 18px;
}
/* Chromium, Safari — non-standard */
input[type="range"]::-webkit-slider-runnable-track {
height: 8px; border-radius: 4px; background: #cbd5e1;
}
input[type="range"]::-webkit-slider-thumb {
appearance: none; width: 24px; height: 24px; margin-top: -8px;
border-radius: 50%; background: #0f766e;
border: 3px solid #fff; box-shadow: 0 0 0 1px #0f766e;
}
/* Firefox — non-standard, আলাদা নিয়মে */
input[type="range"]::-moz-range-track { height: 8px; border-radius: 4px; background: #cbd5e1; }
input[type="range"]::-moz-range-progress { height: 8px; border-radius: 4px; background: #0f766e; }
input[type="range"]::-moz-range-thumb {
box-sizing: border-box; width: 24px; height: 24px; border-radius: 50%;
background: #0f766e; border: 3px solid #fff; box-shadow: 0 0 0 1px #0f766e;
}
input[type="range"]:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 6px; }ধূসর সরু ট্র্যাকের ওপর সাদা ঘেরের সবুজ গোল হাতল, ওপরে “500 টাকা”। হাতলটি টানুন, বা Tab দিয়ে এসে ← → চাপুন — ৫০ টাকা করে বদলায়, লেখাও বদলায় (ছোট oninput-এর কাজ)। margin-top: -8px হাতলকে ট্র্যাকের মাঝখানে আনে: (৮ − ২৪) ÷ ২ = −৮. চেষ্টা করুন: (১) মাপগুলো 14px করুন — আঙুলে ধরা কঠিন হয়; ২৪px-এর নিচে নামাবেন না। (২) দুটি thumb নিয়ম কমা দিয়ে এক selector-এ লিখুন — Chrome বা Edge-এ হাতলের নকশা পুরো হারিয়ে যাবে; কারণ নিচে।
কেন আলাদা নিয়ম? CSS-এর একটি পুরোনো নিয়ম: selector-এর তালিকার একটি অংশও ব্রাউজার না চিনলে পুরো নিয়মটিই বাদ যায়। Chromium ::-moz-range-thumb চেনে না, Firefox ::-webkit-slider-thumb চেনে না — একসাথে লিখলে দুই ব্রাউজারেই নিয়মটি মরে যায়। পর্ব ১৭-এর ক্ষমাশীল :is()-ও এখানে কাজে আসে না, কারণ :is()-এর ভেতরে pseudo-element লেখা যায় না।
আরও গভীরে: select-এর তীর, range-এর হাতল, file-এর বোতাম আসলে ব্রাউজারের ভেতরের একটি লুকানো ছোট DOM-এ থাকে — একে বলে user-agent shadow DOM; আপনার selector সেখানে ঢুকতে পারে না। ::-webkit-slider-thumb-এর মতো নামগুলো সেই লুকানো অংশে ঢোকার ব্রাউজার-নির্দিষ্ট দরজা, আর ::file-selector-button হলো এমন দরজার প্রথম মানসম্মত রূপ। W3C-র Open UI community group এই সমস্যাটি নিয়েই কাজ করছে — কোন কন্ট্রোলের কোন অংশের মানসম্মত নাম হবে, যাতে সব ব্রাউজারে একই CSS চলে; customizable select-এর ::picker(select) সেই কাজেরই ফসল।
৬ · ভুল দেখানো — :user-invalid, :has() আর শুধু রঙে নয়
পর্ব ৯-এ ব্রাউজারের built-in validation দেখেছেন — required, type="email", pattern. CSS দিয়ে ভুল ঘর চিনতে পুরোনো উপায় ছিল :invalid. কিন্তু এর একটি বড় সমস্যা: পাতা খোলামাত্র সব ফাঁকা required ঘর “invalid” — ব্যবহারকারী কিছু লেখার আগেই পুরো ফর্ম লাল! যেন পরীক্ষার হলে খাতা হাতে দেওয়ার সাথে সাথে পরীক্ষক লাল কালিতে লিখে দিলেন “উত্তর নেই”।
সমাধান :user-invalid আর :user-valid (পর্ব ৩৪)। এরা মেলে কেবল ব্যবহারকারী ঘরটি নিয়ে কিছু করার পর — সাধারণত লিখে ঘর ছেড়ে গেলে, বা জমা দিতে চাইলে। ২০২৩ সালের শেষ থেকে এরা সব প্রধান ব্রাউজারে আছে, এখন Baseline Widely available. সময়রেখাটি দেখুন:
:invalid = সত্য (ফাঁকা, অথচ required) :user-invalid = মিথ্যা (ব্যবহারকারী এখনো কিছু করেননি)
:invalid = সত্য (pattern মেলেনি) :user-invalid = মিথ্যা (সাধারণত লেখা শেষ হওয়ার অপেক্ষা)
:user-invalid = সত্য → লাল বর্ডার, ✖ আর লেখা
form:has(:user-invalid) → ওপরে সারাংশ
:user-invalid = মিথ্যা
:user-valid = সত্য → সবুজ বর্ডার; ভুলের বার্তা গায়েব
- পাতা খুলল। ফাঁকা
requiredঘর নিয়ম অনুযায়ী invalid, তাই:invalidসত্য।:invalid-এ লাল রং দিলে এখনই লাল দেখাত — ব্যবহারকারী কিছু করার আগেই ধমক।:user-invalidএখনো মিথ্যা, তাই ঘর শান্ত। - লিখছেন। “0171” এখনো অসম্পূর্ণ, তাই
:invalidসত্য। প্রতিটি অক্ষরে লাল দেখানো বিরক্তিকর — মানুষ তো লেখা শেষই করেননি। ব্রাউজারগুলো সাধারণত ঘর ছাড়ার বা জমা দেওয়ার অপেক্ষা করে, তারপর:user-invalidবসায় (খুঁটিনাটি ব্রাউজারভেদে একটু আলাদা)। - ঘর ছেড়ে গেলেন। এখন
:user-invalidসত্য: লাল বর্ডার, সাথে ✖ চিহ্ন আর কী করতে হবে তার লেখা। একই মুহূর্তেform:has(:user-invalid)সত্য হয়, তাই ফর্মের ওপরে সারাংশ-বার্তাও দেখানো যায় — কোনো JavaScript ছাড়াই। - ঠিক করলেন। নম্বর সম্পূর্ণ হওয়ামাত্র
:user-invalidমিথ্যা,:user-validসত্য। ভুল দেখাতে দেরি, ঠিক হওয়ার খবর দ্রুত — ভালো ফর্মের এটাই ছন্দ।
এবার কয়েকটি নিয়ম, যা ভুলের বার্তাকে সত্যিকারের কাজের বানায়। প্রথম, শুধু রঙে নয় — WCAG-এর 1.4.1 (Use of Color) বলে, তথ্য শুধু রঙে বোঝানো যাবে না। পুরুষদের মধ্যে আনুমানিক প্রতি ১২ জনে একজনের কোনো না কোনো রঙ-অন্ধতা আছে; তাঁদের অনেকের কাছে লাল বর্ডার আর ধূসর বর্ডার প্রায় একই। তাই সাথে আইকন (✖), মোটা বর্ডার, আর সবচেয়ে জরুরি — লেখা: কী ভুল, আর কীভাবে ঠিক করবেন (3.3.1 আর 3.3.3). “ভুল ইনপুট” নয়, “১১ সংখ্যার নম্বর লিখুন, যেমন 01712345678”.
দ্বিতীয়, বার্তার জায়গা — ঘরের ঠিক নিচে, ঘর আর পরের প্রশ্নের মাঝখানে। পাতার কোণের টোস্ট-বার্তা ফোনে প্রায়ই চোখে পড়ে না। তৃতীয়, বার্তাকে ঘরের সাথে জোড়া দেওয়া — aria-describedby দিয়ে, যাতে screen reader ঘরে ঢুকেই নাম আর বার্তা দুটোই শোনায়। আর চতুর্থ, placeholder-এর রং: ব্রাউজারের ডিফল্ট placeholder প্রায়ই খুব হালকা ধূসর, সাদা পটভূমিতে লেখার ৪.৫:১ contrast-এর নিচে। ::placeholder দিয়ে গাঢ় করুন — তবে মনে রাখুন, placeholder label-এর বিকল্প নয় (পর্ব ৮)।
body { font-family: sans-serif; font-size: 17px; }
.field { display: grid; gap: 4px; margin-block: 14px; max-width: 340px; }
input, button { font: inherit; min-height: 44px; padding: 8px 12px; }
input { border: 2px solid #6b7280; border-radius: 8px; }
::placeholder { color: #6b7280; } /* সাদায় প্রায় ৪.৮:১ */
small { color: #374151; }
.err { display: none; color: #b91c1c; font-weight: 600; }
input:user-invalid { border-color: #b91c1c; background: #fef2f2; }
.field:has(input:user-invalid) .err { display: block; }
input:user-valid { border-color: #15803d; }
.summary {
display: none; padding: 10px 14px; border-radius: 8px;
border: 2px solid #b91c1c; background: #fef2f2; color: #7f1d1d;
}
form:has(:user-invalid) .summary { display: block; }শুরুতে সব শান্ত — ফাঁকা required ঘর থাকলেও কোনো লাল নেই। মোবাইলের ঘরে “0171” লিখে Tab চাপুন: ঘর লাল হলো, নিচে ✖-সহ বার্তা এলো, আর ফর্মের ওপরে ⚠ সারাংশ দেখা দিল — সব শুধু CSS-এ। এবার পুরো নম্বর 01712345678 লিখুন — বর্ডার সবুজ, বার্তা আর সারাংশ দুটোই চলে যায়। ইমেইলের ঘরে “abc” লিখে বেরিয়ে আসুন — একই আচরণ; ফাঁকা রাখলে কিছু হয় না, কারণ ঘরটি ঐচ্ছিক। “জমা দিন” চাপলে ব্রাউজারের নিজের বার্তাও আসবে। চেষ্টা করুন: (১) দুই জায়গায় :user-invalid-কে :invalid করুন — পাতা খোলামাত্র মোবাইলের ঘর লাল আর সারাংশ দেখা যাবে; এই অভিজ্ঞতাই এড়াতে চাই। (২) ::placeholder-এর রং #d1d5db করুন — placeholder প্রায় পড়াই যায় না।
এখানে একটি সূক্ষ্ম ব্যাপার আছে। CSS শুধু চেহারা বদলায়; screen reader লাল রং দেখে না। লুকানো বার্তাকে আগে থেকেই aria-describedby-তে জুড়ে রাখাও ঠিক নয় — accessible description হিসাবের নিয়মে aria-describedby দিয়ে সরাসরি জোড়া লুকানো লেখাও সাধারণত পড়া হয়, ফলে ঠিক ঘরেও “নম্বরটি ঠিক নয়” শোনা যেতে পারে। তাই আসল সাইটে ভুল দেখা দিলে ছোট একটি JavaScript দুটি কাজ করে: ঘরে aria-invalid="true" বসায়, আর বার্তার id-টি aria-describedby-তে যোগ করে:
// ঘর ছাড়ার সময় ব্রাউজারের নিজের যাচাই দেখে ARIA ঠিক করা
phone.addEventListener('blur', () => {
const bad = !phone.validity.valid;
phone.setAttribute('aria-invalid', bad);
phone.setAttribute('aria-describedby', bad ? 'ph-hint ph-err' : 'ph-hint');
});ফর্মের ওপরের সারাংশেরও একটি পেশাদার রূপ আছে — error summary: জমা দেওয়ার পর ভুল থাকলে ওপরে একটি বাক্সে সব ভুলের তালিকা, প্রতিটি লিংক সংশ্লিষ্ট ঘরে নিয়ে যায়, আর focus সরাসরি বাক্সটিতে চলে যায়, যাতে screen reader ব্যবহারকারীও সাথে সাথে জানেন। যুক্তরাজ্যের সরকারি ওয়েবসাইটের GOV.UK Design System এই নকশাটিকে জনপ্রিয় করেছে। CSS-এর :has() দিয়ে বাক্সটি দেখানো যায়; focus সরানো আর লিংকের তালিকা বানানো JavaScript-এর কাজ — JavaScript কোর্সের ফর্ম যাচাইয়ের পর্বে সেটি দেখবেন।
৭ · disabled বনাম readonly, field-sizing আর ছোট খুঁটিনাটি
দুটি attribute প্রায়ই গুলিয়ে যায়, আর তাদের সাজানোও তাই ভুল হয়। readonly মানে “দেখুন, কপি করুন, কিন্তু বদলাবেন না” — যেমন আগে থেকে বসানো অর্ডার নম্বর। disabled মানে “এই ঘর এখন কাজের বাইরে” — যেমন “অন্য ঠিকানা” বাছাই না করলে তার ঘরগুলো।
| বৈশিষ্ট্য | readonly | disabled |
|---|---|---|
| Tab দিয়ে focus পাওয়া যায়? | হ্যাঁ | না |
| ফর্ম জমা দিলে মান যায়? | হ্যাঁ | না |
| লেখা select করে কপি? | হ্যাঁ | ব্রাউজারভেদে প্রায়ই না |
| CSS selector | :read-only (বা [readonly]) | :disabled |
| চেহারা কেমন হওয়া উচিত | পড়া যায়, কিন্তু সম্পাদনার ঘর নয় বোঝায় — হালকা পটভূমি, ভাঙা বর্ডার | স্পষ্টভাবে নিষ্ক্রিয় — ফ্যাকাশে, cursor: not-allowed |
সাজানোর সময় একটি ফাঁদ: :read-only শুধু readonly ঘর নয়, disabled ঘরও ধরে (বদলানো যায় না বলে), এমনকি পাতার সাধারণ লেখাও। তাই input:read-only:not(:disabled) লিখুন। WCAG নিষ্ক্রিয় কন্ট্রোলকে contrast-এর নিয়ম থেকে ছাড় দেয়, তবু লেখা পড়ার মতো রাখুন। আর “জমা দিন” বোতাম disabled না রেখে চালু রাখুন, চাপলে কোন ঘর বাকি তা দেখান — disabled বোতাম কেন চলে না, মানুষ প্রায়ই বোঝেন না, কীবোর্ডে খুঁজেও পান না।
পর্ব ৩৭-এর field-sizing: content-ও মনে রাখুন — textarea লেখার সাথে বড় হয়, ছোট input লেখার সমান চওড়া হয়। এটি এখনো not yet Baseline (মূলত Chromium), তাই সবসময় সীমা দিয়ে ব্যবহার করুন; যে ব্রাউজার চেনে না, সেখানে সাধারণ নির্দিষ্ট মাপের ঘর থাকবে:
textarea {
field-sizing: content; /* লেখা অনুযায়ী উচ্চতা */
min-block-size: 3lh; /* অন্তত তিন লাইন */
max-block-size: 12lh; /* তারপর scrollbar */
}lh একক মানে এক লাইনের উচ্চতা — বাংলা লেখার বড় line-height থাকলেও ঠিক তিন লাইন জায়গা হয়।
৮ · সব একসাথে — ফোন থেকে ডেস্কটপ, লাইট থেকে ডার্ক: একটি পেমেন্ট ফর্ম
এবার সব শিক্ষা এক জায়গায়। ফোনে ফর্ম হবে এক কলামে — চোখ আর আঙুল দুটোই ওপর থেকে নিচে চলে; UX গবেষণায় বারবার দেখা গেছে, এক কলামের ফর্ম মানুষ দ্রুত আর কম ভুলে পূরণ করেন। বড় পর্দায় পাশাপাশি বসান শুধু ছোট, সম্পর্কিত ঘর — যেমন মার্চেন্ট আর টাকার পরিমাণ। পর্ব ২৭-এর grid আর পর্ব ৩০-এর mobile-first media query দিয়ে কাজটি কয়েক লাইনের। খেয়াল রাখুন, Tab-এর ক্রম আসে HTML-এর ক্রম থেকে; grid দিয়ে চোখের ক্রম বদলালে কীবোর্ডের ক্রম বদলায় না, তাই দুটো মিলিয়ে রাখুন।
আর ডার্ক মোড: পর্ব ৩৬-এর color-scheme: light dark দিলে ব্রাউজার ঘরগুলোর ভেতর, checkbox, scrollbar — সবই ব্যবহারকারীর থিমে আঁকে। নিজের দেওয়া রংগুলো light-dark() দিয়ে দুই থিমের জন্য লিখুন। নিচের ফর্মটি একটি কাল্পনিক মোবাইল ওয়ালেট “দ্রুতপে”-র — শেখার জন্য বানানো, কোনো আসল প্রতিষ্ঠানের নয়:
:root { color-scheme: light dark; }
body { font-family: sans-serif; font-size: 17px; }
.pay {
max-width: 640px; margin: auto; padding: 4px 20px 20px;
border: 1px solid #94a3b8; border-radius: 14px; accent-color: #0f766e;
}
.grid { display: grid; gap: 14px; } /* ফোনে এক কলাম */
@media (min-width: 560px) {
.grid { grid-template-columns: 1fr 1fr; } /* বড় পর্দায় দুই */
.full { grid-column: 1 / -1; }
}
label { display: block; font-weight: 600; margin-bottom: 4px; }
input:not([type="checkbox"]), button {
font: inherit; box-sizing: border-box; width: 100%; min-height: 44px;
padding: 8px 12px; border: 1px solid #64748b; border-radius: 8px;
}
input:read-only:not(:disabled) {
background: light-dark(#f1f5f9, #1e293b); border-style: dashed;
}
input:disabled { opacity: .6; cursor: not-allowed; }
input:user-invalid { border: 2px solid light-dark(#b91c1c, #f87171); }
:focus-visible { outline: 3px solid light-dark(#1d4ed8, #93c5fd); outline-offset: 2px; }
.chk { display: flex; gap: 8px; align-items: center; margin: 18px 0; font-weight: 400; }
button { background: #0f766e; color: #fff; border: 0; font-weight: 700; }
.note { font-size: .9em; color: light-dark(#475569, #94a3b8); }প্রিভিউ যথেষ্ট চওড়া, তাই দুই কলাম: ওপরে পাশাপাশি মার্চেন্ট (ভাঙা বর্ডার আর হালকা পটভূমি — readonly) আর টাকার পরিমাণ; তার নিচে পুরো প্রস্থে ওয়ালেট নম্বর; তারপর ফ্যাকাশে কুপন ঘর (disabled — Tab চাপলে একে লাফিয়ে যায়) আর রেফারেন্স। আপনার সিস্টেম ডার্ক মোডে থাকলে পুরো ফর্ম, ঘরের ভেতর আর checkbox-ও গাঢ়। কিছু না লিখে বোতাম চাপুন — ব্রাউজার ওয়ালেট নম্বর চাইবে, আর ঘরটি লাল বর্ডার পাবে; ঠিক নম্বর আর টিক দিয়ে জমা দিলে নিচে দেখবেন কোন মানগুলো যেত — merchant থাকবে, coupon থাকবে না। চেষ্টা করুন: (১) 560px-কে 900px করুন — এখন ফোনের মতো এক কলাম। (২) প্রথম লাইনে light dark-কে শুধু dark করুন — সিস্টেমের থিম যা-ই হোক, ফর্মটি ডার্ক রূপে দেখবেন।
নিরাপত্তার শিক্ষা, নকশার অংশ হিসেবে: লক্ষ করুন, এই ফর্মে কোনো PIN বা OTP-র ঘর নেই। বাংলাদেশের মোবাইল ফিনান্সিয়াল সার্ভিসগুলো বারবার সতর্ক করে — PIN বা OTP কাউকে বলবেন না, কোনো অচেনা ওয়েবসাইটে লিখবেন না। আসল পেমেন্টে দোকানের সাইট আপনাকে ওয়ালেটের নিজের পাতা বা অ্যাপে পাঠায়, PIN দেওয়া হয় সেখানে। তাই কোনো দোকানের সাইটে “বিকাশ-এর মতো দেখতে” ফর্ম PIN চাইলে সেটি প্রতারণার লক্ষণ। ডেভেলপার হিসেবে আসল ব্র্যান্ডের নাম, লোগো বা রং নকল করে ফর্ম বানাবেন না — ক্লায়েন্ট চাইলেও না।
পরীক্ষা ও ইন্টারভিউতে এ ধরনের প্রশ্ন প্রায়ই আসে:
appearance: noneকী করে? — কন্ট্রোলের native চেহারা মুছে দেয়, কিন্তু element-এর আচরণ, কীবোর্ড ও accessibility অক্ষত রাখে।- input কেন পাতার ফন্ট পায় না? — ব্রাউজারের নিজের CSS ফর্ম কন্ট্রোলকে আলাদা ফন্ট দেয়;
font: inheritদিয়ে ঠিক করতে হয়। :focusআর:focus-visible-এর পার্থক্য? — প্রথমটি সব focus-এ, দ্বিতীয়টি কেবল যখন ব্রাউজার মনে করে দাগ দরকার (যেমন কীবোর্ড)।:invalid-এর বদলে:user-invalidকেন? — প্রথমটি পাতা খোলামাত্র ফাঁকা required ঘর ধরে; দ্বিতীয়টি ব্যবহারকারী কিছু করার পর।- disabled আর readonly ঘরের মান কি জমা হয়? — readonly-র হয়, disabled-এর হয় না; disabled ঘর focus-ও পায় না।
৯ · AI-এর যুগে — সুন্দর দেখতে AI-ফর্মের ভেতরের ফাঁক
“একটা সুন্দর সাইন-আপ ফর্ম বানিয়ে দাও” বললে AI কয়েক সেকেন্ডে চোখ-জুড়ানো ফর্ম দেয়। কিন্তু AI নিজের ফর্ম কখনো শুধু কীবোর্ডে পূরণ করে দেখে না, screen reader বা contrast theme চালায় না, আর পুরোনো কোড থেকে শেখায় প্রায়ই সেই কৌশলগুলোই ফেরায়, যা এই পর্বে বাদ দিতে শিখলেন।
AI সাধারণত যেসব ভুল করে
| AI-এর ভুল | আপনি যা দেখবেন | ঠিক উপায় |
|---|---|---|
input:focus { outline: none }, বদলে শুধু হালকা border-color | Tab চাপলে কোথায় আছেন বোঝা যায় না | :focus-visible-এ ৩:১ contrast-এর স্পষ্ট outline |
<div class="checkbox"> বা <div> দিয়ে নকল dropdown, ক্লিক JavaScript-এ | Space-এ চলে না, ফর্মে মান যায় না, screen reader চুপ | আসল input/select, appearance: none বা accent-color |
| label নেই, শুধু placeholder | লিখতে শুরু করলেই প্রশ্ন হারায়; হালকা লেখা | ঘরের ওপরে <label>; ::placeholder-এ যথেষ্ট contrast |
:invalid-এ লাল রং | পাতা খুলতেই পুরো ফর্ম লাল | :user-invalid, সাথে আইকন ও লেখা |
-webkit-appearance, -moz-appearance একসাথে, বা range-এর দুই ব্রাউজারের selector এক নিয়মে | অকারণ পুরোনো কোড; কোথাও হাতল সাজানো হয় না | prefix ছাড়া appearance; range-এর জন্য আলাদা নিয়ম |
| ভুলের বার্তা শুধু লাল বর্ডারে, বা ঘর থেকে দূরে টোস্টে | রঙ-অন্ধ ব্যবহারকারী আর screen reader জানতে পারেন না | ঘরের নিচে লেখা, aria-describedby আর aria-invalid |
AI-কে ফর্মের কাজ দেওয়ার সময় শর্তগুলো আগেই বলে দিন, আর প্রতিটি সিদ্ধান্তের কারণ লিখতে বলুন:
AI-এর দেওয়া ফর্ম হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে CSS লেখক থেকে নকশা-চিন্তক বানাবে।
প্র ০১ব্রাউজার কেন ফর্ম কন্ট্রোলকে অপারেটিং সিস্টেমের চেহারায় আঁকে? সব কিছুই CSS দিয়ে সাজানোর মতো বানালে কী ক্ষতি ছিল?
১৯৯০-এর দশকে, প্রথম দিকের ব্রাউজারগুলো যখন HTML ফর্ম আনে, তখন CSS-ই ছিল না বা খুব আদিম। বোতাম আর লেখার ঘর আঁকার সবচেয়ে সহজ উপায় ছিল অপারেটিং সিস্টেমের নিজের widget ধার করা — Windows-এর বোতাম, Mac-এর বোতাম। এতে দুটি বড় লাভ হলো: ব্যবহারকারী চেনা জিনিস দেখলেন, আর কীবোর্ড, কপি-পেস্ট, বানান-পরীক্ষা, ভাষা-ইনপুটের মতো জটিল আচরণ বিনা খরচে পাওয়া গেল। বাংলা লেখার কথাই ভাবুন — যুক্তাক্ষর, ইনপুট মেথড, কার্সর সরানো; এসব প্রতিটি ওয়েবসাইটের নিজে বানাতে হলে অর্ধেক সাইটে বাংলা টাইপই করা যেত না।
পরে CSS এলো, ডিজাইনাররা ব্র্যান্ড অনুযায়ী সাজাতে চাইলেন, আর দ্বন্দ্ব শুরু হলো। Mozilla আর WebKit নিজেদের মতো -moz-appearance আর -webkit-appearance আনে — প্রথমে উল্টো উদ্দেশ্যে, যেকোনো element-কে native বোতামের চেহারা দিতে। দুই ব্রাউজারের মানের তালিকা আলাদা ছিল, আর ওয়েবসাইটগুলো এমনভাবে এগুলোর ওপর নির্ভর করে ফেলেছিল যে পরে মানসম্মত করার সময় বেশ কিছু পুরোনো মান “compat” হিসেবে রেখে দিতে হয়। শেষে CSS Basic User Interface স্পেসিফিকেশনে appearance মানসম্মত হয়, যার সবচেয়ে কাজের মান মূলত দুটি: auto আর none.
মোবাইল যুগে যুক্তিটি আরও শক্ত হলো — ফোনের নিজের select-তালিকা আর ক্যালেন্ডার আঙুলের মাপে, screen reader-এর সাথে মিলিয়ে বানানো। তাই আধুনিক দিক হলো মাঝামাঝি: চেহারার নিয়ন্ত্রণ ধাপে ধাপে ডেভেলপারের হাতে দেওয়া, কিন্তু আচরণ ব্রাউজারের কাছে রাখা। accent-color, ::file-selector-button, field-sizing আর customizable select — সবই এই ভাবনার ফল, আর W3C-র Open UI community group এই কাজেই ব্রাউজার-নির্মাতা ও ডিজাইন-সিস্টেম নির্মাতাদের এক টেবিলে এনেছে।
মূল উপলব্ধি: native কন্ট্রোল কোনো সীমাবদ্ধতা নয়, এটি একটি বিনিময় — ব্র্যান্ডের পুরো নিয়ন্ত্রণ ছেড়ে আপনি পান কোটি মানুষের পরীক্ষিত আচরণ। নিয়ন্ত্রণ নিতে চাইলে সেই আচরণ রাখার দায়িত্বও আপনার।
প্র ০২outline: none এত ছড়িয়ে পড়ল কীভাবে? আর :focus-visible কি সমস্যাটির পুরো সমাধান?
গল্পের একটি বড় অংশ “CSS reset”-এর। ২০০০-এর দশকে ব্রাউজারগুলোর ডিফল্ট style অনেক আলাদা ছিল, তাই ডেভেলপাররা সব ডিফল্ট শূন্য করার একটি ফাইল দিয়ে কাজ শুরু করতেন। এর সবচেয়ে বিখ্যাতটি Eric Meyer-এর reset; তার প্রথম সংস্করণে :focus { outline: 0; } ছিল, সাথে একটি মন্তব্য — নিজের focus style দিতে ভুলবেন না। লক্ষ লক্ষ সাইট লাইনটি কপি করল, মন্তব্যটি পড়ল না। পরের সংস্করণে লাইনটি সরিয়ে নেওয়া হয়, কিন্তু ততদিনে অভ্যাসটি ছড়িয়ে গেছে, আর আজও AI-এর লেখা কোডে সেটি ফিরে আসে।
ডিজাইনারদের অভিযোগও অযৌক্তিক ছিল না: মাউসে বোতাম চাপলে কিছু ব্রাউজার মোটা দাগ দেখাত, যা তাঁদের চোখে “ভাঙা” লাগত। সমস্যাটা ছিল ব্রাউজারের কাছে কোনো উপায় ছিল না জানার — এই focus কীবোর্ড থেকে এসেছে, নাকি মাউস থেকে। Firefox নিজস্ব :-moz-focusring দিয়ে এর একটি সমাধান পরীক্ষা করেছিল; সেই ধারণা থেকে WICG-তে একটি polyfill তৈরি হয়, আর শেষে :focus-visible মানসম্মত হয়ে সব প্রধান ব্রাউজারে আসে। আজ ব্রাউজারের নিজের ডিফল্ট দাগও এই নিয়মেই দেখায়।
তবে :focus-visible সমাধানের অর্ধেক। বাকি অর্ধেক হলো দাগটি ভালো হওয়া। পাতার রঙের সাথে মিশে যাওয়া হালকা নীল, এক পিক্সেলের সরু রেখা, বা গাঢ় পটভূমিতে কালো দাগ — টেকনিক্যালি “visible”, বাস্তবে অদৃশ্য। এজন্যই WCAG 2.2-তে দুটি নতুন নিয়ম এলো: 2.4.11 (AA) — focus পাওয়া element-কে sticky header বা ব্যানার পুরো ঢেকে রাখতে পারবে না; আর 2.4.13 (AAA) — দাগের আকার আর contrast-এর মাপ। খসড়ার সময় দাগের আকারের নিয়মটি AA-তে রাখার প্রস্তাব ছিল, কিন্তু মাপা কঠিন বলে শেষে AAA-তে যায়। AA-তে না থাকলেও এটি একটি ভালো লক্ষ্য: অন্তত ২ পিক্সেল পুরু, পাশের রঙের সাথে ৩:১.
মূল উপলব্ধি: একটি লাইনের মন্তব্য না পড়ার দাম দিয়েছে প্রজন্মের পর প্রজন্ম কীবোর্ড ব্যবহারকারী। কোড কপি করার আগে প্রতিটি “reset” লাইনের মানে জানুন — বিশেষ করে যা কিছু মুছে দেয়।
প্র ০৩Design system বানানোর সময় নিজের নকশার checkbox-radio বানাবেন, নাকি শুধু accent-color? সিদ্ধান্তটি কীভাবে নেবেন?
প্রথমে খরচের হিসাবটি স্পষ্ট করুন। accent-color এক লাইন: native আকার, ডার্ক মোড (color-scheme মানলে), forced colors, উচ্চ zoom, টাচ — সব ব্রাউজার সামলায়। টিকচিহ্নের রঙও ব্রাউজার নিজে বেছে নেয়, যাতে আপনার দেওয়া রঙের ওপর পড়া যায়। দুর্বলতা একটাই: আকার, বর্ডারের পুরুত্ব, কোণার বাঁক আর অ্যানিমেশন আপনার হাতে নেই, আর ব্রাউজারভেদে চেহারা একটু আলাদা।
appearance: none-এর নকশায় সব নিয়ন্ত্রণ আপনার, কিন্তু সাথে আসে একটি দায়িত্বের তালিকা: focus-দাগ নিজে দেওয়া; ডার্ক থিমে বাক্সের রং; forced colors মোডে system color দিয়ে টিক; em এককে মাপ, যাতে লেখা বড় করলে বাক্সও বড় হয়; ৩:১ non-text contrast-এর বর্ডার; :disabled আর :indeterminate অবস্থা (checkbox-এর “আধা টিক”, যা শুধু JavaScript দিয়ে বসানো যায়); আর RTL ভাষায় ঠিক দিক। প্রতিটি নতুন অবস্থা মানে নতুন পরীক্ষা।
সবচেয়ে খারাপ পথটি তৃতীয়: <div role="checkbox" tabindex="0">. ARIA-র প্রথম নিয়মই বলে, native element থাকলে সেটিই ব্যবহার করুন। div-এ role দিলেও Space চাপলে কিছু হয় না যতক্ষণ না JavaScript লেখেন, label-এ ক্লিক কাজ করে না, ফর্মে মান যায় না, required চলে না। WebAIM-এর বার্ষিক জরিপে লেবেলহীন ফর্ম কন্ট্রোল নিয়মিতভাবে সবচেয়ে সাধারণ ত্রুটিগুলোর মধ্যে থাকে।
বাস্তব সিদ্ধান্তের একটি সহজ সূত্র: ব্র্যান্ডের রঙই যদি মূল চাহিদা হয় — accent-color. ডিজাইনার যদি নির্দিষ্ট আকার, বাঁক বা অ্যানিমেশন চান, আর আপনার দলের হাতে উপরের পরীক্ষাগুলো করার সময় থাকে — appearance: none. Toggle switch-এর জন্য দ্বিতীয়টি প্রায় অনিবার্য, কারণ native switch এখনো মানসম্মত নয় (Safari সম্প্রতি checkbox-এর জন্য একটি switch attribute এনেছে, কিন্তু সেটি এখনো HTML standard-এর অংশ নয়, অন্য ব্রাউজারেও নেই)। দুটি পথেই একটি জিনিস বদলায় না — নিচে আসল <input>.
মূল উপলব্ধি: কাস্টম নকশা একটি ঋণ — আজ চেহারা পান, কিস্তিতে শোধ করেন পরীক্ষা আর রক্ষণাবেক্ষণে। যতটুকু দরকার, ঠিক ততটুকুই ঋণ নিন।
প্র ০৪ভুলের বার্তা কখন দেখানো উচিত — প্রতিটি অক্ষরে, ঘর ছাড়ার সময়, নাকি জমা দেওয়ার পর? গবেষণা আর বড় প্রতিষ্ঠানগুলো কী বলে?
২০০৯ সালে ডিজাইনার Luke Wroblewski “Inline Validation in Web Forms” নামে একটি লেখা প্রকাশ করেন, একটি ব্যবহারযোগ্যতা-গবেষণা প্রতিষ্ঠানের সাথে করা পরীক্ষার ভিত্তিতে। মূল ফল: জমা দেওয়ার পরে সব ভুল একসাথে দেখানোর চেয়ে ঘরে ঘরে সাথে সাথে সাড়া দিলে মানুষ কম ভুল করেন, দ্রুত শেষ করেন, আর সন্তুষ্ট থাকেন। কিন্তু সেই গবেষণাতেই আরেকটি খুঁটিনাটি ছিল — খুব আগেভাগে, মানুষ লেখা শেষ করার আগেই “ভুল” দেখানো বিরক্তি বাড়ায়। নিয়মটি তাই দাঁড়াল: ঘর ছাড়ার পর যাচাই করুন, লেখার মাঝে নয়।
এই অভিজ্ঞতাই পরে ব্রাউজারে ঢুকেছে। Firefox বহু বছর আগে :-moz-ui-invalid নামে একটি নিজস্ব pseudo-class রেখেছিল, যা ব্যবহারকারী কিছু করার পরেই মিলত। সেই ধারণা মানসম্মত হয়ে :user-invalid হলো, আর ২০২৩ সালে বাকি প্রধান ব্রাউজারগুলো যোগ দেওয়ার পর সবখানে ব্যবহার করা যায়। একটি পরিচিত নীতি এর সারকথা — “দেরিতে শাস্তি, দ্রুত পুরস্কার”: ভুল দেখান ঘর ছাড়ার পরে, কিন্তু ভুল ঠিক হওয়ামাত্র লাল সরিয়ে দিন।
অন্যদিকে যুক্তরাজ্যের সরকারি ডিজিটাল সেবার GOV.UK Design System সাধারণত জমা দেওয়ার পরেই যাচাই করার পরামর্শ দেয়, ওপরে error summary আর প্রতিটি ঘরের পাশে বার্তা দিয়ে। তাদের যুক্তি: লাখো মানুষের বিচিত্র প্রয়োজন — screen reader ব্যবহারকারী, ধীরে টাইপ করা প্রবীণ, ভাঙা ইন্টারনেটের ফোন — সবার জন্য সাথে সাথে আসা-যাওয়া বার্তা বিভ্রান্তিকর হতে পারে, আর জমা দেওয়ার পরের একটি পরিষ্কার তালিকা সবচেয়ে অনুমানযোগ্য।
দুই মত আসলে পরস্পরবিরোধী নয়। সাধারণ চর্চা দুই স্তরের: জমা দেওয়ার সময় পূর্ণ যাচাই আর সারাংশ, আর যেখানে সত্যিই সাহায্য করে (পাসওয়ার্ডের শর্ত, নম্বরের দৈর্ঘ্য) সেখানে ঘর ছাড়ার পরে নরম ইঙ্গিত। আর সার্ভারে আবার যাচাই বাধ্যতামূলক — পর্ব ৯-এর শিক্ষা।
মূল উপলব্ধি: ভুলের বার্তার সময় একটি UX সিদ্ধান্ত, কোডের নয়। :user-invalid আপনাকে ভালো ডিফল্ট দেয়, কিন্তু কোন ফর্মে কোন ছন্দ — তা ঠিক হয় আসল মানুষকে দিয়ে পরীক্ষা করে।
প্র ০৫পেমেন্ট ফর্মে “বিশ্বাস” কি নকশা দিয়ে তৈরি হয়? আর প্রতারকেরা কেন হুবহু একই নকশা নকল করে?
মানুষ টাকা পাঠানোর আগে খুব দ্রুত, প্রায় অবচেতনে কয়েকটি সংকেত খোঁজেন: পরিচিত রং ও লোগো, পরিষ্কার সাজানো, বানান-ভুল নেই, টাকার অঙ্ক আর প্রাপকের নাম স্পষ্ট। এজন্যই পেশাদার পেমেন্ট ফর্ম এত সংযত — কম রং, বড় অঙ্ক, স্পষ্ট বোতাম (“৳১,২৫০ পাঠান”, শুধু “Submit” নয়), আর প্রতিটি ধাপে কী হবে তা লেখা। ভালো focus, ঠিক autocomplete আর inputmode, ভুলের পরিষ্কার বার্তা — এগুলো শুধু অ্যাক্সেসিবিলিটি নয়, বিশ্বাসেরও উপাদান; আটকে গেলে মানুষ ভাবেন “কিছু একটা গড়বড়”।
ঠিক এই কারণেই প্রতারকেরা নকল করে। ফিশিং (phishing) পাতাগুলো আসল প্রতিষ্ঠানের রং, লোগো আর বিন্যাস হুবহু কপি করে, কারণ মানুষ চেহারা দেখেই বিশ্বাস করেন। বাংলাদেশে মোবাইল ফিনান্সিয়াল সার্ভিসের নামে ভুয়া পাতা, ভুয়া কল আর SMS-এর ঘটনা এত বেশি যে প্রতিষ্ঠানগুলো বারবার প্রচার চালায় — PIN আর OTP কাউকে দেবেন না। নকশা তাই দুই দিকেই কাটে: যে নকশা আসল ব্যবহারকারীর আস্থা আনে, সেটিই প্রতারকের অস্ত্র।
এখান থেকে একজন ডেভেলপারের জন্য কয়েকটি নীতি আসে। এক, পেমেন্টের গোপন তথ্য (PIN, কার্ডের পূর্ণ তথ্য) নিজের সাইটে না নিয়ে পেমেন্ট সেবার নিজের পাতা বা অ্যাপে পাঠানো — এটাই প্রচলিত নিরাপদ নকশা, আর এতে আইনি ও নিরাপত্তার দায়ও কমে। দুই, অন্যের ব্র্যান্ড নকল না করা — “ডেমো” হিসেবেও না; এই পর্বের ফর্মে তাই কাল্পনিক নাম। তিন, HTTPS ছাড়া কোনো ফর্ম নয় — ব্রাউজার নিজেই অনিরাপদ পাতায় ফর্ম পূরণের সময় সতর্ক করে।
মূল উপলব্ধি: ফর্মের নকশা একটি প্রতিশ্রুতি — “এখানে আপনার তথ্য নিরাপদ, আর আপনি যা দেখছেন তাই ঘটবে।” সুন্দর CSS সেই প্রতিশ্রুতিকে দৃশ্যমান করে, কিন্তু প্রতিশ্রুতি রক্ষা হয় নিরাপদ স্থাপত্য আর সৎ নকশায়।
অনুশীলন
বেশিরভাগ কাজ দেখা আর যাচাই করার — শুধু একটিতে নিজে CSS লিখবেন।
-
Tab-পরীক্ষা: আপনার পরিচিত তিনটি সাইটের লগইন বা চেকআউট ফর্ম খুলুন (একটি ব্যাংক বা ওয়ালেট, একটি অনলাইন দোকান, একটি সরকারি সেবা)। মাউস না ছুঁয়ে শুধু Tab আর Space দিয়ে পূরণ করার চেষ্টা করুন। কোথায় focus হারিয়ে গেল? কোন checkbox Space-এ চলল না?
সাধারণত তিন রকম সমস্যা পাবেন: (ক) দাগই নেই — DevTools-এর Styles-এ
outline: noneবাoutline: 0খুঁজুন; (খ) নিজের বানানো checkbox বা dropdown-এ Tab পৌঁছায় না বা Space-এ কিছু হয় না — Elements প্যানেলে দেখবেন সেটি<div>, আসল inputdisplay: none; (গ) focus sticky header-এর নিচে চাপা পড়ে — WCAG 2.4.11-এর সমস্যা,scroll-padding-topদিয়ে সারানো যায়। যে সাইটে সব ঠিক চলে, সেখানে প্রায় নিশ্চিত আসল<input>আর<label>পাবেন। -
বাগ খুঁজুন: একজন লিখেছেন
input, select { width: 100%; padding: 10px; border: 2px solid #999; }. ফলে ঘরের লেখা ছোট আর অন্য ফন্টে, input-গুলো ডানে একটু বেরিয়ে যায়, select যায় না, আর ঘরের বর্ডার প্রায় দেখা যায় না। তিনটি সমস্যার কারণ আর সমাধান কী?(১) ছোট আর ভিন্ন ফন্ট — কন্ট্রোল পাতার ফন্ট inherit করে না;
font: inheritযোগ করুন। (২) input বেরিয়ে যায়, select যায় না — input ডিফল্টেcontent-box, তাই ১০০% প্রস্থের সাথে দুই পাশের padding আর border যোগ হয়ে চওড়া হয়; select ডিফল্টেborder-box. দুটোকেইbox-sizing: border-boxদিন। (৩)#999সাদা পটভূমিতে মোটামুটি ২.৮:১ — ঘরের সীমানার জন্য WCAG 1.4.11-এর ৩:১ শর্ত পূরণ করে না।#6b7280-এর মতো গাঢ় ধূসর নিন (প্রায় ৪.৮:১)। -
Radio-কে নিজের নকশায় আনুন: অংশ ৪-এর নিজের checkbox-এর মতো করে radio বোতাম সাজান — গোল বাক্স, বাছাই হলে ভেতরে একটি ভরাট বৃত্ত, দৃশ্যমান focus-দাগ আর forced colors মোডেও দেখা যাবে।
একটি সম্ভাব্য উত্তর — অংশ ৪-এর প্রথম এডিটরে
.c input-এর নিয়মগুলোর জায়গায় বসিয়ে, আর HTML-এ checkbox-কে radio করে চালিয়ে দেখতে পারেন:input[type="radio"] { appearance: none; margin: 0; width: 1.3em; height: 1.3em; border-radius: 50%; border: 2px solid #475569; display: grid; place-content: center; } input[type="radio"]::before { content: ""; width: .65em; height: .65em; border-radius: 50%; background: #0f766e; transform: scale(0); transition: transform .15s; } input[type="radio"]:checked { border-color: #0f766e; } input[type="radio"]:checked::before { transform: scale(1); } input[type="radio"]:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 2px; } @media (forced-colors: active) { input[type="radio"]:checked::before { background: CanvasText; } }যাচাই: Tab দিয়ে দলে ঢুকে ↑↓ চাপলে বাছাই বদলায় কি না (radio দলের ভেতরে Tab নয়, তীর-চাবি চলে — এটি native আচরণ, আপনাকে কিছু লিখতে হয়নি); একই
name-এর দলে শুধু একটি বাছাই হয় কি না; আর লেখা বড় করলে বৃত্তও বড় হয় কি না (emএককের জন্য)। -
ভুলের বার্তা পড়ে শোনান: Windows-এ NVDA (বিনামূল্যে), Android-এ TalkBack বা iPhone-এ VoiceOver চালু করে অংশ ৬-এর ফর্মের মতো একটি ফর্মে ভুল নম্বর দিয়ে ঘর ছাড়ুন। screen reader কি ভুলের কথা বলল? তারপর DevTools-এর Accessibility প্যানেলে ঘরটির “Description” দেখুন।
শুধু CSS-এর ফর্মে সাধারণত দেখবেন: চোখে লাল বর্ডার আর বার্তা এসেছে, কিন্তু screen reader ঘরে ফিরে শুধু নাম আর সাহায্য-লেখা পড়ে — ভুলের খবর নয়, কারণ বার্তাটি
aria-describedby-তে জোড়া নেই (কিছু ব্রাউজার native “invalid” অবস্থা জানাতে পারে, কিন্তু তার ওপর ভরসা করবেন না)। অংশ ৬-এর ছোট JavaScript যোগ করলে Accessibility প্যানেলে Invalid “true” আর Description-এ ভুলের লেখা দেখবেন, screen reader-ও পড়বে। শিক্ষা: চোখের নকশা আর কানের তথ্য আলাদা দুটি কাজ।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমনিজের নকশার একটি অ্যাক্সেসিবল checkbox বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 🏷️ আসল
<input type="checkbox">লিখে<label>-এর সাথে জোড়া - 🧽
appearance: noneদিয়ে native চেহারা মোছা - 📦 width, border আর border-radius দিয়ে নিজের বাক্স আঁকা
- ✔️
:checked::beforeদিয়ে টিকচিহ্ন দেখানো - 🔵
:focus-visibleআর forced-colors-এর নিয়ম যোগ করা
একটি checkbox-এ CSS-এ appearance: none দিলে কী ঘটে?
- checkbox-টি পাতা থেকে লুকিয়ে যায় আর কীবোর্ডে পৌঁছানো যায় না
- checkbox-টি একটি সাধারণ
<div>-এর মতো হয়ে যায়, screen reader আর checkbox বলে না - native চেহারা মুছে যায়, কিন্তু আচরণ, কীবোর্ড আর accessibility অক্ষত থাকে
- checkbox-এর টিকচিহ্নের রং পাতার লেখার রঙে বদলে যায়
একটি ফর্মে label-এর বাংলা লেখা বড় আর সুন্দর ফন্টে দেখাচ্ছে, কিন্তু input-এর ভেতরের লেখা ছোট আর অন্য ফন্টে। সবচেয়ে সরাসরি সমাধান কোনটি?
- input, select, textarea আর button-এ
font: inheritদেওয়া - input-এ
appearance: noneদেওয়া - input-এ
box-sizing: border-boxদেওয়া - body-তে আরও বড়
font-sizeদেওয়া
কীবোর্ড ব্যবহারকারীর জন্য focus-এর দাগ দেখাতে, অথচ মাউসে বোতাম চাপলে সাধারণত না দেখাতে, কোন নিয়মটি সবচেয়ে উপযুক্ত?
*:focus { outline: none; }button:hover { outline: 3px solid #1d4ed8; }button:focus { outline: 3px solid #1d4ed8; }:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; }
পাতা খোলামাত্র একটি ফর্মের সব ফাঁকা required ঘর লাল দেখাচ্ছে। CSS-এ কী বদলালে ব্যবহারকারী কিছু করার পরেই লাল দেখাবে?
:invalid-কে:requiredকরা:invalid-কে:user-invalidকরা:invalid-কে:placeholder-shownকরা:invalid-কে:focus-withinকরা
range slider-এর হাতল সাজাতে একজন লিখলেন input::-webkit-slider-thumb, input::-moz-range-thumb { background: green; }. Chromium আর Firefox-এ কী হবে?
- দুই ব্রাউজারেই হাতল সবুজ হবে
- শুধু Chromium-এ সবুজ হবে
- কোনো ব্রাউজারেই নিয়মটি কাজ করবে না
- দুই ব্রাউজারই console-এ error দেখাবে আর পাতা লোড হবে না
একটি ফর্মে একটি ঘর readonly, আরেকটি disabled. ফর্ম জমা দিলে কী ঘটে?
- readonly ঘরের মান যায়, disabled ঘরের মান যায় না
- দুটি ঘরের মানই যায়
- কোনোটির মান যায় না
- disabled ঘরের মান যায়, readonly ঘরের মান যায় না
একটি ফর্মে ভুল ঘর শুধু লাল বর্ডার দিয়ে চিহ্নিত করা হয়। এর সবচেয়ে বড় অ্যাক্সেসিবিলিটি সমস্যা কী?
- কিছু ব্রাউজার লাল বর্ডার দেখাতে পারে না
- তথ্যটি শুধু রঙে — রঙ-অন্ধ ও screen reader ব্যবহারকারী কী ভুল তা জানতে পারেন না
- লাল বর্ডার পাতা ধীর করে দেয়
- লাল বর্ডার থাকলে ফর্ম জমাই দেওয়া যায় না
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- ফর্ম কন্ট্রোল আঁকে ব্রাউজার আর অপারেটিং সিস্টেম;
appearance: noneশুধু চেহারা মোছে — আসল element রাখুন, চেহারা বদলান, আচরণ নয়। - ভিত্তি:
font: inherit,box-sizing: border-box, সমানmin-height, ঘরের ওপরে label, কাছের জিনিস কাছে, দল আলাদা fieldset-এ। - Focus-এর দাগ মুছবেন না:
:focus-visible-এ স্পষ্টoutlineআরoutline-offset, ৩:১ contrast; sticky header যেন ঢেকে না দেয় (WCAG 2.4.7, 2.4.11). - রঙে
accent-color; পুরো নকশায়appearance: none+::before, সাথে focus আর forced-colors; toggle =role="switch"-এর checkbox; file-এ::file-selector-button; range-এর prefix-নিয়ম আলাদা আলাদা। - ভুল দেখান
:user-invalidদিয়ে,:has()-এ সারাংশ; সাথে আইকন আর লেখা,aria-describedby+aria-invalid; readonly-র মান যায়, disabled-এর যায় না; ফোনে এক কলাম, ডার্কেcolor-scheme.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৪ · SVG আর আইকন — vector গ্রাফিক্স ওয়েবেপরবর্তী পর্বinline SVG, আইকন, currentColor আর অ্যাক্সেসিবল গ্রাফিক্স।
- পর্ব ০৮ · ফর্মের ভিত্তি — form, input, label আর buttonঝালিয়ে নিনফর্মের HTML — label, fieldset, autocomplete আর inputmode.
- পর্ব ০৯ · ফর্মের নানা কন্ট্রোল আর built-in validationঝালিয়ে নিনcheckbox, select, range, file, pattern, disabled আর readonly-র HTML দিক।
- পর্ব ৪৫ · অ্যাক্সেসিবল CSS — focus, contrast, reduced motion আর বড় লেখাগভীরে যানfocus, contrast আর ব্যবহারকারীর পছন্দ — পুরো সাইটের দৃষ্টিতে।
- HTML ও CSS · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- JavaScript · ফর্ম আর যাচাইপাশের ট্র্যাকConstraint Validation API, নিজের বার্তা আর error summary-তে focus সরানো।