এই পর্বে যা শিখবেন
@media-র পুরো গঠন — media type, media feature, পুরোনোmin-widthআর নতুন range syntax, আর ব্রাউজার শর্তটি কীভাবে যাচাই করেand, কমা,not,onlyদিয়ে শর্ত জোড়া, আর সীমানার ১px-এর সেই পুরোনো বাগ- ব্যবহারকারীর পছন্দ আর যন্ত্র চেনার media feature, আর ছাপার জন্য আলাদা CSS
- Container query কেন দরকার,
container-type,container-name,@containerআরcqiএকক — ভেতরে ব্রাউজার কীভাবে পাত্র খোঁজে - Style query, scroll-state query-র আসল অবস্থা, media বনাম container বেছে নেওয়া, আর JavaScript-এর
matchMedia()
১ · এক পাতা, হাজার রকম পর্দা — শর্তসাপেক্ষ CSS কেন
পর্ব ৩০-এ (রেসপন্সিভ ডিজাইন — viewport আর মোবাইল-ফার্স্ট ভাবনা) আপনি দেখেছেন, একই পাতা ৩৬০px চওড়া সস্তা অ্যান্ড্রয়েড ফোনেও খোলে, আবার অফিসের ২৭ ইঞ্চি মনিটরেও খোলে। Flexbox-এর flex-wrap আর grid-এর auto-fit অনেক কাজ নিজেরাই সামলায়। কিন্তু কিছু সিদ্ধান্ত আছে, যা “জায়গা বুঝে নিজে মানিয়ে নেওয়া” দিয়ে হয় না — যেমন “ছোট পর্দায় মেনু লুকিয়ে একটি বোতাম দেখাও”, “ব্যবহারকারী dark mode চাইলে গাঢ় রং দাও”, বা “ছাপার সময় বিজ্ঞাপন বাদ দাও”। এসব জায়গায় CSS-কে একটি শর্ত দিতে হয়: “যদি এমন হয়, তবেই এই নিয়মগুলো মানো।”
ঢাকার একজন রিকশাচালকের কথা ভাবুন। বৃষ্টি নামলে তিনি হুড তুলে দেন আর যাত্রীর পায়ে প্লাস্টিক বিছিয়ে দেন; রোদ উঠলে হুড নামিয়ে দেন। রিকশা একটাই, শুধু পরিস্থিতি দেখে সাজ বদলায়। Media query ঠিক এই “বৃষ্টি হলে হুড তোলো” নিয়ম — পাতা একটাই, পর্দা আর ব্যবহারকারীর অবস্থা দেখে চেহারা বদলায়। আর container query আরেকটু সূক্ষ্ম: যাত্রী নিজে ভাবেন, “আমি যে রিকশায় বসেছি, সেটা কতটা চওড়া?” — পুরো রাস্তার নয়, নিজের আসনের মাপ দেখে বসার ভঙ্গি ঠিক করা।
এই পর্বে দুটি হাতিয়ারই শিখবেন। প্রথমটি — media query — প্রায় পনেরো বছর ধরে রেসপন্সিভ ওয়েবের মেরুদণ্ড। দ্বিতীয়টি — container query — বহু বছর ধরে ডেভেলপারদের সবচেয়ে বেশি চাওয়া ফিচার ছিল, আর ২০২৩ সাল থেকে সব প্রধান ব্রাউজারে চলে। দুটির প্রশ্ন আলাদা:
Media query জিজ্ঞেস করে
“পুরো viewport কত চওড়া? এটা পর্দা না ছাপা? ব্যবহারকারী dark mode চান? আঙুল দিয়ে চালাচ্ছেন না মাউস দিয়ে?”
@media — পাতা আর মানুষের অবস্থাContainer query জিজ্ঞেস করে
“আমি যে বাক্সের ভেতরে বসে আছি, সেটা কত চওড়া? তার কোনো custom property-র মান কী?”
@container — component-এর নিজের জায়গা“ছাপার জন্য আলাদা CSS” ধারণাটা আসলে খুব পুরোনো — ১৯৯৮ সালের CSS2-তেই @media print আর @media screen ছিল। কিন্তু পর্দার মাপ দেখে নিয়ম বদলানোর ক্ষমতা আসে পরে, “Media Queries” নামের আলাদা স্পেসিফিকেশনে, যা ২০১২ সালে W3C-র Recommendation হয়। এর মধ্যেই, ২০১০ সালে, ওয়েব ডিজাইনার Ethan Marcotte “Responsive Web Design” নামে একটি লেখায় দেখান: নমনীয় grid, নমনীয় ছবি আর media query — এই তিনে মিলে একটি পাতাই সব পর্দায় চলতে পারে। তার আগে বড় সাইটগুলো মোবাইলের জন্য আলাদা m. দিয়ে শুরু হওয়া সাইট বানাত।
২ · @media-র গঠন — media type আর media feature
@media একটি at-rule — অর্থাৎ @ দিয়ে শুরু হওয়া CSS-এর বিশেষ নির্দেশ। এর পরে থাকে একটি শর্ত, তারপর { }-এর ভেতরে সাধারণ CSS নিয়ম। শর্তের দুটি অংশ হতে পারে:
- Media type — মোটা দাগে কোন মাধ্যম:
screen(যেকোনো পর্দা),print(কাগজে ছাপা বা “Save as PDF”),all(সব, না লিখলে এটিই ধরা হয়)। আগেtv,handheld,projection-এর মতো আরও অনেক ধরন ছিল; সেগুলো এখন বাতিল, কারণ “ফোন” আর “টিভি”-র সীমারেখা মুছে গেছে। - Media feature — বন্ধনীর ভেতরে একটি নির্দিষ্ট বৈশিষ্ট্য:
(width >= 600px),(orientation: portrait),(prefers-color-scheme: dark)ইত্যাদি।
/* পুরোনো, এখনো সর্বত্র চলে */
@media screen and (min-width: 600px) { … } /* চওড়া ≥ 600px */
@media (max-width: 599px) { … } /* চওড়া ≤ 599px */
/* নতুন range syntax — গণিতের মতো */
@media (width >= 600px) { … }
@media (width < 600px) { … }
@media (400px <= width <= 800px) { … } /* দুই সীমার মাঝে */
/* আরও কিছু feature */
@media (orientation: landscape) { … } /* পাশে কাত করা */
@media (min-aspect-ratio: 16/9) { … } /* চওড়া:উঁচু অন্তত ১৬:৯ */
@media (min-resolution: 2dppx) { … } /* প্রতি CSS pixel-এ ≥ ২টি আসল pixel */Range syntax এসেছে Media Queries Level 4 থেকে। min-width: 600px পড়তে হয় “কমপক্ষে ৬০০” — উল্টো দিক থেকে ভাবতে হয়, তাই নতুনরা প্রায়ই min আর max গুলিয়ে ফেলেন। width >= 600px সরাসরি পড়া যায়। ২০২৩ সালের মধ্যে Chrome, Edge, Firefox আর Safari — সবাই এটি চালু করেছে, আর এখন এটি Baseline Widely available. খুব পুরোনো ব্রাউজার (কয়েক বছর আপডেট না হওয়া) এই শর্ত বোঝে না — তাদের কাছে পুরো @media ব্লকটি মিথ্যা ধরা হয়। আপনার দর্শকদের মধ্যে এমন ব্রাউজার বেশি থাকলে পুরোনো min-width রূপই নিরাপদ।
বাকি feature-গুলোর মানে: orientation বলে viewport খাড়া (portrait — উচ্চতা ≥ চওড়া) না শোয়ানো (landscape). aspect-ratio চওড়া আর উচ্চতার অনুপাত দেখে। resolution দেখে পর্দা কতটা ঘন — device pixel ratio ২ মানে প্রতিটি CSS pixel আঁকতে ২×২টি আসল pixel লাগে (পর্ব ২০)। পুরোনো কোডে -webkit-min-device-pixel-ratio দেখবেন — সেটি ইতিহাস; এখন prefix ছাড়া resolution লিখুন। আর device-width নামের feature-টি বাতিল — viewport-এর width-ই দেখুন।
এবার নিজে দেখুন। নিচের প্রিভিউটি একটি ছোট আলাদা পাতা (iframe), আর তার ভেতরের media query প্রিভিউর নিজের চওড়া দেখে — আপনার ব্রাউজার-জানালার নয়। ছোট script-টি শুধু চওড়াটি লিখে দেখায়:
<style>
body { font-family: sans-serif; }
.box { padding: 16px; border-radius: 8px; background: #fde68a; }
.box::after { content: " → ছোট পর্দার সাজ"; }
@media (width >= 500px) {
.box { background: #bbf7d0; font-size: 1.25rem; }
.box::after { content: " → বড় পর্দার সাজ"; }
}
</style>
<p class="box">এই প্রিভিউর চওড়া: <b id="w">…</b>px</p>
<script>
function show() { document.getElementById('w').textContent = innerWidth; }
(function m() {
if (!document.body.offsetWidth) return setTimeout(m, 50);
show();
})();
addEventListener('resize', show);
</script>কম্পিউটারে প্রিভিউ সাধারণত ৫০০px-এর বেশি চওড়া, তাই সবুজ বাক্স আর “বড় পর্দার সাজ” দেখবেন; ফোনে খুললে প্রিভিউ সরু, তাই হলুদ। প্রিভিউর চওড়া আপনি টেনে বদলাতে পারবেন না, তাই উল্টো দিক থেকে পরীক্ষা করুন: (১) 500px-কে 2000px করুন — শর্ত মিথ্যা হলো, বাক্স হলুদ। (২) width >= 500px-কে width < 500px করুন — এবার উল্টো ফল। খেয়াল করুন, শর্ত না মিললে কোনো error আসে না; ভেতরের নিয়মগুলো শুধু উপেক্ষিত হয়।
ব্রাউজার এমন একটি শর্ত কীভাবে পড়ে আর কখন আবার যাচাই করে, ধাপে ধাপে দেখুন:
- at-rule চেনা। CSS পড়ার সময় ব্রাউজার
@mediaদেখে বোঝে, এর পরে একটি শর্ত আর একটি নিয়মের দল আসছে। নিয়মগুলো ব্রাউজার পড়ে রাখে ঠিকই, কিন্তু শর্ত সত্য হলেই কেবল সেগুলো খাটে। - Media type যাচাই।
screenমানে যেকোনো পর্দা — ফোন, ল্যাপটপ, টিভি। পাতা ছাপার সময় (বা “Print preview”-তে) type হয়print, তখন এই শর্ত মিথ্যা। type না লিখলেallধরা হয় — আধুনিক কোডে সাধারণত type বাদ দিয়ে শুধু feature লেখা হয়। - প্রথম feature. ব্রাউজার viewport-এর চওড়া মাপে (scrollbar সহ, CSS pixel-এ)। ফোনে 390px হলে
390 >= 600মিথ্যা।andমানে সবগুলো সত্য হতে হবে, তাই একটি মিথ্যা হলেই পুরো শর্ত মিথ্যা — বাকিগুলো দেখার দরকারই নেই। - দ্বিতীয় feature. এবার ধরুন একই পাতা ল্যাপটপে খোলা, চওড়া 1366px — প্রথম শর্ত সত্য।
prefers-reduced-motion: no-preferenceমানে ব্যবহারকারী কম অ্যানিমেশন চেয়ে রাখেননি। এখানেও সত্য। - নিয়ম cascade-এ ঢুকল। সব সত্য, তাই ভেতরের নিয়মটি অন্য সাধারণ নিয়মের মতোই cascade-এ যোগ হলো। খুব জরুরি কথা:
@mediaকোনো বাড়তি specificity দেয় না (পর্ব ১৮)। একই selector-এর দুটি নিয়ম থাকলে পরে লেখাটি জেতে — তাই media query-র নিয়ম সাধারণ নিয়মের পরে লিখুন। - জীবন্ত শর্ত। শর্ত একবার যাচাই করেই শেষ নয়। জানালার মাপ বদলালে, ফোন কাত করলে, বা ব্যবহারকারী সেটিংসে dark mode চালু করলে ব্রাউজার আবার যাচাই করে আর নিয়মগুলো সঙ্গে সঙ্গে চালু বা বন্ধ করে — পাতা reload করতে হয় না।
মোবাইল-ফার্স্ট কোডে আগে শর্ত ছাড়া ছোট পর্দার সাজ লিখুন, তারপর (width >= …) দিয়ে বড় পর্দার জন্য যোগ করুন। ছোট ফোন সবচেয়ে কম CSS খাটায়, আর নতুন কোনো পর্দা এলে সে অন্তত সরল, পড়ার মতো একটি পাতা পায়। Breakpoint-এর মাপ ডিভাইসের নাম দেখে নয়, কনটেন্ট দেখে বেছে নিন — যে চওড়ায় লেআউট ভাঙতে শুরু করে, ঠিক সেখানে।
৩ · যুক্তির শব্দ — and, কমা, or, not আর only
একাধিক শর্ত জুড়তে media query-তে কয়েকটি operator আছে। প্রোগ্রামিংয়ের &&, ||, !-এর মতোই, শুধু লেখা ইংরেজি শব্দে:
| লেখা | মানে | উদাহরণ |
|---|---|---|
and | সবগুলো সত্য হতে হবে | (width >= 600px) and (orientation: landscape) |
, (কমা) | যেকোনো একটি সত্য হলেই চলবে — প্রতিটি অংশ আলাদা query | (width < 400px), print |
or | কমার মতোই “অথবা”, Level 4-এর নতুন শব্দ, বন্ধনীর ভেতরে জোড়া যায় | (width < 400px) or (orientation: portrait) |
not | ফল উল্টে দেয় | not print, not (hover: hover) |
only | আধুনিক ব্রাউজারে কোনো প্রভাব নেই; শুধু খুব পুরোনো ব্রাউজার থেকে নিয়ম লুকাত | only screen and (…) |
কয়েকটি সূক্ষ্ম কথা। এক, পুরোনো (Level 3) নিয়মে not বসত একেবারে শুরুতে, আর উল্টে দিত পুরো query-কে: not screen and (color) মানে “not (screen and color)” — অনেকে ভুল করে ভাবেন শুধু screen উল্টাল। নতুন নিয়মে বন্ধনী দিয়ে স্পষ্ট লেখা যায়: not (hover: hover). দুই, and আর or বন্ধনী ছাড়া একসাথে মেশানো যায় না — (a) and (b) or (c) অবৈধ; লিখতে হয় (a) and ((b) or (c)). তিন, only AI আর পুরোনো টিউটোরিয়ালে এখনো খুব দেখা যায় — এতে ক্ষতি নেই, কিন্তু আজ এর কোনো কাজও নেই।
নিচের এডিটরে প্রতিটি সারির নিজের media query আছে। যে শর্ত আপনার এই প্রিভিউতে সত্য, সেই সারি সবুজ হয়ে “✓ মিলেছে” দেখাবে:
body { font-family: sans-serif; }
ul { padding: 0; }
li { list-style: none; margin: 4px 0; padding: 6px 10px;
border-radius: 6px; background: var(--bg, #e5e7eb); }
li::after { content: var(--mark, " ✗ মেলেনি"); }
@media (width >= 400px) { .a { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }
@media (400px <= width <= 800px) { .b { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }
@media (width < 400px), (orientation: portrait) { .c { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }
@media (width >= 400px) and (orientation: landscape) { .d { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }
@media not print { .e { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }
@media only print { .f { --bg: #bbf7d0; --mark: " ✓ মিলেছে"; } }প্রিভিউর চওড়া বেশি আর উচ্চতা কম, তাই এটি “landscape”. কম্পিউটারে সাধারণত a, b (প্রিভিউ ৮০০px-এর কম হলে), d আর e সবুজ; c আর f ধূসর — c-র দুটি অংশের কোনোটিই সত্য নয়, আর f শুধু ছাপার সময় মেলে। ফোনে খুললে প্রিভিউ সরু হয়, তখন c সবুজ হতে পারে। (রঙের মানগুলো custom property-তে রাখা হয়েছে, যাতে প্রতিটি query-তে এক লাইনেই কাজ হয় — বিস্তারিত পর্ব ৩৩-এ।) চেষ্টা করুন: (১) c-র কমাকে and করুন — এখন দুটিই সত্য হতে হবে, সারিটি আরও কঠিন শর্ত পেল। (২) b-র 800px-কে 500px করুন — কম্পিউটারে b ধূসর হয়ে গেল।
সীমানার ১px-এর বাগ: পুরোনো ধাঁচে লোকে লিখত @media (max-width: 600px) আর @media (min-width: 600px) — ঠিক 600px-এ দুটোই সত্য, দুই সাজ একসাথে খাটে। “ঠিক” করতে লিখত max-width: 599px — কিন্তু zoom করলে বা উচ্চ-ঘনত্বের পর্দায় viewport-এর চওড়া ভগ্নাংশ হতে পারে, যেমন 599.5px; তখন কোনোটিই সত্য নয়, পাতা দুই সাজের ফাঁকে পড়ে যায়। Range syntax এই সমস্যা পুরোপুরি মেটায়: (width < 600px) আর (width >= 600px) — প্রতিটি সংখ্যা ঠিক একটি দলে পড়ে।
৪ · মানুষ আর যন্ত্রকে চেনা — পছন্দ আর ইনপুটের media feature
পর্দার মাপ দেখেই সব বোঝা যায় না। একজন ব্যবহারকারী রাতে চোখের আরামের জন্য ফোনে dark mode চালু রাখেন; আরেকজনের নড়াচড়া দেখলে মাথা ঘোরে, তাই তিনি অ্যানিমেশন কমিয়ে রেখেছেন; কেউ চোখে কম দেখেন, তাই বেশি contrast চান। এসব পছন্দ মানুষ একবার অপারেটিং সিস্টেমে ঠিক করে রাখেন, আর ব্রাউজার media feature হিসেবে সেগুলো CSS-কে জানায়।
ব্যবহারকারীর পছন্দ (user preference)
prefers-color-scheme:light|dark— হালকা না গাঢ় থিম। পুরো dark mode বানানোর কৌশল (আরcolor-schemeproperty) পর্ব ৩৬-এ।prefers-reduced-motion:reduce|no-preference— কম অ্যানিমেশন চাওয়া হয়েছে কি না। ভেতরের কান বা ভারসাম্যের সমস্যা (vestibular disorder) আছে এমন অনেকের জন্য বড় নড়াচড়া বমিভাব বা মাথাব্যথা আনে। এর বিস্তারিত ব্যবহার পর্ব ৪৫-এ।prefers-contrast:more|less|custom|no-preference— বেশি বা কম রঙের পার্থক্য চাওয়া।forced-colors:active|none— ব্যবহারকারীর সিস্টেম নিজের কয়েকটি রং দিয়ে পুরো পাতা জোর করে আঁকছে কি না (যেমন Windows-এর contrast থিম)। তখন আপনার background-image বা box-shadow দিয়ে বানানো সীমারেখা হারিয়ে যেতে পারে — এই query দিয়ে সেগুলো আসলborderদিয়ে বদলে দিন।
/* অ্যানিমেশন শুধু তাঁদের জন্য, যাঁরা কমাতে বলেননি */
@media (prefers-reduced-motion: no-preference) {
.banner { animation: slide-in 600ms ease-out; }
}
/* contrast চাইলে হালকা ধূসর লেখাকে গাঢ় করুন */
@media (prefers-contrast: more) {
.muted { color: #1f2937; }
}লক্ষ করুন প্রথম উদাহরণের দিক: অ্যানিমেশন বাদ দেওয়ার বদলে যোগ করা হয়েছে শর্তের ভেতরে। ফলে যে ব্রাউজার এই feature বোঝে না, সেখানেও কেউ অনাকাঙ্ক্ষিত নড়াচড়া পান না।
হাতের যন্ত্র (interaction)
Interaction media feature জানায় ব্যবহারকারী পাতা কী দিয়ে চালাচ্ছেন:
hover: hover— প্রধান যন্ত্র দিয়ে কোনো কিছুর ওপর ভাসিয়ে রাখা (hover) যায়, যেমন মাউস। টাচস্ক্রিনে সাধারণতhover: none.pointer: fine— সূক্ষ্মভাবে তাক করা যায় (মাউস, stylus);pointer: coarse— মোটা দাগে (আঙুল, টিভির রিমোট);pointer: none— তাক করার যন্ত্রই নেই।any-hover,any-pointer— যুক্ত থাকা যেকোনো যন্ত্রের মধ্যে কেউ পারে কি না। যেমন টাচস্ক্রিন ল্যাপটপে মাউসও লাগানো থাকলেpointerএকটি উত্তর দেয়, কিন্তুany-pointer: fine-ও সত্য।
body { font-family: sans-serif; }
ul { padding: 0; }
li { list-style: none; margin: 4px 0; padding: 6px 10px;
border-radius: 6px; background: var(--bg, #e5e7eb); }
li::after { content: var(--mark, " — না"); }
@media (prefers-color-scheme: dark) { .dark { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (prefers-reduced-motion: reduce) { .motion { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (prefers-contrast: more) { .contrast { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (forced-colors: active) { .forced { --mark: " — হ্যাঁ"; } }
@media (hover: hover) { .hover { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (pointer: fine) { .fine { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (pointer: coarse) { .coarse { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }
@media (any-pointer: fine) { .anyfine { --bg: #bbf7d0; --mark: " — হ্যাঁ"; } }এই প্রিভিউ আপনার নিজের ডিভাইসের একটি ছোট রিপোর্ট। মাউসওয়ালা কম্পিউটারে সাধারণত hover: hover, pointer: fine আর any-pointer: fine সবুজ; ফোনে pointer: coarse সবুজ আর hover ধূসর। (forced-colors চালু থাকলে সিস্টেম নিজেই রং বসায়, তাই সেই সারিতে শুধু লেখা বদলায়।) চেষ্টা করুন: (১) কম্পিউটারের সেটিংসে dark mode চালু করুন — প্রথম সারি সঙ্গে সঙ্গে সবুজ হবে, পাতা reload বা কোনো বোতাম চাপা ছাড়াই। (২) DevTools খুলে Rendering প্যানেলে (Ctrl+Shift+P চেপে “Rendering” লিখুন) “Emulate CSS media feature prefers-reduced-motion” বেছে নিন — দ্বিতীয় সারি বদলাবে।
দুটি ফাঁদ: (১) শুধু hover-এ খোলা ড্রপডাউন মেনু টাচস্ক্রিনে প্রায় অচল — কাজের জিনিস কখনো শুধু hover-এর ওপর রাখবেন না; ক্লিক বা focus-এও যেন খোলে। @media (hover: hover) শুধু “বাড়তি সৌন্দর্য” যোগ করতে ব্যবহার করুন। (২) pointer: coarse দেখে “এটা ফোন” ধরে নেবেন না — বড় টাচস্ক্রিন মনিটর, ট্যাবলেটে কিবোর্ড — নানা মিশ্রণ আছে। আঙুলের জন্য বোতাম বড় রাখুন সবখানে; WCAG ২.২ অন্তত 24 × 24 CSS pixel-এর লক্ষ্য (বা যথেষ্ট ফাঁকা জায়গা) চায়, আর আরামের জন্য ৪৪px-এর কাছাকাছি রাখাই প্রচলিত।
৫ · কাগজের পাতা — @media print
ব্যাংকের স্টেটমেন্ট, ভর্তির রসিদ, অনলাইন অর্ডারের চালান, কোনো সরকারি ফর্মের তথ্য — মানুষ এখনো ওয়েব পাতা ছাপেন, বা “Save as PDF” করে WhatsApp-এ পাঠান। পর্দার জন্য বানানো পাতা কাগজে তুললে বিপত্তি: মেনু, বিজ্ঞাপন, “শেয়ার করুন” বোতাম অকারণে কালি খরচ করে, আর লিংকের নীল লেখা কাগজে ক্লিক করা যায় না — ঠিকানাটাই হারিয়ে যায়। সমাধান একটি print stylesheet.
body { font-family: sans-serif; }
nav { background: #1e3a8a; padding: 8px; }
nav a { color: #fff; margin-right: 12px; }
a { color: #1d4ed8; }
@media print {
nav, .no-print { display: none; }
body { font: 12pt/1.5 Georgia, serif; color: #000; }
a { color: #000; }
a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 90%; }
h2 { break-after: avoid; }
}পর্দায় দেখছেন নীল মেনু, নীল লিংক আর একটি বোতাম — কারণ @media print-এর নিয়মগুলো এখন ঘুমিয়ে। প্রিভিউতে ছাপার চেহারা দেখতে একটি কৌশল: @media print-কে @media screen করে দিন। মেনু আর বোতাম উধাও, লেখা কালো serif ফন্টে, আর লিংকের পরে বন্ধনীতে পুরো ঠিকানা — কাগজেও পাঠক জানবেন কোথায় যেতে হবে। দেখা শেষে আবার print করে দিন। আরেকটি চেষ্টা: প্রথম নিয়ম থেকে nav, অংশটি মুছে screen অবস্থায় দেখুন — এবার মেনুও ছাপায় থেকে যায়, আর বোঝা যায় কাগজে সেটি কতটা অকেজো।
কয়েকটি ব্যাখ্যা। a[href^="http"] একটি attribute selector (পর্ব ১৭) — শুধু বাইরের পূর্ণ ঠিকানাওয়ালা লিংক ধরে, পাতার ভেতরের # লিংক বাদ রাখে। attr(href) element-এর attribute-এর মান পড়ে এনে ::after pseudo-element-এ লেখা হিসেবে বসায় (pseudo-element-এর বিস্তারিত পর্ব ৩৪-এ)। break-after: avoid ব্রাউজারকে অনুরোধ করে, শিরোনামের ঠিক পরেই যেন পাতা না ভাঙে — নইলে শিরোনাম এক পাতার নিচে, লেখা পরের পাতায়। একই পরিবারের break-inside: avoid টেবিলের সারি বা ছবির ক্যাপশন দুই পাতায় ভাগ হতে দেয় না।
@page {
size: A4; /* কাগজের মাপ */
margin: 2cm; /* চারপাশের ফাঁকা */
}
@media print {
tr, figure { break-inside: avoid; }
.ad, video, .chat-widget { display: none; }
}@page at-rule কাগজের মাপ আর margin ঠিক করে। ছাপার CSS পরীক্ষা করতে প্রতিবার আসলে ছাপতে হয় না: Ctrl+P চেপে preview দেখুন, অথবা DevTools-এর Rendering প্যানেলে “Emulate CSS media type: print” বেছে নিন — পাতা পর্দাতেই ছাপার চেহারা নেবে।
৬ · Media query-র সীমা — কার্ড জানে না সে কোথায় বসেছে
এবার এমন একটি সমস্যা দেখি, যা media query দিয়ে সমাধান করা যায় না। একটি অনলাইন বাজারের সাইটে পণ্যের একটি component আছে — ছবি আর বিবরণসহ কার্ড। সেটি পাতার চওড়া মূল কলামে বসে, আবার একই কার্ড ডানের সরু sidebar-এ “আপনার জন্য আরও” অংশেও বসে। ডিজাইনার চান: জায়গা চওড়া হলে ছবি বাঁয়ে, লেখা ডানে; সরু হলে ছবি ওপরে, লেখা নিচে।
<style>
body { font-family: sans-serif; }
.page { display: flex; gap: 10px; }
main { flex: 1; }
aside { width: 170px; flex: none; }
.card { display: grid; gap: 8px; background: #f1f5f9;
padding: 8px; border-radius: 8px; }
.card p { margin: 0; }
.thumb { aspect-ratio: 16 / 9; border-radius: 6px;
background: linear-gradient(135deg, #f59e0b, #ef4444); }
@media (width >= 500px) {
.card { grid-template-columns: 120px 1fr; }
}
</style>
<div class="page">
<main><div class="card"><div class="thumb"></div>
<p>পদ্মার ইলিশ, ১ কেজি — ৳১,৪০০</p></div></main>
<aside><div class="card"><div class="thumb"></div>
<p>পদ্মার ইলিশ, ১ কেজি — ৳১,৪০০</p></div></aside>
</div>কম্পিউটারে প্রিভিউ ৫০০px-এর বেশি চওড়া, তাই media query সত্য — আর দুটি কার্ডই পাশাপাশি সাজ নিল। মূল কলামের কার্ড সুন্দর, কিন্তু sidebar-এর কার্ডে ১২০px ছবির পর লেখার জন্য প্রায় জায়গাই নেই — লেখা ভেঙে চিকন একটি স্তম্ভ। (ফোনে প্রিভিউ সরু, তাই দুটিই ওপর-নিচে — সেখানে সমস্যা চোখে পড়বে না।) চেষ্টা করুন: (১) aside-এর 170px-কে 300px করুন — sidebar-এর কার্ড ভালো হলো, কিন্তু media query-র কাছে কিছুই বদলায়নি; সে আগের মতোই শুধু প্রিভিউর চওড়া দেখছে। (২) .page-এ flex-direction: column যোগ করুন — এবার দুটি কার্ডই চওড়া জায়গা পেল।
সমস্যার মূলটা বুঝে নিন: media query-র কাছে একটিই প্রশ্ন — “viewport কত চওড়া?” কিন্তু একটি component-এর আসল প্রশ্ন — “আমাকে কত জায়গা দেওয়া হয়েছে?” viewport এক থাকলেও একটি component পাতার নানা জায়গায় নানা চওড়া পেতে পারে। নিচের ডায়াগ্রামে একই কার্ডের হিসাব কয়েকটি পরিস্থিতিতে দেখুন:
| পর্দা (viewport) | |
| মূল কলামে কার্ডের জায়গা | |
| sidebar-এ কার্ডের জায়গা | |
| @media (width >= 900px) | |
| @container (width >= 400px) |
- ফোন, ৩৯০px. sidebar মূল কলামের নিচে নেমে এসেছে, দুটি কার্ডই প্রায় ৩৫৮px পাচ্ছে। media query মিথ্যা — দুটিই ওপর-নিচে, ঠিক যেমন চাই। এখন পর্যন্ত media query নিখুঁত।
- ল্যাপটপ, ১৩৬৬px. media query সত্য, তাই সব কার্ড পাশাপাশি। মূল কলামের ৭৬০px-এ ঠিক আছে, কিন্তু sidebar-এর ২৬০px-এ ছবি আর লেখা গাদাগাদি। Media query দিয়ে এটা ঠিক করতে হলে sidebar-এর কার্ডের জন্য আলাদা class (
.card--small) বানাতে হতো — অর্থাৎ component-কে জানতে হতো সে কোথায় বসেছে। - একই ল্যাপটপ, container query দিয়ে। এবার প্রতিটি কার্ড নিজের পাত্রকে জিজ্ঞেস করছে। মূল কলাম ৭৬০px ≥ ৪০০ — পাশাপাশি; sidebar ২৬০px — ওপর-নিচে। একই HTML, একই class, কোনো বাড়তি নিয়ম নেই।
- ট্যাবলেট, ৮২০px. উল্টো সমস্যা: viewport ৯০০-র কম, তাই media query মিথ্যা — অথচ sidebar না থাকায় মূল কলামের কার্ড ৫৪০px পাচ্ছে, পাশাপাশি সাজের যথেষ্ট জায়গা। Media query “জায়গা আছে” বুঝতেই পারল না; container query পারল।
- শিক্ষা। Media query পাতা-স্তরের প্রশ্নের উত্তর দেয়, container query component-স্তরের। একটি component যত বেশি জায়গায় পুনর্ব্যবহার হয় — ড্যাশবোর্ড, design system, CMS-এর ব্লক — container query তত বেশি দরকারি।
৭ · Container query — পাত্র বানানো, প্রশ্ন করা, নাম দেওয়া
Container query দুই ধাপের কাজ। প্রথমে একটি পূর্বপুরুষ element-কে (সাধারণত component-এর ঠিক বাইরের মোড়ক) “পাত্র” ঘোষণা করুন। তারপর ভেতরের element-এর জন্য @container শর্ত লিখুন:
.slot { /* ধাপ ১: পাত্র */
container-type: inline-size; /* চওড়া দেখে প্রশ্ন করা যাবে */
}
.card { display: grid; gap: 8px; } /* সরু অবস্থার সাজ */
@container (width >= 400px) { /* ধাপ ২: কাছের পাত্রকে প্রশ্ন */
.card { grid-template-columns: 140px 1fr; }
}container-type-এর তিনটি মান: inline-size — লেখার দিকের মাপ, অর্থাৎ বাংলা-ইংরেজির মতো আনুভূমিক লেখায় চওড়া (প্রায় সবসময় এটিই চাই); size — চওড়া আর উচ্চতা দুটোই; normal — ডিফল্ট, মাপের প্রশ্ন করা যায় না। শর্তে media query-র মতোই width, height, inline-size, aspect-ratio, orientation আর range syntax ব্যবহার করা যায়। Container size query ২০২৩ সালের শুরু থেকে সব প্রধান ব্রাউজারে আছে — এখন Baseline Widely available.
এবার আগের দোকানের কার্ডটিই container query দিয়ে লিখি। sidebar-এর ডান-নিচের কোণ টেনে চওড়া করুন (মাউস লাগবে; টাচস্ক্রিনে টানার হাতল কাজ নাও করতে পারে — তখন aside-এর width বদলে দেখুন):
<style>
body { font-family: sans-serif; }
.page { display: flex; gap: 10px; align-items: start; }
main { flex: 1; container-type: inline-size; }
aside { width: 170px; flex: none; container-type: inline-size;
resize: horizontal; overflow: auto; max-width: 75%;
border: 2px dashed #94a3b8; padding: 4px; }
.card { display: grid; gap: 8px; background: #f1f5f9;
padding: 8px; border-radius: 8px; }
.card p { margin: 0; }
.thumb { aspect-ratio: 16 / 9; border-radius: 6px;
background: linear-gradient(135deg, #f59e0b, #ef4444); }
@container (width >= 300px) {
.card { grid-template-columns: 120px 1fr; }
}
</style>
<div class="page">
<main><div class="card"><div class="thumb"></div>
<p>পদ্মার ইলিশ, ১ কেজি — ৳১,৪০০</p></div></main>
<aside><div class="card"><div class="thumb"></div>
<p>পদ্মার ইলিশ, ১ কেজি — ৳১,৪০০</p></div></aside>
</div>এখন মূল কলামের কার্ড পাশাপাশি, sidebar-এর কার্ড ওপর-নিচে — একই class, একই HTML. ড্যাশ দেওয়া sidebar-এর কোণ টেনে ৩০০px-এর বেশি চওড়া করুন — ঠিক সেই মুহূর্তে sidebar-এর কার্ডও পাশাপাশি হয়ে যাবে, আর মূল কলাম সরু হতে হতে ৩০০-র নিচে নামলে তার কার্ড ওপর-নিচে চলে যাবে। চেষ্টা করুন: (১) main থেকে container-type: inline-size; মুছে দিন — মূল কলামের কার্ড আর কোনো পাত্র খুঁজে পায় না, শর্ত মিথ্যা, তাই ওপর-নিচে থেকে যায়। (২) শর্তকে (width >= 300px) থেকে (width >= 500px) করুন — কখন সাজ বদলায় দেখুন।
ব্রাউজার পাত্রটি কীভাবে খুঁজে পায়
@container শর্তটি কোন পাত্রের মাপ দেখবে? element নিজের নয় — তার পূর্বপুরুষদের মধ্যে সবচেয়ে কাছের উপযুক্ত পাত্রের। আর পাত্রে নাম দেওয়া থাকলে নাম মিলিয়ে খোঁজে। একটি বাস্তব কাঠামোয় ধাপে ধাপে দেখুন:
- .shop
container: shop / inline-size520px চওড়া — নাম মিলল - .slot
container-type: inline-size260px — পাত্র, কিন্তু নামহীন - .card
(কোনো container-type নেই)সাধারণ মোড়ক - .price
@container shop (…) { .price {…} }যার জন্য নিয়ম
- শুরু element থেকে। নিয়মটি লেখা
.price-এর জন্য। ব্রাউজার.price-এর নিজের মাপ দেখে না — দেখলে একটি চক্র তৈরি হতো (নিয়ম মাপ বদলায়, মাপ নিয়ম বদলায়…)। তাই সে ওপরের দিকে, পূর্বপুরুষদের মধ্যে খোঁজে। - সাধারণ মোড়ক বাদ।
.card-এcontainer-typeনেই, তাই সে মাপের প্রশ্নের পাত্র নয়। খোঁজ আরও ওপরে যায়। - নাম মেলে না।
.slotএকটি পাত্র, কিন্তু শর্তে লেখা আছেshop— আর.slot-এর কোনো container-name নেই। তাই সে বাদ। - নাম না দিলে কী হতো? শর্ত শুধু
@container (width >= 400px)হলে খোঁজ থামত সবচেয়ে কাছের পাত্র.slot-এ — ২৬০px, শর্ত মিথ্যা। এজন্যই বড় প্রজেক্টে নাম দেওয়া নিরাপদ: পরে কেউ মাঝখানে নতুন পাত্র ঢোকালে আপনার শর্তের অর্থ চুপচাপ বদলে যায় না। - পাত্র পাওয়া গেল।
.shop-এর নামshop, ধরনinline-size— ৫২০ ≥ ৪০০, সত্য।.price-এর নিয়ম চালু হলো। কোনো পাত্রই না মিললে শর্তটি শুধু মিথ্যা ধরা হয় — কোনো error নেই।
.shop {
container-name: shop; /* নাম */
container-type: inline-size; /* ধরন */
}
/* একই কথা shorthand-এ: */
.shop { container: shop / inline-size; }
@container shop (width >= 400px) { .price { font-size: 1.25rem; } }Container query unit — পাত্রের মাপে লেখা
পর্ব ২০-এ (একক আর রং) container query units-এর নাম শুনেছেন: cqi (পাত্রের inline-size-এর ১%), cqb (block-size-এর ১%), cqw আর cqh (চওড়া আর উচ্চতার ১%), আর cqmin/cqmax. vw যেমন পুরো জানালার শতাংশ, cqi তেমনি কাছের পাত্রের। লেখার দিক বদলালেও ঠিক থাকে বলে সাধারণত cqi ব্যবহার করুন। নিচের ব্যানারের শিরোনাম পাত্রের সাথে বড়-ছোট হয়:
<style>
body { font-family: sans-serif; }
.box { container: banner / inline-size; resize: horizontal;
overflow: auto; width: 90%; max-width: 100%;
border: 2px dashed #94a3b8; padding: 6px; }
.banner { background: #0f766e; color: #fff;
padding: 4cqi; border-radius: 8px; }
.banner h2 { margin: 0 0 8px;
font-size: clamp(1rem, 7cqi, 2.4rem); }
.banner a { display: inline-block; background: #fbbf24; color: #111;
padding: 6px 12px; border-radius: 6px; }
@container banner (width < 320px) {
.banner a { display: block; text-align: center; }
}
</style>
<div class="box">
<div class="banner">
<h2>ঈদের অফার: সব পোশাকে ২০% ছাড়</h2>
<a href="#buy">এখনই কিনুন</a>
</div>
</div>ব্যানারের কোণ টেনে সরু করুন: শিরোনাম ধীরে ধীরে ছোট হয় (পাত্রের ৭%), ভেতরের padding-ও কমে (৪%), আর পাত্র ৩২০px-এর নিচে নামলেই হলুদ বোতাম পুরো চওড়া জুড়ে নেয় — আঙুলে চাপতে সহজ। clamp(1rem, 7cqi, 2.4rem) নিশ্চিত করে লেখা কখনো ১rem-এর নিচে বা ২.৪rem-এর ওপরে যাবে না — শুধু 7cqi দিলে খুব সরু পাত্রে লেখা পড়ার অযোগ্য ছোট হয়ে যেত, আর ব্যবহারকারী browser zoom দিয়েও তা বড় করতে পারতেন না। চেষ্টা করুন: (১) clamp(...) মুছে শুধু 7cqi লিখে খুব সরু করুন — সমস্যাটা নিজের চোখে দেখুন। (২) @container banner থেকে banner শব্দটি মুছুন — এখনো কাজ করে, কারণ কাছের পাত্র এটিই।
আরও গভীরে: বহু বছর container query-কে “অসম্ভব” বলা হতো একটি চক্রের কারণে: পাত্রের চওড়া সাধারণত তার ভেতরের জিনিসের ওপর নির্ভর করে, আর ভেতরের জিনিসের সাজ যদি পাত্রের চওড়ার ওপর নির্ভর করে — তাহলে ব্রাউজার অনন্ত লুপে পড়ে। সমাধান এল CSS Containment স্পেসিফিকেশন থেকে: container-type: inline-size দিলে ব্রাউজার ওই element-এ inline-axis-এর size containment চালু করে — অর্থাৎ পাত্রের চওড়া হিসাব করার সময় ভেতরের জিনিসকে গোনাই হয় না। এর একটি পার্শ্বফল আছে: যে পাত্রের চওড়া বাইরে থেকে আসে না (যেমন display: inline-block, float, বা চওড়া না দেওয়া কোনো flex item), সে শূন্যে গুটিয়ে যেতে পারে। তাই পাত্র বানান এমন element-কে, যার চওড়া লেআউট নিজেই ঠিক করে — block element, grid-এর ঘর, বা flex: 1 দেওয়া item.
৮ · Style query আর scroll-state query — নতুন দিগন্ত, সাবধানে
মাপের প্রশ্ন ছাড়াও container-কে আরেক ধরনের প্রশ্ন করা যায়: “এই পাত্রে বসানো custom property-র মান কী?” একে বলে style query. যেমন একটি পণ্যের কার্ডে --deal: sale থাকলে দামটি লাল আর “ছাড়!” লেখা। এর জন্য container-type দিতে হয় না — প্রতিটি element আপনা-আপনি style query-র পাত্র, আর শর্তটি দেখে সবচেয়ে কাছের পূর্বপুরুষকে।
<style>
body { font-family: sans-serif; }
.list { display: flex; gap: 10px; flex-wrap: wrap; }
.product { border: 1px solid #cbd5e1; border-radius: 8px;
padding: 10px; width: 150px; }
.price { margin: 6px 0 0; }
@container style(--deal: sale) {
.price { color: #b91c1c; font-weight: bold; }
.price::before { content: "ছাড়! "; }
}
</style>
<div class="list">
<div class="product"><b>মসুর ডাল</b>
<p class="price">৳১১০</p></div>
<div class="product" style="--deal: sale"><b>সয়াবিন তেল</b>
<p class="price">৳১৬৫</p></div>
</div>Chrome, Edge বা Safari-র নতুন সংস্করণে দ্বিতীয় কার্ডের দাম লাল, সামনে “ছাড়!” — কারণ তার মোড়কে --deal: sale. প্রথম কার্ড সাধারণ। আপনার ব্রাউজার style query না বুঝলে দুটিই সাধারণ দেখাবে — কোনো error নয়, শুধু বাড়তি সাজটুকু পাবেন না। চেষ্টা করুন: (১) প্রথম কার্ডেও style="--deal: sale" যোগ করুন। (২) দ্বিতীয় কার্ডের মান sale-কে Sale করুন — মিলল না, কারণ তুলনাটি হুবহু লেখার।
Baseline অবস্থা, সাবধানে পড়ুন: custom property-র style query Chromium-ভিত্তিক ব্রাউজারে (Chrome, Edge, Opera, Samsung Internet) কয়েক বছর ধরে আছে, Safari-তেও এসেছে; কিন্তু Firefox-এ দীর্ঘদিন চালু ছিল না — তাই একে এখনো Baseline ধরে নেবেন না। সব প্রধান ব্রাউজারে আসছে, তবে ব্যবহারের দিনে Can I use বা MDN-এ বর্তমান অবস্থা দেখে নিন। আর সাধারণ property-র (যেমন style(color: red)) style query স্পেসিফিকেশনে থাকলেও এখনো কোনো ব্রাউজারে চলে না। নিরাপদ পথ: মূল চেহারা একটি সাধারণ class দিয়ে করুন (.product.sale .price), আর style query দিয়ে শুধু বাড়তি সৌন্দর্য — যাতে না-বোঝা ব্রাউজারেও তথ্য হারায় না। এটাই progressive enhancement.
আরও নতুন একটি ধরন: scroll-state query. এতে পাত্রকে জিজ্ঞেস করা যায় তার স্ক্রল-অবস্থা — একটি sticky header ওপরে আটকে গেছে কি না (পর্ব ২৪), বা একটি স্ক্রলের বাক্স আরও স্ক্রল করা যায় কি না।
header {
position: sticky; top: 0;
container-type: scroll-state;
}
@container scroll-state(stuck: top) {
header > nav { box-shadow: 0 2px 8px rgb(0 0 0 / .2); }
}এটি এখন শুধু Chromium-ভিত্তিক ব্রাউজারে আছে, Baseline থেকে অনেক দূরে। শেখার জন্য জেনে রাখুন, কিন্তু কাজের সাইটে শুধু “থাকলে ভালো” ধরনের ছোট সাজে ব্যবহার করুন। স্ক্রলের অন্যান্য ইফেক্ট পর্ব ৪২-এ, আর ব্রাউজার-সাপোর্ট সামলানোর পুরো কৌশল (@supports, fallback) পর্ব ৪৮-এ।
৯ · কোনটি কখন — আর JavaScript-এর matchMedia()
এখন হাতে তিনটি হাতিয়ার: query ছাড়াই নমনীয় লেআউট (intrinsic layout — flex-wrap, auto-fit, clamp(); পর্ব ২৮), media query আর container query. বেছে নেওয়ার একটি সহজ নিয়ম:
| প্রশ্নটা কী | হাতিয়ার | উদাহরণ |
|---|---|---|
| “যত জায়গা, তত কলাম” — কোনো নির্দিষ্ট মাপে সিদ্ধান্ত লাগে না | Query ছাড়া (intrinsic) | কার্ডের grid, ট্যাগের সারি |
| পুরো পাতার কাঠামো — মেনু বোতাম, sidebar দেখানো | @media (width) | ফোনে হ্যামবার্গার মেনু |
| ব্যবহারকারীর পছন্দ বা যন্ত্র | @media (prefers-*, hover, pointer) | dark mode, কম অ্যানিমেশন |
| ছাপা | @media print | রসিদ, স্টেটমেন্ট |
| বারবার ব্যবহার হওয়া component — নিজের জায়গা বুঝে সাজ | @container | পণ্যের কার্ড, উইজেট, CMS ব্লক |
| component-এর “ধরন” (variant) অনুযায়ী ভেতরের সাজ | সাধারণ class; বাড়তি হিসেবে style query | অফারের কার্ড, সতর্কবার্তার বাক্স |
একটি বাস্তব কথা: container query দিয়ে পাতার কাঠামো বানানো কঠিন, কারণ সবচেয়ে বাইরের পাত্রের মাপ আসলে viewport-ই। আবার media query দিয়ে component বানালে সেটি পুনর্ব্যবহারযোগ্য থাকে না। দুটি প্রতিদ্বন্দ্বী নয়, একসাথে কাজ করে।
JavaScript থেকে একই প্রশ্ন — matchMedia()
কখনো কখনো শর্তটা JavaScript-এও জানা দরকার — যেমন কম অ্যানিমেশন চাইলে একটি স্লাইডশো নিজে থেকে না ঘোরানো, বা চওড়া পর্দায় একটি মানচিত্র লোড করা। এর জন্য matchMedia() CSS-এর হুবহু একই query নেয় (container query-র জন্য এমন কোনো সরাসরি ফাংশন নেই):
(function m() {
if (!document.body.offsetWidth) return setTimeout(m, 50);
const wide = matchMedia('(width >= 500px)');
const calm = matchMedia('(prefers-reduced-motion: reduce)');
console.log('preview width:', innerWidth);
console.log('(width >= 500px) matches:', wide.matches);
console.log('reduced motion wanted:', calm.matches);
wide.addEventListener('change', (e) => {
console.log('changed! now wide:', e.matches);
});
})();কম্পিউটারে সাধারণত দেখবেন চওড়া ৫০০-র বেশি আর matches: true; কম অ্যানিমেশনের সেটিং চালু না থাকলে false. matches এই মুহূর্তের উত্তর, আর change ইভেন্ট শর্তের ফল বদলালেই একবার জানায় — প্রতিটি pixel-এ নয়, যেমনটা resize ইভেন্ট করে। তাই এটি অনেক সাশ্রয়ী। চেষ্টা করুন: 500px-কে 5000px করে আবার চালান। পুরোনো কোডে addListener() দেখবেন — সেটি পুরোনো রূপ, এখন addEventListener('change', …) লিখুন। ইভেন্ট নিয়ে বিস্তারিত JavaScript কোর্সের ইভেন্ট পর্বে।
পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:
- Media query আর container query-র পার্থক্য কী? — media query viewport আর ডিভাইস/ব্যবহারকারীর অবস্থা দেখে; container query একটি পূর্বপুরুষ পাত্রের মাপ বা style দেখে।
- mobile-first মানে media query-তে কী? — শর্ত ছাড়া ছোট পর্দার CSS, তারপর
min-widthবাwidth >=দিয়ে বড় পর্দার নিয়ম যোগ। @mediaকি specificity বাড়ায়? — না; একই specificity হলে পরে লেখা নিয়ম জেতে।- container query কাজ করছে না — প্রথমে কী দেখবেন? — কোনো পূর্বপুরুষে
container-typeআছে কি না, আর নাম দিলে নামের বানান মিলছে কি না। - ছাপার সময় লিংকের ঠিকানা কীভাবে দেখাবেন? —
@media print { a[href^="http"]::after { content: " (" attr(href) ")"; } }.
১০ · AI-এর যুগে — রেসপন্সিভ কোড AI-কে দিলে কী দেখবেন
“এটাকে রেসপন্সিভ করে দাও” — AI-কে এই এক লাইন বললে সে প্রায় সবসময় তিন-চারটি @media ব্লক জুড়ে দেয়, প্রায়ই max-width: 768px আর max-width: 480px দিয়ে। কাজ চলে, কিন্তু AI যে কোড থেকে শিখেছে তার বড় অংশ বহু বছরের পুরোনো, আর সে আপনার কার্ডটি sidebar-এ বসিয়ে বা Windows-এর contrast থিমে খুলে কখনো দেখে না।
AI সাধারণত যেসব ভুল করে
| AI-এর ভুল | আপনি যা দেখবেন | ঠিক উপায় |
|---|---|---|
ডিভাইসের নামে breakpoint (iPhone, iPad) আর desktop-first max-width | মাঝের মাপে অদ্ভুত চেহারা, অনেক উল্টো নিয়ম | কনটেন্ট-ভিত্তিক breakpoint, mobile-first width >= |
max-width: 767px আর min-width: 768px | zoom করলে মাঝের ভগ্নাংশে কোনো সাজই খাটে না | range syntax: < 768px / >= 768px |
| component-এর সাজ media query দিয়ে | sidebar-এ কার্ড ভেঙে যায় | container-type + @container |
| media query-র ব্লক সাধারণ নিয়মের আগে লেখা | শর্ত সত্য, তবু কিছু বদলায় না | query-র নিয়ম পরে লিখুন (specificity সমান) |
পাত্র ঘোষণা ছাড়াই @container, বা পাত্রকেই স্টাইল করার চেষ্টা | কিছুই হয় না | পূর্বপুরুষে container-type, স্টাইল দিন ভেতরের element-কে |
| style query বা scroll-state-কে “সব ব্রাউজারে চলে” বলা | কিছু ব্রাউজারে (বা পুরোনো সংস্করণে) সাজ নেই | Baseline অবস্থা যাচাই, সাধারণ class-এ fallback |
শুধু hover-এ খোলা মেনু, prefers-reduced-motion উপেক্ষা | ফোনে মেনু খোলে না, অ্যানিমেশন থামানো যায় না | ক্লিক/focus-এ খোলা, অ্যানিমেশন no-preference-এর ভেতরে |
AI-কে শর্তগুলো আগেই বলে দিন, আর সিদ্ধান্তের কারণ লিখতে বলুন — কারণ লিখতে গিয়ে AI প্রায়ই নিজের ভুল নিজেই ধরে ফেলে।
AI-এর দেওয়া রেসপন্সিভ কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে CSS লেখক থেকে রেসপন্সিভ-চিন্তক বানাবে।
প্র ০১রেসপন্সিভ ডিজাইনের আগে বড় সাইটগুলো মোবাইলের জন্য আলাদা “m.” সাইট বানাত। একটি পাতা আর media query কেন শেষ পর্যন্ত জিতল?
২০০৭ সালে iPhone আসার পর হঠাৎ মানুষ ছোট পর্দায় “পূর্ণ” ওয়েব দেখতে শুরু করল। তখনকার সহজ সমাধান ছিল দুটি আলাদা সাইট: ডেস্কটপের জন্য www., ফোনের জন্য m.. সার্ভার ব্রাউজারের পরিচয়-লেখা (user agent) দেখে ঠিক করত কাকে কোন সাইটে পাঠাবে।
- দ্বিগুণ খরচ: দুটি সাইট মানে দুই সেট টেমপ্লেট, দুই সেট বাগ। নতুন ফিচার প্রায়ই মোবাইল সাইটে আসত দেরিতে, বা আসতই না।
- ভুল অনুমান: user agent দেখে ডিভাইস চেনা বারবার ভুল করত। ট্যাবলেট কোন দলে? বড় ফোন? নতুন কোনো ব্রাউজার নিজের পরিচয় অন্যভাবে লিখলে সে ভুল সাইট পেত।
- শেয়ারের সমস্যা: কেউ ফোন থেকে
m.লিংক শেয়ার করলে বন্ধু ডেস্কটপে খুলে পেতেন ছোট, ফাঁকা একটি পাতা। একই খবরের দুটি ঠিকানা সার্চ ইঞ্জিনের কাছেও বিভ্রান্তিকর ছিল।
২০১০ সালে Ethan Marcotte দেখালেন অন্য পথ: একটিই HTML, নমনীয় grid আর ছবি, আর media query দিয়ে মাপ অনুযায়ী সাজ বদল। এখানে সার্ভারকে কিছু অনুমান করতে হয় না — ব্রাউজার নিজেই নিজের পর্দার মাপ জানে, আর সঙ্গে সঙ্গে ঠিক নিয়ম বেছে নেয়। জানালা ছোট-বড় করলেও সাথে সাথে বদলায়। পরে Google-এর মতো সার্চ ইঞ্জিন মোবাইল-বান্ধব পাতাকে অগ্রাধিকার দিতে শুরু করে আর রেসপন্সিভ ডিজাইনকেই সুপারিশকৃত পদ্ধতি বলে — ফলে বেশিরভাগ বড় সাইট আলাদা m. সাইট ছেড়ে দেয়।
তবে একটি পাতারও দাম আছে: ফোনকেও পুরো পাতার HTML আর CSS নামাতে হয়। এজন্যই mobile-first ভাবনা (ছোট পর্দার CSS আগে, বাকিটা যোগ), রেসপন্সিভ ছবি (পর্ব ৩২) আর পারফরম্যান্সের কৌশল (পর্ব ৪৯) এত জরুরি। ঢাকার বাইরে ধীর মোবাইল ডেটায় যিনি পাতা খুলছেন, তাঁর কাছে এক মেগাবাইট বাড়তি CSS-ও অনেক।
মূল উপলব্ধি: “ডিভাইস অনুমান করে আলাদা জিনিস পাঠানো”-র চেয়ে “একটি জিনিস পাঠিয়ে ব্রাউজারকে নিজের অবস্থা বুঝে সাজাতে দেওয়া” বেশি টেকসই — কারণ ভবিষ্যতে কোন ডিভাইস আসবে, তা কেউ আগে জানে না।
প্র ০২Container query বহু বছর ধরে “অসম্ভব” বলা হতো। তাহলে শেষ পর্যন্ত এটি কীভাবে সম্ভব হলো, আর এর দাম কী?
media query আসার কিছুদিন পর থেকেই ডেভেলপাররা চাইছিলেন “element query” — একটি element-এর নিজের মাপ দেখে নিয়ম। কিন্তু ব্রাউজার নির্মাতারা বারবার একটি মৌলিক সমস্যার কথা বলতেন: চক্রাকার নির্ভরতা।
CSS-এ সাধারণত বাবার মাপ সন্তানের ওপর নির্ভর করে (যেমন একটি বাক্স তার লেখার সমান চওড়া), আর সন্তানের মাপ বাবার ওপর (যেমন width: 50%). এবার ধরুন নিয়ম লিখলেন: “বাক্স ৪০০px-এর বেশি হলে ভেতরের লেখা ছোট করো।” লেখা ছোট হলো → বাক্স সরু হলো → ৪০০-র নিচে → নিয়ম বন্ধ → লেখা বড় → বাক্স চওড়া → নিয়ম চালু… ব্রাউজার অনন্তকাল দোল খেত। ঠিক এই কারণে প্রথম দিকের প্রস্তাবগুলো বাতিল হয়।
সমাধান এল দুটি ধারণা মিলিয়ে। প্রথমত, CSS Containment: ব্রাউজারকে একটি প্রতিশ্রুতি দেওয়া যে “এই element-এর মাপ তার ভেতরের জিনিসের ওপর নির্ভর করবে না।” দ্বিতীয়ত, প্রশ্ন করা হবে শুধু পূর্বপুরুষকে, নিজেকে নয়। পাত্রের মাপ বাইরের লেআউট থেকে ঠিক হয়, ভেতরের বদল সেখানে ফিরে যায় না — চক্র ভেঙে গেল। এই নকশার স্পেসিফিকেশন লেখার কাজে Miriam Suzanne-সহ কয়েকজনের বড় ভূমিকা ছিল। ২০২২ সালে Chrome আর Safari, আর ২০২৩ সালের শুরুতে Firefox এটি চালু করে।
দাম কী? পাত্র আর তার ভেতরের জিনিস দেখে বড় হতে পারে না। container-type: inline-size দেওয়া একটি inline-block বা চওড়া না দেওয়া flex item শূন্যে গুটিয়ে যেতে পারে। container-type: size আরও কড়া — উচ্চতাও ভেতর থেকে আসে না, তাই উচ্চতা নিজে না দিলে বাক্স সমতল হয়ে যায়। এজন্যই প্রায় সব ক্ষেত্রে inline-size ব্যবহার করা হয়।
মূল উপলব্ধি: “অসম্ভব” অনেক সময় মানে “এখনকার নিয়মে অসম্ভব”। একটি সীমাবদ্ধতা (containment) মেনে নিলে নতুন ক্ষমতা খুলে যায় — প্রকৌশলের বহু সমাধান এভাবেই আসে।
প্র ০৩Breakpoint কি জনপ্রিয় ডিভাইসের মাপ (360, 768, 1024) দেখে বসানো উচিত, নাকি অন্য কোনোভাবে? em দিয়ে media query লেখার পরামর্শ কেন শোনা যায়?
বহু টিউটোরিয়াল আর framework “ফোন ৪৮০, ট্যাবলেট ৭৬৮, ডেস্কটপ ১০২৪” — এমন একটি তালিকা দেয়। শুরুর দিকে এটা যুক্তিসংগত ছিল, যখন ডিভাইসের মাপ হাতে গোনা কয়েকটি ছিল। আজ কয়েক হাজার আলাদা পর্দা: ৩৬০ থেকে ৪৩০px-এর অসংখ্য অ্যান্ড্রয়েড ফোন, ভাঁজ করা ফোন, ট্যাবলেট খাড়া ও শোয়ানো, ল্যাপটপে অর্ধেক জানালায় খোলা ব্রাউজার। ডিভাইস ধরে breakpoint বানালে পরের বছরের নতুন ফোনে আবার ঠিক করতে হয়।
পেশাদার পরামর্শ হলো কনটেন্ট-ভিত্তিক breakpoint: সবচেয়ে সরু পর্দায় পাতা বানান, তারপর জানালা ধীরে ধীরে চওড়া করুন। যেখানে লেখার লাইন অস্বস্তিকর লম্বা হয়ে যায়, বা কার্ডের পাশে অনেক ফাঁকা জায়গা দেখা যায় — ঠিক সেখানে breakpoint বসান। প্রতিটি component-এর আলাদা “ভাঙার বিন্দু” থাকতে পারে, আর সেজন্যই container query এই চিন্তার স্বাভাবিক পরবর্তী ধাপ।
em কেন? media query-র ভেতরে em বা rem মানে ব্রাউজারের প্রাথমিক ফন্ট-মাপ (সাধারণত 16px) — পাতার CSS-এ দেওয়া ফন্ট-মাপ নয়। যিনি চোখের সুবিধার জন্য ব্রাউজারের সেটিংসে ডিফল্ট ফন্ট বড় করে রেখেছেন, তাঁর জন্য (width >= 40em) আপনা-আপনি দেরিতে সত্য হয়: লেখা বড়, তাই চওড়া লেআউট আসে আরও বড় পর্দায় — ঠিক যেমন হওয়া উচিত। px-এর breakpoint এই মানুষটির কথা জানে না। অন্যদিকে browser zoom (Ctrl +) করলে viewport-এর CSS চওড়াই কমে যায়, তাই px আর em দুটিতেই query বদলায়। একসময় কিছু ব্রাউজারে em-ভিত্তিক media query-র zoom আচরণে অসংগতি ছিল, যা এখন মূলত মিটে গেছে।
মূল উপলব্ধি: breakpoint ডিভাইসের বৈশিষ্ট্য নয়, আপনার কনটেন্টের বৈশিষ্ট্য। আর মাপের একক বাছাইও অ্যাক্সেসিবিলিটির সিদ্ধান্ত — লেখা বড় করা মানুষটিকে মাথায় রেখে।
প্র ০৪prefers-reduced-motion-এর মতো feature কোথা থেকে এল? আর ব্যবহারকারীর এত পছন্দ ওয়েবসাইটকে জানানো কি গোপনীয়তার ঝুঁকি নয়?
২০১৩ সালে Apple iOS 7-এ বড় ধরনের নড়াচড়ার নকশা আনে — হোম স্ক্রিনের parallax (ফোন নাড়ালে পটভূমি সরে), অ্যাপ খোলার সময় জুম। অনেক ব্যবহারকারী জানান, এতে তাঁদের মাথা ঘোরে, বমিভাব হয়। এঁদের অনেকের vestibular (ভেতরের কান ও ভারসাম্য-সংক্রান্ত) সমস্যা ছিল। Apple তখন “Reduce Motion” নামে একটি সেটিং যোগ করে। পরে অন্য অপারেটিং সিস্টেমেও একই ধরনের সেটিং আসে। কিন্তু ওয়েবসাইট এই সেটিং জানত না — ফলে অ্যাপে নড়াচড়া বন্ধ থাকলেও ওয়েব পাতা আগের মতোই লাফাত। সেই ফাঁক পূরণ করতে WebKit (Safari) প্রায় ২০১৭ সালে প্রথম prefers-reduced-motion চালু করে, পরে এটি Media Queries Level 5 স্পেসিফিকেশনে যায় আর সব প্রধান ব্রাউজারে আসে। prefers-color-scheme-ও একইভাবে অপারেটিং সিস্টেমের dark mode থেকে ওয়েবে এসেছে।
গোপনীয়তার প্রশ্নটি একদম যৌক্তিক। কুকি ছাড়াই একজন ব্যবহারকারীকে চেনার একটি কৌশল হলো browser fingerprinting: পর্দার মাপ, ফন্ট, সময়-অঞ্চল, আর এমন অনেক ছোট তথ্য মিলিয়ে একটি প্রায়-অনন্য “আঙুলের ছাপ”। প্রতিটি নতুন media feature সেই ছাপে আরও একটু তথ্য যোগ করে — বিশেষ করে বিরল সেটিং (যেমন forced colors চালু) মানুষটিকে ভিড় থেকে আলাদা করে ফেলে, আর পরোক্ষে তাঁর কোনো প্রতিবন্ধিতার ইঙ্গিতও দিতে পারে।
এজন্যই স্পেসিফিকেশন লেখকেরা প্রতিটি নতুন feature-এ গোপনীয়তার আলোচনা রাখেন, মানগুলো মোটা দাগে রাখেন (যেমন শুধু reduce / no-preference, আসল কোনো সংখ্যা নয়), আর কিছু ব্রাউজার গোপনীয়তা-মোডে কিছু মান ইচ্ছা করে সাধারণ করে দেয়। ডেভেলপার হিসেবে আপনার দায়িত্ব: এই তথ্য শুধু পাতা সাজাতে ব্যবহার করা — কখনো analytics-এ পাঠিয়ে মানুষ চেনার কাজে নয়।
মূল উপলব্ধি: অ্যাক্সেসিবিলিটি আর গোপনীয়তা কখনো কখনো বিপরীত দিকে টানে। ভালো নকশা দুটোকেই সম্মান করে — প্রয়োজনের ন্যূনতম তথ্য, আর সেটি শুধু ব্যবহারকারীর উপকারে।
প্র ০৫Container query আসার পর কি media query-র দিন শেষ? একটি বড় design system-এ কোনটি কোথায় থাকা উচিত?
না — বরং দুটির কাজ এখন আরও পরিষ্কারভাবে ভাগ হয়েছে। Bootstrap-এর মতো পুরোনো framework-এ সবকিছু viewport-এর breakpoint দিয়ে চলত (col-md-6 মানে “মাঝারি পর্দা থেকে অর্ধেক চওড়া”)। এতে একটি সমস্যা ছিল: component-কে জানতে হতো পাতায় সে কোথায় বসবে। একই কার্ড sidebar-এ বসাতে আলাদা variant লাগত, আর design system বড় হতে হতে variant-এর সংখ্যা ফুলে উঠত।
আধুনিক design system-এ একটি স্তর-ভাগ দেখা যায়:
- পাতার স্তর (media query): পুরো পাতার কাঠামো — sidebar দেখাবে কি না, মেনু বোতাম না পুরো মেনু, কলামের সংখ্যা। এগুলোর সবচেয়ে বাইরের পাত্র আসলে viewport-ই, তাই media query-ই স্বাভাবিক।
- component-এর স্তর (container query): কার্ড, উইজেট, ফর্মের অংশ — যা নানা জায়গায় বসে। component নিজের মোড়কের মাপ দেখে সাজে, পাতার কথা জানে না। এতে component সত্যিকারের পুনর্ব্যবহারযোগ্য হয়।
- ব্যবহারকারী আর মাধ্যমের স্তর (media query): dark mode, কম অ্যানিমেশন, contrast, hover/pointer, print — এগুলো কোনো পাত্রের বৈশিষ্ট্য নয়, মানুষ আর ডিভাইসের। container query এখানে কোনো বিকল্পই নয়।
- তার নিচে intrinsic layout: যেখানে কোনো নির্দিষ্ট মাপে সিদ্ধান্ত লাগে না, সেখানে query-ই লাগে না —
flex-wrap,auto-fit,clamp(). কম query মানে কম কোড, কম “মাঝের মাপে ভাঙা” বাগ।
একটি সতর্কতাও আছে: সব জায়গায় container-type বসিয়ে দেওয়া ঠিক নয়, কারণ প্রতিটি পাত্রে containment-এর সীমাবদ্ধতা আসে (ভেতর দেখে বড় হতে না পারা)। শুধু যেখানে সত্যিই component-এর মাপ দরকার, সেখানে পাত্র বানান।
মূল উপলব্ধি: প্রশ্নটা “কোন query ভালো” নয়, “এই সিদ্ধান্তটা কার তথ্যের ওপর নির্ভর করে” — viewport-এর, পাত্রের, না মানুষের? উত্তরটাই হাতিয়ার বলে দেয়।
অনুশীলন
বেশিরভাগ কাজে কোড লিখতে হবে না — শর্ত পড়ে হিসাব করা আর DevTools-এ দেখাই আসল কাজ।
-
কোনটি সত্য? একটি ফোন খাড়া করে ধরা, viewport 390px × 844px, মাউস নেই। নিচের প্রতিটি শর্ত সত্য না মিথ্যা? (ক)
(width >= 400px)(খ)(orientation: portrait)(গ)(width < 600px) and (hover: hover)(ঘ)(width >= 1000px), (pointer: coarse)(ঙ)not print(ক) মিথ্যা — ৩৯০ < ৪০০. (খ) সত্য — উচ্চতা চওড়ার চেয়ে বেশি। (গ) মিথ্যা — প্রথম অংশ সত্য, কিন্তু টাচস্ক্রিনে
hover: none, আরand-এ সবগুলো সত্য হতে হয়। (ঘ) সত্য — কমা মানে “অথবা”; প্রথম অংশ মিথ্যা হলেও আঙুলের ইনপুট মানেpointer: coarseসত্য। (ঙ) সত্য — পর্দায় দেখা হচ্ছে, ছাপা নয়। -
বাগ খুঁজুন: একজন লিখেছেন —
.menu { display: flex; }তার আগে@media (width < 600px) { .menu { display: none; } }. ফোনে মেনু লুকাচ্ছে না। কেন? আরmax-width: 599px/min-width: 600pxজোড়ায় কী সমস্যা লুকিয়ে থাকে?দুটি নিয়মের selector একই (
.menu), তাই specificity সমান —@mediaকিছু যোগ করে না। সমান হলে উৎসের ক্রমে পরে লেখা নিয়ম জেতে; এখানেdisplay: flexপরে, তাই ফোনেও মেনু দেখায়। সমাধান: media query-র ব্লকটি সাধারণ নিয়মের পরে সরান।max-width: 599px/min-width: 600px-এর মাঝে একটি ফাঁক আছে: zoom বা উচ্চ-ঘনত্বের পর্দায় viewport 599.5px-এর মতো ভগ্নাংশ হলে দুটির কোনোটিই সত্য নয়। লিখুন(width < 600px)আর(width >= 600px). -
DevTools-এ দেখুন: যেকোনো বড় সংবাদ বা ই-কমার্স সাইট খুলে F12 চাপুন। (ক) Device toolbar (Ctrl+Shift+M) চালু করে চওড়া ধীরে ধীরে বদলান — কোন কোন মাপে লেআউট লাফ দেয়? (খ) Rendering প্যানেলে “Emulate CSS media type: print” বেছে নিন — সাইটটির কি print stylesheet আছে? (গ) Elements প্যানেলে কোনো element-এর পাশে
containerব্যাজ পান কি না খুঁজুন।(ক) সাধারণত দুই-চারটি মাপে বড় বদল দেখবেন — সেগুলোই সাইটের breakpoint. Styles প্যানেলে
@mediaলেখা নিয়মের ওপর মাউস রাখলে শর্তটিও দেখা যায়। Chromium-এর device toolbar-এ ওপরের রঙিন দাগগুলোতে ক্লিক করলে সরাসরি সেই breakpoint-এ যাওয়া যায়।(খ) অনেক সাইটে print-এর আলাদা সাজ নেই — মেনু, বিজ্ঞাপন সব ছাপা হয়। যেসব সাইটে আছে (প্রায়ই রেসিপি, ডকুমেন্টেশন বা সংবাদ সাইট), সেখানে মেনু উধাও আর লেখা সাদা-কালো হবে।
(গ) নতুন সাইটে প্রায়ই পাবেন। ব্যাজে ক্লিক করলে পাত্রের মাপ দেখায়, আর Styles প্যানেলে
@containerনিয়মের পাশে কোন পাত্রের সাথে মিলছে তা-ও দেখা যায়। -
কোড লিখুন: একটি নোটিশ বাক্স বানান (একটি আইকন আর এক লাইন লেখা), যা পাত্র ২৮০px-এর কম হলে আইকন ওপরে আর লেখা নিচে, তার বেশি হলে পাশাপাশি দেখাক — container query দিয়ে, আর পাত্রের নাম দিন
notice.মোড়কে নাম আর ধরন, ভেতরে mobile-first সাজ, তারপর নামসহ শর্ত। কোডটি কপি করে ওপরের যেকোনো HTML লাইভ এডিটরে বসিয়ে কোণ টেনে পরীক্ষা করুন:
<style> body { font-family: sans-serif; } .wrap { container: notice / inline-size; resize: horizontal; overflow: auto; width: 90%; max-width: 100%; border: 2px dashed #94a3b8; padding: 6px; } .notice { display: grid; gap: 6px; justify-items: center; background: #fef3c7; padding: 10px; border-radius: 8px; } .notice p { margin: 0; } .icon { font-size: 2rem; } @container notice (width >= 280px) { .notice { grid-template-columns: auto 1fr; justify-items: start; align-items: center; } } </style> <div class="wrap"> <div class="notice"><span class="icon" aria-hidden="true">⚠️</span> <p>আগামীকাল সকাল ১০টা থেকে ১২টা পর্যন্ত সার্ভারের কাজ চলবে।</p> </div> </div>চওড়া অবস্থায় আইকন বাঁয়ে, লেখা ডানে; কোণ টেনে ২৮০px-এর নিচে আনলে আইকন ওপরে, মাঝখানে। খেয়াল করুন, স্টাইল দেওয়া হয়েছে ভেতরের
.notice-কে — পাত্র.wrapনিজে নিজের শর্তে সাজ বদলাতে পারে না। চেষ্টা করুন:280px-কে450pxকরে দেখুন।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমContainer query দিয়ে একটি রেসপন্সিভ কার্ড বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- কার্ডের ঠিক বাইরের মোড়কটি চিহ্নিত করা, যার চওড়া লেআউট থেকে আসে
- মোড়কে
container: card / inline-sizeদেওয়া - শর্ত ছাড়া সরু জায়গার (ওপর-নিচে) সাজ লেখা
@container card (width >= 400px)-এর ভেতরে চওড়া জায়গার সাজ লেখা- কার্ডটি সরু sidebar আর চওড়া কলাম — দুই জায়গায় বসিয়ে পরীক্ষা করা
CSS-এ @media (400px <= width <= 800px)-এর সমান অর্থের পুরোনো লেখা কোনটি?
- (min-width: 400px), (max-width: 800px)
- (min-width: 400px) and (max-width: 800px)
- (min-width: 800px) and (max-width: 400px)
- (width: between 400px 800px)
@media (width < 400px), print { … } — কমাটির অর্থ কী?
- দুটি শর্তই একসাথে সত্য হতে হবে
- শুধু প্রথম শর্তটি দেখা হবে, print উপেক্ষিত
- যেকোনো একটি সত্য হলেই চলবে — সরু পর্দা অথবা ছাপা
- এটি অবৈধ, পুরো ব্লক বাতিল
টাচস্ক্রিনের মতো মোটা দাগের ইনপুট (আঙুল) প্রধান যন্ত্র কি না, কোন media feature দিয়ে জানা যায়?
- (orientation: portrait)
- (hover: hover)
- (touch: true)
- (pointer: coarse)
একটি @container (width >= 400px) নিয়ম কোনো কাজ করছে না। সবচেয়ে সম্ভাব্য কারণ কোনটি?
- কোনো পূর্বপুরুষ element-এ
container-typeদেওয়া হয়নি - শর্তে container-এর নাম লেখা হয়নি
- container query-তে px একক চলে না
- পাতায় viewport meta tag নেই
CSS-এ .btn { color: blue; } লেখার আগে আছে @media (width < 600px) { .btn { color: red; } }. ৩৯০px চওড়া ফোনে বোতামের রং কী?
- লাল, কারণ media query-র নিয়মের specificity বেশি
- ব্রাউজারের ডিফল্ট রং, কারণ দুই নিয়মে সংঘাত
- নীল, কারণ specificity সমান আর নীলের নিয়ম পরে লেখা
- লাল, কারণ শর্ত সত্য হলে media query সবসময় জেতে
২০২৫–২৬ সালের হিসাবে নিচের কোনটি এখনো Baseline নয়?
- container-type: inline-size দিয়ে @container size query
- media query-র range syntax (width >= 600px)
- prefers-reduced-motion media feature
- @container scroll-state(stuck: top)
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
@media= media type (screen/print/all) + media feature (বন্ধনীতে); range syntax(width >= 600px)এখন Widely available, আর সীমানার ১px-এর ফাঁক মেটায়।and= সবগুলো, কমা/or= যেকোনো একটি,not= উল্টো,only= আজ অকেজো।@mediaspecificity বাড়ায় না — query-র নিয়ম পরে লিখুন।- মানুষ আর যন্ত্র:
prefers-color-scheme,prefers-reduced-motion,prefers-contrast,forced-colors,hover,pointer; ছাপায়@media print+attr(href). - Container query: পূর্বপুরুষে
container-type: inline-size(চাইলে নাম), ভেতরে@container (width >= …); পাত্র নিজেকে প্রশ্ন করে না;cqiপাত্রের ১%,clamp()-এ মুড়ে দিন। - Style query সব ব্রাউজারে নয়, scroll-state শুধু Chromium-এ — দুটিই Baseline নয়, শুধু বাড়তি সাজে। পাতা আর মানুষ → media, component → container, JS-এ
matchMedia().
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৩২ · রেসপন্সিভ ছবি — srcset, sizes, picture আর lazy loadingপরবর্তী পর্বপর্দার মাপ আর ঘনত্ব দেখে ঠিক ছবিটি পাঠানো।
- পর্ব ৩০ · রেসপন্সিভ ডিজাইন — viewport আর মোবাইল-ফার্স্ট ভাবনাঝালিয়ে নিনviewport meta tag আর মোবাইল-ফার্স্ট চিন্তা — এই পর্বের ভিত্তি।
- HTML ও CSS · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- JavaScript · ইভেন্টপাশের ট্র্যাক
matchMedia()-এরchangeইভেন্টসহ ব্রাউজারের ইভেন্ট কীভাবে কাজ করে।