Как работает CSS-скоупинг в Svelte? Что такое модификатор :global()?
Svelte добавляет уникальный хэш-атрибут к элементам и CSS-селекторам компонента, изолируя стили. :global() отключает хэш для конкретного правила, позволяя стилизовать потомков или динамический HTML.
Как работает CSS-скоупинг в Svelte
Компилятор Svelte анализирует блок <style> и добавляет к каждому CSS-правилу уникальный атрибутный селектор, например .title.svelte-abc123. Одновременно в сгенерированный HTML для элементов этого компонента добавляется атрибут class="svelte-abc123". В итоге стили физически скоупированы: они не протекают в дочерние компоненты и не перезаписываются стилями родителя.
// MyCard.svelte
<style>
.title {
font-size: 1.5rem;
color: #c28b00;
}
</style>
<h2 class="title">Card Title</h2>
После компиляции в DOM появится что-то вроде:
<h2 class="title svelte-abc123">Card Title</h2>
А в CSS-бандле:
.title.svelte-abc123 { font-size: 1.5rem; color: #c28b00; }
Модификатор :global()
:global(selector) говорит компилятору не добавлять к этому правилу атрибутный хэш — правило применится ко всему DOM. Это необходимо в нескольких сценариях:
- Стилизация HTML, генерируемого динамически (например, через
{@html}) - Стилизация дочерних компонентов из родителя
- Глобальные сбросы или переопределения
// App.svelte
<style>
/* Глобальный сброс */
:global(*, *::before, *::after) {
box-sizing: border-box;
}
/* Стилизация вложенного компонента <Button> из родителя */
.card :global(.btn) {
border-radius: 8px;
}
/* Стилизация HTML из {@html markup} */
.content :global(p) {
line-height: 1.7;
}
</style>
Частичный :global
Можно комбинировать скоупированные и глобальные части в одном селекторе:
<style>
/* .wrapper скоупирован, .icon — глобальный */
.wrapper :global(.icon) {
fill: currentColor;
}
</style>
Анимации и keyframes
Keyframes по умолчанию не скоупируются (они глобальны в CSS). Svelte предоставляет директиву -global- для именования, но обычно достаточно добавить уникальный префикс вручную:
<style>
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.box {
animation: fadeIn 0.3s ease;
}
</style>
CSS custom properties и переменные
CSS-переменные (custom properties) прокидываются сквозь скоупинг, поэтому они удобны для тематизации дочерних компонентов без :global:
// Parent.svelte
<ChildComponent --primary-color="#c28b00" />
// ChildComponent.svelte
<style>
.btn {
background: var(--primary-color, #007bff);
}
</style>
Подводные камни
- Стили из
<style>не применяются к элементам внутри дочерних.svelte-компонентов — у них свой хэш. Без:globalстилизовать потомка из родителя невозможно. - Компилятор удаляет CSS-правила, к которым не привязаны элементы в шаблоне этого компонента — динамически добавляемые через
{@html}классы потребуют:global. - Злоупотребление
:globalбез контекстного ограничения (например,:global(.btn)без родительского скоупированного класса) приводит к глобальным утечкам стилей и трудно отлаживается. :globalвнутри@mediaработает, но порядок вложенности важен:@media (max-width: 768px) { :global(.el) { ... } }— правильно.- Svelte 5 сохраняет тот же подход к скоупингу, но синтаксис компонентов изменился — убедитесь, что используете актуальные примеры.
- При использовании CSS-in-JS или внешних UI-библиотек (например, Tailwind) скоупинг Svelte не влияет на утилитарные классы — они приходят из глобального CSS-файла.
- Хэш в атрибуте меняется при изменении контента компонента — не полагайтесь на него в тестах или внешних CSS-правилах.
Common mistakes
- Путать CSS scoping с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «CSS scoping».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.