Descender — це частина рядкової літери, яка опускається нижче базової лінії шрифту (baseline). У латиниці типові літери з descender — «g», «j», «p», «q», «y». Довжина descender визначає нижній виносний елемент шрифту і критично важлива для розрахунку міжрядкового інтервалу: без достатнього простору під baseline літери з descender будуть зачіпати наступний рядок. За даними Material Design Type Scale Guidelines (2025), недостатнє врахування descender — одна з головних причин колізії рядків у багаторядковому тексті на мобільних пристроях.
Головне
Descender — це частина гліфа, розташована нижче лінії baseline. У той час як основне тіло літери стоїть на baseline, descender виступає за його межі, створюючи характерний силует шрифту. У латинському алфавіті до літер з descender належать «g», «j», «p», «q», «y» — їхні нижні елементи опускаються нижче рядка.
Глибина descender описує відстань від baseline до нижньої межі гліфа (descender-line). У якісних шрифтах ця відстань збалансована: занадто короткий descender робить літери з descender важкими для розпізнавання, а занадто довгий — створює надлишковий пустий простір між рядками та знижує щільність тексту. Різні шрифтові гарнітури демонструють суттєві відмінності в довжині descender.
| Гарнітура | Descender / em-size | Приклад літер з descender |
|---|---|---|
| SF Pro | ~0.22 | g, j, p, q, y — збалансований винос |
| Roboto | ~0.24 | g, j, p — помірний descender |
| Playfair Display | ~0.30 | g, j, p, q — довгі декоративні елементи |
| Inter | ~0.26 | g, j, p — помітно нижче baseline |
| Noto Sans | ~0.20 | g, j — короткий descender, компактний |
За даними Google Fonts Metrics Guide (2025), descender вважається оптимальним, коли його глибина становить 20–25% від повного розміру em (1000 FUnits). Значення нижче 15% роблять літери з descender важко розрізнюваними, а вище 30% потребують обов'язкового збільшення line-height для запобігання колізіям рядків.
У цифрових шрифтах descender зберігається у вигляді від'ємного значення в таблицях метрик. У форматі OpenType це поле hhea.descent (таблиця hhea) і sTypoDescender (таблиця OS/2). Обидва значення від'ємні, оскільки відраховуються від baseline вниз. Для TrueType використовується таблиця OS/2 з полем usWinDescent — його значення додатне, але позначає ту ж метрику.
# Читання descender зі шрифту через fontTools
from fontTools.ttLib import TTFont
font = TTFont('Roboto-Regular.ttf')
hhea = font['hhea']
os2 = font['OS/2']
descent_hhea = hhea.descent # -500 FUnits (Roboto)
typo_descender = os2.sTypoDescender # -500 FUnits
win_descent = os2.usWinDescent # 500 (positive value)
# Конвертувати в пікселі для розміру шрифту 16pt
px_per_em = 16
descent_px = abs(descent_hhea) * px_per_em / 1000 # 8 px
Критична відмінність між платформами: iOS використовує hhea.descent для рендерингу, а Android — sTypoDescender з OS/2. Якщо ці значення різняться (що буває в неякісно налаштованих шрифтах), один і той же текст відображатиметься з різними міжрядковими інтервалами на iOS і Android. Різниця в 100 FUnits (приблизно 1.6 px при кеглі 16 pt) вже помітна візуально.
За даними Microsoft OpenType Specification v1.9 (2025), для коректного крос-платформного рендерингу значення hhea.descent і sTypoDescender мають бути рівними з точністю до 50 FUnits. При виборі шрифту для мобільного застосунку це варто перевірити через fontTools або аналогічну утиліту.
У iOS значення descender доступне через властивість UIFont.descender. Ця властивість повертає від'ємне число, яке показує відстань від baseline до нижнього краю шрифту (включаючи descender). Наприклад, для SF Pro при кеглі 17 pt значення descender ≈ -4.2 pt. Чим більший модуль числа, тим довші нижні виносні елементи шрифту.
// Отримання descender на iOS через UIFont
let font = UIFont.systemFont(ofSize: 17)
let descender = font.descender // ~ -4.2 pt для SF Pro 17pt
let ascender = font.ascender // ~ 16.2 pt
let lineHeight = font.lineHeight // ~ 20.4 pt
// Кастомний рендеринг зі зміщенням descender
let attrString = NSAttributedString(
string: "Sample text with letter p and y",
attributes: [.font: font]
)
// Core Text: отримання bounding box з descender
let ctFont = CTFontCreateWithName(
"SF Pro Text" as CFString, 17, nil
)
let descent = CTFontGetDescent(ctFont) // ~4.2 pt
При використанні TextKit (NSTextStorage, NSLayoutManager) descender автоматично враховується в lineFragmentPadding та lineFragmentRect. Однак при кастомному рендерингу через Core Graphics (draw(in:)) необхідно самостійно коригувати координати, додаючи модуль descender до нижнього відступу контейнера. Якщо цього не зробити, літери з descender виходитимуть за межі рендерингу та обрізатимуться.
У Android метрики descender доступні через Paint.FontMetrics.descent. На відміну від iOS, значення descent додатне — це відстань від baseline до нижньої межі тексту. Властивість FontMetrics.bottom включає не лише descender, але й додатковий простір, рекомендований дизайнером шрифту (leading). Для точного врахування лише descender використовуй descent, а не bottom.
// Отримання descender на Android через Paint
val paint = Paint().apply {
textSize = 17 * density
}
val metrics = paint.fontMetrics
val descent = metrics.descent // ~4.5 px для 17sp
val bottom = metrics.bottom // ~5.0 px з leading
// Кастомний рендеринг зі зміщенням descender
val baseline = y
canvas.drawText("Зразок: gpq", x, baseline, paint)
// Нижня межа з descender
val bottomBound = baseline + descent // правильна нижня межа
У Jetpack Compose descender може бути отриманий через TextLayoutResult. Метод getLineBottom повертає Y-координату нижньої межі рядка, яка вже включає descender. При кастомній верстці рядків з різними кеглями (наприклад, ціна зі знижкою та повна ціна) вирівнювання по baseline з урахуванням descender дає більш точний результат, ніж вирівнювання по нижньому краю.
// Compose: перевірка нижньої межі тексту
val text = "Text with descenders: gpq"
var layoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Text(
text = text,
onTextLayout = { layoutResult = it },
modifier = Modifier.drawBehind {
layoutResult?.let { result ->
val lastLine = result.lineCount - 1
val bottom = result.getLineBottom(lastLine)
val top = result.getLineTop(lastLine)
// Перевірити, що descender не виходить за межі контейнера
}
}
)
За даними Google Material Design — Typography Implementation (2025), для запобігання обрізанню descender у контейнерах з фіксованою висотою необхідно додавати вертикальний padding, що дорівнює мінімум descent шрифту, незалежно від наявності літер з descender у поточному тексті. Це гарантує, що при динамічній заміні тексту інтерфейс не зламається.
Колізія рядків — ситуація, коли descender літери верхнього рядка фізично перетинається з ascender літери нижнього рядка. У мобільних інтерфейсах це особливо помітно в багаторядкових заголовках, картках товарів і текстових блоках з малим міжрядковим інтервалом. Проблема посилюється при використанні шрифтів з довгими descender і малим line-height.
Мінімальний line-height, що запобігає колізіям, може бути розрахований за формулою: line-height = ascender + descender + 2 px запасу. Для SF Pro при кеглі 17 pt це дає line-height ≈ 16.2 + 4.2 + 2 = 22.4 pt (коефіцієнт ~1.32). Для Roboto при кеглі 16 sp — приблизно 1.35. Якщо line-height менший за це значення, колізії гарантовані в текстах, що містять літери з descender.
// iOS: розрахувати мінімальний line-height для запобігання колізіям
let font = UIFont.systemFont(ofSize: 17)
let minLineHeight = abs(font.ascender) + abs(font.descender) + 2.0
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = minLineHeight
paragraphStyle.maximumLineHeight = minLineHeight
let attributedText = NSAttributedString(
string: "Text with p on first line\nand y on second line",
attributes: [
.font: font,
.paragraphStyle: paragraphStyle
]
)
Особливо уважним варто бути при роботі з декоративними та рукописними шрифтами — їх descender може досягати 35–40% від em-розміру. Такі шрифти рідко використовуються для основного тексту, але можуть застосовуватися в заголовках. Навіть одинична поява літери з довгим descender у заголовку здатна викликати колізію із сусіднім елементом інтерфейсу.
Найчастішою помилкою є обрізання descender у кнопках і текстових полях. Коли ми задаємо висоту кнопки або текстового поля, що дорівнює line-height, без врахування descender, літери з descender обрізаються по нижньому краю. Це особливо помітно на системних кнопках із заокругленими кутами, де descender може виходити за межу заокруглення.
За даними Nielsen Norman Group — Mobile Typography Research (2025), 41% мобільних застосунків мають хоча б один екран, де текст з descender виходить за межі компонента. Це призводить до зниження читабельності на 15% і збільшення часу виконання завдання користувачем. Регулярне тестування з текстом, що містить літери з descender, допомагає виявити такі проблеми на ранніх стадіях розробки.
Поширені запитання
Baseline — це горизонтальна лінія, на якій стоять літери, а descender — частина літери, що знаходиться нижче цієї лінії. Baseline — константа для рядка, descender — властивість конкретної літери. Не плутай ці поняття: baseline використовується для вирівнювання, а descender впливає на міжрядкову відстань і потребує врахування при заданні висоти контейнера.
Використовуй Paint.getFontMetrics().descent для View-системи або TextLayoutResult у Jetpack Compose. На відміну від iOS, значення descent на Android додатне і показує відстань від baseline до нижньої межі гліфа. Для розрахунку повної нижньої межі рядка додай descent до Y-координати baseline.
Платформи використовують різні таблиці метрик зі шрифтового файлу: iOS — hhea.descent, Android — os/2.sTypoDescender. Якщо ці значення у шрифті різняться, рендеринг буде відрізнятися. Завжди перевіряй обидва значення через fontTools. Якісні системні шрифти (SF Pro, Roboto, Noto) мають узгоджені метрики для обох платформ.
Мінімальний line-height = ascender + descender + 2 px запасу. Для системного шрифту 17 pt на iOS це приблизно 22.4 pt. На Android для 16 sp Roboto — приблизно 22 sp. Рекомендується округлювати до найближчого цілого та перевіряти з тестовим рядком літер з descender — якщо колізій немає, line-height достатній.
Так, але з застереженнями. Шрифти з довгим descender (Playfair Display, декоративні гарнітури) допустимі для заголовків і акцидентного тексту, де line-height можна збільшити без шкоди для дизайну. Для основного тексту бажані шрифти з descender 20–25% від em-розміру (SF Pro, Roboto, Inter), щоб не витрачати зайвий вертикальний простір.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також