Haunted — это библиотека для создания веб-компонентов с использованием функционального подхода в JavaScript. Одним из ключевых преимуществ веб-компонентов является возможность инкапсуляции стилей, что обеспечивает независимость внешнего и внутреннего оформления элементов. В Haunted это реализуется через Shadow DOM, CSS-in-JS и специфические подходы к связыванию стилей с компонентами.
Shadow DOM создаёт изолированное дерево элементов внутри веб-компонента. Любые стили, объявленные внутри shadow root, применяются только к этому компоненту и не влияют на внешние элементы страницы.
Пример создания компонента с Shadow DOM в Haunted:
import { component, html } from 'haunted';
function MyButton({ label }) {
return html`
<style>
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #2980b9;
}
</style>
<button>${label}</button>
`;
}
customElements.define('my-button', component(MyButton));
В этом примере:
<style> помещается внутрь шаблона, что делает
стили локальными.adoptedStyleSheetsСовременные браузеры поддерживают CSSStyleSheet API, что позволяет создавать и повторно использовать стили для нескольких компонентов без дублирования кода.
Пример с adoptedStyleSheets:
const buttonStyles = new CSSStyleSheet();
buttonStyles.replaceSync(`
button {
background-color: #2ecc71;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
button:hover {
background-color: #27ae60;
}
`);
function StyledButton({ label }) {
const ref = document.createElement('div').attachShadow({ mode: 'open' });
ref.shadowRoot.adoptedStyleSheets = [buttonStyles];
return html`
<button>${label}</button>
`;
}
customElements.define('styled-button', component(StyledButton));
Преимущества подхода:
<style> в
каждом компоненте.Haunted позволяет использовать CSS внутри шаблонов через строки, что создаёт локальные scoped стили без прямого взаимодействия с shadow root:
import { component, html } from 'haunted';
function Card({ title, content }) {
const cardStyles = `
.card {
border: 1px solid #ccc;
padding: 16px;
border-radius: 6px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card h2 {
margin: 0 0 8px 0;
font-size: 1.2em;
}
.card p {
margin: 0;
color: #555;
}
`;
return html`
<style>${cardStyles}</style>
<div class="card">
<h2>${title}</h2>
<p>${content}</p>
</div>
`;
}
customElements.define('my-card', component(Card));
Этот метод удобен для быстрого внедрения локальных стилей, хотя и
создаёт новый <style> при каждом рендере
компонента.
Haunted поддерживает реактивные свойства, что позволяет динамически изменять стили в зависимости от состояния компонента.
Пример:
import { component, html, useState } from 'haunted';
function ToggleButton() {
const [active, setActive] = useState(false);
const styles = `
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
background-color: ${active ? '#e74c3c' : '#2ecc71'};
color: white;
cursor: pointer;
}
`;
return html`
<style>${styles}</style>
<button @click=${() => setActive(!active)}>
${active ? 'Активен' : 'Не активен'}
</button>
`;
}
customElements.define('toggle-button', component(ToggleButton));
Особенности подхода:
adoptedStyleSheets, а специфические
оставлять внутри шаблонов.<style> при рендере больших компонентов может
негативно сказаться на производительности.:host и :host-context
помогают взаимодействовать с внешним контекстом.adoptedStyleSheets поддерживается не во всех старых
браузерах.<style>
внутри шаблона создаёт новые элементы при каждом обновлении, что нужно
учитывать при оптимизации.Haunted сочетает реактивность функциональных
компонентов с мощными средствами инкапсуляции стилей,
предоставляя гибкость как для локальных, так и для повторно используемых
CSS. Правильное применение Shadow DOM, adoptedStyleSheets и
динамических <style> позволяет создавать
масштабируемые, изолированные и управляемые компоненты с чистой
архитектурой.