চাইল্ড স্টেট ভিত্তিক স্টাইলিং

ওয়েব ফিচার: :has() :not()

ঐতিহাসিকভাবে, CSS বাছাইকারী শুধুমাত্র নিচের দিকে যেতে পারে—আপনি প্যারেন্টের উপর ভিত্তি করে চাইল্ড স্টাইল করতে পারেন, কিন্তু চাইল্ডের উপর ভিত্তি করে প্যারেন্ট স্টাইল করতে পারেন না। :has() সিউডো-ক্লাস এটি পরিবর্তন করে, যা আপনাকে কন্টেনার এলিমেন্টের বংশধরদের উপস্থিতি বা অবস্থার উপর নির্ভর করে শর্তসাপেক্ষে স্টাইল করার অনুমতি দেয়।

স্টেট সিউডো-ক্লাসের (যেমন :checked, :focus, :valid, :invalid বা :not()) সাথে :has() কম্বাইন করে, আপনি সম্পূর্ণভাবে CSS-এ জটিল, ইন্টার‍্যাক্টিভ UI কম্পোনেন্ট তৈরি করতে পারবেন, এর জন্য পেরেন্ট এলিমেন্টে "মডিফায়ার" ক্লাস (যেমন .is-active বা .has-error) টগল করার জন্য জাভাস্ক্রিপ্টের প্রয়োজন হবে না।

এটি বিশেষ করে সেইসব কম্পোনেন্টের জন্য উপযোগী যেগুলিকে ইন্টার‍্যাকশনে সাড়া দিতে হয়, যেমন চেকবক্সে (:checked) প্রতিক্রিয়া জানানো লোকালাইজড থিম টগল, কোনও ভুল হাইলাইট করা ফর্ম গ্রুপ (:invalid) অথবা চাইল্ড লিঙ্কে ফোকাস করা হলে কার্ড এলিভেট করা (:focus-within)।

স্টেট-ভিত্তিক কন্টেনার স্টাইলিং প্রয়োগ করা

বাধ্যতামূলক: নির্দিষ্ট অবস্থা (যেমন, :checked, :focus, :invalid) শনাক্ত করার জন্য আপনাকে অবশ্যই কন্টেনার এলিমেন্টে :has() সিলেক্টর ব্যবহার করতে হবে।

চাইল্ডের অবস্থার উপর ভিত্তি করে স্টাইলিং পরিবর্তন করে এমন কম্পোনেন্ট তৈরি করতে:

  1. ডিফল্ট স্টাইলিং নির্ধারণ করুন: কন্টেনারের বেস স্টেট নির্ধারণ করতে সেটিতে CSS ভেরিয়েবল সেট করুন।
  2. স্টেট-ভিত্তিক ওভাররাইড প্রয়োগ করা: :has([child-selector]:[state])-এর মাধ্যমে কন্টেনার টার্গেট করুন এবং অ্যাক্টিভ বা বিকল্প স্টেটের জন্য CSS ভেরিয়েবল আবার নির্ধারণ করুন।

উদাহরণ: এমন একটি কম্পোনেন্ট যা চাইল্ড টগলের উপর ভিত্তি করে থিম পরিবর্তন করে।

/* 1. Define the default state on the component container */
.theme-card {
  /* Using custom properties makes state-switching cleaner */
  --card-bg: #ffffff;
  --card-text: #333333;
  --card-border: #cccccc;

  background-color: var(--card-bg);
  color: var(--card-text);
  border: 1px solid var(--card-border);

  /* Use a transition for smooth state changes */
  transition: background-color 0.3s, color 0.3s;
}

/* 2. Apply styles when the child enters the specific state */
/* MANDATORY: Target the container and use :has() to check the descendant's state */
.theme-card:has(.theme-toggle:checked) {
  /* Override the properties for the active or alternate state */
  --card-bg: #222222;
  --card-text: #f0f0f0;
  --card-border: #555555;
}
/* You can also combine :has() and :not() to target specific negative states */
/* This selects a card that DOES NOT have a toggle in the checked state */
.theme-card:not(:has(.theme-toggle:checked)) {
  /* Optional: Explicit styles for the unchecked state if needed */
}
<!-- The container element that receives the styling -->
<div class="theme-card">
  <!-- The child element whose state controls the parent -->
  <label>
    <input type="checkbox" class="theme-toggle">
    Enable Dark Mode
  </label>

  <h2>Card Title</h2>
  <p>The style of this entire card is controlled by the checkbox above.</p>
</div>

পারফর্ম্যান্স সংক্রান্ত পরামর্শ: :has() ব্যবহার করার সময়, যতটা সম্ভব নির্দিষ্ট কন্টেনারে সিলেক্টর স্কোপ করুন (যেমন .theme-card)। স্টাইলিং পরিবর্তন লোকালাইজ করা থাকলে, body:has(...)-এর মতো খুব হাই-লেভেল এলিমেন্টে এটি অ্যাঙ্কর করা এড়িয়ে চলুন, কারণ ব্রড :has() কোয়েরি আরও লেআউট রিক্যালকুলেশন ট্রিগার করতে পারে।

ফলব্যাক স্ট্র্যাটেজি

ব্যবহারকারীর অভিজ্ঞতা বা পৃষ্ঠা লেআউটের জন্য স্টেট-ভিত্তিক স্টাইলিং গুরুত্বপূর্ণ হলে, যেসব ব্রাউজার :has() সিলেক্টরকে সাপোর্ট করে না, সেগুলির জন্য আপনাকে একটি ফলব্যাক প্রদান করতে হবে। শুধুমাত্র আলঙ্কারিক এফেক্টের জন্য, :has() ফলব্যাক ছাড়াই প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে ব্যবহার করা যেতে পারে।

বাধ্যতামূলক: গুরুত্বপূর্ণ ফিচারের জন্য ফলব্যাক প্রয়োগ করার সময়, আপনাকে অবশ্যই CSS-এ @supports not selector(:has(*)) ব্যবহার করে একটি প্রথাগত ক্লাস-ভিত্তিক ফলব্যাক সংজ্ঞায়িত করতে হবে। গুরুত্বপূর্ণ স্টেট পরিবর্তন যদি ব্যবহারকারীর ইন্টার‍্যাকশনের উপর নির্ভর করে, তাহলে আপনাকে অবশ্যই CSS.supports() সহ একটি ছোট ইনলাইন স্ক্রিপ্ট ব্যবহার করতে হবে যাতে সমতুল্য জাভাস্ক্রিপ্ট ইভেন্টের (যেমন, change, focus, blur) উপর ভিত্তি করে সেই ক্লাস টগল করা যায় যা স্টেট পরিবর্তনকে উপস্থাপন করে।

/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
  /* Define a traditional modifier class that applies the exact same overrides */
  .theme-card.is-active {
    --card-bg: #222222;
    --card-text: #f0f0f0;
    --card-border: #555555;
  }
}
/* Fallback JavaScript for older browsers */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
  const toggle = document.querySelector('.theme-toggle');
  const card = document.querySelector('.theme-card');

  if (toggle && card) {
    // Manually toggle the fallback class when the input state changes
    toggle.addEventListener('change', (e) => {
      card.classList.toggle('is-active', e.target.checked);
    });
  }
}