Задайте значения текста страницы по умолчанию для body, позвольте наследуемым свойствам передаваться компонентам и задавайте правила макета целенаправленно. В этом руководстве показано, как создать и проверить локальную HTML-страницу на Mac, не заставляя каждый элемент отменять универсальное правило.

Почему универсальное правило мешает значениям компонентов по умолчанию?

Универсальный селектор * соответствует каждому элементу. Поэтому объявление внутри этого правила применяется непосредственно к каждому совпавшему элементу, а не просто задает значение, наследуемое потомками. Он также не соответствует псевдоэлементам напрямую: для них нужны селекторы вроде *::before. Универсальные селекторы.

Рассмотрим этот фрагмент CSS для компонента с абзацем и вложенным текстовым элементом span:

/* Illustrative fragment: assumes a .tile with text descendants. */
* {
  color: #303840;
}

.tile {
  color: #245d75;
}

Сам элемент плитки получает синий цвет текста. Абзац и элемент span напрямую получают темный цвет из универсального правила. Цвет компонента не может передаться им через обычное наследование при наличии этих объявлений. Согласно правилам CSS для значений по умолчанию, наследуемые свойства получают вычисленное значение родителя, если каскад не задает для этого свойства значение самого элемента. CSS Cascading and Inheritance Level 4, значения по умолчанию.

Это может сбивать с толку, поскольку специфичность * равна нулю. Специфичность сравнивает конкурирующие объявления для одного и того же элемента в применимых источнике и слое каскада. Объявление для родителя не является совпавшим селектором для его потомка. Поэтому повышение специфичности селектора родителя не решает эту проблему наследования. Специфичность.

Полезное изменение — убрать универсальное объявление текста и задать значение по умолчанию для body. Тогда потомки смогут наследовать его, пока компонент или элемент не задаст другое значение. MDN указывает, что color — наследуемое свойство, и поясняет, что наследование использует вычисленное значение родителя. Наследование.

Какие значения по умолчанию задавать для страницы, компонента или каждого элемента?

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

Желаемый результат Рекомендуемое место Причина
Для обычного текста страницы заданы общие базовый цвет и типографика body Задает тексту общее исходное значение, передаваемое через наследование
Для плитки и обычного текста внутри нее задан другой общий цвет .tile Задает новое наследуемое значение внутри компонента
Для одной подписи задан другой цвет .caption Применяет исключение к нужному элементу
Для одной панели заданы внутренние отступы, рамка или фон Класс панели Привязывает оформление блока к самому блоку
Для блоков используется единая модель расчета размеров Намеренное правило box-sizing Задает явное соглашение о расчете размеров
Для заголовков задан отступ по умолчанию, который легко переопределить Целевой селектор заголовков, при желании обернутый в :where() Ограничивает значение по умолчанию нужными элементами

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

Универсальное правило для размеров все же может быть уместным. При border-box заданная ширина элемента включает его внутренние отступы и рамку; внешние отступы остаются за пределами этой ширины. Это конкретное решение для блочной модели, а не основание универсально задавать все значения страницы по умолчанию. box-sizing.

Создайте на Mac полноценный локальный пример

Используйте TextEdit в режиме обычного текста или установленный редактор кода. В TextEdit выберите File > New, затем Format > Make Plain Text. Вставьте весь документ ниже, выберите File > Save и назовите файл page-defaults.html. Если появится запрос, выберите Use .html. Apple описывает этот процесс в руководстве по HTML в TextEdit.

В этом примере используются условные подписи и фигуры, нарисованные с помощью CSS. Это полноценный документ со встроенной таблицей стилей, поэтому дополнительные файлы не нужны. Код был проверен здесь; его отображение в браузере Mac не запускалось.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page defaults demonstration</title>
  <style>
    *, *::before, *::after {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: system-ui, sans-serif;
      font-size: 1rem;
      line-height: 1.5;
      color: #303840;
      background-color: #f4f5f6;
    }

    main {
      max-width: 44rem;
      margin: 0 auto;
      padding: 2rem 1rem;
    }

    h1 {
      line-height: 1.2;
    }

    .tile {
      margin: 1.5rem 0;
      padding: 1.25rem;
      border: 2px solid currentColor;
      border-radius: 0.75rem;
      color: #245d75;
      background-color: #ffffff;
    }

    .tile h2 {
      margin-top: 0;
    }

    .shape {
      width: 4rem;
      height: 4rem;
      background-color: currentColor;
    }

    .shape--round {
      border-radius: 50%;
    }

    .caption {
      color: #66523c;
    }
  </style>
</head>
<body>
  <main>
    <h1>Shape notes</h1>
    <p>Page sample: <span>Label A</span>.</p>

    <section class="tile">
      <h2>Tile B</h2>
      <div class="shape shape--round" aria-hidden="true"></div>
      <p>Component sample: <span>Label C</span>.</p>
      <p class="caption">Caption D has its own text color.</p>
    </section>

    <p>Page sample: <span>Label E</span>.</p>
  </main>
</body>
</html>

Откройте page-defaults.html в браузере. Когда будете повторно открывать исходный код в TextEdit, выберите TextEdit > Settings > Open and Save и включите Display HTML files as HTML code instead of formatted text, если хотите, чтобы TextEdit всегда показывал разметку единообразно. Работа с HTML-документами в TextEdit на Mac.

Проверьте границу наследования с помощью трех изменений

Эти проверки превращают документ в небольшое диагностическое упражнение. После каждого изменения сохраняйте исходный файл и обновляйте страницу в браузере. Сохраните копию исходного документа или отменяйте каждое изменение перед следующим.

  1. Измените только цвет body на #4b465f. Текст вокруг Label A и Label E должен измениться. Цвет плитки останется прежним.
  2. Восстановите цвет страницы, затем измените только .tile на color: #526637. Обычный текст заголовка и абзаца внутри нее должен подстроиться, в том числе Label C. Caption D сохранит собственный цвет.
  3. Восстановите цвет плитки, затем временно добавьте * { color: #303840; } в начало таблицы стилей. Абзац с Label C теперь получит это прямое объявление. После этого удалите временное правило.

Эти проверки показывают различие между компонентом, задающим наследуемое значение, и потомком, получающим собственное объявление. Ожидаемое поведение текста соответствует правилам наследования и значений по умолчанию спецификации.

Как связаны HTML-страница и ее таблица стилей?

Отделяйте загрузку ресурсов от поведения селекторов. В примере CSS встроен в элемент style. Если удобнее использовать отдельную таблицу стилей, перенесите только содержимое этого элемента в обычный текстовый файл с именем page-defaults.css и сохраните его рядом с HTML-файлом. Замените весь элемент style следующей строкой внутри head. Это фрагмент для замены, а не еще один полный документ.

<link rel="stylesheet" href="page-defaults.css">

Тип ссылки stylesheet обозначает внешний ресурс CSS, используемый для оформления документа. Имя файла в href должно указывать на файл, который вы действительно сохранили; само объявление не создает этот файл. Связь описана в стандарте HTML в разделе Тип ссылки: stylesheet.

В этом примере связь проста: HTML задает элементы и классы, а CSS — их оформление. Если переименовать .tile в таблице стилей, нужно изменить соответствующий класс в HTML. Если переместить файл CSS, нужно обновить ссылку на ресурс. Ни для одного из этих изменений не нужен более специфичный селектор.

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

Когда использовать :where() или слои каскада?

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

Используйте :where() для целевых значений по умолчанию с нулевой специфичностью

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

/* Optional fragment: add to the example stylesheet. */
:where(h1, h2, h3) {
  margin-bottom: 0.75rem;
}

h2 {
  margin-bottom: 1rem;
}

Специфичность всего селектора :where(), включая аргументы, равна нулю. Но замена * на :where(*) не решит прежнюю проблему с цветом: селектор по-прежнему соответствует каждому элементу и задает прямое объявление. Эту проблему решает наследование. MDN указывает, что :where() широко поддерживается браузерами с января 2021 года; если нужно поддерживать старый браузер, проверьте таблицу совместимости. Псевдокласс :where().

Если браузер не поддерживает :where(), задайте значения отступов обычным списком селекторов, например h1, h2, h3, а затем разместите исключения с такой же специфичностью ниже в той же таблице стилей и том же слое. Так сохраняются целевые значения по умолчанию, хотя их специфичность уже не будет нулевой. Специфичность.

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

В этом необязательном фрагменте для более крупной таблицы стилей показан объявленный порядок слоев. Он дополняет полный пример, а не заменяет весь его CSS.

/* Optional fragment for browsers supporting cascade layers. */
@layer defaults, components;

@layer defaults {
  p {
    margin-bottom: 1rem;
  }
}

@layer components {
  .tile p {
    margin-bottom: 0.5rem;
  }
}

Для обычных авторских объявлений более поздние слои имеют приоритет над более ранними; объявления без слоя имеют приоритет над объявлениями в слоях. Для объявлений с !important порядок слоев меняется на обратный, поэтому добавление !important меняет этот порядок. Слои упорядочивают конкурирующие правила, но не заставляют напрямую стилизованный дочерний элемент наследовать значение родителя. Правило @layer.

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

Как исправить существующую таблицу стилей, не добавляя новые переопределения?

Работайте с каждым свойством по очереди. Если возникла проблема с цветом текста, найдите широкое правило, которое напрямую задает цвет потомкам. Удалите это объявление из универсального правила, задайте значение страницы для body, а цвета компонентов оставьте на их корневых элементах. Объявления для отдельных элементов сохраняйте только там, где дизайн требует исключений.

Затем повторите три изменения из примера. Если обычный текст компонента по-прежнему не наследует значение родителя, найдите объявление, нацеленное на этот дочерний элемент. Если изменение рамки компонента не меняет рамки его потомков, это ожидаемо: наследование рамки отличается от наследования цвета текста. Наследование.

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

Источники