স্টিকি হেডার হল একটি সাধারণ UI প্যাটার্ন, কিন্তু পাঠযোগ্যতা বজায় রাখতে বা স্পেস বাঁচাতে "স্টাক" হয়ে গেলে প্রায়শই তাদের চেহারা পরিবর্তন করতে হয়। প্রথাগত সমাধানের জন্য জাভাস্ক্রিপ্ট স্ক্রল লিসেনার প্রয়োজন হয়, যা পারফর্ম্যান্স সংক্রান্ত সমস্যা সৃষ্টি করতে পারে। আধুনিক CSS আপনাকে স্ক্রল স্টেট কোয়েরি ব্যবহার করে নেটিভভাবে এটি ম্যানেজ করার সুবিধা দেয়।
প্রয়োগ করার বিভিন্ন ধাপ
১. স্টিকি কন্টেনার তৈরি করুন
আপনার একটি কন্টেনার প্রয়োজন যা স্টিকি এলিমেন্ট এবং স্ক্রল স্টেট কোয়েরির জন্য কন্টেনার হিসেবে কাজ করবে।
<!-- Wrap in a section to define the containment area for the sticky element -->
<div class="section">
<div class="sticky-container">
<div class="sticky-header">
Section Header
</div>
</div>
<div class="content">
<!-- Content goes here -->
</div>
</div>
২. স্টিকি আচরণ ও কন্টেনারের ধরনের জন্য CSS প্রয়োগ করুন
position: sticky এলিমেন্টে container-type: scroll-state সেট করুন, এর স্ক্রল করা যায় এমন অ্যানসেস্টরে নয়। সবসময় একটি container-name উল্লেখ করুন, কারণ এইসব কোয়েরি নেস্ট করা হতে পারে এবং নামহীন কন্টেনার একে অপরের সাথে সংঘর্ষে লিপ্ত হবে। এছাড়াও, আপনি সাইজ কোয়েরির সাথে এটি যোগ করতে পারেন, যেমন, container-type: scroll-state inline-size।
.sticky-container {
position: sticky;
top: 0;
/* MANDATORY: Always specify a container name to avoid collisions if CQs are nested */
container-type: scroll-state;
container-name: section-header;
z-index: 10;
}
.sticky-header {
/* Base styles for the header */
background: #f0f0f0;
padding: 20px;
transition: background-color 0.3s, box-shadow 0.3s;
}
গুরুত্বপূর্ণ: স্ক্রল-স্টেট কন্টেনারের ডিসেন্ডেন্ট স্ক্রল স্টেট কোয়েরি করে। @container কোয়েরির স্টাইল .sticky-container-এর ক্ষেত্রে প্রযোজ্য হবে না, তবে এর চাইল্ডের (যেমন .sticky-header) ক্ষেত্রে প্রযোজ্য হবে। আপনি কন্টেনার এলিমেন্টকে এর নিজস্ব স্ক্রল-স্টেট কোয়েরি দিয়ে স্টাইল করতে পারবেন না।
৩. আটকে থাকা স্ট্যাটাস টার্গেট করুন
হেডার আটকে গেলে স্টাইল প্রয়োগ করতে @container scroll-state(...) কোয়েরি ব্যবহার করুন।
/* Apply styles when the named container is stuck at the top */
@container section-header scroll-state(stuck: top) {
.sticky-header {
background: #0056b3;
color: white;
box-shadow: 0 4px 6px rgb(0 0 0 / 0.15);
}
}
পরামর্শ: আপনি stuck: inset-block-start বা stuck: inset-inline-start-এর মতো লজিক্যাল প্রপার্টিও ব্যবহার করতে পারেন যাতে ফিজিক্যাল এজ নয় বরং ফ্লো-রিলেটিভ এজ কোয়েরি করার মাধ্যমে ইন্টারন্যাশনালাইজেশন (i18n) এবং ডান-থেকে-বাঁদিকে (RTL) লেআউট আরও ভালোভাবে কাজ করে।
ডাইমেনশন পরিবর্তন সংক্রান্ত নোট
স্ক্রল অ্যাঙ্করিং বন্ধ না করে, আটকে থাকার সময় লেআউট-প্রভাবিত প্রপার্টি (উচ্চতা, প্যাডিং, ফন্ট-সাইজ) পরিবর্তন করলে ভিজ্যুয়াল ফ্লিকারিং হতে পারে। স্টিকি এলিমেন্টের নিচে ইন-ফ্লো কন্টেন্টে স্ক্রল অ্যাঙ্করিং লেআউট পরিবর্তনের জন্য ক্ষতিপূরণ দিতে স্ক্রল অফসেট অ্যাডজাস্ট করে, যা এলিমেন্টটিকে তার আটকে থাকা পজিশন থেকে বের করে দেয় এবং একটি অসিলেশন ট্রিগার করে।
এটি এড়াতে স্টিকি এলিমেন্টের মূল এলিমেন্টে স্ক্রল অ্যাঙ্করিং বন্ধ করুন:
.section {
overflow-anchor: none;
}
overflow-anchor: none-কে position: sticky এলিমেন্টের ডাইরেক্ট পেরেন্টে প্রয়োগ করুন, :root বা body-এ নয়, যা সম্পূর্ণ পৃষ্ঠার জন্য স্ক্রল অ্যাঙ্করিং বন্ধ করে দেবে। এটি থাকলে, আপনি বক্স-মডেল প্রপার্টি এবং ট্রান্সফর্ম সহ স্টাক স্টেটে যেকোনও প্রপার্টি অবাধে পরিবর্তন করতে পারবেন।
যা করবেন না: নিচে আটকে থাকা এলিমেন্টের জন্য overflow-anchor: none-এর উপর নির্ভর করবেন না। স্ক্রল অ্যাঙ্করিং শুধুমাত্র বর্তমান স্ক্রল পজিশনের উপরে লেআউট শিফ্টের জন্য ক্ষতিপূরণ দেয়, তাই নিচের দিকে আটকে থাকা এলিমেন্টের উপর এর কোনও প্রভাব নেই। নিচে-আটকানো হেডারের জন্য আটকানো অবস্থায় বক্স-মডেল প্রপার্টি পরিবর্তন করা এড়িয়ে চলুন।
ফলব্যাক স্ট্র্যাটেজি
স্ক্রল স্টেট কোয়েরি হল প্রোগ্রেসিভ এনহ্যান্সমেন্ট। যেসব ব্রাউজারে container-type: scroll-state কাজ করে না, সেখানেও হেডারটি উপরেই থাকবে (position: sticky-এর কারণে), তবে এটি ভিজ্যুয়ালি ট্রান্সফর্ম হবে না। বেশিরভাগ ব্যবহারের ক্ষেত্রে, এটিই সাজেস্ট করা পদ্ধতি।
স্টিকিনেস অপরিহার্য না হলেও, position: sticky-এর জন্য আলাদা স্টাইলিং না থাকলে, স্টিকিনেস না থাকার চেয়েও খারাপ অভিজ্ঞতা হতে পারে। সেক্ষেত্রে, আপনি position: sticky এবং সংশ্লিষ্ট ডিক্লারেশনকে @supports কোয়েরির মধ্যে র্যাপ করতে পারেন:
.sticky-container {
@supports (container-type: scroll-state) {
position: sticky;
top: 0;
container-type: scroll-state;
container-name: section-header;
z-index: 10;
}
}
পরামর্শ: আপনার "স্টাক" স্টাইলিংয়ের জন্য যদি পড়ার সুবিধার জন্য আলাদা ব্যাকগ্রাউন্ড রঙের প্রয়োজন হয়, তাহলে আপনার লেআউট সেট-আপ করার কথা বিবেচনা করুন যাতে ডিফল্ট স্টাইলিং সব জায়গায় কাজ করে (প্রোগ্রেসিভ এনহ্যান্সমেন্ট)। আপনাকে যদি কোনও ফলব্যাক ব্যবহার করতেই হয়, তাহলে @supports (container-type: scroll-state) কোয়েরির মধ্যে position: sticky-এর আচরণ গেট করতে পারবেন।
ডিজাইনের জন্য ভিজ্যুয়াল ট্রান্সফর্মেশন অত্যন্ত গুরুত্বপূর্ণ হলে (যেমন, স্টাক স্টেট ব্যাকগ্রাউন্ড বা কম্প্যাকশন নিয়ে আসে, যা ছাড়া কন্টেন্ট পড়া যায় না), আপনি IntersectionObserver ব্যবহার করে একটি শক্তিশালী জাভাস্ক্রিপ্ট ফলব্যাক প্রয়োগ করতে পারেন। আপনাকে অবশ্যই .is-stuck ক্লাসের অধীনে আপনার CSS স্টাইল ডুপ্লিকেট করতে হবে। নিম্নলিখিত জেনেরিক পলিফিল getComputedStyle চেক করে যাতে উপরে গিয়ে সঠিক স্ক্রল কন্টেনার (ভিউ পোর্টে ডিফল্ট করা) খুঁজে পাওয়া যায়, যা @container scroll-state(stuck: top)-এর আচরণের সাথে মেলে:
function getScrollParent(node) {
if (node == null || node === document.body || node === document.documentElement) {
return null; // default to viewport
}
if (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth) {
const overflow = getComputedStyle(node).overflow;
if (overflow !== 'visible' && overflow !== 'clip') {
return node;
}
}
return getScrollParent(node.parentNode);
}
document.querySelectorAll('.sticky-container').forEach(container => {
const root = getScrollParent(container);
const topOffset = parseFloat(getComputedStyle(container).top) || 0;
const observer = new IntersectionObserver(
([e]) => {
// Toggle the fallback class on the sticky header container
e.target.classList.toggle('is-stuck', e.intersectionRatio < 1);
},
{
root: root,
threshold: [1],
rootMargin: `-${topOffset + 1}px 0px 0px 0px`
}
);
observer.observe(container);
});
মনে রাখবেন: এই জেনেরিক IntersectionObserver প্যাটার্ন scroll-state(scrollable) কোয়েরির জন্য পলিফিল হিসেবেও ব্যবহার করা যেতে পারে।