@extend
Часто при проектировании страницы возникают ситуации, когда один класс должен иметь все стили другого класса, а также свои собственные специфические стили. Например, методология BEM методология рекомендует модификаторные классы, которые применяются к тем же элементам, что и блочные или элементные классы. Однако это может привести к загромождённому HTML, подверженному ошибкам из-за забывания включить оба класса, и может вносить несемантические стили в разметку.
<div class="error error--serious">
Oh no! You've been hacked!
</div> /* CSS */
.error {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
} Правило @extend в Sass решает эту проблему. Оно записывается @extend <selector>, и сообщает Sass, что один селектор должен унаследовать стили другого.
.error {
border: 1px #f00;
background-color: #fdd;
&--serious {
@extend .error;
border-width: 3px;
}
}// SASS
.error
border: 1px #f00
background-color: #fdd
&--serious
@extend .error
border-width: 3px/* CSS */
.error, .error--serious {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
}Когда один класс расширяет другой, Sass применяет стили ко всем элементам, соответствующим расширяемому классу, как если бы они также соответствовали расширяемому классу. Когда один селектор класса расширяет другой, это работает так, как будто вы добавили расширяемый класс к каждому элементу в вашем HTML, который уже содержит расширяющий класс. Вы можете просто написать class="error--serious", и Sass позаботится о том, чтобы он был стилизован так, как будто у него также есть class="error".
Конечно, селекторы используются не только самостоятельно в правилах стиля. Sass знает, как расширять везде, где используется селектор. Это гарантирует, что ваши элементы будут стилизованы точно так же, как если бы они соответствовали расширяемому селектору.
.error:hover {
background-color: #fee;
}
.error--serious {
@extend .error;
border-width: 3px;
}// SASS .error:hover background-color: #fee .error--serious @extend .error border-width: 3px
/* CSS */
.error:hover, .error--serious:hover {
background-color: #fee;
}
.error--serious {
border-width: 3px;
}Расширения обрабатываются после компиляции остальной части вашего стилевого файла. В частности, это происходит после обработки родительских селекторов. Это означает, что если вы @extend .error, это не повлияет на внутренний селектор в .error { &__icon { ... } }. Это также означает, что родительские селекторы в SassScript не могут увидеть результаты расширения.
Как это работает
В отличие от миксов, которые копируют стили в текущее правило стиля, @extend обновляет правила стиля, содержащие расширяемый селектор, так, чтобы они также содержали расширяющий селектор. При расширении селекторов Sass использует интеллектуальное объединение:
-
Он никогда не генерирует селекторы, такие как
#main#footer, которые не могут соответствовать каким-либо элементам. -
Он гарантирует, что сложные селекторы чередуются таким образом, чтобы они работали независимо от порядка вложенности элементов HTML.
-
Он убирает избыточные селекторы по возможности, сохраняя при этом специфичность, не меньшую, чем у расширяющего селектора.
-
Он понимает, когда один селектор соответствует всем элементам, соответствующим другому, и может объединить их.
-
Он интеллектуально обрабатывает комбинаторы, универсальные селекторы и псевдоклассы, содержащие селекторы.
.content nav.sidebar {
@extend .info;
}
// This won't be extended, because `p` is incompatible with `nav`.
p.info {
background-color: #dee9fc;
}
// There's no way to know whether `<div class="guide">` will be inside or
// outside `<div class="content">`, so Sass generates both to be safe.
.guide .info {
border: 1px solid rgba(#000, 0.8);
border-radius: 2px;
}
// Sass knows that every element matching "main.content" also matches ".content"
// and avoids generating unnecessary interleaved selectors.
main.content .info {
font-size: 0.8em;
}// SASS .content nav.sidebar @extend .info // This won't be extended, because `p` is incompatible with `nav`. p.info background-color: #dee9fc // There's no way to know whether `<div class="guide">` will be inside or // outside `<div class="content">`, so Sass generates both to be safe. .guide .info border: 1px solid rgba(#000, 0.8) border-radius: 2px // Sass knows that every element matching "main.content" also matches ".content" // and avoids generating unnecessary interleaved selectors. main.content .info font-size: 0.8em
/* CSS */
p.info {
background-color: #dee9fc;
}
.guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar {
border: 1px solid rgba(0, 0, 0, 0.8);
border-radius: 2px;
}
main.content .info, main.content nav.sidebar {
font-size: 0.8em;
}💡 Интересный факт:
Вы можете напрямую использовать интеллектуальное объединение Sass, используя функции селектора! Функция selector.unify() возвращает селектор, соответствующий пересечению двух селекторов, в то время как функция selector.extend() работает так же, как @extend, но над одним селектором.
Поскольку @extend обновляет правила стиля, содержащие расширяемый селектор, их стили имеют приоритет в каскаде на основе того, где появляются правила стиля расширяемого селектора, а не на основе того, где появляется правило стиля @extend. Это может быть запутанным, но просто помните: это тот же приоритет, который эти правила имели бы, если бы вы добавили расширяемый класс в ваш HTML!
Селекторы-заполнители
Иногда вам нужно написать правило стиля, которое только предназначено для расширения. В этом случае вы можете использовать селекторы-заполнители, которые выглядят как селекторы классов, начинающиеся с % вместо .. Любые селекторы, включающие заполнители, не включаются в выходной CSS, но селекторы, которые их расширяют, включаются.
.alert:hover, %strong-alert {
font-weight: bold;
}
%strong-alert:hover {
color: red;
}// SASS .alert:hover, %strong-alert font-weight: bold %strong-alert:hover color: red
/* CSS */
.alert:hover {
font-weight: bold;
}Приватные заполнители
Как и члены модулей, селектор-заполнитель может быть помечен как приватный, начав его имя с - или _. Приватный селектор-заполнитель может быть расширен только внутри стилевого файла, который его определяет. Для других стилевых файлов он будет выглядеть так, как будто этого селектора не существует.
Область действия расширения
Когда один стилевой файл расширяет селектор, это расширение повлияет только на правила стиля, написанные в входящих модулях — то есть, модулях, которые загружаются этим стилевым файлом с помощью правила @use или правила @forward, модулях, загруженных этими модулями, и так далее. Это помогает сделать ваши правила @extend более предсказуемыми, гарантируя, что они повлияют только на те стили, которые вы знали, когда их писали.
Расширения вообще не имеют области действия, если вы используете правило @import. Они повлияют не только на каждый импортируемый вами стилевой файл, но и на каждый стилевой файл, импортирующий ваш стилевой файл, всё, что эти стилевые файлы импортируют, и так далее. Без @use, расширения являются глобальными.
Обязательные и необязательные расширения
Обычно, если @extend не соответствует ни одному селектору в стилевом файле, Sass выдаст ошибку. Это помогает защитить от опечаток или от переименования селектора без переименования селекторов, которые от него наследуют. Расширения, которые требуют, чтобы расширяемый селектор существовал, являются обязательными.
Однако это не всегда то, что вы хотите. Если вы хотите, чтобы @extend ничего не делал, если расширяемый селектор не существует, просто добавьте !optional в конец.
Расширения или миксины?
Расширения и миксы — это оба способа инкапсуляции и повторного использования стилей в Sass, что естественным образом поднимает вопрос, когда использовать что. Миксины, очевидно, необходимы, когда вам нужно настроить стили с помощью аргументов, но что если это просто набор стилей?
Как правило, расширения — лучший вариант, когда вы выражаете взаимоотношение между семантическими классами (или другими семантическими селекторами). Поскольку элемент с классом .error--serious является ошибкой, имеет смысл, чтобы он расширял .error. Но для наборов стилей, не связанных с семантикой, использование миксина может предотвратить проблемы с каскадом и упростить настройку в будущем.
💡 Интересный факт:
Большинство веб-серверов сжимают CSS, который они предоставляют, с помощью алгоритма, очень хорошо работающего с повторяющимися идентичными кусками текста. Это означает, что, хотя миксины могут генерировать больше CSS, чем расширения, они, вероятно, не существенно увеличат объём, который пользователям нужно загрузить. Поэтому выбирайте тот инструмент, который лучше подходит для вашей ситуации, а не тот, который генерирует меньше CSS!
Ограничения
Запрещённые селекторы
- Dart Sass
- ✓
- LibSass
- ✗
- Ruby Sass
- ✗
LibSass и Ruby Sass в настоящее время позволяют расширять составные селекторы, такие как .message.info. Однако это поведение не соответствует определению @extend: вместо того, чтобы стилизовать элементы, соответствующие расширяющему селектору, как будто у него есть class="message info", что могло бы повлиять на правила стиля, включающие либо .message, или .info, оно стилизует их только правилами, включающими как .message, так и info.
Для сохранения простоты и понятности определения @extend, а также для поддержания чистоты и эффективности реализации, это поведение теперь устарело и будет удалено в будущих версиях.
См. страницу о разрыве совместимости для получения дополнительной информации.
Только простые селекторы—отдельные селекторы, такие как .info или a—могут быть расширены. Если .message.info можно было бы расширить, определение @extend гласит, что элементы, соответствующие расширителю, будут стилизованы так, как будто они соответствуют .message.info. Это то же самое, что соответствие и .message и .info, поэтому нет никакой выгоды в написании этого вместо @extend .message, .info.
Аналогично, если .main .info можно было бы расширить, это (почти) то же самое, что расширение .info самостоятельно. Незначительные различия не стоят путаницы, что это делает что-то существенно отличное, поэтому это также запрещено.
.alert {
@extend .message.info;
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.
@extend .main .info;
// ^^^^^^^^^^^
// Error: write @extend .info instead.
}// SASS .alert @extend .message.info // ^^^^^^^^^^^^^ // Error: Write @extend .message, .info instead. @extend .main .info // ^^^^^^^^^^^ // Error: write @extend .info instead.
HTML Эвристики
Когда @extend переплетает сложные селекторы, он не генерирует все возможные комбинации селекторов предков. Многие из селекторов, которые он мог бы сгенерировать, вряд ли будут соответствовать реальному HTML, а генерация всех из них сделает таблицы стилей слишком громоздкими для очень малой реальной выгоды. Вместо этого он использует эвристику: он предполагает, что предки каждого селектора будут самодостаточными, не переплетёнными с предками других селекторов.
header .warning li {
font-weight: bold;
}
aside .notice dd {
// Sass doesn't generate CSS to match the <dd> in
//
// <header>
// <aside>
// <div class="warning">
// <div class="notice">
// <dd>...</dd>
// </div>
// </div>
// </aside>
// </header>
//
// because matching all elements like that would require us to generate nine
// new selectors instead of just two.
@extend li;
}// SASS header .warning li font-weight: bold aside .notice dd // Sass doesn't generate CSS to match the <dd> in // // <header> // <aside> // <div class="warning"> // <div class="notice"> // <dd>...</dd> // </div> // </div> // </aside> // </header> // // because matching all elements like that would require us to generate nine // new selectors instead of just two. @extend li
/* CSS */
header .warning li, header .warning aside .notice dd, aside .notice header .warning dd {
font-weight: bold;
}Расширение в @media
Хотя @extend разрешено внутри @media и других правил CSS, оно не может расширять селекторы, которые появляются за пределами своего правила. Это потому, что расширяющий селектор применяется только в данном контексте среды, и нет способа убедиться, что это ограничение сохраняется в сгенерированном селекторе без дублирования всего правила стиля.
@media screen and (max-width: 600px) {
.error--serious {
@extend .error;
// ^^^^^^
// Error: ".error" was extended in @media, but used outside it.
}
}
.error {
border: 1px #f00;
background-color: #fdd;
}// SASS
@media screen and (max-width: 600px)
.error--serious
@extend .error
// ^^^^^^
// Error: ".error" was extended in @media, but used outside it.
.error
border: 1px #f00
background-color: #fdd
© 2006–2025 the Sass team, and numerous contributors
Licensed under the MIT License.
https://sass-lang.com/documentation/at-rules/extend