Модуль 3 · урок 6 з 11
ViewEncapsulation
Стилі компонента за замовчуванням не витікають назовні й не приймають чужого впливу.
Це зручно рівно доти, доки не треба стилізувати щось усередині чужого компонента — і тоді
починається війна з ::ng-deep. Розберемось, як воно влаштоване й що робити натомість.
Три режими
| Режим | Як працює | Коли |
|---|---|---|
Emulated (за замовчуванням) | Angular додає елементам унікальні атрибути й дописує їх у селектори | Майже завжди |
None | Стилі стають глобальними, як звичайний CSS-файл | Тема, скидання, стилі для чужих компонентів |
ShadowDom | Справжня ізоляція браузером через Shadow DOM | Віджети для вбудовування в чужі сторінки |
Як влаштована Emulated
/* ти пишеш */
.card__title { font-weight: 700; }
/* Angular генерує */
.card__title[_ngcontent-abc-1] { font-weight: 700; }
<h3 class="card__title" _ngcontent-abc-1>…</h3>
Тобто ізоляції на рівні браузера немає — це домовленість через атрибути. Наслідки, які варто знати:
- Специфічність зростає на одиницю (атрибут важить як клас) — тому чужі глобальні стилі перебити стало трохи легше, а свої компонентні ззовні — трохи важче.
- Атрибут ставиться лише на елементи шаблону цього компонента. Розмітка, яка прийшла
через
<ng-content>або з дитячого компонента, його не має — тому й не стилізується. - Динамічно створений через
innerHTMLвміст теж лишається без атрибута.
:host і :host-context
:host {
display: block; /* хост за замовчуванням inline — класична пастка */
border: 1px solid var(--rule);
}
:host(.is-selected) { /* коли на самому елементі є клас */
border-color: var(--accent);
}
:host([data-size="sm"]) { padding: 8px; }
:host-context(.dense) .card { padding: 4px; } /* коли клас є в будь-якого предка */
:host — це єдиний спосіб стилізувати сам елемент компонента: у шаблоні його немає,
бо шаблон описує лише вміст.
::ng-deep і чому його не варто
/* ❌ пробиває ізоляцію: стиль тече в усі вкладені компоненти */
::ng-deep .mat-mdc-form-field { padding: 0; }
/* трохи менше зло: обмежити своїм хостом */
:host ::ng-deep .mat-mdc-form-field { padding: 0; }
Проблем із ::ng-deep дві. Перша — він офіційно застарілий: колись його приберуть.
Друга гірша: без :host перед ним стиль стає фактично глобальним і зачіпає компоненти,
про які ти не думав. Знайти таке потім важко — селектор лежить у файлі одного компонента,
а ламає інший.
Що робити натомість
/* 1. CSS-змінні проходять крізь будь-яку ізоляцію — це головний інструмент */
:host {
--field-padding: 4px 8px;
}
/* 2. Глобальні стилі для чужої бібліотеки — у styles.scss, свідомо й в одному місці */
/* 3. Для власних компонентів — нормальний API: input, клас-модифікатор, токен */
Ізоляція обмежує селектори, але не каскад значень. Кастомна властивість, оголошена
на :host або на :root, доступна всередині будь-якого нащадка —
навіть у Shadow DOM. Тому дизайн-токени з модуля 1 працюють скрізь і не потребують
жодних ::ng-deep. Наступний урок саме про це.
ViewEncapsulation.None: коли це доречно
@Component({
selector: 'app-root',
encapsulation: ViewEncapsulation.None, // свідомо, з коментарем чому
})
Виправданих випадків небагато: кореневий компонент, який задає тему; окремий компонент-«міст»
для стилізації сторонньої бібліотеки. У всіх інших місцях None — це той самий
::ng-deep, тільки на весь файл.
Лід просить явну ViewEncapsulation. Тобто справа не в тому, щоб щось
вимкнути, а в тому, щоб у метаданих компонента було видно намір. Найчастіше це
ViewEncapsulation.Emulated написана явно — і одне-два свідомі виключення
з коментарем.