Skip to content
PПромтбук
RUEN
02Адаптив

Fluid typography через clamp(): формула и edge cases

Шрифты и spacing, которые сами масштабируются без брейкпойнтов. Как считать preferred, что НЕ клампить, accessibility.

Действуй как дизайн-систем инженер. Настрой fluid typography через clamp() для всех уровней шрифтов и spacing-токенов. Диапазон viewport: {{min_viewport}}–{{max_viewport}}px.

1. Анатомия clamp()

clamp(MIN, PREFERRED, MAX)
  • MIN — нижняя граница (на самом узком экране)
  • PREFERRED — целевое значение, обычно зависящее от viewport
  • MAX — верхняя граница (на самом широком экране)

Браузер выбирает: если PREFERRED < MIN → MIN; если > MAX → MAX; иначе — PREFERRED.

2. Формула preferred

Цель: значение растёт линейно от MIN на узком экране до MAX на широком.

slope = (MAX - MIN) / (MAX_VW - MIN_VW)
intercept = MIN - slope * MIN_VW

preferred = calc({intercept}rem + {slope * 100}vw)

Пример: H1 16px → 48px от 320 до 1440

  • MIN = 16px = 1rem
  • MAX = 48px = 3rem
  • slope = (48 - 16) / (1440 - 320) = 0.0286
  • intercept = 16 - 0.0286 × 320 = 6.86px = 0.429rem
h1 { font-size: clamp(1rem, 0.429rem + 2.86vw, 3rem); }

Готовая mixin-функция (SCSS):

@function fluid($min, $max, $min-vw: 320, $max-vw: 1440) {
  $slope: ($max - $min) / ($max-vw - $min-vw);
  $intercept: $min - $slope * $min-vw;
  @return clamp(#{$min}px, #{$intercept}px + #{$slope * 100}vw, #{$max}px);
}

h1 { font-size: fluid(16, 48); }
h2 { font-size: fluid(14, 32); }

3. Type scale

:root {
  --fs-xs:  clamp(0.75rem, 0.71rem + 0.18vw, 0.875rem);  /* 12 → 14 */
  --fs-sm:  clamp(0.875rem, 0.83rem + 0.22vw, 1rem);     /* 14 → 16 */
  --fs-md:  clamp(1rem, 0.93rem + 0.36vw, 1.25rem);      /* 16 → 20 */
  --fs-lg:  clamp(1.25rem, 1.12rem + 0.63vw, 1.625rem);  /* 20 → 26 */
  --fs-xl:  clamp(1.625rem, 1.39rem + 1.16vw, 2.25rem);  /* 26 → 36 */
  --fs-2xl: clamp(2.25rem, 1.82rem + 2.14vw, 3.5rem);    /* 36 → 56 */
  --fs-3xl: clamp(2.75rem, 1.96rem + 3.93vw, 5rem);      /* 44 → 80 */
}

4. Spacing tokens — туда же

:root {
  --space-1: clamp(0.25rem, 0.23rem + 0.09vw, 0.3125rem);  /* 4 → 5 */
  --space-2: clamp(0.5rem, 0.46rem + 0.18vw, 0.625rem);    /* 8 → 10 */
  --space-4: clamp(1rem, 0.92rem + 0.36vw, 1.25rem);       /* 16 → 20 */
  --space-8: clamp(2rem, 1.71rem + 1.43vw, 2.75rem);       /* 32 → 44 */
  --space-16: clamp(4rem, 3.14rem + 4.29vw, 6.25rem);      /* 64 → 100 */
}

5. Edge cases

Ультра-узкий (≤ 320px)

Старый iPhone SE первого поколения, фичефоны в landscape. Если MIN = 16px — текст всё равно читаем. Если поставил MIN = 14px на body → проблема, минимум для body = 16px (см. accessibility).

Ультра-широкий (4K, 27"+)

На 3840px viewport vw уходит в космос. Без MAX H1 станет 120px+. Всегда задавай MAX. Опционально: ограничь весь сайт через max-width: 1440px на body — тогда vw считается от ограниченной ширины.

Сжатие zoom 200%

Если использовать только vw без rem в preferred — зум сломан, шрифт не реагирует. Поэтому формула всегда {rem} + {vw}. Это требование WCAG 1.4.4.

Между MIN и MAX viewport

Между 320 и 1440 значение растёт линейно. Хочешь S-curve? Используй несколько clamp через @container или CSS min()/max() композиции.

6. Accessibility

  • Body text: MIN ≥ 16px. Меньше — нарушение WCAG 1.4.4 для большинства пользователей.
  • Не клампь line-height в абсолютных единицах. Юзай unitless (line-height: 1.5) — масштабируется само.
  • Caption / помощь: MIN ≥ 14px. Меньше — только для уникальных лейблов, никогда для body content.
  • Headings: можно агрессивнее (H1 12px на 320 — окей), но проверь, что иерархия читается.
  • Зум 200%: проверь через DevTools → Settings → Zoom 200%. Текст должен расти, а не быть зафиксирован.

7. Формат вывода

## Fluid type scale

| Token | MIN (320px) | MAX (1440px) | Formula |
|---|---|---|---|
| --fs-md | 16px | 20px | clamp(1rem, 0.93rem + 0.36vw, 1.25rem) |

## Spacing scale
[аналогично]

## Validation
- [ ] Body ≥ 16px на 320px
- [ ] Зум 200% — текст растёт
- [ ] 4K — текст не уходит в космос
- [ ] line-height unitless везде

Anti-patterns

  • font-size: 3vw без clamp — на 320px = 9.6px (нечитаемо), на 4K = 115px (карикатурно).
  • ❌ Preferred без rem, только vw — зум 200% не работает, провал WCAG 1.4.4.
  • ❌ MIN < 16px на body — половина читателей со средним зрением страдает.
  • ❌ Один clamp для всей типографики — крошечный H6 = 12px и здоровенный H1 = 72px не сделаешь одной формулой, нужна шкала.
  • line-height: clamp(20px, ...) — фиксированные единицы в line-height, при изменении font-size — рассинхрон. Используй line-height: 1.5.
  • ❌ Расчёт slope вручную для каждого токена — ошибки, забытые знаки. SCSS-функция или CSS calc() с переменными.
  • ❌ Игнор ультра-широкого — без MAX дизайн на 27" мониторе выглядит сломанно, типографика плывёт.
  • ❌ Fluid spacing БЕЗ fluid type — текст растёт, паддинги фиксированы, ритм рушится. Либо всё, либо ничего.
К подразделу «Адаптив»
Похожие промты