Техноведро
«Сеточность» на сайте бюро

Рассказывает разработчик Юрий Мазурский

Совет Миши Нозика про сетку на сайте бюро

На новом сайте бюро используется девятипиксельная сетка, она помогает добиться вертикального ритма на страницах, стандартизировать отступы, размеры элементов и кегли используемых шрифтов. По задумке все элементы на странице должны вставать на сетку своими краями, а текст — базовыми линиями.

Совет Миши Нозика про сетку на сайте бюро

Подробнее о проблеме с высотой строки в совете Руста Кулматова

Для осуществления «мечты дизайнера» нужна система, с помощью которой выравнивание элементов по сетке будет происходить автоматически, без ручной подгонки значений отступов. С элементами вроде картинок всё просто, они имеют явные края. Ставить текст базовыми линиями на сетку — проблема, потому что в ХТМЛ нельзя управлять вертикальным положением символа в строке. Я расскажу о том, как мы решали эту проблему.

Подробнее о проблеме с высотой строки в совете Руста Кулматова

Точки отсчёта текстовых элементов

Можно подумать, что высота строки и интерлиньяж — это одно и то же, но интерлиньяж это расстояние между базовыми линиями, а высота строки — величина, определяемая при создании шрифта.

Её легко увидеть, просто выделив кусок текста. В большинстве случаев высота выделения будет равна высоте строки

Верхняя и нижняя границы текстового блока совпадают с верхом и низом первой и последней строк, положение букв по вертикали зависит от высоты строки:

Можно подумать, что высота строки и интерлиньяж — это одно и то же, но интерлиньяж это расстояние между базовыми линиями, а высота строки — величина, определяемая при создании шрифта.

Её легко увидеть, просто выделив кусок текста. В большинстве случаев высота выделения будет равна высоте строки

Но нам нужны другие точки отсчёта — базовая линия снизу и верхний край строчных букв сверху:

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

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

Итого:

  1. Интерлиньяж должен быть кратен шагу сетки.

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

  3. Базовая линия последней строки текстового блока должна сопадать с нижним краем блока.

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

Первые два подхода

Впервые задача выравнивать все элементы друг с другом появилась в книге «Типографика и вёрстка». Разворот делится на прямоугольники (мы называем их модулями), а содержимое текстовых блоков выравнивается со своими внешними границами отрицательными отступами (вытяжками).

Абзац внутри текстового блока вытягивается вверх и вниз за пределы своих границ, чтобы верх строчных букв выровнялся с верхней границей блока, а базовая линия — с нижней. Просто, но узкоспециализированно.

Красные рамки — границы модулей, синяя плашка — абзац

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

Решаем разбить текстовые стили по смыслу и размеру: обычный текст, заголовки, въезд:

.p.is__sizeSmall {
  font-size: $small-font-size;
  font-weight: 100;
  line-height: $small-line-height;

  &.is__firstChild { margin-top: -$small-top-compensator; }
  &.is__lastChild { margin-bottom: -$small-bottom-compensator; }

  .p.is__sizeMedium + & { margin-top: 6px; }
  .p.is__sizeLead + & { margin-top: 7px; }
  .p.is__sizeLarge + & { margin-top: 12px; }
  .p.is__sizeTitle + & { margin-top: -1px; }
  .p.is__sizePageTitle + & { margin-top: 7px; }
}

.p.is__sizeMedium {
  font-size: $medium-font-size;
  line-height: $medium-line-height;

  &.is__firstChild { margin-top: -$medium-top-compensator; }
  &.is__lastChild { margin-bottom: -$medium-bottom-compensator; }

  .p.is__sizeLead + & { margin-top: 1px; }
  .p.is__sizeLarge + & { margin-top: 6px; }
  .p.is__sizeTitle + & { margin-top: 11px; }
  .p.is__sizePageTitle + & { margin-top: 10px; }
}

.p.is__sizeLead {
  font-size: $lead-font-size;
  line-height: $lead-line-height;

  &.is__firstChild { margin-top: -$lead-top-compensator; }
  &.is__lastChild { margin-bottom: -$lead-bottom-compensator; }

  .p.is__sizeLarge + & { margin-top: 5px; }
  .p.is__sizeTitle + & { margin-top: 10px; }
  .p.is__sizePageTitle + & { margin-top: 9px; }
}
Фрагмент стилей для комбинаций абзацев различных кеглей друг с другом

Логика та же — у первого и последнего абзацев есть отрицательные отступы, которые выравнивают края с границами своих модулей. Плюс к этому, описываем все возможные сочетания элементов друг с другом. Стили разрастаются в геометрической прогресии. А ещё нужно описать всё то же самое для мобильной версии.

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

Вёрстка здорового человека

block content
  +webpage({ type: 'text' })
    h1 Текст и редактура. Стоп-слова и информативность
    h2 Как работать со стоп-словами
    +youtubeVideo({id: "FZhbMawB6Ew"})
    p Процесс работы со стоп-словами очень простой: мы убираем из текста всё, что не несёт в себе информацию. Я называю это большим количеством эпитетов и метафор: жир, вода, шелуха, чешуя, головы, хвосты — всё, что не является ценным. И здесь многие останавливаются, мол, всё убрано и классно, но это не так. На место, где было что-то убрано, мы должны добавить нечто полезное: не просто из рыбы достать хребет, кишки, отрубить голову и всё прочее, но ещё и добавить рис, мясо, картошечки, что-то клёвое, ценное и полезное. Это второй этап работы со стоп-словами.
    p Следующий слайд — навигатор по категориям стоп-слов. Смысл этого навигатора в том, что слова разбиты по группам.
    +image({mod: 'bordered textWidth', src: 'stop-1.png'})
    p Первая группа — всё, что является междометием и все вводные конструкции, вводные предложения и вводные слова.
    p Чем плохи «ах», «эх», «ну»?
    p — Это разговорные слова.
    p М. И. — А чем плохи разговорные?
    p — В них нет смысла.
    p М. И. — Задача разговорного стиля — передать устную речь, очень точно охарактеризовать человека с помощью его устной речи. Эти фразы, фактически, междометия, помогают охарактеризовать человека, но не несут информации. Чем плохи вводные предложения, вводные фразы и слова?
    p — До информации долго идти, ты хочешь получить её раньше. Мнение выражают.
    p М. И. — Нет, мнение они не выражают, тут уже логические связки пошли. Что подразумевает сама концепция вводного?
    p — Она говорит о неправильной структуре всего текста. До этого было не понятно и поэтому приходится сказать «в общем».

Как пришлось бы верстать

block content
  +webpage({ type: 'text' })
    h1 Текст и редактура. Стоп-слова и информативность
    h2 Как работать со стоп-словами
    +youtubeVideo({id: "FZhbMawB6Ew"})
    p.p.is__sizeMedium Процесс работы со стоп-словами очень простой: мы убираем из текста всё, что не несёт в себе информацию. Я называю это большим количеством эпитетов и метафор: жир, вода, шелуха, чешуя, головы, хвосты – всё, что не является ценным. И здесь многие останавливаются, мол, всё убрано и классно, но это не так. На место, где было что-то убрано, мы должны добавить нечто полезное: не просто из рыбы достать хребет, кишки, отрубить голову и всё прочее, но ещё и добавить рис, мясо, картошечки, что-то клёвое, ценное и полезное. Это второй этап работы со стоп-словами.
    p.p.is__sizeMedium Следующий слайд – навигатор по категориям стоп-слов. Смысл этого навигатора в том, что слова разбиты по группам.
    +image({mod: 'bordered textWidth', src: 'stop-1.png'})
    p.p.is__sizeMedium Первая группа – всё, что является междометием и все вводные конструкции, вводные предложения и вводные слова.
    p.p.is__sizeMedium Чем плохи «ах», «эх», «ну»?
    p.p.is__sizeMedium – Это разговорные слова.
    p.p.is__sizeMedium М. И. – А чем плохи разговорные?
    p.p.is__sizeMedium – В них нет смысла.
    p.p.is__sizeMedium М. И. – Задача разговорного стиля – передать устную речь, очень точно охарактеризовать человека с помощью его устной речи. Эти фразы, фактически, междометия, помогают охарактеризовать человека, но не несут информации. Чем плохи вводные предложения, вводные фразы и слова?
    p.p.is__sizeMedium – До информации долго идти, ты хочешь получить её раньше. Мнение выражают.
    p.p.is__sizeMedium М. И. – Нет, мнение они не выражают, тут уже логические связки пошли. Что подразумевает сама концепция вводного?
    p.p.is__sizeMedium – Она говорит о неправильной структуре всего текста. До этого было не понятно и поэтому приходится сказать «в общем».

Ком проблем с таким подходом растёт неконтролируемо ещё и из‑за введения новых модулей, например: видео, подпись, крик, «экзампл». Появление каждого нового модуля означает, что нужно описать его сочетание со всеми другими модулями, ведь нам нужна универсальность.

Поддержка такого монстра становится очень трудоёмким занятием. К тому же появляется большое количество исключений, стили элементов накладываются друг на друга, приходится покрывать ЦСС правила толстыми слоями «импортантов» (!important).

+module({class: 'books-spacer'})
  .h3.p.is__sizeLead Книги
  +cols({divisionProportions: '3:3:3:3:3:4'})
    +module({href: '/projects/bookshelf-bureau/'})
      +module({class: 'books-spacer-holder', fitToProportion: '1000:1414', mod: 'positionOrigin fitToContent'})
        +image({src: '/assets/images/spacers/books/shelf-wide.png', class: 'books-spacer-cover', style: 'margin-bottom: 0; height: 100%;', align: 'left'})
      +image({src: '/assets/images/spacers/books/shelf.png', class: 'books-spacer-cover', device: 'mobile'})

      .h3.p.is__sizeSmall.books-spacer-author Электронная полка
      .p.is__sizeSmall
        span.link Учебники бюро<br />
        | 660, затем <nobr>100 ₽/мес</nobr>.

    +module({href: '/projects/book-typography/'})
      +image({src: '/assets/images/spacers/books/typography.png', class: 'books-spacer-cover'})

      .h3.p.is__sizeSmall.books-spacer-author Артём Горбунов
      .p.is__sizeSmall
        span.link Типографика и&nbsp;вёрстка<br />
        | 400, затем <nobr>50 ₽/мес</nobr>.

    +module({href: '/projects/book-ui/'})
      +image({src: '/assets/images/spacers/books/ui.png', class: 'books-spacer-cover'})

      .h3.p.is__sizeSmall.books-spacer-author Илья Бирман
      .p.is__sizeSmall
        span.link <hyphen>Пользовательский</hyphen> интерфейс<br />
        | 400, затем <nobr>50 ₽/мес</nobr>.

    +module({href: '/projects/book-text/'})
      +module({
        mod: 'positionOrigin fitToContent',
        fitToProportion: '1000:1414',
        class: 'books-spacer-cover'
      })
        +image({
          src: '/assets/images/spacers/books/text.png',
          style: 'position: absolute; bottom: 0; left: 0; width: 153%;'
        })

      .h3.p.is__sizeSmall.books-spacer-author Максим Ильяхов
      .p.is__sizeSmall
        span.link <hyphen>Информационный</hyphen> стиль<br />
        span(style="color: rgb(252, 86, 32);") Предзаказ
Типичный модуль с засильем .p.is__sizeSmall

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

Третий подход

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

Сформулируем требования, которые у нас есть:

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

  2. На подходе статичные текстовые страницы, поэтому верстать должно быть легко любому человеку, нужно использовать простые и стандартные элементы: абзацы, изображения, списки. Всё что сложнее шаблонизируем с помощью миксинов.

Обёртка текстовых элементов. Теория

<div class="textNode">
  <p>
    Текст абзаца
  </p>
</div>
Структура тестового блока

Для изоляции текстового блока оборачиваем его в div. Это будет внешней обёрткой и позволит задавать отступы до других блоков.

<div class="textNode">
  <p>
    Текст абзаца
  </p>
</div>
Структура тестового блока

Остаётся проблема с высотой строки. Для её решения применим вытяжки к родному элементу, в котором находится текст. Это может быть p, ul, h2 или другой подобный элемент. Назовём его внутренней обёрткой текста.

Задача внутренней обёртки — выровнять базовую линию текста с нижней границей внешней обёртки, и, в идеале, высоту строчных с верхней границей. С помощью неё блок «знает», куда подвинуться, чтобы встать точно по сетке.

Высота абзаца кратна сетке (189), но расстояния сверху и снизу мешают, убираем их вытяжками внутренней обёртки и сокращаем высоту внешней до 171

Величины вытяжек подбираются для каждого шрифта и каждого кегля‑интерлиньяжа индивидуально, но у Бюросерифа и Бюросанса они совпадают.

$small-font-size: 16px;
$small-line-height: 18px;
$small-top-compensator: 4px;
$small-bottom-compensator: 5px;

$medium-small-font-size: 18px;
$medium-small-line-height: 18px;
$medium-small-top-compensator: 4px;
$medium-small-bottom-compensator: 5px;

$medium-font-size: 20px;
$medium-line-height: 27px;
$medium-top-compensator: 10px;
$medium-bottom-compensator: 8px;

$lead-font-size: 24px;
$lead-line-height: 27px;
$lead-top-compensator: 11px;
$lead-bottom-compensator: 7px;

$heading-font-size: 36px;
$heading-line-height: 36px;
$heading-top-compensator: 10px;
$heading-bottom-compensator: 8px;

$large-font-size: 43px;
$large-line-height: 45px;
$large-top-compensator: 16px;
$large-bottom-compensator: 11px;

$title-font-size: 65px;
$title-line-height: 63px;
$title-top-compensator: 22px;
$title-bottom-compensator: 14px;

$page-title-font-size: 85px;
$page-title-line-height: 81px;
$page-title-top-compensator: 27px;
$page-title-bottom-compensator: 18px;
Переменные с вытяжками и кеглями‑интерлиньяжами

Используя внешнюю обёртку, мы изолируем текст от влияния внешних блоков, а сама внешняя обёртка ничего не знает о внутренней и выступает в роли кирпичика, из которого строится страница.

Как сделать, чтобы высота блока всегда была кратна сетке? Подобрать размер шрифта, при котором высота строчных была бы кратна или почти кратна ей же.

Например, кегль и интерлиньяж у заголовка второго уровня равны 36 пикселям. При таком кегле, высота строчных букв Бюросерифа равна 15 пикселям — не кратно 9. Можно было бы поставить размер 42 и высота строчных была бы ровно 18 пикселей, но тогда интерлиньяж в 36 был бы маловат, а следующее допустимое значение 36 + 9 = 45 великовато.

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

При этом, у основного текста размер шрифта и интерлиньяж 20 и 27 пикселей, соответственно. При таком кегле Бюросанса и Бюросерифа высота строчных равна 9 пикселям, что позволяет идеально выравнивать основной текст по сетке.

Базовая линия последней строки каждого текстового блока выравнивается с нижней границей контейнера, а верх строчных букв в первой строке — с верхней. Высоты всех текстовых блоков и отступы кратны девяти. При этом строчные буквы в заголовке не дотягивают по высоте до верхнего края своей обёртки

Обёртка текстовых элементов. Практика

Оборачиваются все стандартные текстовые элементы, используемые для разметки: p, ul, ol, dl, h1, h2, h3.

Каждый блок проходит цепочку преобразований:

Паг → ХТМЛ → Постпроцессинг

На стадии постпроцессинга элементы оборачиваются, трансформируются, типографятся, готовым блокам назначаются все нужные классы и атрибуты.

Паг

h2 Зачем нужна абстракция
p Чтобы закрыть какие-то детали и улучшить коммуникацию как частный случай взаимодействия. Чтобы не объяснять, что такое «деревянная платформа с четырьмя опорами», придумали слово «стол». Оплаченная статья в журнале — «нативная реклама».
+image({mod: 'textWidth', src: 'analytics-006.jpg'}) 
p Чем выше уровень абстракции, тем проще нам взаимодействовать. Три математика собрались в кабинете, сыплют терминами и отлично друг друга понимают. Они экономят таким образом огромное количество ресурсов — временных и ментальных. Но при этом у них высокий риск ошибки: если я приду неподготовленным в эту толпу математиков, то, как минимум, ничего не пойму, а худшем случае я подумаю, что понял — но пойму не то. Потом начну принимать решения на основе информации, которая у меня есть, не осознавая, что эта информация совершенно ошибочная.

ХТМЛ

<div class="heading-2">
  <h2 class="is__firstChild is__lastChild">Зачем нужна абстракция</h2>
</div>
<div class="textNode">
  <p class="is__firstChild is__lastChild">Чтобы закрыть какие‑то детали и улучшить коммуникацию как частный случай взаимодействия. Чтобы не объяснять, что такое «деревянная платформа с четырьмя опорами», придумали слово «стол». Оплаченная статья в журнале – «нативная реклама».</p>
</div>
<div class="image is__original media is__fitToContent is__textWidth module is__inFlow is__loaded" data-src="/school/analytics/marketingovaya-zadacha-i-printsip-piramidy/files/analytics-006.jpg?v=1503998833129" data-width="3000" data-height="1688"> 
  <div>
    <div class="image-spacer" style="padding-bottom: 56.27%"></div>
  </div>
</div>
<div class="textNode">
  <p class="is__firstChild is__lastChild">Чем выше уровень абстракции, тем проще нам взаимодействовать. Три математика собрались в кабинете, сыплют терминами и отлично друг друга понимают. Они экономят таким образом огромное количество ресурсов – временных и ментальных. Но при этом у них высокий риск ошибки: если я приду неподготовленным в эту толпу математиков, то, как минимум, ничего не пойму, а худшем случае я подумаю, что понял – но пойму не то. Потом начну принимать решения на основе информации, которая у меня есть, не осознавая, что эта информация совершенно ошибочная.</p>
</div>

Конечно же, страницы состоят не только из текстовых блоков. Все типы используемых элементов стандартизованы. Всем медиаэлементам назначается класс .media c отступом 72 пикселя снизу, все подписи к изображениям мы ставим специальным модулем +caption, а заметки на полях +sidenote сами знают куда им встать и как себя вести в мобильной версии страницы.

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

Отправить
Поделиться
Запинить