Shadow DOM создаёт инкапсулированный DOM-дерево внутри веб-компонента, отделяя его стили от глобальных. Это позволяет управлять внешним видом компонента без риска конфликта с другими стилями на странице. В библиотеке Haunted, которая использует Web Components и функциональный подход через хуки, понимание CSS в Shadow DOM критично для корректного оформления компонентов.
Haunted строится поверх стандартных веб-компонентов, поэтому для каждого кастомного элемента создаётся shadow root:
import { component, html, useState } from 'haunted';
function MyComponent() {
const [count, setCount] = useState(0);
return html`
<style>
button {
background-color: #007acc;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #005fa3;
}
p {
font-size: 16px;
color: #333;
}
</style>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
`;
}
customElements.define('my-component', component(MyComponent));
Ключевой момент: <style> внутри
шаблона Haunted автоматически помещается в shadow root и применяется
только к этому компоненту. Это обеспечивает полную инкапсуляцию
стилей.
Изоляция стилей Стили внутри shadow root не наследуются извне, и глобальные CSS правила не влияют на содержимое компонента. Это значит, что можно использовать универсальные селекторы внутри компонента без опасений повлиять на остальную страницу.
Использование CSS-переменных Shadow DOM позволяет передавать значения из глобального CSS через кастомные свойства (CSS variables):
:host {
--button-bg: #007acc;
--button-hover-bg: #005fa3;
}
button {
background-color: var(--button-bg);
}
button:hover {
background-color: var(--button-hover-bg);
}
:host ссылается на сам компонент, а не на вложенные
элементы. Это облегчает создание настраиваемых компонентов, где цвета и
отступы можно изменять снаружи через CSS-переменные.
Селекторы :host и
::slotted
:host позволяет стилизовать сам элемент, а не его
содержимое.::slotted(selector) применяется к элементам, переданным
в слот:function SlotComponent() {
return html`
<style>
:host {
display: block;
padding: 12px;
border: 1px solid #ccc;
}
::slotted(p) {
color: red;
}
</style>
<slot></slot>
`;
}
customElements.define('slot-component', component(SlotComponent));
В этом примере любой <p> внутри слота окрашивается
в красный, при этом остальная структура компонента остаётся
изолированной.
<style> в шаблоне, так как
это гарантирует попадание стилей в shadow root. Подключение внешних
CSS-файлов возможно, но требует использования
<link rel="stylesheet"> внутри shadow root или
динамической вставки стилей через JavaScript.const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';
shadowRoot.appendChild(link);
Этот подход позволяет переиспользовать общие CSS-файлы, но при этом не нарушает инкапсуляцию.
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
p {
animation: fadeIn 0.5s ease-in;
}
::slotted:
чтобы передаваемый контент гармонично сочетался с компонентом, не
нарушая инкапсуляцию.Хотя shadow DOM изолирует CSS, глобальные шрифты и медиа-запросы всё
же действуют внутри компонента. Это означает, что можно наследовать
семейство шрифтов или использовать @media внутри
<style> Haunted-компонента:
@media (max-width: 600px) {
p {
font-size: 14px;
}
}
Таким образом, компоненты остаются адаптивными без зависимости от внешней верстки.
function AdvancedComponent() {
return html`
<style>
:host {
display: block;
padding: 16px;
border-radius: 8px;
background-color: #f9f9f9;
}
h2 {
margin: 0 0 12px 0;
color: #222;
}
button {
background-color: var(--button-bg, #007acc);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: var(--button-hover-bg, #005fa3);
}
</style>
<h2>Заголовок компонента</h2>
<button @click=${() => console.log('Clicked')}>Кнопка</button>
`;
}
customElements.define('advanced-component', component(AdvancedComponent));
Такой подход обеспечивает полностью изолированные, настраиваемые и адаптивные компоненты с полной поддержкой CSS внутри Shadow DOM.