Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков CSS фишки. Я отбираю их так, чтобы они были полезны в разного рода проектах.


Неважно, верстаете ли вы сайт для малого бизнеса или создаёте супермодное React приложение. Они поддерживаются большинством браузеров. Отдельно отмечу, что я не считаю IE11 современным браузером. По этой причине я не учитывал его.


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

  • сброс стилей до значений, взятых из веб-стандартов;
  • возврат значений свойств, установленных в браузере;
  • что можно сделать с прыжками контента при открытии и закрытии модального окна;
  • возможность отобразить текст «красиво» с помощью ключевого слова system-ui;
  • способ стилизации элементов на языке, отличающимся от основного.

Больше не буду затягивать. Давайте посмотрим, что я вам подготовил.


▍ Ключевое слово unset и revert


Периодически я копаюсь в CSS коде популярных библиотек. Так, однажды я наткнулся на версию CSS Reset от Элада Шехтер.


Сама библиотека довольно старая. Я сам первый раз познакомился с ней в году 2015. Элад обновил её. Его решение основано на использовании ключевых слов unset и revert. Например, так автор сбрасывает стили почти у всех элементов:


/*
  Удаляет все "User-Agent-Stylesheet" стили, за исключением свойства display
*/
*:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) {
  all: unset;
  display: revert;
}

Что же делают ключевые слова? Разберём по порядку.


Для объяснения ключевого слова unset вспомним, что в CSS есть два типа свойств, а именно: наследуемые и ненаследуемые. Разница между ними заключается в том, что первые могут получить значение в результате механизма наследования от родительского элемента, а вторые так не могут.


Если ключевое слово unset используется для наследуемых свойств, то итоговое рассчитанное значение будет получено в результате механизма наследование. Например, в следующем примере значение у свойства color у элемента <p> будет получено из элемента <body>.


body {
  color: #222; 
}

p {
  color: unset; /* итоговое значение будет #222 */
}

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


body {
  color: #222; 
}

p {
  color: unset; /* итоговое значение будет #222 */
  border-width: 2px;
  border-style: solid;
  border-color: unset; /* итоговое значение будет #222, которое было получено с помощью currentColor */
}

Открыты инструменты разработчика. Отображается значение rgb(34, 34, 34) для свойства border

Здесь стоит пояснить, почему в devTools мы увидим rgb(34, 34, 34)(#222 в HEX формате). Ключевое слово currentColor передаёт в свойство значение свойства color. Поскольку для элемента <p> установлено ключевое слово unset, с помощью него браузеры установят значение #222. Оно же будет использовано для свойства border-color.


В итоге Элад в своём варианте CSS Reset строкой all: unset сбрасывает все значения, чтобы они либо наследовали значение от родителя, либо использовали установленные стандартами значения.


Такой подход в целом работает, но не без исключений. Первое исключение — это свойство display. После применения строки all: unset у всех элементов будет установлено значение inline, потому что оно установлено стандартами.


По этой причине Элад использует значение revert для свойства display.


/*
  Удаляет все "User-Agent-Stylesheet" стили, за исключением свойства display
*/
*:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) {
  all: unset;
  display: revert;
}

Таким способом браузеры возьмут итоговое значение из «User-Agent-Stylesheet» стилей, установленных в них по умолчанию. Например, для элемента <p> будет взято значение block, а для элемента <span>inline.


Для демонстрации принципа работы я добавлю код к элементам <p> и <span>.


p {
  all: unset;
  display: revert; /* здесь будет display: block */
}

span {
  all: unset;
  display: revert; /* здесь будет display: inline */
}

Открыты инструменты разработчика. Для элемента p отображается значение block для свойства display

Для элемента span отображается значение inline для свойства display

Мне лично не нравится подход «Сначала установим, а потом сбросим». По этой причине я не использую CSS Reset. Но сами значения unset и revert полезны.


Например, в компонентах, у которых меняется значение свойства display. Или в качестве значений по умолчанию для переменных. Кстати, поделитесь, пожалуйста, в комментариях, как вы используете данные значения.


▍ Избавляемся от прыжков текста контента при открытии и закрытии модального окна


Модальные окна — один из наиболее ненавидимых мной элементов интерфейсов. Такое количество проблем и нюансов я нигде не видел. Одна из наиболее часто встречающихся — это смещение контента при открытии и закрытии окна. Вы можете посмотреть на неё с помощью демонстрации на сайте Дока.


Почему происходит смещение? Обратите внимание на полосу прокрутки браузера. Когда модальное окно закрыто — она есть, когда открыто — она убирается. Происходит это с помощью значение hidden для свойства overflow, добавленного к элементу <body>. Убирая и добавляя полосу прокрутки, браузеры смещают контент на её ширину.


Существуют несколько техник, чтобы избежать смещения. Моя любимая основана на управлении пространством, выделяемым под полосу прокрутки. Отвечает за это поведение свойство scrollbar-gutter.


В нашей задаче оно позволит навсегда зарезервировать место. Для этого нужно использовать значение stable. Например, разработчики сайта Дока добавляют его к элементам <body> и <html>.


html,
body {
  scrollbar-gutter: stable;
}

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


▍ Воспользуемся для контента встроенным в платформу шрифтом


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


В нём у меня есть возможность установить шрифт для текста. Для этого я создал переменную --ds-typography-main-font-family и объявил её для элемента <body>.


body {
  font-family: var(--ds-typography-main-font-family, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Open Sans, Ubuntu, Fira Sans, Helvetica Neue, sans-serif);
}

Когда я реализовывал эту задачу, у меня был вопрос: «Какое значение по умолчанию установить?» Я погуглил и нашёл список шрифтов, которые рекомендуют для различных платформ. Его я стал использовать долгое время. А потом узнал про ключевое слово system-ui.


В стандарте CSS Fonts Module Level 4 ему дано определение. Я понял его так, что ключевое system-ui позволяет нам использовать для веб-контента шрифт, который встроен в операционную систему. Это помогает избежать отличий между текстом в интерфейсе ОС и текстом в веб-приложении.


«Блин, да это то, что нужно в моей задаче!» — подумал я. Так я добавил ключевое слово в свой сниппет.


body {
  font-family: var(--ds-typography-main-font-family, system-ui);
}

Вместо длинной портянки получилось краткое ёмкое значение по умолчанию.


▍ Стилизация элементов и псевдокласс :lang()


В интерфейсах обычная практика использовать слова из другого языка. Часто они должны быть как-то иначе стилизированы. Для примера возьмём страницу Википедии о CSS. На ней есть фраза «Cascading Style Sheets».


<!DOCTYPE html>
<html lang="ru">
  <head>
    <!-- здесь элементы -->
  </head>
  <body>
    <p>
      CSS (англ. <span lang="en">Cascading Style Sheets</span> «каскадные таблицы стилей») — формальный язык декодирования и описания внешнего вида документа (веб-страницы), написанного с использованием языка разметки (чаще всего HTML или XHTML). 
    </p>
  </body>
</html>

Параграф текста с определением CSS. Фраза Cascading Style Sheets отображается курсивом

Как её сделать курсивом? Да, легко! Добавить класс к элементу, написать font-style: italic и дело сделано. Конечно, это сработает. Но в CSS есть более элегантное решение, а именно псевдокласс :lang().


Он выбирает элементы, в зависимости от их языка. Есть несколько способов установить его. Самый популярный это — атрибут lang. На примере стилизации фразы «Cascading Style Sheets» разберём, как применить псевдоэлемент.


Для элемента <span> уже установлен атрибут lang со значением en. Осталось объявить псевдокласс :lang().


:lang(en) {
  font-style: italic;
}

Можно пойти дальше. Определение стилей для каждого конкретного языка приведёт к раздуванию кода. Можно сделать проще. Написать стили для любого языка, кроме основного.


Напомню, что основной язык определяется при установке атрибута lang для элемента html. В моём примере используется lang="ru". Получается, наша задача заключается в том, чтобы выбрать все элементы, у которых значение атрибута lang отличается от ru.


Псевдокласс :not() тут, как родной.


:not(:lang(ru)) {
  font-style: italic;
}

▍ Заключение


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

  • как ключевое слово unset позволяет сбросить стили до значений веб-стандартов, а ключевое слово revert вернуть их обратно;
  • способ избегать прыжков контента при открытии и закрытии модального окна, основанном на свойстве scrollbar-gutter;
  • ключевое слово system-ui для использования шрифта платформы;
  • псевдокласс :lang() для стилизации элементов на языке, отличающегося от основного.

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

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


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


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

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


  1. wadowad
    27.08.2024 13:45
    +3

    Со scrollbar-gutter всё было бы отлично, если бы не отсутствие поддержки в Safari :( Поэтому костыли, костыли и ещё раз костыли.


    1. xDamneDx
      27.08.2024 13:45

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

      Upd: ааа, видимо речь про сафари в винде, там да, наверное не будет работать.


      1. Yozi
        27.08.2024 13:45

        Скрол на маках можно включить в настройках


      1. vanxant
        27.08.2024 13:45
        +1

        сафари в винде

        мертвее мёртвого лет 15 как

        хочешь верстать под эплл - покупай макбук с ойфоном.


  1. vanxant
    27.08.2024 13:45

    Пожалуй, самая слабая статья из всего цикла, уж извините.


    1. melnik909 Автор
      27.08.2024 13:45

      Расскажите, пожалуйста, почему она самая слабая?


      1. vanxant
        27.08.2024 13:45

        Ну, лично для меня в первой был полезен display: contents (я о нём не знал и реально мучился), во второй scroll-margin-top (сильно реже нужно, но всё же), в третьей про padding-inline, которые нафиг никому не нужны, но они есть в стандартном CSS хрома и как бы надо понимать про что речь. А тут вы нам предлагаете :lang, который не задумываясь делается через [lang]. Ещё есть шрифт system-ui, который в том же бутстрапе лет 15, и нужный сугубо для собеседований revert.


        1. melnik909 Автор
          27.08.2024 13:45

           А тут вы нам предлагаете :lang, который не задумываясь делается через [lang]

          Значит не зря готовлю статью. Будет для вас открытие, что [lang] сильно отличается от :lang()


          1. vanxant
            27.08.2024 13:45

            [lang] сильно отличается от :lang()

            не могли бы вы пояснить, чем именно?


            1. melnik909 Автор
              27.08.2024 13:45

              В новой статье расскажу. Должен же я собирать просмотры


              1. vanxant
                27.08.2024 13:45

                Вы на хабре, здесь так не принято


  1. Zoolander
    27.08.2024 13:45

    Про атрибут lang - в CSS же есть селектор по атрибуту, причем даже с конкретным значением.


    1. wadowad
      27.08.2024 13:45

      Я думаю тут фишка в том, что в качестве примера показан способ через отрицание. Т.е. если используются какие-либо другие языки, даже которые не предполагалось использовать изначально, то они будут сразу стилизоваться.


  1. bubn0ff
    27.08.2024 13:45

    Спасибо за статью, нашёл новое для себя.