Урок 04

Вложенность (nesting)

Что такое вложенность

Nesting — запись CSS-правил внутри других правил.

.card {
    padding: 20px;

    .title {
        font-size: 20px;
    }

    .text {
        color: gray;
    }
}

Компилируется в:

.card { padding: 20px; }
.card .title { font-size: 20px; }
.card .text { color: gray; }

Что даёт: CSS повторяет структуру HTML.

Вложенность селекторов

Без вложенности:

.nav ul { list-style: none; }
.nav ul li { display: inline; }
.nav ul li a { color: black; }

С вложенностью:

.nav {
    ul {
        list-style: none;

        li {
            display: inline;

            a {
                color: black;
            }
        }
    }
}

Что даёт: видно структуру.

& — родительский селектор

& — ссылка на родителя.

.button {
    background: blue;

    &:hover {
        background: darkblue;
    }

    &.active {
        background: green;
    }

    &:focus-visible {
        outline: 2px solid blue;
    }
}

Компилируется в:

.button { background: blue; }
.button:hover { background: darkblue; }
.button.active { background: green; }
.button:focus-visible { outline: 2px solid blue; }

Правило: & без пробела — приклеивает к родителю. С пробелом — потомок.

& в BEM

BEM-модификаторы:

.button {
    padding: 10px 20px;

    &--large {
        padding: 16px 32px;
        font-size: 18px;
    }

    &--primary {
        background: blue;
    }

    &--danger {
        background: red;
    }
}

Компилируется в:

.button { padding: 10px 20px; }
.button--large { padding: 16px 32px; font-size: 18px; }
.button--primary { background: blue; }
.button--danger { background: red; }

Правило: &--modifier — стандарт для BEM.

&__element

.card {
    padding: 20px;

    &__title {
        font-size: 20px;
    }

    &__text {
        color: gray;
    }
}

Компилируется в:

.card { padding: 20px; }
.card__title { font-size: 20px; }
.card__text { color: gray; }

Что даёт: BEM-структура без длинных селекторов.

& с контекстом

Родитель + доп. класс:

.button {
    &--primary {
        &:hover {
            background: darkblue;
        }
    }
}

Компилируется в:

.button--primary:hover { background: darkblue; }

& в конце

Родитель становится потомком:

.icon {
    .card & {
        color: red;
    }
}

Компилируется в:

.card .icon { color: red; }

Что даёт: стили родителя, когда он внутри чего-то.

Правило: & можно использовать в любом месте селектора.

Комбинаторы

.card {
    > .title {
        font-size: 20px;
    }

    + .card {
        margin-top: 16px;
    }

    ~ .card {
        margin-top: 16px;
    }
}

Что даёт: прямые дети, соседи.

Вложенные медиазапросы

.card {
    padding: 16px;

    @media (min-width: 768px) {
        padding: 24px;
    }
}

Компилируется в:

.card { padding: 16px; }
@media (min-width: 768px) {
    .card { padding: 24px; }
}

Что даёт: медиазапрос рядом с правилом.

Правило: в SCSS медиазапросы вкладываются, в CSS — нет.

Вложенные @supports

.grid {
    display: flex;

    @supports (display: grid) {
        display: grid;
    }
}

Что даёт: прогрессивное улучшение.

@at-root

Выйти из вложенности:

.parent {
    @at-root .child {
        color: red;
    }
}

Компилируется в:

.child { color: red; }

Когда: нужно вложить для читаемости, но получить плоский CSS.

Правило: редко нужно.

Глубина вложенности

Плохо:

.page {
    .container {
        .row {
            .col {
                .card {
                    .title {
                        .link {
                            color: red;
                        }
                    }
                }
            }
        }
    }
}

Компилируется в:

.page .container .row .col .card .title .link { color: red; }

Проблемы:

  • Высокая специфичность.
  • Тяжело переопределить.
  • Медленнее.
  • Хрупко.

Правило: максимум 3 уровня вложенности.

BEM + nesting

Правильно:

.card {
    padding: 20px;

    &__title {
        font-size: 20px;

        &--large {
            font-size: 28px;
        }
    }

    &__text {
        color: gray;
    }

    &:hover {
        border-color: blue;
    }
}

Компилируется в плоский CSS:

.card { }
.card__title { }
.card__title--large { }
.card__text { }
.card:hover { }

Что даёт: читаемость SCSS + плоский CSS.

Практика

1. BEM-компонент:

.button {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.15s;

    &--primary {
        background: #c1442e;
        color: white;

        &:hover {
            background: #a83926;
        }
    }

    &--secondary {
        background: transparent;
        border: 1px solid #e5e5e5;

        &:hover {
            border-color: #c1442e;
        }
    }

    &--large {
        padding: 16px 32px;
        font-size: 18px;
    }
}

2. Медиазапросы внутри компонента.

3. & в комбинаторах.

4. Проверьте скомпилированный CSS — плоский?

Частые ошибки

1. Слишком глубокая вложенность:

.a .b .c .d .e .f { }

Решение: BEM, плоские селекторы.

2. & с пробелом:

.button {
    & :hover { }   // .button :hover (не то)
}

Решение: &:hover.

3. Всё в одном месте:

body {
    .button { }
    .card { }
    .modal { }
}

Решение: отдельные файлы.

4. Дубли стилей:

.button { }
.card .button { }
.modal .button { }

Решение: один .button, модификаторы.

5. & для всего:

.title { & { & { } } }

Решение: использовать осмысленно.

6. Забыть про специфичность:

.page .card .title { }   // 0,3,0

Решение: BEM, 0,1,0.

7. Вложенность как в HTML:

Повторяет структуру один-в-один

Решение: по смыслу, не по DOM.

8. @at-root везде:

Хуже читается

Решение: только когда нужно.

Итог

  • Nesting — CSS как HTML.
  • & — родитель.
  • &:hover — псевдокласс.
  • &--modifier — BEM.
  • &__element — BEM.
  • Медиазапросы вкладываются.
  • Максимум 3 уровня.
  • @at-root — редко.
  • BEM + nesting — читаемость + плоский CSS.

Дальше — партиалы и импорты.