Shadow 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) از مرز سایه عبور میکنند. این امر باعث میشود ویژگیهای سفارشی به میانای برنامهسازی کاربردی اصلی زمینهسازی تبدیل شوند: آنها را با جایگزینهای منطقی نمایش دهید.
: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) گرههای سطح بالا اختصاصدادهشده به یک جایگاه را هدف قرار میدهد. نمیتواند به فرزندان آن برسد و دربرابر هر قانون Light DOM (مصرفکننده مالک محتوای خودش است) بازنده است، بنابراین از آن برای رتوشهای جزئی استفاده کنید، نه کنترل. ::slotted(...) !important میتواند قوانین مصرفکننده را شکست دهد، اما انجام این کار سبکبندیای را که مصرفکننده حق دارد داشته باشد لغو میکند — آن را برای اصلاحات ساختاری نگه دارید، نه لغوهای زیباییشناختی.
/* Matches a slotted <p>, but NOT a <p> nested inside a slotted <div>. */
::slotted(p) { margin: 0; }
::part() and exportparts: expose internals for theming
یک عنصر داخلی را با 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>
بین این دو کانال آگاهانه انتخاب کنید: ویژگیهای سفارشی برای مجموعهای از مقادیر محدود و ایمن ازنظر سیستم طراحی؛ بخشها برای سبکبندی نامحدود یک عنصر خاص.
درحال بازارسال سبکها به عنصر بومی پیچیده
وقتی عنصر سفارشی کنترل بومی را میپوشاند (<my-button> اطراف <button> واقعی)، سه چیز معمولاً باعث اشتباه افراد میشود:
- کنترلهای فرم از تایپوگرافی ارثبری نمیکنند.
colorو ویژگیهای سفارشی از مرز عبور میکنند، اما<button>،<input>، و<select>fontو موارد مشابه را از برگه سبک نگارش UA میگیرند که به ارث نمیرسد. بنابراین، کنترل پیچیدهشده در قلم پلاتفرم (نه قلم صفحه) ارائه میشود تا زمانی کهfont: inherit(و معمولاًcolor: inherit) را روی آن در درخت سایه تنظیم کنید. این رایجترین اشتباه است. - سبکهای
:hostبه عنصر داخلی نمیرسند. تنظیمbackground/border/paddingدر:hostبه جعبه میزبان سبک میدهد، نه به کنترل داخل آن. ارسال عامدانه: کنترل را بهعنوان::partبرای سبکبندی باز نشان دهید، یا مجموعهای ثابت از مقادیر را ازطریق ویژگیهای سفارشی هدایت کنید — قانون:hostبهصورت آبشاری به عنصر پیچیدهشده اعمال نمیشود. display: contents+all: inheritیک معاوضه واقعی است، نه یک میانبر. درحالحاضر، جمع کردن میزبان باdisplay: contentsو بازنشانی کنترل داخلی باall: inheritتنها روش عمومی برای انتقال سبکهای دلخواه به عنصر بومی پیچیدهشده است، بدون اینکه مصرفکنندگان بخشهای مربوط به هر مؤلفه یا ویژگیهای سفارشی را یاد بگیرند — و برداشتن جعبه خود میزبان معمولاً هدف این الگو است (ترکیببندی جبرانکننده وراثت توکار مفقودشده است). Two real caveats:all: inheritwipes UA defaults, so scope to specific properties or userevertwhen you want defaults back; and the concrete blocker —getBoundingClientRect()andgetClientRects()return zero fordisplay: contentselements, so anchor positioning against the host is broken. وقتی این موازنهها مناسب نیستند، از::partیا ویژگیهای سفارشی استفاده کنید.
:state(): سبکدهی به حالتهای خود مؤلفه
عنصر سفارشی میتواند حالتهای منطقی داخلی را ازطریق ElementInternals بااستفاده از this.#internals.states.add('busy') و .delete('busy') نمایش دهد (برای استفاده از چرخه حیات، به عناصر سفارشی مراجعه کنید)، و این حالتها به کانال سبکدهی در سراسر مرز تبدیل میشوند — مانند ::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 است و گزینهای آیندهنگرانه نیست.