Descender is the part of a lowercase letter that extends below the baseline of the font. In Cyrillic, typical letters with a descender are er, u, ef, in Latin — “g”, “j”, “p”, “q”, “y”. The length of the descender determines the lower extension of the font and is critically important for calculating line spacing: without sufficient space below the baseline, letters with descenders will collide with the next line. According to the Material Design Type Scale Guidelines (2025), insufficient consideration of the descender is one of the main causes of line collisions in multi-line text on mobile devices.
Key Takeaways
Descender is the part of a glyph located below the baseline. While the main body of the letter sits on the baseline, the descender extends beyond it, creating the characteristic silhouette of the font. In Latin script, letters with descenders include “g”, “j”, “p”, “q”, “y” — their lower elements drop below the line.
The depth of the descender describes the distance from the baseline to the lower edge of the glyph (descender-line). In quality fonts, this distance is balanced: too short a descender makes the descender letters difficult to recognize, while too long a descender creates excessive empty space between lines and reduces text density. Different typefaces show significant differences in descender length.
| Typeface | Descender / em-size | Example letters with descender |
|---|---|---|
| SF Pro | ~0.22 | g, j, p, q, y — balanced extension |
| Roboto | ~0.24 | g, j, p — moderate descender |
| Playfair Display | ~0.30 | g, j, p, q — long decorative elements |
| Inter | ~0.26 | g, j, p — noticeably below baseline |
| Noto Sans | ~0.20 | g, j — short descender, compact |
According to the Google Fonts Metrics Guide (2025), a descender is considered optimal when its depth is 20–25% of the full em size (1000 FUnits). Values below 15% make descender letters difficult to distinguish, while values above 30% require a mandatory increase in line-height to prevent line collisions.
In digital fonts, the descender is stored as a negative value in the metric tables. In the OpenType format, this is the hhea.descent field (hhea table) and sTypoDescender (OS/2 table). Both values are negative because they are measured from the baseline downward. For TrueType, the OS/2 table is used with the usWinDescent field — its value is positive but denotes the same metric.
# Reading descender from font via 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)
# Convert to pixels for 16pt font size
px_per_em = 16
descent_px = abs(descent_hhea) * px_per_em / 1000 # 8 px
The critical difference between platforms: iOS uses hhea.descent for rendering, while Android uses sTypoDescender from OS/2. If these values differ (which happens in poorly configured fonts), the same text will display with different line spacing on iOS and Android. A difference of 100 FUnits (approximately 1.6 px at 16 pt font size) is already visually noticeable.
According to the Microsoft OpenType Specification v1.9 (2025), for correct cross-platform rendering, the hhea.descent and sTypoDescender values should be equal to within 50 FUnits. When choosing a font for a mobile application, this should be checked via fontTools or a similar utility.
In iOS, the descender value is available through the UIFont.descender property. This property returns a negative number indicating the distance from the baseline to the lower edge of the font (including the descender). For example, for SF Pro at 17 pt, the descender value is approximately -4.2 pt. The larger the absolute value, the longer the lower extensions of the font.
// Getting descender on iOS via UIFont
let font = UIFont.systemFont(ofSize: 17)
let descender = font.descender // ~ -4.2 pt for SF Pro 17pt
let ascender = font.ascender // ~ 16.2 pt
let lineHeight = font.lineHeight // ~ 20.4 pt
// Custom rendering with descender offset
let attrString = NSAttributedString(
string: "Sample text with letter p and y",
attributes: [.font: font]
)
// Core Text: getting bounding box with descender
let ctFont = CTFontCreateWithName(
"SF Pro Text" as CFString, 17, nil
)
let descent = CTFontGetDescent(ctFont) // ~4.2 pt
When using TextKit (NSTextStorage, NSLayoutManager), the descender is automatically taken into account in lineFragmentPadding and lineFragmentRect. However, when doing custom rendering via Core Graphics (draw(in:)), you must manually adjust the coordinates by adding the absolute value of the descender to the bottom inset of the container. If this is not done, letters with descenders will extend beyond the rendering bounds and be clipped.
In Android, descender metrics are available through Paint.FontMetrics.descent. Unlike iOS, the descent value is positive — it represents the distance from the baseline to the bottom edge of the text. The FontMetrics.bottom property includes not only the descender but also additional space recommended by the font designer (leading). For precise accounting of just the descender, use descent rather than bottom.
// Getting descender on Android via Paint
val paint = Paint().apply {
textSize = 17 * density
}
val metrics = paint.fontMetrics
val descent = metrics.descent // ~4.5 px for 17sp
val bottom = metrics.bottom // ~5.0 px with leading
// Custom rendering with descender offset
val baseline = y
canvas.drawText("Sample: gpq", x, baseline, paint)
// Bottom boundary with descender
val bottomBound = baseline + descent // correct bottom boundary
In Jetpack Compose, the descender can be obtained through TextLayoutResult. The getLineBottom method returns the Y-coordinate of the bottom edge of the line, which already includes the descender. When doing custom layout of strings with different font sizes (for example, a discounted price and a full price), baseline alignment accounting for the descender gives a more accurate result than bottom-edge alignment.
// Compose: checking text bottom boundary
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)
// Check descender does not exceed container bounds
}
}
)
According to Google Material Design — Typography Implementation (2025), to prevent descender clipping in containers with fixed height, you must add vertical padding equal to at least the font’s descent, regardless of whether the current text contains descender letters. This ensures that the interface will not break when the text is dynamically replaced.
A line collision is a situation where the descender of a letter in the upper line physically intersects with the ascender of a letter in the lower line. In mobile interfaces, this is especially noticeable in multi-line headings, product cards, and text blocks with small line spacing. The problem is exacerbated when using fonts with long descenders and small line-height.
The minimum line-height that prevents collisions can be calculated using the formula: line-height = ascender + descender + 2 px margin. For SF Pro at 17 pt, this gives a line-height of approximately 16.2 + 4.2 + 2 = 22.4 pt (a coefficient of ~1.32). For Roboto at 16 sp, approximately 1.35. If the line-height is less than this value, collisions are guaranteed in texts containing letters with descenders.
// iOS: calculate minimum line-height to prevent collisions
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
]
)
Special care should be taken when working with decorative and script fonts — their descender can reach 35–40% of the em size. Such fonts are rarely used for body text but may be applied in headings. Even a single occurrence of a letter with a long descender in a heading can cause a collision with a neighboring interface element.
The most common mistake is clipping the descender in buttons and text fields. When we set the height of a button or text field equal to the line-height without accounting for the descender, descender letters are cut off at the bottom edge. This is especially noticeable in system buttons with rounded corners, where the descender can extend beyond the corner radius boundary.
According to Nielsen Norman Group — Mobile Typography Research (2025), 41% of mobile applications have at least one screen where text with descenders extends beyond component boundaries. This leads to a 15% decrease in readability and an increase in task completion time for users. Regular testing with text containing descender letters helps identify such problems in the early stages of development.
Frequently Asked Questions
Baseline is the horizontal line on which letters rest, while the descender is the part of the letter located below this line. The baseline is a constant for the line, the descender is a property of a specific letter. Do not confuse these concepts: the baseline is used for alignment, while the descender affects line spacing and must be considered when setting the container height.
Use Paint.getFontMetrics().descent for the View system or TextLayoutResult in Jetpack Compose. Unlike iOS, the descent value on Android is positive and indicates the distance from the baseline to the lower edge of the glyph. To calculate the full bottom boundary of the line, add the descent to the Y-coordinate of the baseline.
The platforms use different metric tables from the font file: iOS uses hhea.descent, Android uses os/2.sTypoDescender. If these values differ in the font, the rendering will differ. Always check both values via fontTools. Quality system fonts (SF Pro, Roboto, Noto) have consistent metrics for both platforms.
The minimum line-height = ascender + descender + 2 px margin. For a 17 pt system font on iOS this is approximately 22.4 pt. On Android for 16 sp Roboto — approximately 22 sp. It is recommended to round to the nearest integer and test with a sample string of descender letters — if there are no collisions, the line-height is sufficient.
Yes, but with caveats. Fonts with a long descender (Playfair Display, decorative typefaces) are acceptable for headings and accent text where line-height can be increased without compromising design. For body text, fonts with a descender of 20–25% of the em size (SF Pro, Roboto, Inter) are preferred to avoid wasting vertical space.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also