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() وexportparts: عرض العناصر الداخلية للمظاهر
يمكنك وضع علامة على عنصر داخلي باستخدام part="name" للسماح للمستهلكين بتنسيقه باستخدام أي سمة، بدون الحاجة إلى معرفة البنية الداخلية.
<!-- inside the shadow template -->
<button part="submit">Submit</button>
/* consumer stylesheet */
my-card::part(submit) { background: navy; color: white; }
اعرض بعض الأجزاء عالية المستوى (الزر، الإدخال، التصنيف)، وليس كل <div>؛ تصبح أسطح الأجزاء العريضة واجهة برمجة تطبيقات هشة. لإعادة عرض أجزاء مكوّن مدمج في مكوّنك الخاص، يمكنك إعادة توجيهها باستخدام 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هما حاليًا الطريقة العامة الوحيدة لتمرير الأنماط العشوائية إلى عنصر أصلي مغلّف بدون أن يتعرّف المستهلكون على الأجزاء لكل مكوّن أو الخصائص المخصّصة، وعادةً ما تكون إزالة مربّع المضيف هي الهدف من النمط (التركيب الذي يعوّض عن الوراثة المضمّنة المفقودة). هناك تحذيران حقيقيان: يمحوall: inheritالإعدادات التلقائية لـ UA، لذا يمكنك تحديد مواقع معيّنة أو استخدامrevertعندما تريد استعادة الإعدادات التلقائية، والعائق الملموس: يعرضgetBoundingClientRect()وgetClientRects()القيمة صفر لعناصرdisplay: contents، لذا يتم كسر موضع الارتساء بالنسبة إلى المضيف. ويمكنك استخدام::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 فقط وليس خيارًا مستقبليًا.