ওয়েব কম্পোনেন্ট স্টাইল করা

শ্যাডো DOM হল একটি স্টাইল বাউন্ডারি: উত্তরাধিকার সূত্রে প্রাপ্ত প্রপার্টি (কাস্টম প্রপার্টি সহ) ছাড়া, পৃষ্ঠার নিয়মগুলি ভিতরে পৌঁছায় না এবং কম্পোনেন্টের নিয়মগুলি বাইরে লিক হয় না। নিচে উল্লেখ করা চ্যানেলের মাধ্যমে ইচ্ছাকৃতভাবে এটি অতিক্রম করা।

:host, :host(), :host-context()

/* The component's own host element, styled from inside its shadow tree. */
:host { display: block; }

/* Only when the host matches a selector (e.g. a reflected state attribute). */
:host([disabled]) { opacity: 0.5; pointer-events: none; }

/* Based on an ancestor in the Light DOM; useful for theming. */
:host-context([theme="dark"]) { background: #111; color: #eee; }

:host-context() এড়িয়ে চলুন। এটি পূর্বপুরুষদের পরীক্ষা করার জন্য সীমানার বাইরে পৌঁছায়, কিন্তু Firefox এবং Safari এটি শিপ করতে অস্বীকার করেছে এবং CSSWG এটিকে আরও ডেভেলপ না করার দিকে ঝুঁকছে — এটিকে শুধুমাত্র Chromium-এর ডেড এন্ড হিসেবে বিবেচনা করুন, ভবিষ্যতের বিকল্প হিসেবে নয়। পরিবর্তে, ইনহেরিট করা CSS কাস্টম প্রপার্টির মাধ্যমে থিম (নিচে): কনজিউমার পূর্বপুরুষের উপর প্রপার্টি সেট করে এবং কম্পোনেন্ট সীমানা জুড়ে এটি পড়ে, যা সর্বত্র কাজ করে এবং উভয় পক্ষকে ডিকাপল করে রাখে।

ইনহেরিট করা প্রপার্টি ও কাস্টম প্রপার্টি সীমা অতিক্রম করেছে

ইনহেরিট করা CSS প্রপার্টি (color, font-family, line-height এবং CSS কাস্টম প্রপার্টি) শ্যাডো বাউন্ডারির মধ্যে দিয়ে পাস করে। এটি কাস্টম প্রপার্টিকে প্রাথমিক থিমিং API করে তোলে: সংবেদনশীল ফলব্যাক সহ সেগুলি প্রকাশ করুন।

:host {
  /* Consumers override --card-bg from outside; the fallback keeps it usable. */
  background: var(--card-bg, white);
  color: var(--card-fg, black);
}

:host-এ সংজ্ঞায়িত কাস্টম প্রপার্টি শুধুমাত্র হোস্ট এলিমেন্টকে নির্দিষ্টভাবে টার্গেট করা নিয়মের মাধ্যমে ওভাররাইড করা যেতে পারে। ট্রি-এর যেকোনও জায়গা থেকে এটি ইনহেরিট ও ওভাররাইড করার অনুমতি দিতে, :host-এ পাবলিক প্রপার্টি আনসেট করে রাখুন এবং প্রতিটি var()-এ ফলব্যাকের উপর নির্ভর করুন; :host-এ পাবলিক প্রপার্টি সিড করবেন না।

প্রতিটি var()-এ বারবার না করে একবারই ফলব্যাক ঘোষণা করতে, :host-এ একটি ব্যক্তিগত "শ্যাডো" প্রপার্টি নির্ধারণ করুন যা সর্বজনীন প্রপার্টি পড়ে এবং ব্যক্তিগত প্রপার্টিকে ইন্টার্নালি রেফারেন্স করে:

:host {
  --_card-bg: var(--card-bg, white);
  --_card-fg: var(--card-fg, black);
}
.card { background: var(--_card-bg); color: var(--_card-fg); }

গ্রাহকরা এখনও বাইরে থেকে --card-bg ওভাররাইড করেন; লিডিং আন্ডারস্কোর সিগন্যাল দেয় যে উপনামটি ইন্টার্নাল এবং ফলব্যাক ঠিক একটি জায়গায় থাকে।

::slotted(): প্রজেক্ট করা Light DOM স্টাইল করা হচ্ছে

::slotted(selector) স্লটে অ্যাসাইন করা টপ-লেভেল নোডকে টার্গেট করে। এটি তার ডিসেন্ড্যান্টে পৌঁছাতে পারে না এবং যেকোনও লাইট DOM নিয়মের (গ্রাহক তার নিজের কন্টেন্টের মালিক) কাছে হেরে যায়, তাই এটি হালকা টাচ-আপের জন্য ব্যবহার করুন, নিয়ন্ত্রণের জন্য নয়। ::slotted(...) !important কনজিউমার রুলকে ওভাররাইড করতে পারে, তবে এটি করলে কনজিউমারের নিজস্ব স্টাইলিং ওভাররাইড হয়ে যায় — এটি স্ট্রাকচারাল ফিক্সের জন্য ব্যবহার করুন, নান্দনিক ওভাররাইডের জন্য নয়।

/* Matches a slotted <p>, but NOT a <p> nested inside a slotted <div>. */
::slotted(p) { margin: 0; }

::part() এবং exportparts: থিমিংয়ের জন্য ইন্টার্নাল এক্সপোজ করুন

part="name" দিয়ে ইন্টার্নাল এলিমেন্ট চিহ্নিত করুন যাতে গ্রাহক আপনার ইন্টার্নাল স্ট্রাকচার না জেনেও যেকোনও প্রপার্টি দিয়ে এটি স্টাইল করতে পারেন।

<!-- inside the shadow template -->
<button part="submit">Submit</button>
/* consumer stylesheet */
my-card::part(submit) { background: navy; color: white; }

কয়েকটি হাই-লেভেল অংশ (বোতাম, ইনপুট, লেবেল) এক্সপোজ করুন, প্রতিটি <div> নয়; ব্রড পার্ট সারফেস ভঙ্গুর API হয়ে যায়। আপনার নিজের কম্পোনেন্টের মাধ্যমে নেস্টেড কম্পোনেন্টের অংশগুলি উপরের দিকে আবার এক্সপোজ করতে, exportparts-এর মাধ্যমে সেগুলি ফরওয়ার্ড করুন:

<inner-widget exportparts="submit: card-submit"></inner-widget>

দুটি চ্যানেলের মধ্যে থেকে ইচ্ছাকৃতভাবে বেছে নিন: সীমাবদ্ধ, ডিজাইন-সিস্টেম-নিরাপদ সেট অফ ভ্যালুর জন্য কাস্টম প্রপার্টি; নির্দিষ্ট এলিমেন্টের আনরেস্ট্রিক্টেড স্টাইলিংয়ের জন্য পার্ট।

র‍্যাপ করা নেটিভ এলিমেন্টে স্টাইল ফরওয়ার্ড করা হচ্ছে

যখন কোনও কাস্টম এলিমেন্ট নেটিভ কন্ট্রোলকে র‍্যাপ করে (আসল <button>-এর চারপাশে <my-button>), তখন তিনটি জিনিস সাধারণত সমস্যা তৈরি করে:

  • ফর্ম কন্ট্রোল টাইপোগ্রাফি ইনহেরিট করে না। color এবং কাস্টম প্রপার্টি বাউন্ডারি ক্রস করে, কিন্তু <button>, <input> এবং <select> UA স্টাইলশিট থেকে font এবং বন্ধুদের নেয়, যা ইনহেরিট করে না। তাই, আপনি শ্যাডো ট্রির মধ্যে font: inherit (এবং সাধারণত color: inherit) সেট না করা পর্যন্ত, র‍্যাপ করা কন্ট্রোল প্ল্যাটফর্ম ফন্টে রেন্ডার হয়, পৃষ্ঠার ফন্টে নয়। এটিই সবচেয়ে সাধারণ ভুল।
  • :host স্টাইল ইনার এলিমেন্টে পৌঁছায় না। :host-এ background/border/padding সেট করলে হোস্ট বক্স স্টাইল করা হয়, এর মধ্যে থাকা কন্ট্রোল নয়। ইচ্ছাকৃতভাবে ফরওয়ার্ড করা: ওপেন-এন্ডেড স্টাইলিংয়ের জন্য কন্ট্রোলকে ::part হিসেবে এক্সপোজ করা অথবা কাস্টম প্রপার্টির মাধ্যমে নির্দিষ্ট সেট অফ ভ্যালু পাইপ করা — :host নিয়ম র‍্যাপ করা এলিমেন্টে ক্যাসকেড করে না।
  • display: contents + all: inherit হল একটি বাস্তব ট্রেডঅফ, কোনও শর্টকাট নয়। display: contents-এর মাধ্যমে হোস্টকে কোল্যাপ্স করা এবং all: inherit-এর মাধ্যমে ইনার কন্ট্রোল রিসেট করা হল বর্তমানে একমাত্র সাধারণ উপায় যার মাধ্যমে র‍্যাপ করা নেটিভ এলিমেন্টে ইচ্ছামতো স্টাইল পাস করা যায়, যেখানে গ্রাহকদের প্রতি-কম্পোনেন্ট পার্ট বা কাস্টম প্রপার্টি সম্পর্কে জানতে হয় না — এবং হোস্টের নিজস্ব বক্স সরিয়ে দেওয়া হল সাধারণত এই প্যাটার্নের উদ্দেশ্য (কম্পোজিশন, বিল্ট-ইন ইনহেরিটেন্সের অভাব পূরণ করে)। দুটি বাস্তব সতর্কতা: all: inherit UA ডিফল্ট মুছে দেয়, তাই নির্দিষ্ট প্রপার্টিতে স্কোপ করুন বা ডিফল্ট ফিরিয়ে আনতে চাইলে revert ব্যবহার করুন; এবং কংক্রিট ব্লকার — getBoundingClientRect() এবং getClientRects() display: contents এলিমেন্টের জন্য শূন্য রিটার্ন করে, তাই হোস্টের বিপরীতে অ্যাঙ্কর পজিশনিং ভেঙে যায়। এইসব ট্রেডঅফ মানানসই না হলে, ::part বা কাস্টম প্রপার্টি ব্যবহার করুন।

:state(): কম্পোনেন্টের নিজস্ব স্টেট স্টাইল করুন

একটি কাস্টম এলিমেন্ট this.#internals.states.add('busy') এবং .delete('busy')-এর মাধ্যমে ElementInternals-এর মাধ্যমে ইন্টার্নাল বুলিয়ান স্টেট প্রকাশ করতে পারে (লাইফসাইকেল ব্যবহারের জন্য কাস্টম এলিমেন্ট দেখুন) এবং সেই স্টেটগুলি সীমানা জুড়ে একটি স্টাইলিং চ্যানেল হয়ে ওঠে — যেমন ::part এবং কাস্টম প্রপার্টি, তবে স্ট্রাকচার বা ভ্যালুর পরিবর্তে স্টেট-এর জন্য। :host(:state(name))-এর সাহায্যে শ্যাডো ট্রির মধ্যে এবং tag-name:state(name)-এর সাহায্যে গ্রাহকের স্টাইলশিট থেকে এগুলি ম্যাচ করুন:

/* Inside the component's shadow tree. */
:host(:state(busy)) button { opacity: 0.6; pointer-events: none; }

/* From the consumer's stylesheet — a read-only public hook. */
favorite-button:state(favorited) { filter: drop-shadow(0 0 4px crimson); }

রিফ্লেক্টেড অ্যাট্রিবিউটের মতো নয়, কাস্টম স্টেট হল বাইরে থেকে শুধু-পড়ার-জন্য: গ্রাহকরা এটিকে স্টাইল করতে পারেন কিন্তু সেট বা জাল করতে পারেন না, তাই কম্পোনেন্টটি একমাত্র লেখক হিসেবে থাকে। যখনই স্টেট কম্পোনেন্টের মালিকানাধীন হয় এবং শুধুমাত্র স্টাইল করার প্রয়োজন হয় তখন data-* অ্যাট্রিবিউটের চেয়ে এটি পছন্দ করুন।

@layer এবং কম্পোনেন্টের মধ্যে কন্টেনার কোয়েরি

  • ক্যাসকেড লেয়ার (@layer) শ্যাডো রুটের মধ্যে কাজ করে এবং এর স্কোপ সেখানেই সীমাবদ্ধ; শ্যাডো ট্রিতে কোনও লেয়ারের নাম পৃষ্ঠায় একই নামের লেয়ারের থেকে আলাদা হয়। আপনার নিজস্ব কম্পোনেন্ট স্টাইল (যেমন, @layer base, theme;) অর্ডার করতে এগুলি ব্যবহার করুন যাতে কাস্টম প্রপার্টি এবং ::part-এর মাধ্যমে গ্রাহক ওভাররাইড এখনও অনুমানযোগ্যভাবে জিততে পারে।
  • রেসপন্সিভ কম্পোনেন্টের জন্য কন্টেনার কোয়েরি হল সঠিক টুল: কোনও কম্পোনেন্টকে ভিউপোর্ট নয়, বরং যে স্পেস দেওয়া হয়েছে তার ভিত্তিতে রেসপন্ড করতে হবে। হোস্ট বা র‍্যাপারকে কন্টেনার হিসেবে ঘোষণা করুন (:host-কে display: block-এর মতো লেআউট বক্স দিন, কারণ কাস্টম এলিমেন্ট ডিফল্ট হিসেবে display: inline-এ সেট করা থাকে এবং সাইজ কন্টেনমেন্ট inline বা contents এলিমেন্টের ক্ষেত্রে প্রযোজ্য হয় না) এবং এটি কোয়েরি করুন, যাতে একই কম্পোনেন্ট সাইডবার বা সম্পূর্ণ প্রস্থের অঞ্চলে মানিয়ে নিতে পারে।
:host {
  display: block; /* Required: size containment does not apply to default display: inline */
  container-type: inline-size;
}

@container (min-width: 30rem) {
  .card { grid-template-columns: 1fr 2fr; }
}

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

::part, ক্যাসকেড লেয়ার এবং কন্টেনার কোয়েরি ব্যাপকভাবে উপলভ্য; থিম মূলত ইনহেরিট করা কাস্টম প্রপার্টির মাধ্যমে কাজ করে, যা এগুলি না থাকলেও কাজ করে। উপরে যেমন উল্লেখ করা হয়েছে, :host-context()-এর উপর নির্ভর করবেন না — এটি শুধু Chromium-এর জন্য কার্যকর এবং এটি কোনও ভবিষ্যৎমুখী বিকল্প নয়।