Приветствую!

Примерно месяц назад я написал статью, в которой выдвинул идею об оптимизации @media screen. Идея заключается в том, чтобы иметь возможность писать значения для всех экранов в одну строку. Более подробно можете почитать по ссылке. Большинство комментариев — это критика относительно реализации, к сожалению идей никто не подкинул. Но если посмотреть с другой стороны, из каждой критики можно вытащить идею, поэтому опираясь на мнение читателей, я поставил себе цель написать миксин, который:

  • легко читается (максимально повторяющий синтаксис sass/scss/css);
  • легко поддерживать (чтобы через год ты понимал, что там написано);
  • гибкий (поддержка максимального количества описаний @media);

Давайте посмотрим, что у меня вышло (репозиторий Github)!

Синтаксис

.class{
    @include media($properties, $orientation);
}

Миксин media поддерживает два параметра $properties и $orientation.
$properties — массив css правил.
$orientation — ориентация экрана (необязательный).

$properties


.class{
    @include media((
        width: 100%;
        height: (lg: 800px, md: 600px, sm: 300px),
        transform: (all: translateX(100px) translateY(100px), sm: translateX(50px) translateY(50px)),
        color: (sm-md: $white, md-lg: $gray),
        font-size: (320: 12px, min-480: 18px, 480-md: 24px, print: 14pt)
    ));
}

Давайте разберем код подробней…

Параметр $properties является массивом, поэтому все свойства берутся в ().

width: 100%;

Обычное правило для всех экранов. компилируется в

.class{
    width: 100%;
  }

Интересное дальше:

height: (lg: 800px, md: 600px, sm: 300px)

Здесь мы описываем высоту для экранов с максимальной шириной lg, md и sm (задаются разработчиком, об этом позже).

Результат компиляции:

@media only screen and (max-width: 1024px) {
    .class{
        height: 800px;
   }
}
@media only screen and (max-width: 768px) {
    .class{
        height: 600px;
   }
}
@media only screen and (max-width: 640px) {
    .class{
        height: 300px;
   }
}

Так же обратите внимание на код ниже:

transform: (all: translateX(100px) translateY(100px), sm: translateX(50px) translateY(50px))

В данном примере присутствует экран all, я думаю вы догадались, что это все экраны. Есть принципиальная разница между all и обычным правилом, как width: 100%. Но об этом так же немного позже.

color: (sm-md: $white, md-lg: $gray)

Здесь я постарался максимально гибко соединить диапазоны экранов между min-width и max-width. Т.е. код, представленный выше скомпилирует минимальную ширину sm (640) — максимальную ширину md (768), и для экранов в этом диапазоне задаст белый цвет текста, или серый для md (768) — lg (1024).

Скомпилированный вариант:

.class{
    @media only screen and (min-width: 768px)  and (max-width: 1024px) {
        color: gray;
    }
}
.class{
    @media only screen and (min-width: 640px)  and (max-width: 768px) {
        color: white;
    }
}

Так же бывают случаи, когда мы не можем опираться только на заданные экраны. Поэтому ес��ь возможность задавать кастомную ширину динамически. Так же если вам внезапно понадобится указать вместо максимальной ширины минимальную, или наоборот, такая возможность так же присутствует:

font-size: (880: 12px, min-480: 18px, 480-md: 24px, print: 14pt)

Первый экран max-width: 880px. По умолчанию ширина проставляется именно максимальная (так же легко поменять). Для этого экрана получим font-size: 12px;
Второй экран min-480 указывает на то, что будем отталкиваться от минимальной ширины экрана 480px (приставка min-), и в итоге получим font-size: 18px для всех экранов шире 480px.

480-md — создаст кастомный минимальный размер 480px и максимальный md (768). Т.е. это вариант диапазона экранов, как в предыдущем примере, только с произвольным значением.

Обратите внимание, что если написать 480-768 мы получим экран -288px, т.е. знак "-" отработает как минус. Поэтому такой пример стоит взять в кавычки «480-768», если конечно вы намерено не писали операцию с "-" (кстати +, *, / так же будет работать).

print: 14pt

print — является неизменным параметром и предназначен для печати:

@media print {
  .class{
    font-size: 14pt;
  }
}

Надеюсь на данном этапе все понятно… Если нет, с радостью разъясню в комментариях.

Давайте перейдем к параметру $orientation


Тут все намного проще. Есть два варианта ориентации экрана landscape и portrait. Собственно, в случае необходимости, вторым параметром прописываем именно эти значения.

.class{
    @include media((
        width: 100%;
        height: (all: 100%, md: 50%)
    ), portrait);
}

В итоге мы получим:

.class{
     width: 100%;
}
@media only screen and (orientation:portrait){
     .class{
          height: 100%;
     }
}
@media only screen and (max-width: 768px) and (orientation:portrait){
     .class{
          height: 50%;
     }
}

На этом примере можно четко увидеть разницу между all и стандартным правилом. В случае width: 100% правило при любых обстоятельствах подпадает под все экраны. В случае all — все экраны с ориентацией portrait/landscape.

Настройки


Две стандартные переменные, которые нужно отредактировать — это $breakpoints и $media-direction. Вот как они выглядят по умолчанию:

$breakpoints: (
        lg: 1024,
        md: 768,
        sm: 640
) !default; //- размеры экранов
$media-direction: max !default; - направление по умолчанию (max/min)

Т.е. для того чтобы создать себе нужное количество экранов с нужными названиями, размерами и первоначальным направлением (min/max) необходимо создать новые переменные в удобном для вас месте:

$breakpoints: (
        desktop: 1280,
        ipad: 1024,
        tablet: 768,
        mobile: 640
);
$media-direction: min;

Этого достаточно чтобы начать работу.

Как еще можно использовать данный миксин?

Если параметр $properties поместить в переменную, тогда можно получить отличную функцию. Например:

$title-style: (
     line-height: (lg: 24px, md: 20px, sm: 16px),
     font-size: (lg: 20px, md: 16px, sm: 12px;),
     text-align: (all: left, sm: center)
     ...
) 
.block{
     h2{
          color: gray;
          @include media($title-style);
     }
     h3{
          color: black;
          @include media($title-style, landscape);
     }
}

Получаем своего рода extend класс. Согласитесь, это может быть очень удобно.

Подведем итоги


Плюсы


  • легко читаемый — код максимально повторяет синтаксис CSS;
  • легко поддерживаемый — экраны прописаны достаточно понятно, и разобраться в коде не будет проблем;
  • гибкий — мы получили возможность описывать @media screen для всех экранов, диапазоны экранов, минимальную/максимальную ширину динамически, в любой момент есть возможность прописать кастомную ширину, а так же не отходя от кода настраивать правила для печати.

Минусы


  • Отсутствует описание экрана по высоте. К этой части я пока не добрался, и если данный миксин будет вам интересен, я обязательно допишу и такую возможность. Так что не забывайте писать комментарии, чтобы я мог понимать насколько для вас это актуально и полезно.
  • Отсутствует возможности прописывать @include mixin для разных экранов. На данный момент такую возможность реализовать в приведенном выше миксине посредством Sass в принципе невозможно. Насколько это критично, я оставлю вам на размышление, решайте сами, стоит ли жертвовать подобной функцией или нет. Лично для меня это вообще не критично, так как я не могу припомнить, чтобы я вообще когда-нибудь прописывал @include mixin внутри @media screen.

Вывод


Думаю, цель достигнута. Миксин получился достаточно удобным и лаконичным.

Если вы со мной не согласны, пишите комментарии. В любом случае с удовольствием выслушаю ваш совет по улучшению данного миксина, или попробую извлечь что-то полезное из вашей критики.

P.S. Скачать миксин вы можете в репозитории Github по ссылке. _mixin.scss — тот файл, который вам нужен. Так же файл package.json содержит в себе плагины для Gulp, которы�� было бы неплохо подключить:

  • gulp-autoprefixer добавит префиксы браузеров, там где это необходимо;
  • gulp-group-css-media-queries отлично группирует @media screen;
  • gulp-minify-css оптимизирует css, можно сказать, что данный плагин необходим.

Уверен, что для webpack или для любого другого сборщика, вы без труда найдете аналоги.

Спасибо за внимание!