From 5091e4e25a49ac620db93a8b0a4f35b5ab3ededa Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Fri, 4 Sep 2026 20:09:42 -0700 Subject: [PATCH] Fix Android thread number alignment across font scales (#11667) --- .../components/ThreadItemPostNumber.tsx | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/screens/PostThread/components/ThreadItemPostNumber.tsx b/src/screens/PostThread/components/ThreadItemPostNumber.tsx index 40868270db..667cb07084 100644 --- a/src/screens/PostThread/components/ThreadItemPostNumber.tsx +++ b/src/screens/PostThread/components/ThreadItemPostNumber.tsx @@ -2,13 +2,35 @@ import {Text, View} from 'react-native' import {Trans, useLingui} from '@lingui/react/macro' import {atoms as a, ios, platform, useTheme} from '#/alf' +import {useNativeFontScale} from '#/alf/util/dimensions' import {type app} from '#/lexicons' /** - * How far the inline badge is nudged below the text baseline. Android's + * Map of Android font scale values to inline offsets. + */ +const ANDROID_INLINE_OFFSETS = [ + [0.85, 2], + [1, 4], + [1.15, 6], + [1.3, 8], + [1.5, 10], + [1.8, 12], + [2, 14], +] as const + +/** + * Max amount the inline badge is nudged below the text baseline. Android's * containing `RichText` reserves matching room via `suffixOffset`. */ -export const POST_NUMBER_INLINE_OFFSET = 6 +export const POST_NUMBER_INLINE_OFFSET = 14 + +function getAndroidInlineOffset(fontScale: number) { + return ANDROID_INLINE_OFFSETS.reduce((nearest, candidate) => + Math.abs(candidate[0] - fontScale) < Math.abs(nearest[0] - fontScale) + ? candidate + : nearest, + )[1] +} export type ThreadItemPostNumbering = Pick< app.bsky.unspecced.defs.ThreadItemPost, @@ -39,6 +61,7 @@ export function ThreadItemPostNumber({ }) { const t = useTheme() const {t: l} = useLingui() + const nativeFontScale = useNativeFontScale() const shouldRender = useHasThreadItemPostNumber(value) const index = value?.opThreadPostIndex const count = value?.opThreadPostCount @@ -61,11 +84,27 @@ export function ThreadItemPostNumber({ }, inline ? platform({ - android: {transform: [{translateY: POST_NUMBER_INLINE_OFFSET}]}, - ios: {transform: [{translateY: a.py_2xs.paddingBottom}]}, + android: { + /* + * Android aligns the inline view's bottom to the surrounding + * text baseline. + * We want it to align the inline view's child/text baseline. + */ + transform: [ + {translateY: getAndroidInlineOffset(nativeFontScale)}, + ], + }, + ios: { + /* + * For iOS, we just need to offset the padding we applied. + */ + transform: [{translateY: a.py_2xs.paddingBottom}], + }, web: { - // Inline views inherit the surrounding line height on web. Keep - // the badge at its usual size when emoji-only text enlarges it. + /* + * Inline views inherit the surrounding line height on web. Keep + * the badge at its usual size when emoji-only text enlarges it. + */ lineHeight: a.text_xs.fontSize * a.leading_normal.lineHeight, }, })