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


Сегодня мы рассмотрим:

  • подходы при стилизации элементов для вёрстки текста;
  • какая может быть проблема с радиокнопками в вашем проекте;
  • как задать размеры с использованием функций min() и max();
  • CSS-наследование и свойство line-height;
  • для чего делать подсказки в имени класса.

Давайте посмотрим, что я вам подготовил.


▍ Способы стилизации текста


Кажется, стилизация текста довольна простая задача. Написал всякие font-size и line-height и дело готово. Только есть пару нюансов.


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


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

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


Часть статьи с заголовком, несколькими абзацами текста и списком. Они отображены друг под другом

В первом случае, кажется, нет никаких нюансов. Добавил класс и написал стили для заголовков и абзацев. А вот во втором случае он есть. Многие редакторы не позволяют добавлять класс. После редактирования текста в редакторе, в коде будут просто элементы. Такие, как <p>, <ul>, <h2> и т. п.


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


.article-formatted-body ol,
.article-formatted-body ul {
  padding-inline-start: 32px;
}

.article-formatted-body h2 {
  font-size: 1.25rem;
  line-height: 1.625rem;
}

.article-formatted-body p {
  margin: 0;
  padding: 0;
}

В данном подходе создаётся специальный класс .article-formatted-body. Он не даёт случиться утечке стилей, чтобы они не стали применяться к таким же элементам в других частях приложения.


Второй способ основан на селекторе :not([class]).


:is(ul, ol):not([class]) {
  margin-block: 2rem 0;
}

h2:not([class]) {
  font-size: 1.25rem;
  line-height: 1.625rem;
}

p:not([class]) {
  margin-block: 2rem 0;
}

Для реализации этой идеи существует правило. Атрибут class добавляется только для элементов, доступных для редактирования разработчиками. Элементы, редактируемые через редактор, должны быть без атрибута. В этом случае селектор :not([class]) сработает отлично.


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


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


▍ Подружим радиокнопки и режим высокой контрастности в Windows


Я посмотрел много популярных решений кастомных радиокнопок на Codepen. У них у всех есть общая проблема. Они используют свойство background-color для установки цвета точки при активном состоянии. Взгляните на псевдоэлемент ::before.


.toggle {
  appearance: none;
  margin: 0;
  width: 1rem;
  height: 1rem;

  border: 1px solid #242424;
  border-radius: 50%;
	
  display: grid;
  place-items: center;
}

.toggle[type="radio"]::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  background-color: #242424;

  border-radius: 50%;
  opacity: 0;
  position: absolute;
  scale: 0;
}

.toggle[type="radio"]:checked::before {
  opacity: 1;
  scale: 1;
}

Почему я говорю, что есть проблема. Давайте посмотрим на радиокнопки в режиме высокой контрастности в Windows.


На темном фоне отображены два кружка без точек внутри. У каждого рядом надпись. У первого кружка надпись Вариант №1. У второго Вариант №2.

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


<body>
  <div class="custom-radio-button">
    <input id="rb-1" class="toggle custom-radio-button__input" type="radio" name="radio" checked>
    <label for="rb-1" class="custom-radio-button__label">Вариант №1</label>
  </div>
  <div class="custom-radio-button">
    <input id="rb-2" class="toggle custom-radio-button__input" type="radio" name="radio">
    <label for="rb-2" class="custom-radio-button__label">Вариант №2</label>
  </div>
</body>

На белом фоне отображены предыдущие два кружка. У первого есть точка внутри

Почему так получилось? Дело в том, что в режиме высокой контрастности браузеры подставляют значение transparent для свойства background. Это изменить нельзя. Так, что наше значение пропадёт.


Правда, я нашёл решение. Достаточно точку сделать с помощью свойства border.


.toggle[type="radio"]::before {
  content: "";
  border: 0.25rem solid #242424;
  /* оставшиеся CSS */
}

На темном фоне отображены предыдущие два кружка. У первого есть точка внутри

Теперь радиокнопки отлично работают. Думаю, теперь вы знаете, что поправить в своём проекте. Надеюсь, вы это сделаете.


▍ Функции min() и max() помогают мне установить максимальный или минимальный размер у элемента


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


.container {
  width: 100%;
  max-width: 75rem;
}

Это надёжный фрагмент кода, проверенный временем. Лично я предпочитаю другой способ. Можно сократить код до одной строки. Для этого подходит математическая функция min().


.container {
  width: min(100%, 75rem);
}

Весь трюк заключается в том, что данная функция позволяет браузеру выбрать наименьшее значение из представленных аргументов. В моём примере их два — 100% и 75rem. Далее браузеры рассчитывают итоговое значение для каждого аргумента в пикселях и сравнивают между собой. В итоге мы получим значение 100%, пока оно не станет больше значения 75rem.


Круто ещё то, что есть функция max(). С помощью неё я могу указать минимальный размер элемента. Например, ширину.


.container {
  width: max(100%, 75rem);
}

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


▍ Моя самая обидная ошибка со свойством line-height


Я должен признаться. Раньше я постоянно встречал свойство line-height, определённое для элемента <body>. Отключая его в DevTools, я думал: «Зачем оно здесь?». Ленившись поискать ответ, забивал.


А моё недоумение было следствием моей привычки. Я для каждого типа элемента определял свойство line-height.


p {
  line-height: 1.5;
}

ul {
  line-height: 1.5;
}

Скорее всего, вы знаете о том, что свойство line-height является наследуемым. И уже догадались, что я зря дублировал значение.


Я узнал об этом случайно. Мне попались CSS советы с типичным названием «CSS Protips». В этом списке был раздел, в котором написано: «Вам не нужно отдельно добавлять line-height для каждого элемента <p>, <h*> и других. Вместо этого добавьте свойство к элементу <body>».


body {
  line-height: 1.5;
}

И всё. У меня щёлкнуло в голове. С тех пор объявляю всегда.


▍ Рассказываем о назначении класса с помощью его имени


Я фанат БЭМа с 2013 года. Мне нравится идея использовать библиотеку универсальных компонентов. Только у меня всегда был затык. В проектах с большим количеством кода сложно ориентироваться в нём. Приходится тратить очень много времени на осмысление нужного фрагмента кода. По этой причине мне нравится делать подсказки в названии CSS класса.


Саму идею я где-то встретил много лет назад. Не вспомню, где конкретно. Поэтому пошёл за примером к знакомым. Они прислали мне несколько. Одним из них был АБЭМ. Суть заключается в соединении БЭМа и принципов атомарного дизайна (Atomic Design).


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


Так, классы-организмы начинаются с o-, классы-молекулы с m-, классы-атомы с a-.


.o-accordion {
  /* здесь свойства */
}

.m-revealer {
  /* здесь свойства */
}

.a-control {
  /* здесь свойства */
}

Я не фанат этой идеи. Да, если честно, мне нравится изобретать свой велосипед. Так что АБЭМ натолкнул меня на идею: «А почему бы в названии класса не показать цель компонента?». Так у меня появились обозначения uia- и ra-.


<body class="page">
  <header class="page__banner">
    <div class="page__container">
      <h1 class="page__cv-heading uia-heading ra-heading">
        <span>Catherine Starobor-Strakhova</span>
        <span class="page__cv-role">Full stack game UX/UI designer with strong visual development</span>
      </h1>
      <a href="#0" class="page__cv-download ra-link uia-control">
        <span class="uia-control__group">Download CV</span>	
      </a>	
    </div>
  </header>
</body>

В коде есть универсальные компоненты uia-heading и uia-control. Они используются в разных проектах. Я отношу их к интерфейсным компонентам, поэтому называю User Interface Atoms или кратко — uia-.


Кроме них есть компоненты для сброса браузерных стилей у заголовков и у ссылок, а именно ra-heading и ra-link. Они являются Reset Atoms, соответственно, у них обозначение — ra-.


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


▍ Заключение


Давайте подведём итог. В этой статье мы рассмотрели:

  • как селектор :not([class]) помогает при стилизации текста;
  • пользу свойства border при реализации кастомных радиокнопок;
  • сокращённую форму установки минимальных и максимальных размеров;
  • установку свойства line-height без дублирования значения в нескольких местах;
  • подсказки в именах классов, сообщающие их назначение.

Оставлю ссылки на все выпуски:

Спасибо за чтение!


P.S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (12)


  1. Haze27
    13.08.2024 12:19
    +1

    Префиксы у аторманых селекторов o-* и т.д. выглядят так, будто мы вернулись в эру именования переменных с префиксами типов (https://en.wikipedia.org/wiki/Hungarian_notation)


  1. p07a1330
    13.08.2024 12:19
    +2

    ▍ Функции min() и max() помогают мне установить максимальный или минимальный размер у элемента

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

    Это не магия
    Это 10 минут чтения документации

    Как бы в такой статье не ожидаешь пересказ стандартных функций из доки


    1. vanxant
      13.08.2024 12:19
      +1

      Да в современном css примерно всё - это пересказ стандартных функций из доки. Прошли времена, когда приходилось использовать text-indent для сокрытия элементов, теги <ins> и <del> как единственные с поведением inline-block в IE, и padding-bottom в процентах при нулевой высоте вместо aspect-ratio.

      Лично для меня использование функций min и max это небольшое открытие. При том что в каких-нибудь гридах я использую их постоянно, но блин, перенести идею на обычные div-ы не догадался.


    1. melnik909 Автор
      13.08.2024 12:19
      +1

      Скажите, пожалуйста, что вы ожидали увидеть под словом "магия"?


  1. Baigildin
    13.08.2024 12:19

    Для реализации этой идеи существует правило. Атрибут class добавляется только для элементов, доступных для редактирования разработчиками. Элементы, редактируемые через редактор, должны быть без атрибута. В этом случае селектор :not([class]) сработает отлично.

    То есть стили применятся для всех элементов без атрибута class? А если изначально например в body было задано стили для p, то его перезапишет? В первом случае же применится для элементов внутри специального класса. Или я неправильно понял?


    1. melnik909 Автор
      13.08.2024 12:19

      Указываю элемент, например h2, к нему добавляю :not([class]) и получается так:

      h2:not([class]) {
        font-size: 1.25rem;
        line-height: 1.625rem;
      }

      В HTML элемент будет без атрибута class .


      1. Source
        13.08.2024 12:19

        Придётся везде дописывать этот суффикс и надеяться не получить случайно сайд-эффектов от того, что где-то ещё есть элементы без классов.

        А общий класс можно просто вынести за скобки и всё будет структурировано:

        .article-formatted-body {
          ol, ul {
            padding-inline-start: 32px;
          }
        
          h2 {
            font-size: 1.25rem;
            line-height: 1.625rem;
          }
        
          p {
            margin: 0;
            padding: 0;
          }
        }


  1. vlmonk
    13.08.2024 12:19
    +2

    Магия - это допустим Pure CSS Calculator

    А у вас - пересказ документации.


    1. melnik909 Автор
      13.08.2024 12:19

      Скажите, пожалуйста, что вы ожидали увидеть, прочитав "магия"?


      1. bolk
        13.08.2024 12:19

        У него прямо в комментарии написано что.


  1. director-rentv
    13.08.2024 12:19

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

    А в дополнение к min/max также стоит рассмотреть и clamp


    1. melnik909 Автор
      13.08.2024 12:19
      +1

      Подскажите, пожалуйста, практический кейс, кроме указания размера шрифта