16 советов по продвинутой верстке с языком SCSS

Подборка крутых вещей от веб-разработчика из Хельсинки - Jarno Rantanen, 16 советов, которые сделают работу с SCSS интереснее, легче и быстрее.

Или 16 крутых вещей, о которых вы, возможно не знали. Конечно хотелось бы округлить это число, но к сожалению, ничего не вышло.

Я использую SCSS/SASS для большинства своих проектов начиная с 2009 года, и являюсь большим фанатом Compass. Он помог мне пройти через огромное количество разного кросс-браузерного хлама. И даже несмотря на то, что браузеры стали намного лучше ладить с CSS, появилась другая проблема: сложность управления в стилевых страницах становится только больше. SCSS – незаменимый инструмент для её решения.

Это не введение в язык, многие вещи, вероятно, вас не удивят, особенно, если у вас уже есть SCSS. Но тем не менее, если основы вам наскучили и хочется большего, то предлагаем вам ознакомиться с :

  • вложенными селекторами,
  • переменными,
  • mixins,
  • и многим другим

Советы упорядочены от базового уровня к продвинутому. Если вам становится скучно, то предлагаем уже приступить к делу.

1. Префиксация ссылок родительского селектора

Это ситуация будет вам знакома, если вы используете:


a {
    :hover {
        color: red;
    }
}
	

/* скомпилированный CSS */
a:hover {
  color: red;
}

Но может также быть использован с префиксом:


	p {
    body.no-touch {
        display: none; // скрыть сообщение, если устройство не с сенсором
    }
}


/* скомпилированный CSS */
body.no-touch p {
  display: none;
}

Это может быть очень полезно, когда у вас много вложений в правилах, а вы хотите внести изменение в стиль элемента, основанный на совпадении селектора ближе к корню DOM. Флаги возможностей клиента, такие как no-touch класс Modernizr, часто применяются таким образом к элементу. Таким образом, вам не нужно отказываться от своего гнездования, чтобы иметь возможность изменить свой <p> стиль на основе класса <body>.

2. Расширение переменных в селекторах

Переменные также могут быть расширены в селекторах:


$alertClass: "error";

p.message-#{$alertClass} {
    color: red;
}

/* скомпилированный CSS */
p.message-error {
  color: red;
}

... или почти в любом другом месте, как в случае с медиа-запросами или комментариями CSS:


$breakpoint: 768px;

@media (max-width: #{$breakpoint}) {
    /* Этот блок применяется только к viewport-ам шириной <= #{$breakpoint} ... */
}


/* скомпилированный CSS */
@media (max-width: 768px) {
  /* Этот блок применяется только к viewport-ам шириной <= 768px ... */
}

Пример медиа-запроса особенно полезен, если переменная $breakpoint определена где-то в параметрах (скажем, _settings.scss), поэтому ,брейкпоинты всего приложения настраиваются из одного файла.

3. Значения переменных по умолчанию

Если ваш SCSS-модуль можно настроить с использованием глобальных переменных (которые, как правило, являются способом SCSS), вы можете объявить их со значением по умолчанию:


// _my-module.scss:
$message-color: blue !default;

p.message {
    color: $message-color;
}

/* скомпилированный CSS */
p.message {
  color: blue;
}

Но затем вы можете дополнительно переопределить значения модуля по умолчанию перед его включением:


$message-color: black;
@import 'my-module';

/* скомпилированный CSS */
p.message {
  color: black;
}

То есть, присваивание с !default сработает только в том случае, если эта переменная не была инициализирована раньше (в отличие от стандартного присваивания, которое всегда будет перезаписывать возможное предыдущее значение).
Вот сколько модулей SCSS (включая большинство, которые поставляются вместе с Compass) могут быть настроены.

4. Контрольные директивы

SCSS поддерживает стандартный набор директив управления потоком, например, if:


$debug: false; // TODO: переместить в to _settings.scss

article {
    color: black;

    @if ($debug) { // визуализация внутренних элементов разметки
        border: 1px dotted red;
    }
}


/* скомпилированный CSS */
article {
  color: black;
}

Наличие таких флагов времени компиляции в стиле вашего проекта может помочь визуально отлаживать сложные вопросы компоновки, намного быстрее, чем если бы вы просто проверяли по странице за один раз. Также и @for, @eachи @while. Они хороши в случаях, когда вам необходимо множество повторений (S) CSS, например:


@each $name in 'save' 'cancel' 'help' {
    .icon-#{$name} {
        background-image: url('/images/#{$name}.png');
    }
}


/* скомпилированный CSS */
.icon-save {
  background-image: url("/images/save.png");
}
.icon-cancel {
  background-image: url("/images/cancel.png");
}
.icon-help {
  background-image: url("/images/help.png");
}

...и многое другое. Имейте в виду, что, если это нужно вам в повседневной работе, вы, вероятно, немного переусердствуете. Однако добавленная сложность обычно оправдана при создании настраиваемых модулей SCSS и т. д. Если вас это заинтересовало можете ознакомиться с полной документацией по директивам управления.

5. Тип данных - список

Как было показано в предыдущем примере, с помощью @each можно перебирать список. Списки на самом деле являются фундаментальной частью языка SCSS, но быстрая демонстрация их полезности может заключаться в настройке некоторого сгенерированного стиля:


$buttonConfig: 'save' 50px, 'cancel' 50px, 'help' 100px; // TODO: move to _settings.scss

@each $tuple in $buttonConfig {
    .button-#{nth($tuple, 1)} {
        width: nth($tuple, 2);
    }
}


/* скомпилированный CSS */
.button-save {
  width: 50px;
}
.button-cancel {
  width: 50px;
}
.button-help {
  width: 100px;
}	

Это демонстрирует две особенности типа данных списка, а именно nth() функцию доступа к списку , и более интересную неопределенность перечисления: в нотации JavaScript вышеупомянутое будет эквивалентно:


var buttonConfig = [[ 'save', 50 ], [ 'cancel', 50 ], [ 'help', 100 ]];

То есть, списки могут быть разделены как пробелами, так и запятыми, а чередование между двумя обозначениями создает вложенные списки.

6. Определение пользовательских функций

Mixins - хорошо известная часть языка, но SCSS также позволяет вам определять пользовательские функции. Вопреки тому, что можно было бы ожидать, это также можно сделать в чистом SCSS вместо расширения SCSS в Ruby :


@function make-greener($value) {
    @return $value + rgb(0,50,0); // арифметика цветов, кстати, в порядке
}
p {
    background: make-greener(gray);
}


/* скомпилированный CSS */
p {
  background: #80b280;
}

Вышеуказанный цвет - серый с небольшим зеленым оттенком. Пользовательские функции наиболее полезны для избежания повторных вычислений в выражении. Они также неявно документируют это вычисление, давая ему имя: приведенный выше пример, хоть и немного замудрен, он по-прежнему выглядит более ясно, чем просто произвольные вычисления цвета в блоке стиля. SCSS поставляется с множеством полезных встроенных функций , а Compass добавляет еще больше, поэтому сначала проверьте, имеется ли встроенный эквивалент перед тем как его внедрять.

7. Аргументы по умолчанию

Mixins и функции поддерживают значения по умолчанию для аргументов; последний нулевой - N аргументы инициализировать не обязательно:


@mixin foobar($a, $b, $padding: 20px) {
    padding: $padding;
    // ...и что-то с $a и $b
}

p {
    @include foobar(123, "abc"); // отступы по умолчанию в порядке
}

p.important {
    @include foobar(123, "abc", 50px); // переопределение значений по умолчанию
}

8. Ключевые аргументы

Если ваш mixin (или функция) принимает множество аргументов, есть аналогичный call-time синтаксис для выбора аргументов для переопределения:


@mixin foobar($topPadding: 10px, $rightPadding: 20px,
$bottomPadding: 10px, $leftPadding: 20px, $evenMorePadding: 10px) {
    // сделай что-нибудь со всеми этим аргументами
}

p {
    @include foobar($bottomPadding: 50px);
}	

Не имея возможности назвать аргументы во время вызова, сначала вам нужно будет указать $topPadding и $rightPadding. Теперь вместо этого вы можете переопределить только требуемый аргумент, оставив остальные значения по умолчанию.

Обратите внимание, что в тех случаях, когда множество аргументов предназначены для переопределения определенных свойств CSS (например, top-padding, bottom-padding и т. д.), лучше подойдет следующий способ «Перекрытие блока содержимого» (см. ниже).

9. Var-args для функций/mixin

Var-args работают так же, как и в других языках, поддерживающих эту функцию; Любые дополнительные аргументы вызова функции / mixin заворачиваются в список и назначаются аргументу с суффиксом ... :


@mixin config-icon-colors($prefix, $colors...) {
    @each $i in $colors {
        .#{$prefix}#{nth($i, 1)} {
            color: nth($i, 2);
        }
    }
}
@include config-icon-colors('icon-',
    'save'   green,
    'cancel' gray,
    'delete' red
);


/* скомпилированный CSS */
.icon-save {
  color: green;
}
.icon-cancel {
  color: gray;
}
.icon-delete {
  color: red;
}

Вышеупомянутый помощник может использоваться для настройки цветов для шрифтов иконок например, шрифта Awesome , без необходимости повтора. Помощник работает, за счет передачи переменной количества аргументов (после первого, требуемого). Предполагается, что каждый из этих аргументов является кортежем из двух элементов (например, в нотации JavaScript [ "save", "green" ]).

Фактически, синтаксис ... также работает во время вызова, где он расширяет список до отдельных аргументов для подачи в целевой mixin:


@mixin foobar($a, $b, $c) {
    // получает аргументы $a = 5px, $b = red, и так далее
}

$myArgs: 5px red "bla bla";
// в этом месте, можно программно добавлять/удалять аргументы

@include foobar($myArgs...);

Лично мне еще предстоит найти случаи использования этого, но в документации есть хороший вариант для передачи текущих аргументов в другой mixin.

10. Аргументы контент-блока для mixin

Начиная с версии 3.2.0 , SCSS имеет неявный аргумент mixin, доступный через директиву @content. Он позволяет передавать весь контентный блок SCSS в качестве аргумента для mixin:


@mixin only-for-mobile {
    @media (max-width: 768px) {
        @content;
    }
}

@include only-for-mobile() /* заметка: @content начинается здесь */ {
    p {
        font-size: 150%;
    }
} /* @content ends */


/* скомпилированный CSS */
@media (max-width: 768px) {
  p {
    font-size: 150%;
  }
}

Это очень мощная языковая функция для авторов фреймворков, так как она позволяет вам проходить произвольные блоки стиля, которые вы можете выбрать, чтобы обернуть определенными селекторами, повторить в циклах, сделать условными с помощью @ifи т. д. Вы также можете смешивать аргументы стандартного и контентного блока:


@mixin only-for-mobile($breakpoint) {
    @media (max-width: #{$breakpoint}) {
        @content;
    }
}

@include only-for-mobile(480px) {
    p {
        font-size: 150%;
    }
}

11. Шаблон переопределения контент-блока

Рассмотрим mixin, который генерирует селектор и некоторые стили для него, позволяя вызывающему настраивать стиль при необходимости:


@mixin message($class, $color: yellow, $margin: 20px, $padding: 10px) {
    .message-#{$class} {
        border: 1px dotted $color;
        color: $color;
        margin: $margin;
        padding: $padding;
    }
}

Вызов этого mixin с использованием ключевых аргументов (см. выше) довольно удобен, потому что если значения по умолчанию подходят, дополнительных аргументов не требуется. Если это не так, вам нужно только указать те аргументы, которые вы хотите переопределить:


@include message("subtle", $margin: 5px);

Но для этого нужно, чтобы вы перечислили все переопределяемые свойства в сигнатуре mixin. Тем не менее, аргументы блока(content block) содержимого позволяют произвольные переопределения без аргумента jungle:


@mixin message($class) {
    .message-#{$class} {
        border: 1px dotted yellow;
        color: yellow;
        margin: 20px;
        padding: 10px;
        @content;
    }
}

@include message("subtle") {
    margin: 5px;
}


/* скомпилированный CSS */
.message-subtle {
  border: 1px dotted yellow;
  color: yellow;
  margin: 20px;
  padding: 10px;
  margin: 5px;
}

Здесь мы разрешаем каскад CSS с хорошим ole для выполнения переопределения свойства (последний margin переопределяет первый). Кроме того, мы не ограничиваемся переопределением свойств, о которых думал автор mixin. Фактически, это позволяет передавать также вложенные блоки:


@include message("actionable") {
    button { // actionable сообщения могут содержать кнопки для совершения действий
        float: right;
    }
}


/* скомпилированный CSS */
.message-actionable {
  border: 1px dotted yellow;
  color: yellow;
  margin: 20px;
  padding: 10px;
}
.message-actionable button {
  float: right;
}

Этот шаблон может быть полезен в любом библиотечном коде, который выводит нетривиальный стиль с созданными селекторами, поскольку он позволяет пользователю настраивать точку, превышающую то, что предвидел автор библиотеки. Заметьте, однако, что в простых случаях (когда селекционеры не выбрасываются в mixin) это необязательно, так как любые переопределения могут быть сделаны только с использованием стандартного каскада CSS.

12. Пузырение медиа-запросов

@media блоки не должны быть объявлены на корневом уровне таблицы стилей:


	body {
    article {
        p {
            font-size: 100%;
            color: black;
            padding: 10px;

            @media (max-width: 768px) {
                font-size: 150%; // использует больший текст на малых экранах
            }
        }
    }
}


/* скомпилированный CSS */
body article p {
  font-size: 100%;
  color: black;
  padding: 10px;
}

@media (max-width: 768px) {
  body article p {
    font-size: 150%;
  }
}

Обратите внимание, как компилятор «пузырится» над блоком @media на корневом уровне (поскольку обычный CSS не поддерживает селекторное вложение), и внутри него выводит весь стиль, который встречался внутри блока @media в источнике SCSS.
Это очень полезно, так как позволяет вам media-specific tweaks практически в любом месте вашего стиля, прямо там, где они актуальны, вместо того чтобы собирать все эти настройки до конца таблицы стилей и надеяться, что их селекторы будут синхронизироваться с теми, которые они переопределяют (они не будут).

13. Вложенность медиа-запросов

Вышеупомянутый механизм «пузырения» также учитывает вложенность и объединяет все применимые запросы с оператором and:


p {
    @media (max-width: 768px) {
        font-size: 150%; // больший текст для малых экранов
        @media (orientation: landscape) {
            line-height: 75%; // немного сжимает текст из-за вертикального отступа
        }
    }
}


/* скомпилированный CSS */
@media (max-width: 768px) {
  p {
    font-size: 150%;
  }
}
@media (max-width: 768px) and (orientation: landscape) {
  p {
    line-height: 75%;
  }
}

14. Расширение селекторов

SCSS позволяет расширить селектор, скопировав и объединив селекторы в выводе CSS. Интересно, что, хотя механизм (очевидно) очень отличается, семантика @extend аналогична традиционным объектно-ориентированным языкам программирования (например, Java whatnot):


.animal {
    background: gray;
}
.cat {
    @extend .animal;
    color: white;
}


/* скомпилированный CSS */
.animal, .cat {
  background: gray;
}
.cat {
  color: white;
}

То есть, .cat имеет все свойства своего «родительского класса» .animal плюс любые специальные, которые он добавляет или переопределяет. Если в обычном CSS вам нужно будет ссылаться как на расширяющийся класс, так и на родительский класс (например,

), теперь вы можете просто назвать нужный класс (

). То, что он наследует (или не наследует), зависит от определения .cat и может впоследствии измениться, не касаясь разметки.
Классическое наследование, не так ли? Переопределение свойств в «дочернем классе» работает из-за каскада стиля в браузере: стилизация для того же селектора, который появляется позже в файле, всегда выигрывает у стиля, который был перед ним. Возможно, немного неинтуитивно, но это отлично работает, даже если комбинированные селекторы имеют различную специфичность (думаю, .class расширяя #id). Расширение селекторов часто может быть предпочтительным для использования mixins для достижения такого же эффекта:


@mixin animal {
    background: gray;
    border: 1px solid red;
    font-weight: bold;
    font-size: 50px;
    color: red;
    padding: 20px;
}
.cat {
    @include animal;
    color: white;
}
.dog {
    @include animal;
    color: black;
}


/* скомпилированный CSS */
.cat {
  background: gray;
  border: 1px solid red;
  font-weight: bold;
  font-size: 50px;
  color: red;
  padding: 20px;
  color: white;
}
.dog {
  background: gray;
  border: 1px solid red;
  font-weight: bold;
  font-size: 50px;
  color: red;
  padding: 20px;
  color: black;
}

Обратите внимание, что только последнее свойство ( color) отличается, в остальном они одинаковы. Поскольку мы определяем больше типов животных, количество повторяющихся свойств стиля в выходе CSS продолжает расти. Это в отличие от того, как расширение селектора решит ту же проблему:


.animal {
    background: gray;
    border: 1px solid red;
    font-weight: bold;
    font-size: 50px;
    color: red;
    padding: 20px;
}
.cat {
    @extend .animal;
    color: white;
}
.dog {
    @extend .animal;
    color: black;
}


/* скомпилированный CSS */
.animal, .cat, .dog {
  background: gray;
  border: 1px solid red;
  font-weight: bold;
  font-size: 50px;
  color: red;
  padding: 20px;
}
.cat {
  color: white;
}
.dog {
  color: black;
}

Наконец, расширение селектора допускает интеграцию в сторонние библиотеки CSS, которые не обязательно должны быть специально разработаны для расширения или даже записываться в SCSS. Twitter Bootstrap , например, включает приятный стиль для кнопок .btn, но не применяет его к

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Продуктовый аналитик в поддержку
по итогам собеседования
Аналитик данных
Екатеринбург, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ