На 100% правильный способ делать контрольные точки в CSS

Перевод статьи The 100% correct way to do CSS breakpoints для CSS-live.ru, автор — Дэвид Гилбертсон

Постарайтесь на пару минут забыть про CSS. Не думайте о веб-разработке. Забудьте про цифровые пользовательские интерфейсы.

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

Многое тогда было проще, когда единственной вашей заботой было рисовать фигуры и стараться удержать себя в руках.

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

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

Скорее всего у вас получилось что-то вроде этого, так? (Только не говорите мне, что проскроллили вниз, не сделав этого упражнения. Я очень расстроюсь.)

Конечно, те две точки справа можно было обвести по-разному. Если вы объединили их в группу, думаю, это нормально. Говорят, что не бывает неправильного ответа, но я всегда бываю прав, так что мне не приходится выслушивать это пошлое утешение.

Прежде чем я продолжу, вы точно не нарисовали чего-то наподобие такого?

Вряд ли. Правда?

Но именно это по сути вы делаете, когда задаете контрольные точки в положениях, отвечающих точной ширине конкретных устройств (320px, 768px, 1024px).

Доводилось ли вам слышать недоуменное бормотание вроде такого? Или, может, это вы и были?

«Средняя контрольная точка — это до 768px включительно или нет? Так, вижу… а это iPad в портретном режиме, или это уже „большой“? Ага, большой — это от 768px и выше. А маленький — это 320px? Что же тогда такое диапазон от 0 до 319px? Контрольная точка для муравьев?»

Могу на этом остановиться и перейти сразу к правильным контрольным точкам. Но мне любопытно, почему вышеописанный метод («тупая группировка») так распространен.

Почему так вышло?

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

Думаю, в обсуждениях и реализациях контрольных точек мы путаем «границы» и «диапазоны».

Скажите, если вы делаете контрольные точки в Sass, вы заводите переменную $large со значением, скажем, 768px?

Это нижняя граница диапазона, который вы называете large («большой») или верхняя? Если нижняя, то переменной $small быть не должно, потому что это должен быть 0, так ведь?

А если это верхняя граница, то как вы определите контрольную точку $large-and-up («от большго и выше»)? Это должно быть медиавыражение с min-width, равным $medium, так?

А если вы называете так только саму границу, то нас ждет путаница в дальнейшем, потому что медиавыражение — это всегда диапазон.

Получается бардак, и даже думать об этом — зря время тратить. Так что у меня есть три совета:

  1. Выбирайте контрольные точки правильно
  2. Называйте диапазоны со смыслом
  3. Пишите код декларативно

Совет № 1: Выбирайте контрольные точки правильно

Что же такое правильная контрольная точка?

Ваше детсадовское «я» уже нарисовало кружочки. Я лишь сделал вам из них прямоугольники.

600px, 900px, 1200px и 1800px, если собираетесь делать что-то особенное для гигантских мониторов. На всякий случай, если заказываете в интернете «гигантский монитор», уточняйте, что речь про компьютерный. Если вам пришлют старинный бронированный корабль, с вас могут многовато взять за доставку.

Точки, с которыми только что играло ваше детское «я», представляют 14 самых распространенных размеров экрана:

Источник картинки

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

Зря я выбрал оранжевый и зеленый цвета, но не переделывать же теперь все картинки

Совет № 2. Называйте диапазоны со смыслом

Конечно, никто не мешает назвать контрольные точки «папа-медведь» и «маленький-медвежонок». Но если я собираюсь посидеть с дизайнером и обсудить, как должен выглядеть сайт на разных устройствах, я хочу как можно быстрее с этим закруглиться. Если для этого нужно назвать размер планшетом в портретном положении — мне это подходит. Ёлки-палки, да назовите его хоть «iPad в портретном положении», я не обижусь.

«Но расклад же меняется!» — можете воскликнуть вы. «Телефоны растут, планшеты мельчают!»

Но срок годности у CSS вашего сайта — около трех лет (если это не Gmail). iPad с нами уже вдвое дольше, и с трона его пока так и не спихнули. И мы знаем, что Apple уже не делает новых продуктов, а только убирает что-то (кнопочки, дырочки и т.п.) у существующих.

Так что 1024 на 768 — это надолго, ребята. Не надо прятать голову в песок (занятный факт: страусы не водятся в городах, потому что там нет песка, а значит, некуда прятаться от хищников).

Вывод: без общения нет взаимопонимания. Не надо намеренно отказываться от полезных слов.

Совет № 3. Пишите код декларативно

Знаю, знаю, опять это слово «декларативно». Скажу по-другому: ваш CSS должен определять, что в нем должно происходить, а не как это должно происходить. Это «как» лучше спрятать в специальном миксине.

Как мы уже выяснили, изрядная часть путаницы с контрольными точками заключена в том, что переменные, обозначающие границу диапазона, называются так же, как сам диапазон. У $large: 600px попросту нет смысла, если large («большой») — это диапазон. Это всё равно что сказать var coordinates = 4;.

Так что все технические подробности можно упрятать в миксин, а не давать использовать их в коде в открытую. А можно и того лучше — вообще обойтись без переменных.

Следующий пример я поначалу делал как упрощенный. Но на деле, по-моему, он охватывает всё что надо. Можете взглянуть на него в действии на Codepen. Я использую Sass, потому что не мыслю сайта без него. Но логика в CSS или Less будет та же.

@mixin for-phone-only {
  @media (max-width: 599px) { @content; }
}
@mixin for-tablet-portrait-up {
  @media (min-width: 600px) { @content; }
}
@mixin for-tablet-landscape-up {
  @media (min-width: 900px) { @content; }
}
@mixin for-desktop-up {
  @media (min-width: 1200px) { @content; }
}
@mixin for-big-desktop-up {
  @media (min-width: 1800px) { @content; }
}

// usage
.my-box {
  padding: 10px;
  
  @include for-desktop-up {
    padding: 20px;
  }
}

Возможно, я пристрастен, но, вроде, вполне симпатичный декларативный CSS

Обратите внимание, что я вынуждаю разработчика указывать суффикс -up («и выше») или -only («и только»).

Неоднозначность плодит путаницу

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

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

@mixin for-size($size) {
  @if $size == phone-only {
    @media (max-width: 599px) { @content; }
  } @else if $size == tablet-portrait-up {
    @media (min-width: 600px) { @content; }
  } @else if $size == tablet-landscape-up {
    @media (min-width: 900px) { @content; }
  } @else if $size == desktop-up {
    @media (min-width: 1200px) { @content; }
  } @else if $size == big-desktop-up {
    @media (min-width: 1800px) { @content; }
  }
}

// usage
.my-box {
  padding: 10px;
  
  @include for-size(desktop-up) {
    padding: 20px;
  }
}

Да, это работает. Но если вы передадите неподдерживаемое имя, компилятор вам никакой ошибки не покажет. А передать sass-переменную — значит сделать доступными в коде 8 переменных, и лишь для того, чтобы отдать их переключателю в миксин.

Не говоря о том, что синтаксис @include for-desktop-up {...} со всех сторон красивее, чем @include for-size(desktop-up) {...}.

Оба примера кода можно поругать за то, что я дважды пишу 900px, да еще 899px. Конечно же, можно обойтись одной переменной и отнять 1, где нужно.

Хотите этого — флаг вам в руки, но я не стал бы, и вот почему:

  1. Это не то, что часто меняется. Это не те числа, что используются повсюду в коде. От того, что они не переменные, нет никаких проблем — если только вы не хотите дать к контрольным точкам Sass доступ скрипту, вставляющему в страницу JS-объект с этими переменными.
  2. Синтаксис для перевода чисел в строки в Sass ужасен. Внизу — цена, которую вы заплатите за веру в то, что повторение числа дважды — худшее из зол:
@mixin for-size($range) {
  $phone-upper-boundary: 600px;
  $tablet-portrait-upper-boundary: 900px;
  $tablet-landscape-upper-boundary: 1200px;
  $desktop-upper-boundary: 1800px;

  @if $range == phone-only {
    @media (max-width: #{$phone-upper-boundary - 1}) { @content; }
  } @else if $range == tablet-portrait-up {
    @media (min-width: $phone-upper-boundary) { @content; }
  } @else if $range == tablet-landscape-up {
    @media (min-width: $tablet-landscape-upper-boundary) { @content; }
  } @else if $range == desktop-up {
    @media (min-width: $tablet-landscape-upper-boundary) { @content; }
  } @else if $range == big-desktop-up {
    @media (min-width: $desktop-upper-boundary) { @content; }
  }
}

// usage
.my-box {
  padding: 10px;
  
  @include for-size(desktop-up) {
    padding: 20px;
  }
}

Ну как, улучшилась читаемость? Или наоборот?

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

Где сложность, там и баги

Наконец, вы можете подумать «Разве не правильнее отталкиваться в контрольных точках от контента, а не от устройств?». Что ж, круто, что вы дочитали аж досюда, и ответ будет «да»… для сайтов с одним видом раскладки. Или если у вас несколько раскладок и вас устраивает делать свой набор контрольных точек для каждой. Ах да, и еще если дизайн вашего сайта не меняется часто, или вас устраивает обновлять контрольные точки при каждом обновлении дизайна, потому что вам хочется, чтобы они по-прежнему зависели от контента, правда?

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

Всё! Но этой заметке как-то явно не хватает пушистости, дайте-ка подумать, не найдется ли у меня повода добавить немного…

О, придумал!

Добавочные советы по разработке контрольных точек

Да, даже у flickr есть контрольные точки на 768 и 1400

  1. Если хотите увидеть в действии контрольные точки CSS для экранов с разрешением больше чем у монитора, за которым вы сидите, воспользуйтесь «отзывчивым» режимом в отладчике Chrome и введите сколь угодно гигантский размер.
  2. Голубая полоска показывает медиавыражения для ‘max-width’, оранжевая — для ‘min-width’, а зеленая — медиавыражения, в которых есть и то и другое.
  3. Клик по медиавыражению задает экрану такую ширину. Если кликнуть по зеленому медиавыражению несколько раз, он переключается между максимальной и минимальной шириной.
  4. Кликайте правой кнопкой по медиавыражению в панели медиавыражений, чтобы перейти к определению этого правила в CSS.

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

P.S. Это тоже может быть интересно:

13 Комментарии

  1. Алексей

    Очень полезная информация получается. Жаль, что не нашел её раньше, когда искал ответ на вопрос, как же выбрать правильные брейкпоинты для своего сайта

    1. SelenIT (Автор записи)

      На самом деле, несмотря на название и то, что автор «всегда бывает прав», статья не так уж бесспорна и принимать ее за окончательную истину не стоит. В комментариях к оригиналу к ней высказано немало дельной критики (в т.ч. в защиту подхода о выборе брейкпойнтов по контенту). Но меня подкупил, во-первых, ее озорной стиль, а во-вторых, я тоже нашел для себя кое-что полезное (напр. советы по отладке) и в целом подход автора мне скорее близок (хотя я бы уже отнес 1280px к «планшетам в альбомной ориентации»:).

  2. Алексей

    Контрольные точки? Вы на работе тоже так называете брейкпоинты? Или переводчики не в курсе темы?

    1. SelenIT (Автор записи)

      Ну вы же поняли, что речь о брейкпойнтах:). На работе называю по-разному, смотря с кем (по тем же причинам, что во втором совете у автора). А переводчик ориентировался на вполне общепринятый словарь.

  3. Констатин

    А как на счёт полос прокрутки? На мобильных устройствах они часто располагаются поверх контента, но на десктопах вроде отгрызают часть видимой области.

    1. SelenIT (Автор записи)

      На Windows отгрызают. Но старый вебкитно-блинковый баг, когда от появления полосы прокрутки ширина вьюпорта менялась и могла перескочить через брейкпойнт, судя по всему, пофиксили (во всяком случае, я воспроизвести не смог). Так что, по-моему, ничего страшного нет, просто это надо учитывать, чтобы макет не был «впритык» к целевому вьюпорту, и оставлять небольшой запас. Подход из статьи как раз хорош тем, что для типичных экранов в нем это само получается.

      1. firefoxic

        > Подход из статьи как раз хорош тем, что для типичных экранов в нем это само получается.

        Это для фиксированных адаптивов, но не для резиновых. Для резиновых лучше таки закладывать отступы ;)

  4. Соня

    Не нахожу я сердечек с мобильного, так что оставлю его здесь ❤

    1. SelenIT (Автор записи)

      Спасибо (от переводчика, но, думаю, автор бы присоединился)!

      Дело в том, что оригинал статьи писался для Medium, где сердечки есть. А на этом сайте по историческим причинам с сердечками определенные сложности. Зато есть кнопочки «поделиться в соцсетях»:)

  5. Игорь

    Согласен с автором на 100%! Медиа-запросы всегда нужно держать в порядке. До знакомства с Sass — выносил их в отдельный файл и все свойства распихивал туда. А за контрольные точки у меня всегда точки из Bootstrap, вроде норм.

  6. Вадим

    В промежуточные точки также нужно включить «высоты» мобильных. Дело в том, что мобильные устройства можно поворачивать и высота становится шириной. Например, на рисунке из данной статьи около 600 px пробел и вроде бы «контрольная точка» просится именно сюда. Но только на самом деле вокруг этой области и находятся «высоты» экранов.

    1. SelenIT (Автор записи)

      Всё так, но, судя по приведенной автором статкаунтеровской статистике, подавляющее большинство пользователей смартфонов всё-таки смотрят с них сайты в портретном режиме (альбомные варианты типа 640×360, судя по всему, остались в куче «Other»). С другой стороны, что самое страшное случится от того, что на смартфоне отобразится вариант сайта, рассчитанный на планшеты в портретном режиме?

      1. Вадим

        Страшного ничего в этом нет. Но если в портретном режиме телефона нужна версия для планшета, то «точку» лучше взять не на 600 px, а пониже. Например, ширина пятого айфона 568 px.

        Также лучше никогда не выбирать «точки», попадающие в точный размер экрана — вроде 640 px. Есть некая вероятность забыть про разницу в 1 px при min-width и max-width.

Добавить комментарий для firefoxic Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.