Адаптивная верстка с помощью SCSS и Gulp

Доброго времени суток, коллеги!

Давно задавался вопросом, как оптимизировать и сделать более удобным код используя @media screen. Ибо код

body{font-size: 1em;}
@media screen and (max-width: 1024px){
    body{font-size: 0.8em;}
}

создает достаточно крупные файлы. Конечно, можно написать миксин вроде respond-to(tablet), его многие используют и выглядит он приятней и удобней чем выше описанный вариант, но основную проблему он не решает. В крупных проектах получим уйму @include и разобраться не то чтобы сложно, но приходится много карулесить по файлу, в поисках нужного класса или вложения.

И как же все таки получить читаемый, ну или как минимум файл в два или даже в три раза меньше? Такой вопрос я задавал себе, и пришел к ответу:

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

В итоге должно получиться что-то вроде

body{font-size: [1em, 0.8em]}

где первое значение [1em] — для всех экранов, а второе [0.8em] для экранов меньше 1024px. Согласитесь, такой вариант значительно упрощает жизнь и уменьшает файл в разы, легко описывает правило для всех разрешений.

На первый вопрос ответ найден. А вот другой вопрос, как этого добиться? Первая мысль была написать модуль для Gulp, который бы переносил значения из [] в @media screen… Но такой вариант не очень хорош, так как во-первых, это достаточно сложный процесс. Нужно многое продумать, уметь делать не конфликтующие модули, в общем, в моем случае это проблематично. Во-вторых, такой синтаксис не воспримет ни один idea, и подобные выходки будут подчеркиваться красным, что тоже не есть хорошо.

Поэтому я решил что @mixin в Sass вариант подходящий, хоть и не такой изящный и читабельный как использование []. И так, вот что у меня получилось:

/*Прописываем массив нужных размеров*/
$screens: (all, 1024, 640); 
@mixin media($property, $values){
    /*разбиваем введенные значения в цикле*/
    @for $i from 1 through length($values) { 
        /*Проверяем, если значение прописано как '' тогда пропускаем его*/
        @if nth($values, $i) != ''{ 
            @if nth($screens, $i) == 'all'{ 
                /*Если это первое значение, тогда значение пропишется без @media screen */
                #{$property}: unquote(#{nth($values, $i)});
            } @else {
                /*иначе помещаем свойство в @media screen с соответствующим индексом*/
                @media screen and (max-width: nth($screens, $i) + 'px') {
                    #{$property}: unquote(#{nth($values, $i)});
                }
            }
        }
    }
}

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

body{
     @include media(font-size, (1em, 0.8em));
}

CSS:

body{font-size: 1em;}
@media screen only (max-width: 1024px){
	body{font-size: 0.8em;}
}

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

body{
     @include media(font-size, (1em, 0.8em, 0.6em));
     @include media(color, (black, green, red));
}
.class{
     @include media(display, (none, block, none));
}

в итоге получил не лучший CSS:

body{font-size: 1em;}
@media screen only (max-width: 1024px){
	body{font-size: 0.8em;}
}
@media screen only (max-width: 640px){
	body{font-size: 0.6em;}
}
body{color: black;}
@media screen only (max-width: 1024px){
	body{color: green;}
}
@media screen only (max-width: 640px){
	body{color: red;}
}
.class{display:none;}
@media screen only (max-width: 1024px){
	.class{display:block;}
}
@media screen only (max-width: 640px){
	.class{display:none;}
}

От дублирования @media screen only (max-width: 1024px) нужно как-то избавляться. Для этого я установил модуль для Gulp — gulp-group-css-media-queries, описание по установке здесь

Результат:

body{font-size: 1em;}
body{color: black;}
.class{display: none;}
@media screen only (max-width: 640px){
	body{font-size: 0.6em;}
	body{color: red;}
	.class{display: none;}
}
@media screen only (max-width: 1024px){
	body{font-size: 0.8em;}
	body{color: green;}
	.class{display: block;}
}

Так намного лучше, но все равно не хорошо, так как каждое значение описывается повторно с новым элементом body. В решении этой проблемы мне помог модуль gulp-minify-css, описание здесь.

И вуаля:

body{
     font-size: 1em;
     color: black;
}
.class{display: none;}
@media screen only (max-width: 640px){
	body{
		font-size: 0.6em;
		color: red;
	}
	.class{display: none;}
}
@media screen only (max-width: 1024px){
	body{
		font-size: 0.8em;
		color: green;
	}
	.class{display: block;}
}

Получаем идеальный css используя минимум строк в Sass. Я использую Gulp+Sass, но для любителей Less+Webpack или Sass+Compass, или в любой другой связке, думаю не будет проблем переписать миксин и найти нужные модули.

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

.class{
	@include media(color, (red, '', '', '', green, black));
	@include media(width, (50%, '', '', '', '', 100%));
}

В таком случае по началу немного теряешься и не всегда понятно для какого экрана правишь, но это не было проблемой или неудобством для меня (открыл, проверил, поменял). В случае если у вас 2-3 разрешения, выглядеть все будет читабельно и лаконично, хоть и не привычно по началу.

Вывод


Данным миксином я добился желаемого результата, все варианты экранов в одну строку. Конечно код сыроват, и возмножно что-то подобное уже написанно кем-то куда лучше чем это сделал я, но я не нашел, может плохо искал. В любом случае буду рад услышать ваше мнение по данному методу. Может кто-то знает что-то по-лучше, или знает как приукрасить данный метод, пишите. Возможно я написал полную ересь, и вы никогда не будете исользовать данный метод потому что… В таком случае прошу обосновать почему, для меня важно понять насколько может быть полезен/бесполезен данный метод.

P.S.
Для тех кто решил опробовать, хочу уточнить что если вы используете сокращенные свойства типа маrgin: 0 auto;, тогда «0 auto» обязательно нужно взять в кавычки, иначе получите margin: 0;

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

More
Ads

Comments 23

    –2
    На дворе 21 век, писать в исходниках величины в em — плохая идея.

    В исходниках нужно указывать величины, которые используются в макете — px. А при компиляции CSS эти величины нужно пересчитывать в rem. Что на scss делается очень просто…
      0
      em указаны просто как для примера, в данной статье не ведется речь о величинах, что лучше px, em, rem и т.д.
        +2
        А зачем пересчитывать на выходе в rem? Вроде бы браузеры умеют правильно масштабировать значения в пикселях.
          0

          Пересчёт делается для того, чтобы если клиент посмотрит в масштабе и скажет что ему понравился вид сайта на 90%, можно было одним махом все уменьшить/увеличить, это как один из вариантов. Когда-то реально помогло

        0
        Выглядит лаконично, но путаницы будет вносить кучу. В том же пресловутом respond-to явно указано для каких устройств пишешь правила.
          0

          Спасибо за комментарий! Я думал над тем, чтобы сделать возможность прописывать нужный экран, типа width, (desktop: 100%, tablet: 50% и тд. При необходимости. Но в таком случае кода будет больше. Хотя выглядеть должно понятно

            0
            Возможно, это будет лучше. А как должно это дело выглядеть, когда в базовой версии нет правила, но появляется на других ширинах экрана?
              0

              На данный момент вот так:
              include media(display, (‘’, none, block)) т.е. Просто пропускаем экран который не нужен, в том числе и базовое значение.

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

          Ещё меня посетила мысль что со временем мы занимаемся автоматизацией ради автоматизации и становимся заложниками своих решений. Я вот недавно попробовал использовать нативные media безо всяких миксинов и знаете что — Это даже удобнее и позволяет писать более лаконичный код.
            0
            Спасибо за Ваш комментарий!

            Если разбивать scss на компоненты, получаем много файлов, а в этих файлах получаем еще больше дублирования кода. Я проверил в проекте, и скажу что результат меня порадовал. У меня была подробная адаптация, т.е. 5 экранов, и таким миксином я написал файл в 5 раз меньше. Соответственно когда мне нужно было что-то поменять, мне не нужно было искать @media screen с нужным разрешением, а когда 5 разных разрешений, поиски затягиваются, и ошибки с нужным экраном не редкость. Тут так же легко ошибиться, но для исправления ошибки нужно приложить минимум усилий. Это ответ насчет смысла данного метода.

            Второй момент по поводу понимания кода посторонним разработчиком, полностью
            соглашусь… Но давайте на чистоту, сейчас постоянно появляются новые фишки, и всегда находится человек который никогда не слышал о чем либо. Люди пишут свои варианты миксинов постоянно. Любой кодер который работает с Sass и знает что-такое миксины, сможет разобраться. В конце концов всегда можно написать комментарий для таких случаев, даже с ссылкой например на данную статью.

            Автоматизация ради автоматизации) Интересная фраза, не лишена смысла. Если так подумать, когда не было никаких подобных фишек, работа продвигалась куда быстрей и проще. Мы долго ждали когда браузеры начнут поддерживать CSS3 чтобы облегчить себе жизнь, и сразу же придумали партию чего-то нового, что создает еще больше геморроя. Но заложниками мы стали настолько давно, что уже и не помним что было по-другому. Информационный поток растет в геометрической прогрессии, и единственный способ бороться с такими потоками данных — это автоматизация и оптимизация.
            +1
            Работа над такой кодовой базой будет абсолютно невозможна, вас будет проклинать любой человек, которому придется работать с этим после вас. И возможно вы сами через год.
            Применяйте такое для собственных одноразовых сайтов, но не делайте такого на работе.

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

            Вы вообще в курсе что в sass можно вот так писать?
            Никаких повторений и все понятно с первого взгляда, можно даже никогда не зная sass предположить как будет выглядеть скомпилированный результат.
            body {
                font-size: 1em;
            
                @media (max-width: 1024px) {
                    font-size: 0.8em;
                }
            }
            


            И вот здесь уже можно как-то отдельные правила скорректировать, вынести значение 1024px в переменную, или написать миксин с названием $desktop-and-down или еще какую-нибудь систему именования придумать, есть статьи на эту тему. Все будет в разы лучше чем то что у вас. У вас, повторюсь, применено потенциально фатальное для поддержки всей кодовой базы решение несуществующей проблемы.
              0
              Конечно я в курсе что можно писать вот так. А еще можно вот так:

              .class{
                   width: 100%;
                   height: 100%;
                   font-size: 12px;
                   color: black;
                   .class-inner{
                        display: inline-block;
                        height: 500px;
                        background: red;
                        font-size: 14px;
                             a{
                                     text-decoration: none;
                               }
                   }
                   @media screen only (max-width: 1024px){
                        width: 50%;
                        height: 300px;
                        font-size: 10px;
                        .class-inner{
                             height: 300px;
                             background: black;
                             font-size: 18px;
                                  a{
                                         text-decoration: underline;
                                    }
                         }
                    }
                   @media screen only (max-width: 880px){
                        width: 25%;
                        height: 200px;
                        font-size: 14px;
                        color: gray;
                        .class-inner{
                             height: 200px;
                             background: yelllow;
                             font-size: 20px;
                             a{
                                    text-decoration: none;
                               }
                         }
                    }
                    @media screen only (max-width: 640px){
                        width: 20%;
                        height: 100px;
                        font-size: 12px;
                        color: white;
                        .class-inner{
                             display: none;
                         }
                    }
              }

              Это далеко не самый длинный код который может быть, и тут все дублируется, как ты его не напиши, будет или много @media screen или повторных элементов, или правил. просто для сравнения тот же код:

              .class{
                   @include media(width, (100%, 50%, 25%, 20%));
                   @include media(height, (100%, 300px, 200px, 100px));
                   @include media(font-size, (12px, 10px, 14px, 12px));
                   @include media(color, (black, '', gray, white));
                   .class-inner{
                        @include media(display, (inline-block, '', '', none));
                        @include media(height, (500px, 300px, 200px));
                        @include media(background, (red , black , yellow));
                        @include media(font-size, (14px, 18px , 20px));
                             a{
                                    @include media(text-decoration, (none, underline, none));
                               }
                   }


              Соглашусь что читать сразу такой код не привычно, но править проще не скролля по всему файлу в поисках. Я очень часто сталкиваюсь с новыми непонятными на первый взгляд миксинами, но разбираюсь в них и подстраиваюсь. Миксины для этого и созданы. Повторюсь, код сыроват, именно поэтому он в этой статье, на данном этапе важна даже не реализация и вид, а сама мысль. Уверен что сделать данный метод понятным и читабельным не такая уж и большая проблема. Конечно может я и ошибаюсь. Спасибо за Ваше мнение!
                +2
                Скролл это тоже не проблема. Вы уже реализовали какой-нибудь проект с применением этого чтобы говорить о том что с этим работать проще чем со скроллом?

                Много media в и исходниках это не проблема, с использованием вашего миксина у вас их получается даже больше чем без него (sic!). Как только число свойств, изменяющихся хотя бы один раз при изменении размера экрана становится больше количества брейкпойнтов в проекте (3-5, сколько их у вас?), то миксинов становится больше чем было бы обычных media. А ведь число media всегда меньше чем количество брейкпойнтов в рамках одного класса. Конечно с учетом тех же самых ограничений в их структуре что вы вносите миксином.

                Зачем вам `screen only`? Задумывались зачем? Было это обдуманное решение добавить его или вы не знаете для чего он нужен и нужен ли вообще?

                Как комбинировать это все с `media print`?

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

                У вас еще есть проблема, с который вы вероятно сталкиваетесь и не осознаете, из-за чего пытаетесь решить в том числе и ее таким мягко говоря необычными способами. Разбивайте группы свойств внутри одного класса отступами, не бойтесь, от скролла еще никто не умирал. Порядок свойств у вас уже вроде как есть, что хорошо, теперь только еще группы свойств поразбивайте и пройдет желание улучшать читаемость ибо станет супер-читаемо.

                width: 100px;
                height: 200px;
                
                background: red;
                border: 2px solid green;
                
                color: white;
                font-size: 23px;
                


                И еще насчет результирующего сss, gzip абсолютно неважно сколько у вас будет в нем повторений media, одно или в пятьдесят раз больше, заботясь о размере получающегося сss вы не просто экономите на спичках, а экономите на байтах, даже до килобайта не дойдет экономия. Задумайтесь на минутку зачем и кому это надо, придете к выводу что никому не надо о такой экономии заботиться.
                blog.servergrove.com/2014/04/14/gzip-compression-works

                Еще у вас порядок простановки media в первом примере кода нарушен, у вас сначала идет для самого маленького экрана, потом по брейкпойнту для самого большого, потом от него уже вниз по брейкпойнтам. Выберите один порядок и придерживайтесь его.

                Еще ровные значения брейкпойнтов 640, 768, 1024 обычно означают начало диапазона размеров, а не конец, вот так например должно быть 320-639, 640-1023, 1024+
                Именно так делают макеты дизайнеры, макет под ширину 640 будет показывать начальное состояние страницы и именно оно будет растягиваться до включительно 1023. А вот 1024 начнется уже условно десктопное отображение.
                Может конечно попасться дизайнер наркоман и нарисовать как попало, но это уже другая история.

                А еще не надо вкладывать классы друг в друга в общем случае, возьмите именование по БЭМ, SMACSS или еще чему такому и откажитесь от излишней вложенности уже сегодня бесплатно и без смс.

                Скажете что нравится структура, которая получается при вложенности? Что она якобы повторяет html и помогает не глядя на html понять какой класс внутри другого окажется?
                Ну так и используйте эту структуру, только без вложенности. Вот так:

                $breakpoint--desktop: 1024px;
                $breakpoint--tablet-wide: 880px;
                $breakpoint--tablet: 640px;
                $breakpoint--mobile: 320px;
                
                .b-class {
                    width: 100%;
                    height: 100%;
                
                    color: black;
                    font-size: 12px;
                
                    @media (max-width: $breakpoint--tablet - 1) {
                        width: 20%;
                        height: 100px;
                
                        color: white;
                        font-size: 12px;
                        }
                
                    @media (max-width: $breakpoint--tablet-wide - 1) {
                        width: 25%;
                        height: 200px;
                
                        color: gray;
                        font-size: 14px;
                        }
                
                    @media (max-width: $breakpoint--desktop - 1) {
                        width: 50%;
                        height: 300px;
                        
                        font-size: 10px;
                        }
                    }
                
                    .b-class__inner  {
                        display: inline-block;
                        
                        height: 500px;
                        
                        background: red;
                        font-size: 14px;
                `
                        @media (max-width: $breakpoint--tablet - 1) {
                            display: none;
                            }
                            
                        @media (max-width: $breakpoint--tablet-wide - 1) {
                            height: 200px;
                            
                            background: yelllow;
                            
                            font-size: 20px;
                            }
                        
                        @media (max-width: $breakpoint--desktop - 1) {
                            height: 300px;
                            
                            background: black;
                            
                            font-size: 18px;
                            }
                        }
                        .b-class__link {
                            text-decoration: none;
                
                            @media (max-width: $breakpoint--tablet-wide - 1) {
                                // Вот так вот в этой структуре сразу видно что здесь, например,
                                // это свойство избыточное. Это к вопросу о группировке всех 
                                // media, относящихся к конкретному классу, внутри этого класса
                                text-decoration: none; 
                            }
                            
                            @media (max-width: $breakpoint--desktop - 1) {
                                text-decoration: underline;
                            }
                        }
                
                  0
                  Спасибо за сильные аргументы. С ними сложно спорить. Замечание по поводу порядка, очень кстати, я не обратил внимание, но это чудит gulp-minify-css, нужно полазить по настройкам
              0
              !!!
                0
                Сам размышлял над чем то подобным, но потом пришел к выводу, что смысла нет. Экономия не так уж велика, читаемость страдает, гибкость тоже.
                  0
                  Мы такое обошли через placeholder-ы. Избавляет он необходимости группировки. А вообще еще лучше выделять селекторы с media-query в отдельный файл и грузить по необходимости (media-query в rel)
                  0
                  Можно упростить себе написание медиа запросов миксинами следующего вида:

                  @mixin xs {
                      @media (max-width: $grid-xs-breakpoint - 1) {
                          @content
                      }
                  }
                  

                  После использовать в коде примерно так:

                  .b-class {
                      color: blue;
                  
                  	@include xs {
                  		color: red;
                  	}
                  }
                  

                    0

                    Шел 2018 год… Люди так и не освоили тарс или фрактал…

                      0
                      Как это относится к статье то вообще?

                      1. По-вашему фрактал (первый раз о нем слышу) имеет какое-то отношение?
                      Fractal is a tool to help you build and document web component libraries, and then integrate them into your projects.

                      Какая-то непонятная маргинальная тулза вообще непонятно что делает и зачем нужна пока не прочитаешь несколько страниц их документации. Как вы вообще наткнулись на это?

                      2. Тарс. По-вашему тарс это какая-то панацея?
                      Это просто набор тасок в gulp, который вдобавок еще и диктует свои правила к организации файлов, свои ограничения и тому подобное. У таких вещей как тарс вообще крайне ограниченная область применения и целевая аудитория, в них надо сначала еще и вложить время поверх вложенного в сам gulp, изучить, и только после нескольких применений это время окупится или не окупится никогда если по работе не нужно клепать статичные сайты каждые пару недель.

                      Собственно у меня к вам только один вопрос: Wat?
                        0
                        Ув. читатель! Данная статья является именно доводом мысли, но никак не готовым кодом для использования, хотя по желанию его можно использовать, но по мнению многих не стоит. Если Вы внимательно читали, там написано что код сыроват, и вынесен не как урок по оптимизации, а для людей которым это может быть интересно, может кто-то подхватит мысль и напишет что-то подобное, но только с вау эффектом. А возможно укажет на что-то подобное но уже готовое. В общем… данная статья является просто примером как можно оптимизировать код, а не предметом для изучения

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