אתה בונה מערכת עיצוב לפרויקט מסחר אלקטרוני גדול. עשרות רכיבים—כפתורים, כרטיסים, מודאלים—חיים על עמוד אחד. אתה מוסיף סגנונות גלובליים כמו Bootstrap או Tailwind, והפריסה נשברת: השוליים זזים, הצבעים נדרסים. שורש הבעיה הוא דליפת סגנונות. Shadow DOM הוא מנגנון הדפדפן שיוצר תת-עץ DOM מבודד לכל רכיב. סגנונות מבחוץ לא חודרים פנימה, וסגנונות פנימיים לא משפיעים על ההקשר הגלובלי. זה מאפשר לרכיבים לעבוד באופן עקבי בכל סביבת CSS—מאפליקציות legacy ועד SPA מודרניים. הצוות שלנו, עם ניסיון של 10+ שנים בייצור, מיישם Shadow DOM מקצה לקצה. עם 20+ פרויקטים שהושלמו, אנחנו יודעים איך לחסל קונפליקטי CSS.
אילו בעיות CSS Shadow DOM פותר?
-
התנגשויות סגנון גלובליות:
div { color: red }גלובלי לא ייגע באלמנטים בתוך עץ shadow. - סלקטורים סקופיים שנשברים: אין צורך ב-BEM מקונן יתר על המידה—האנקפסולציה מובנית.
- בידוד ווידג'טים של צד שלישי: ווידג'טים של צ'אט או טפסי תשלום נשארים מושפעים מסגנונות האתר המארח.
איך אנחנו משלבים Shadow DOM (גישה מוכחת)
בפרויקט אחרון לפלטפורמת מסחר אלקטרוני גדולה עם 50+ רכיבים, צמצמנו את זמן ניפוי ה-CSS ב-70% והקטנו את גודל ה-bundle ב-40% באמצעות Adoptable Stylesheets. הנה זרימת העבודה הטכנית שלנו:
- ביקורת על ארכיטקטורת ה-CSS הנוכחית וזיהוי אזורי קונפליקט.
- עיצוב גבולות רכיבים באמצעות Custom Elements עם Shadow DOM פתוח (
// Открытый режим — доступ к shadowRoot извне const openShadow = element.attachShadow({ mode: 'open' }) // Закрытый режим — shadowRoot === null снаружи const closedShadow = element.attachShadow({ mode: 'closed' })). - חשיפת עיצוב (theming) דרך CSS Custom Properties; חשיפת נקודות חיבור עדינות דרך תכונות
mode: 'open'. - שימוש ב-Adoptable Stylesheets עבור reset וסגנונות בסיס משותפים.
- עבור אלמנטים של טפסים, שילוב ElementInternals כדי להבטיח ששליחת נתונים עובדת כרגיל.
// Открытый режим — доступ к shadowRoot извне
const openShadow = element.attachShadow({ mode: 'open' })
// Закрытый режим — shadowRoot === null снаружи
const closedShadow = element.attachShadow({ mode: 'closed' })ברוב המכריע של המקרים אנחנו משתמשים ב-// Снаружи: задаём переменные styled-card { --card-bg: #f8f9fa; --card-radius: 16px; } // Внутри Shadow DOM: используем переменные с fallback :host { background: var(--card-bg, #fff); border-radius: var(--card-radius, 8px); } . כלי פיתוח, כלי בדיקה וספריות טפסים דורשים גישה ל-shadowRoot. מצב סגור מוצדק רק לאנקפסולציה מקסימלית (למשל, אלמנטים מקוריים של דפדפן).
מתי כדאי להשתמש ב-Shadow DOM?
Shadow DOM אידיאלי עבור:
- מערכות עיצוב וספריות רכיבים (Material UI, Web Components)
- Micro-frontends שבהם כל מודול לא חייב להשפיע על סגנונות גלובליים
- ווידג'טים המוטמעים באתרי צד שלישי (צ'אט, טפסי תשלום)
- עורכים ואלמנטים אינטראקטיביים מורכבים
עיצוב רכיבים עם Shadow DOM
CSS Custom Properties (משתנים)
הדרך היחידה להעביר סגנונות מבחוץ אל תוך Shadow DOM היא דרך משתני CSS. הם חוצים את הגבול באופן טבעי.
// Снаружи: задаём переменные
styled-card {
--card-bg: #f8f9fa;
--card-radius: 16px;
}
// Внутри Shadow DOM: используем переменные с fallback
:host {
background: var(--card-bg, #fff);
border-radius: var(--card-radius, 8px);
} CSS Parts
התכונה part חושפת אלמנטים ספציפיים לעיצוב חיצוני דרך ::part(). זהו כלי מדויק מבלי לשבור את האנקפסולציה.
shadow.innerHTML = `
<div class="wrapper" part="wrapper">
<button class="btn" part="button trigger"><slot></slot></button>
<div class="dropdown" part="dropdown"><slot name="items"></slot></div>
</div>
` my-dropdown::part(button) { background: #7000ff; color: #fff; border-radius: 8px; } Adoptable Stylesheets
כדי לעשות שימוש חוזר בסגנונות על פני מספר מופעי Shadow DOM מבלי לשכפל מחרוזות, השתמש ב-shadow.innerHTML = ` <div class="wrapper" part="wrapper"> <button class="btn" part="button trigger"><slot></slot></button> <div class="dropdown" part="dropdown"><slot name="items"></slot></div> </div> ` . זה מקטין את גודל ה-bundle ב-60% בהשוואה לאלמנטים my-dropdown::part(button) { background: #7000ff; color: #fff; border-radius: 8px; } מוטבעים.
const sharedStyles = new CSSStyleSheet()
sharedStyles.replaceSync(`
:host {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
`)
// Применяем в компонентах
class ComponentA extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
shadow.adoptedStyleSheets = [sharedStyles]
}
} | שיטה | היקף | מתי להשתמש | ביצועים |
|---|---|---|---|
| CSS Custom Properties | עיצוב רכיבים | מראה ותחושה גלובליים | ללא השפעה (משתנים יורשים) |
| CSS Parts | עיצוב ממוקד | חריגים נדירים | השפעה נמוכה |
| Adoptable Stylesheets | סגנונות משותפים | סגנונות חוזרים על פני רכיבים | גבוה (אובייקט אחד לכל מופע) |
Shadow DOM ושילוב טפסים
אלמנטים מקוריים של טפסים בתוך Shadow DOM אינם משתתפים ב-CSSStyleSheet ואינם מופיעים ב-<style>. הפתרון הוא const sharedStyles = new CSSStyleSheet() sharedStyles.replaceSync(` :host { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; } `) // Применяем в компонентах class ComponentA extends HTMLElement { constructor() { super() const shadow = this.attachShadow({ mode: 'open' }) shadow.adoptedStyleSheets = [sharedStyles] } } .
class CustomInput extends HTMLElement {
static get formAssociated() {
return true
}
private internals: ElementInternals
private shadow: ShadowRoot
constructor() {
super()
this.internals = this.attachInternals()
this.shadow = this.attachShadow({ mode: 'open' })
}
connectedCallback() {
this.shadow.innerHTML = `
<style>
input {
width: 100%;
padding: 10px 14px;
border: 1px solid var(--border-color, #d0d5dd);
border-radius: 8px;
font: inherit;
}
input:focus {
border-color: var(--focus-color, #7000ff);
box-shadow: 0 0 0 3px var(--focus-ring, rgba(112, 0, 255, 0.15));
}
</style>
<input type="text" />
`
const input = this.shadow.querySelector('input')!
input.addEventListener('input', () => {
this.internals.setFormValue(input.value)
this.internals.setValidity(input.validity, input.validationMessage, input)
})
}
formResetCallback() {
const input = this.shadow.querySelector('input')
if (input) input.value = ''
this.internals.setFormValue('')
}
} טעויות נפוצות ופתרונות
| טעות | השלכה | פתרון |
|---|---|---|
שימוש ב-form.elements שלא לצורך | בעיות בבדיקות וספריות | השתמש ב-FormData |
שכחת ElementInternals עבור טפסים | טפסים מאבדים נתונים | הוסף class CustomInput extends HTMLElement { static get formAssociated() { return true } private internals: ElementInternals private shadow: ShadowRoot constructor() { super() this.internals = this.attachInternals() this.shadow = this.attachShadow({ mode: 'open' }) } connectedCallback() { this.shadow.innerHTML = ` <style> input { width: 100%; padding: 10px 14px; border: 1px solid var(--border-color, #d0d5dd); border-radius: 8px; font: inherit; } input:focus { border-color: var(--focus-color, #7000ff); box-shadow: 0 0 0 3px var(--focus-ring, rgba(112, 0, 255, 0.15)); } </style> <input type="text" /> ` const input = this.shadow.querySelector('input')! input.addEventListener('input', () => { this.internals.setFormValue(input.value) this.internals.setValidity(input.validity, input.validationMessage, input) }) } formResetCallback() { const input = this.shadow.querySelector('input') if (input) input.value = '' this.internals.setFormValue('') } } ו-mode: 'closed' |
עיצוב הכל דרך mode: 'open' | שובר אנקפסולציה | השתמש ב-CSS Custom Properties לעיצוב |
| שכפול סגנונות בכל רכיב | גודל bundle מוגדל | החל Adoptable Stylesheets |
איך ליישם Shadow DOM בפועל
- צור מחלקת custom element המרחיבה את
ElementInternals. - בקונסטרוקטור, קרא ל-
formAssociated. - אכלס את ה-shadow root ב-HTML וסגנונות (דרך
attachInternals()או::part). - הכרז על CSS Custom Properties עבור קונפיגורציה חיצונית והשתמש ב-
HTMLElementלעיצוב ממוקד. - עבור טפסים, הוסף
attachShadow({ mode: 'open' })והשתמש ב-adoptedStyleSheets.
לפי מפרט Shadow DOM ב-MDN, אנקפסולציה היא חובה לרכיבים אמינים. Shadow DOM מאפשר יצירת רכיבי web עצמאיים ללא חשש מקונפליקטי סגנונות.
למה Shadow DOM עדיף על BEM ו-CSS Modules
BEM ו-CSS Modules פותרים התנגשויות שמות אך אינם מגנים מפני סגנונות גלובליים (למשל, normalize.css). Shadow DOM מספק בידוד DOM וסגנונות מלא—מצמצם את זמן ניפוי ה-CSS פי 3 בפרויקטים עם 10+ רכיבים. Adoptable Stylesheets מקטינים את ה-bundle ב-40–60% בהשוואה לסגנונות מוטבעים.
מה כלול בשילוב Shadow DOM שלנו
- ביקורת על ארכיטקטורת ה-CSS הנוכחית שלך וזיהוי קונפליקטים
- עיצוב רכיבים המותאם למערכת העיצוב שלך
- יישום עם תמיכה בטפסים (ElementInternals), עיצוב (CSS Custom Properties), וסגנונות (CSS Parts)
- תיעוד רכיבים ומדריך שימוש
- בדיקות חוצות דפדפנים, תאימות מובטחת
- תמיכה לאחר היישום: הדרכת צוות, ייעוץ
לוחות זמנים: רכיב אחד לוקח עד יום אחד; מערכת של 5–8 רכיבים לוקחת 1–2 שבועות. נבחן את הפרויקט שלך—פשוט צור קשר. קבל ביקורת CSS היום.
טיפ: איך להימנע מטעויות נפוצות
לפני היישום, בדוק אם קהל היעד שלך תומך ב-Shadow DOM (~97% כיסוי דפדפנים). השתמש ב-polyfills לדפדפנים legacy. אל תשתמש יתר על המידה ב-::part()—זה שובר אנקפסולציה בשימוש מוגזם.







