Главная / Статьи / Длина строки, масштабы отступов, тёмные поверхности, тени и кольца фокуса в CSS

Длина строки, масштабы отступов, тёмные поверхности, тени и кольца фокуса в CSS

Ознакомьтесь с основами CSS, лежащими в основе качественных интерфейсов: длина строк, определяемая параметром ch, шкала отступов в 4 пикселя, многоуровневые темные поверхности, многослойные тени и кольца для выделения активных элементов.

1903 слов

Веб-приложение может иметь стильную градиентную заливку, прозрачные карточки и плавные анимации, но уже через несколько секунд использования казаться незавершенным. Ничего не сломано, однако визуальный стиль напоминает прототип, созданный на выходных, а не продукт уровня Linear, Stripe или Vercel. Причина редко кроется в палитре цветов или отсутствии художественного таланта. Обычно дело в нескольких измеримых решениях, связанных с CSS: длиной строк, отступами, контрастом, высотой элементов и состояниями фокуса, и в этой статье показано, как правильно настроить каждый из этих аспектов.

Ограничение длины строк с помощью единиц ch

Текст, занимающий всю ширину монитора 1440 пикселей, — один из явных признаков неотшлифованного фронтенда. Очень длинные строки трудно читать: как только длина строки превышает примерно 80 символов, взгляду приходится возвращаться к левому краю, и он часто попадает не на ту строку при перемещении вниз. На длинной странице постоянное поиск места утомительно.

Как правило, такая ситуация выглядит следующим образом: контейнер полной ширины без каких-либо ограничений для абзаца:

{/* BAD: Unbounded text stretches across the whole viewport */}
<div className="w-full p-8">
  <h1 className="text-3xl font-bold">API Documentation</h1>
  <p className="text-slate-300 mt-4 text-base">
    Our platform enables developers to authenticate and stream webhook events in real-time... (stretches 1200px wide)
  </p>
</div>

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

В CSS существует специальная единица для решения этой проблемы. Единица ch соответствует ширине символа «0» выбранного шрифта, поэтому использование этой единицы для задания размеров позволяет контролировать количество символов в строке независимо от размера шрифта. Удобное чтение обычно предполагает от 45 до 75 символов в строке. Класс для прозы, который можно использовать повторно, может сочетать ограничение по количеству символов с достаточно большой высотой строки и немного уменьшенным расстоянием между буквами:

/* Clean readable prose container */
.prose-container {
  max-width: 68ch; /* Optimal line length regardless of font size */
  line-height: 1.65;
  letter-spacing: -0.01em;
}

В Tailwind утилита max-w-prose обеспечивает аналогичное ограничение (65ch), а mx-auto центрирует контент в колонке:

{/* Enterprise Grade: Beautiful, focused reading experience */}
<div className="max-w-prose mx-auto px-6 py-12">
  <h1 className="text-3xl font-bold tracking-tight text-white">
    API Documentation
  </h1>
  <p className="mt-4 text-slate-300 text-base leading-relaxed">
    Our platform enables developers to authenticate and stream webhook events in real-time...
  </p>
</div>

Если документацию, посты в блоге и описательный текст форматировать так, чтобы его ширина составляла примерно 65–70 ch, эти страницы сразу будут выглядеть сбалансированными и привлекательными. Применяйте это правило к блокам текста, а не ко всему лейауту.

Используйте шаг 4px/8px для всех отступов

Проанализируйте классы утилит в кодбазе с несовершенной структурой, и вы часто найдете подобные значения:

  • Отступы внутри карточки — p-[18px]
  • Отступы для модального окна — mt-5 (20px)
  • Отступы вокруг кнопки — px-3.5 py-[7px]
  • Разделение между секциями — gap-7 (28px)

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

Шкала значений для использования

Устоявшиеся системы дизайна ограничивают значения отступов кратно 4 или 8 пикселям и присваивают каждому шагу название и функцию. Практичная шкала выглядит следующим образом:

  • space-1 (4px): минимальное расстояние, например, между иконкой и её подписью
  • space-2 (8px): компактное отступление для индикаторов и маленьких тегов
  • space-3 (12px): внутреннее отступление полей формы
  • space-4 (16px): стандартное отступление для карточек и кнопок
  • space-6 (24px): расстояние между карточками
  • space-8 (32px): разделение между секциями
  • space-12 (48px): разделение между основными блоками панели управления

Любые значения, выходящие за рамки этой шкалы, такие как margin-top: 19px, следует считать дефектом, а не выбором стиля. По умолчанию в Tailwind используются шаги в 4px, поэтому на практике рекомендуется избегать произвольных значений в круглых скобках.

Избегайте чисто черных фонов в режиме темной темы

Типичная первая попытка создания темного интерфейса SaaS заключается в том, что страница окрашивается в чисто черный цвет с текстом чисто белого цвета:

/* The Harsh Dark Mode Trap */
body {
  background-color: #000000;
  color: #ffffff;
}

Белый на черном обеспечивает максимально возможное соотношение контраста 21:1. Этого вполне достаточно для соблюдения минимальных стандартов доступности, но при таком крайнем значении яркий текст может казаться розоватым или светящимся на фоне (эффект гало), что многим читателям, особенно тем, у кого астигматизм, кажется утомительным, особенно на экранах OLED с высоким контрастом.

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

Создание иерархии слоев

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

:root {
  /* Slate / Charcoal Depth Stack */
  --bg-canvas: #090d16;   /* Deepest background */
  --bg-surface: #0f172a;  /* Cards, tables, sidebar */
  --bg-elevated: #1e293b; /* Dropdowns, popovers, active tabs */
  --bg-overlay: #334155;  /* Modals, tooltips */

Остальная часть того же блока :root добавляет два уровня прозрачности для рамок и три уровня текста — от заголовков до временных меток и неактивных иконок. Обратите внимание, что в приведенном фрагменте декларации --border-active и --text-primary расположены на одной строке; это допустимо в CSS, но лучше переформатировать, а #f8fafc представляет собой почти белый цвет, а не белый с прозрачностью 95%:

  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-active: rgba(255, 255, 255, 0.16);  --text-primary: #f8fafc;   /* 95% opacity white for headings */
  --text-secondary: #94a3b8; /* Muted slate for body text */
  --text-tertiary: #64748b;  /* Inactive icons, timestamps */
}

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

{/* Clean, layered elevation */}
<div className="bg-[var(--bg-canvas)] min-h-screen p-8">
  <div className="bg-[var(--bg-surface)] border border-[var(--border-subtle)] rounded-xl p-6 shadow-sm">
    <h2 className="text-[var(--text-primary)] font-semibold">
      Workspace Overview
    </h2>
    <p className="text-[var(--text-secondary)] text-sm mt-1">
      Manage team roles and API keys.
    </p>
  </div>
</div>

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

Замените одну темную тень на несколько светлых

Интерфейсы для начинающих часто используют одну темную, размытую тень:

/* BAD: One thick, dark, muddy shadow */
.card-bad {
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
}

Результат напоминает устаревший эффект редактирования фотографий. Настоящие объекты не создают однородного размытия. Физическая тень состоит из двух компонентов:

  • тесная тень с высоким контрастом прямо рядом с объектом, образуемая основным источником света
  • широкая, мягкая тень, постепенно сливающаяся с окружающим пространством, известная как амбиентное затемнение

Складывайте тени с низкими значениями альфа

Признак box-shadow принимает список, разделенный запятыми, поэтому вы можете наложить две или три тени, каждая из которых имеет небольшое значение альфа. Здесь тень шириной 1 пиксель придает элементу основание, умеренное размытие добавляет глубину, а широкий, очень мягкий слой распространяет эффект:

/* Polished Enterprise Shadow */
.card-elevation-high {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.06),   /* Crisp grounding line */
    0 8px 16px rgba(0, 0, 0, 0.08),  /* Middle ambient blur */
    0 24px 48px rgba(0, 0, 0, 0.12); /* Soft dispersed glow */
}

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

/* In dark mode, pair subtle shadow with a crisp top border */
.card-dark-elevation {
  box-shadow:
    0 20px 25px -5px rgba(0, 0, 0, 0.5),
    0 8px 10px -6px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

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

Никогда не удаляйте контуры фокуса без замены

Одна строка кода наносит больший вред, чем любая другая, в стилях фронтенда:

/* DO NOT DO THIS */
*:focus {
  outline: none;
}

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

Вместо этого используйте :focus-visible

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

/* Remove ugly mouse clicks, preserve crystal-clear keyboard rings */
button:focus {
  outline: none;
}

Второй шаг — определение четкого пользовательского индикатора фокуса для клавиатуры:

button:focus-visible {
  outline: 2px solid #6366f1; /* Crisp indigo ring */
  outline-offset: 2px;
  border-radius: 6px;
}

Стоит знать два важных момента. Во-первых, более безопасной формой первого правила является button:focus:not(:focus-visible), которая удаляет контур только тогда, когда не требуется видимый индикатор; таким образом, браузеры без поддержки :focus-visible сохраняют свой стандартный контур. Во-вторых, свойство border-radius в правиле фокуса изменяет форму кнопки во время её активации; если у кнопки уже есть заокругленные углы, эту строку можно опустить, поскольку современные браузеры рисуют контур в соответствии с радиусом элемента.

В Tailwind та же идея реализуется с помощью вариантов focus-visible:, которые добавляют кольцо, смещение и цвет смещения, соответствующий темному фону:

<button className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 rounded-lg text-white font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900 transition-all">
  Save Changes
</button>

Поведение свойства outline-none в Tailwind изменилось между крупными версиями (в новых релизах появилось свойство outline-hidden), поэтому проверьте его на своем проекте. В любом случае пользователи мыши получают аккуратные элементы управления, а пользователи клавиатуры всегда видят, где находится фокус.

Чек-лист для доработки перед слиянием

Пройдитесь по этим проверкам перед выпуском изменений фронтенда:

  1. Ширина текста: ограничен ли размер блоков текста примерно 50–75 частями шрифта?
  2. Размеры отступов: все внутренние и внешние отступы заданы по строгой шкале 4px/8px?
  3. Уровни темного режима: поверхности созданы с использованием слоев оттенков угля или сланца, а не просто #000000?
  4. Тени: тени мягкие, слоистые и рассеянные, а не один темный размытый слой?
  • Состояния фокуса: может ли кто-то управлять всем экраном с помощью клавиши Tab и постоянно видеть, где находится фокус?
  • Заключение

    Качество реализации достигается не столько художественной интуицией, сколько последовательными ограничениями: длина строк, основанная на элементе ch, фиксированная шкала отступов, использование светлости в качестве меры глубины в темных темах, тени, имитирующие реальный свет, и кольца фокуса, остающиеся видимыми. Каждое из этих правил является простым и легко проверяемым, поэтому его можно обеспечить с помощью специальных токенов, правил проверки кода и чек-листов, вместо того чтобы полагаться на субъективное мнение при каждом pull request.

    Связанные материалы

  • Сравнение девяти методов реализации темного режима: от трюков с фильтрами до серверных куки — Сравните девять способов добавления темного режима в веб-приложение, от инверсии фильтров до токенов, функции light-dark() и серверных куки, а также узнайте, какие ошибки тихо возникают при использовании каждого из них.