SASS: Оптимизируем media screen

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

    Примерно месяц назад я написал статью, в которой выдвинул идею об оптимизации @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 или для любого другого сборщика, вы без труда найдете аналоги.

    Спасибо за внимание!
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      0
      Намного лучше относительно прошлой реализации. Жаль нельзя внутри миксины использовать. на реальном проекте была такая задача — на десктопе одни иконки, на мобильном другие. А испльзуются они через миксин.
      Но честно говоря я у себя применять такой миксин поостерегся бы — после меня кто-то другой будет поддерживать этот код и столкнется с чем-то неизвестным. Так же я так понял нельзя использовать css свойства, которые записаны через дефис, или это не так?
        0
        можно использовать абсолютно любые свойства все те что и в css, даже с использованием переменных. Т.е. можно написать font-weight: (sm: $font-weight-bold). Дефис работает как минус только в случае описания экрана вроде 480-320 (т.е. два числа)… Если написать sm-880, тогда получим строку которая разобьется на два экрана sm и 880px
          0
          А почему не поддерживается свойство max-height?
            0
            я пока не дошел до этого… нужно подумать как реализовать, чтобы не выбиваться из общего концепта. Надеюсь доработать в будущем
              0
              Я немного не правильно написал, чем завел в заблуждение и Вас и себя… max-height — я имел ввиду описание экрана по высоте, а не как свойство. Прошу прощения. В статье отредактировал этот пункт
                0
                Теперь понял =)
            0
            По поводу миксинов внутри… я честно потратил пару дней на поиски возможности реализовать, но столкнулся с тем что в sass невозможно динамически вызывать миксины. Т.е.
            @include $mixin-name()
            — не прокатит ни в каком виде
            0
            А чего Bootstrap 4 миксины не использовать бы? Там всё хорошо работает. github.com/twbs/bootstrap/blob/v4-dev/scss/mixins/_breakpoints.scss
              0
              Спасибо за ссылку, раньше не натыкался на него. Бегло пробежался, и если я правильно понял, bootstrap миксин не избавляет от дублирования свойств, по сути у нас похожи, но все же разные подходы. Но как альтернатива, то конечно…
                0
                Про дублирование свойств это просто экономия на спичках. Тот код, который вы показывали в прошлой статье, якобы ваш миксин экономит место, я бы вам посоветовал изучить подход вёрстки mobile first, тогда большинство дублирующего кода просто отпадёт.
                  0
                  У mobile first есть свои плюсы и недостатки, но я макет для мобильных экранов получаю максимум в 20% случаев, а вот десктопные постоянно. Еще часто бывает, что по дизайну необходимо экранов достаточно много. Если 5 экранов, 5 раз дублировать код. всегда проще поменять в одном месте, согласитесь. Так что с «экономия на спичках», не согласен абсолютно. Вы можете со мной соглашаться или нет, дело ваше, но я опробовал вариант из первой статьи на не большом сайте, с большим количеством экранов, и реально править было легко и быстро. Да, код слабо читаемый, но для этого и был написан миксин который представлен в данной статье.
                  0
                  не избавляет от дублирования свойств

                  www.npmjs.com/package/gulp-group-css-media-queries?
                  0
                  Если я не ошибаюсь, это тот же миксин respond-to.
                    0
                    вот и мне так кажется, но немного расширенный… там есть возможность задавать минимальный и максимальный размер экрана. И кажется еще что-то с вариациями экранов
                  0

                  А мы у себя заюзали миксин к конкретному правилу (кстати можно подправить что бы как у вас передавался масив свойств)
                  ну собственно gist-link
                  миксин зовется response-rule
                  =) критиру и предложения в студию
                  (по поводу трудности сопровождения думаю что решается прямыми руками и знанием grep-sed-awk)

                    0

                    Ваш миксин, по сути похож на мой первый вариант https://m.habrahabr.ru/post/350466/, только с названиями экранов. Можете почитать отзывы, и сделать выводы

                      0
                      Спасибо прочитал про первый вариант, и теперь понял почему такая реализация!
                        0
                        вы не думали послать к черту названия эканов?

                        include media(480px)
                        include media(590px)
                        include media(850px)

                        И не нужно голову забивать размерами. Когда у вас при уменьшении экрана все плохо, вы подбираете нужный брейкпоинт и все.
                        Если думаете, что у вас разрастется зоопарк media, то не нужно делать предварительных выводов, а в случае группировки
                        www.npmjs.com/package/gulp-group-css-media-queries

                        В случае типографики www.npmjs.com/package/rfs
                      0
                      Привет автору, заморочился ты конешн знатно, но я тут наткнулся на гитхабе на такую штуку, подскажите, объясните, на сколько ваш вариант лучше/хуже чем данный?
                        0
                        Я не знаком с PostCSS, но похоже что он работает только с CSS. У меня же миксин написан на Scss (Sass) и предназначен для работы в scss файлах. В целом, синтаксис PostCSS конечно выглядит очень не плохо, но смысл похож на тот что и у меня. Миксин занимает немного больше места в файлах. Но у моего миксина есть возможно прописывать диапазоны ширины экранов. Например 480 — 640 будет означать @media screen (min-width: 480px) and (max-width: 640px). А так же в случае необходимости можно указать динамически min или max width например max-480 — выдаст @media screen(max-width: 480px), а min-880 выдаст, соответственно @media screen(min-width: 880px). Во всем остальном не увидел разницы.

                      Only users with full accounts can post comments. Log in, please.