question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Width measures of Text components using a custom font are wrong

See original GitHub issue

React Native version:

System:
    OS: macOS 10.14.5
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 1002.17 MB / 32.00 GB
    Shell: 5.3 - /bin/zsh
  Binaries:
    Node: 10.15.3 - ~/.nvm/versions/node/v10.15.3/bin/node
    Yarn: 1.16.0 - /usr/local/bin/yarn
    npm: 6.4.1 - ~/.nvm/versions/node/v10.15.3/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
    Android SDK:
      API Levels: 23, 25, 26, 27, 28
      Build Tools: 27.0.3, 28.0.2, 28.0.3, 29.0.0
      System Images: android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom, android-29 | Google Play Intel x86 Atom
      Android NDK: 17.2.4988734
  IDEs:
    Android Studio: 3.4 AI-183.6156.11.34.5522156
    Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
  npmPackages:
    react: 16.8.6 => 16.8.6
    react-native: 0.60.0 => 0.60.0

⚠️ This is not a regression from RN 0.60, can also be reproduced in RN 0.57.


On Android, some manufacturers — like Samsung, OnePlus, LG, HTC, and others — let you change the default font on your device without having a rooted device, and, of course, rooted devices can also do that.

The <Text> component provided by React Native doesn’t seem to measure the layout correctly. Observed cases were when the default font is not Roboto and when a thick font weight is applied.

The result is that on these specifics devices, text components with a thicker font weight will be chopped off (seems to be wrapped in some cases?). It could be a dot missing, or even an entire word.

<Text style={{ fontWeight: 'bold' }}>
  This looks great!
</Text>
Roboto (Default font) OnePlus Slate

Repository to reproduce this.

Workaround

To influence the layout measures of the text component, we can set a border of at least 2 on it. By doing this, the layout will be properly measured, but the text won’t be vertically centered.

From there, to vertically center the text, we can subtract the double of the border we previously set as a negative marginBottom. (As seen in the screenshots above).

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:24
  • Comments:25 (8 by maintainers)

github_iconTop GitHub Comments

6reactions
treypcommented, Nov 12, 2019

Related, but I’ve noticed that because the wrapping calculation is wrong on custom fonts, sometimes you will get an extra line where the text renderer thinks the text will wrap but doesn’t. There is no way to get rid of it.

Example:

Screen Shot 2019-11-12 at 1 56 35 PM
5reactions
MoOxcommented, Mar 1, 2021

I discovered a “trick” to avoid this kind of issue: don’t rely on fontWeight on Android alone! Instead use fontFamily. Android Roboto have only 6 variants, same for OnePlus Slate. I have spend a few hours trying to find an acceptable combo on stock Android (using a Pixel 3), ColorsOS (Oppo) and OxygenOS (OnePlus)

You can safely (on Android) use the following name instead of weight

  • sans-serif-thin (= 100/200 depending on android custom font)
  • sans-serif-light (=300)
  • sans-serif / sans-serif-regular (400) (note that OnePlus Slate doesn’t handle sans-serif like sans-serif-regular, when used with a weight)
  • sans-serif-medium (500)
  • sans-serif-bold (600/700)
  • sans-serif-black (800/900)

Note that fontWeight coupled with a fontFamily give better result on Android, if you want to rely on OS font.

Here is a stylesheet you can use safely to think about “weight” and forget about the trick

  // iOS: no comment, weight are perfect
  // Android: only has 100/300/400/500/700/900
  // it's a total mess depanding on brands...
  // more details here https://github.com/MoOx/LifeTime/blob/5ce54e13096642da02e46bf54b48548f7b5b1890/src/components/shareable/Theme.res#L120-L168
  // ios | st. and. | oppo | 1+
  // 100 | 100 | 100 | 200
  // 200 | 300 | 300 | 300
  // 300 | 300 | 300 | 300
  // 400 | 400 | 400 | 400
  // 500 | 500 | 500 | 500
  // 600 | 500 | 500 | 500
  // 700 | 700 | 700 | 700
  // 800 | 700 | 700 | 900
  // 900 | 800 | 800 | 900
const weightStyles = StyleSheet.create({
  "weight100": Platform.OS === "android"
    ? {fontFamily: "sans-serif-thin", fontWeight: "100"}
    : {fontWeight: "100"},
  "weight200": Platform.OS === "android"
    ? {fontFamily: "sans-serif-light", fontWeight: "200"}
    : {fontWeight: "200"},
  "weight300": Platform.OS === "android"
    ? {fontFamily: "sans-serif-light", fontWeight: "300"}
    : {fontWeight: "300"},
  "weight400": Platform.OS === "android"
    ? {fontFamily: "sans-serif", fontWeight: "400"}
    : {fontWeight: "400"},
  "weight500": Platform.OS === "android"
    ? {fontFamily: "sans-serif-medium", fontWeight: "500"}
    : {fontWeight: "500"},
  "weight600": Platform.OS === "android"
    ? {fontFamily: "sans-serif-medium", fontWeight: "600"}
    : {fontWeight: "600"},
  // reminder: oneplus handle sans-serif-regular / 700 != sans-serif / 700 !!
  "weight700": Platform.OS === "android"
    ? {fontFamily: "sans-serif-regular", fontWeight: "700"}
    : {fontWeight: "700"},
  "weight800": Platform.OS === "android"
    ? {fontFamily: "sans-serif-bold", fontWeight: "700"}
    : {fontWeight: "800"},
  "weight900": Platform.OS === "android"
    ? {fontFamily: "sans-serif-black", fontWeight: "900"}
    : {fontWeight: "900"},
})

Just to tag issues so people would find the workaround: #15114 #25258 #29259 #18258 #17629 #21729

Read more comments on GitHub >

github_iconTop Results From Across the Web

character width calculation wrong with some font which can ...
If a text object is added when its own font is not loaded yet, you will get wrong measurement and so wrong bounding...
Read more >
font-size-adjust - CSS: Cascading Style Sheets | MDN
The font-size-adjust CSS property sets the size of lower-case letters relative to the current font size (which defines the size of upper-case ...
Read more >
Preventing the Performance Hit from Custom Fonts
The first idea I saw was Dave Rupert's tests on only loading @font-face on large screens. Turns out if you use @font-face but...
Read more >
How to avoid layout shifts caused by web fonts
When laying out a page, browsers will use the dimensions and properties of the fallback font to determine the size of the containing...
Read more >
Settings | U.S. Web Design System (USWDS)
Name Variable Default Type Test color tokens $test‑system‑color‑tokens false boolean Base color family $theme‑color‑base‑family "gray‑cool" color base‑lightest theme color token $theme‑color‑base‑lightest "gray‑5" color
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found