Публичный контракт вместо внутренних формул в typographics 5

03 Сен 2026

Выпустил typographics@5.0.0. Изменение в самом CSS небольшое — блоки кода теперь по умолчанию используют ту же плавную базу, что и основной текст. Это хорошо закрывает задачу, над которой пакет постепенно менялся после 3.0 — пользователю не нужно знать внутренние формулы и пути сборки без необходимости.

В 3.0 убрал html { font-size: 10px } и разделил основной текст и заголовки на две плавные шкалы. Сама модель стала аккуратнее, но пользоваться ей всё ещё можно на разных уровнях. Иногда нужно просто сделать типографику чуть меньше внутри статьи. Иногда — задать точные размеры одному заголовку на краях адаптивного диапазона. А подключение пакета вообще не должно требовать знания, где именно лежит собранный CSS.

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

Масштаб без копирования формулы

После 3.0 размер основного текста вычислялся через --t-body-font-size-clamp, а заголовков — через --t-heading-font-size-clamp. Если хотелось уменьшить всю типографику, технически можно было переопределить исходные min/max. Но это слишком низкий уровень для обычной задачи “сделать этот блок на 5% компактнее”.

В 3.1.0 добавил две переменные:

:root {
  --t-body-scale: 1;
  --t-heading-scale: 1;
}

Они применяются уже поверх рассчитанной fluid-базы:

font-size: calc(
  var(--t-heading-font-size-clamp)
  * var(--t-heading-scale, 1)
  * #{$k}
);

Для ролей основного текста схема такая же:

font-size: calc(
  var(--t-body-font-size-clamp)
  * var(--t-body-scale, 1)
  * #{$k}
);

За счёт каскада одна и та же настройка работает и глобально, и локально:

:root {
  --t-body-scale: 0.9;
  --t-heading-scale: 0.9;
}

.article {
  --t-body-scale: 0.95;
}

.hero {
  --t-heading-scale: 0.9;
}

Получился нужный уровень API: не копировать clamp() и внутренние calc(), а указать, какую группу текста нужно масштабировать. Внутренняя математика остаётся внутри пакета.

Патч 3.0.1 перед этим ничего глобально в CSS не менял — я только исправил примеры в README. Реальная настройка новой модели началась в 3.1.0.

Когда общего масштаба заголовков недостаточно

Общий коэффициент решает много случаев, но реальные макеты показали его границу. Иногда разные роли заголовков должны иметь разные адаптивные диапазоны. Уменьшить всю шкалу заголовков на 0.9 недостаточно, если, например, только .headline-medium должен быть 28px на узком экране и 34px на широком.

В 3.2.0 добавил необязательные границы для каждой роли заголовка:

.headline-medium {
  --t-headline-medium-min: 28px;
  --t-headline-medium-max: 34px;
}

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

--t-heading-resolved-min: var(
  --t-#{$fluid-key}-min,
  calc(
    var(--t-heading-font-size-min)
    * var(--t-heading-scale, 1)
    * #{$k}
  )
);

--t-heading-resolved-max: var(
  --t-#{$fluid-key}-max,
  calc(
    var(--t-heading-font-size-max)
    * var(--t-heading-scale, 1)
    * #{$k}
  )
);

Дальше эти два значения становятся границами обычного clamp().

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

Перед стабильным релизом опубликовал 3.2.0-dev.0 для обкатки. В финальном 3.2.0 общая шкала заголовков остаётся вариантом по умолчанию, а отдельные роли при необходимости получают собственные границы.

Подключение пакета тоже часть API

В 4.0.0 типографический CSS относительно 3.2.0 не изменился. Мажорный релиз был про то, как пакет приезжает к пользователю.

К этому моменту я унифицировал npm-пакеты своего портфолио вокруг общего стандарта сборки и публикации. Для typographics это означало, в частности, корневой CSS export:

{
  "style": "./dist/index.css",
  "exports": {
    ".": {
      "style": "./dist/index.css",
      "default": "./dist/index.css"
    },
    "./dist/*": "./dist/*"
  }
}

В README основной способ подключения после этого выглядит так:

import 'typographics';

Путь к файлу внутри dist всё ещё доступен, но для обычного сценария больше не нужен.

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

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

Блок кода следует основному тексту

К 5.0.0 в плавной модели оставалось заметное исключение. Основной текст, абзацы и списки уже зависели от --t-body-font-size-clamp, а блок кода жил от фиксированного значения 1.4rem:

font-size: calc(
  var(--t-code-block-font-size, 1.4rem)
  * var(--t-body-scale, 1)
);

В 5.0 значение по умолчанию теперь берётся из базы основного текста:

:root {
  --t-code-block-scale: 1;
}

@mixin typography-code-block() {
  font-size: var(
    --t-code-block-font-size,
    calc(
      var(--t-body-font-size-clamp)
      * var(--t-body-scale, 1)
      * var(--t-code-block-scale, 1)
    )
  );
}

Здесь получились три уровня управления.

Если ничего не задавать, блок кода следует плавному размеру основного текста. Если нужно чуть изменить только код, достаточно --t-code-block-scale:

.article {
  --t-code-block-scale: 0.9;
}

Если нужен полностью фиксированный или собственный размер, остаётся явное переопределение:

.article {
  --t-code-block-font-size: 13px;
}

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

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

К 5.0.0 публичная поверхность выглядит достаточно цельно. --t-body-scale и --t-heading-scale решают грубую настройку, min/max конкретной роли — точную, блок кода наследует общую плавную базу, а импорт начинается с имени пакета. Внутри по-прежнему есть clamp(), коэффициенты и резервные формулы, но пользоваться библиотекой можно, почти не зная их устройства.

Материалы