Prior to 0.60.0 font weights that were not supported but were high would default to the nearest 'bold' font. Now they just default to a non-bold font.
React Native version:
1a. react-native init AwesomeProject
(at time of writing defaults to version 0.60.3)
2a. Use this code in App.js
import React, {Fragment} from 'react';
import { Text, SafeAreaView } from 'react-native';
const App = () => {
return (
<SafeAreaView>
<Text style={{fontWeight: '900'}}>FONT WEIGHT 900</Text>
<Text style={{fontWeight: '800'}}>FONT WEIGHT 800</Text>
<Text style={{fontWeight: '700'}}>FONT WEIGHT 700</Text>
<Text style={{fontWeight: '600'}}>FONT WEIGHT 600</Text>
<Text style={{fontWeight: '500'}}>FONT WEIGHT 500</Text>
<Text style={{fontWeight: '400'}}>FONT WEIGHT 400</Text>
<Text style={{fontWeight: '300'}}>FONT WEIGHT 300</Text>
<Text style={{fontWeight: '200'}}>FONT WEIGHT 200</Text>
<Text style={{fontWeight: '100'}}>FONT WEIGHT 100</Text>
</SafeAreaView>
);
};
export default App;
3a. Run the app in an emulator or on device
4a. Output: https://imgur.com/VEpgKzC
react-native info
info Fetching system and libraries information...
System:
OS: macOS 10.14.5
CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Memory: 1.28 GB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 12.2.0 - /usr/local/bin/node
Yarn: 1.15.2 - /usr/local/bin/yarn
npm: 6.10.0 - /usr/local/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, 28
Build Tools: 28.0.3
System Images: android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom
IDEs:
Android Studio: 3.4 AI-183.6156.11.34.5692245
Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
npmPackages:
react: 16.8.6 => 16.8.6
react-native: 0.60.3 => 0.60.3
npmGlobalPackages:
create-react-native-app: 1.0.0
react-native-cli: 2.0.1
THEN
1b. react-native init AwesomeProject2 --version 0.59.9
2b. Use code from above in App.js
3b. Run the app in an emulator or on device
4b. Output: https://imgur.com/ZW9hZGU
react-native info
info
React Native Environment Info:
System:
OS: macOS 10.14.5
CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Memory: 1.34 GB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 12.2.0 - /usr/local/bin/node
Yarn: 1.15.2 - /usr/local/bin/yarn
npm: 6.10.0 - /usr/local/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, 28
Build Tools: 28.0.3
System Images: android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom
IDEs:
Android Studio: 3.4 AI-183.6156.11.34.5692245
Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
npmPackages:
react: 16.8.3 => 16.8.3
react-native: 0.59.9 => 0.59.9
npmGlobalPackages:
create-react-native-app: 1.0.0
react-native-cli: 2.0.1
Describe what you expected to happen:
When an unsupported font weight is higher than a supported, non standard font weight, the default should be picked as the fatter one not the skinny one.
Snack, code example, or link to a repository:
It looks like this originates from this commit https://github.com/facebook/react-native/commit/3915c0fa6178fbb8aa9b8bd54881cfdbde8fde70#diff-d4141c42bc6789637b6fdc7754ec1dd9
In particular, the commit message says:
This change might break texts where fontWeight use improperly, because this PR removes conversion of values above 500 to BOLD and below 500 to normal.
I don't really know if this is an issue or not then - this is the code i am using to 'fix' the fonts in my code base, this does not apply if you are using custom fonts and it is not using the roboto default which does not seem to support very many font weights out of the box.
iOS:
Android:
const oldRender = Text.render;
const settings = [
// we use this empty object for when there is no weight specified
{},
{
fontFamily: 'sans-serif-thin',
fontWeight: 'normal',
}, {
fontFamily: 'sans-serif-light',
fontWeight: 'normal',
}, {
fontFamily: 'sans-serif',
fontWeight: 'normal',
}, {
fontFamily: 'sans-serif-medium',
fontWeight: 'normal',
}, {
fontFamily: 'sans-serif',
fontWeight: 'bold',
}, {
fontFamily: 'sans-serif-medium',
fontWeight: 'bold',
},
];
const defaultIndex = 0;
Text.render = (...args) => {
const origin = oldRender.call(this, ...args);
if (Platform.OS === 'android') {
let useIndex = defaultIndex;
if (typeof origin.props.style !== 'undefined' && typeof origin.props.style.fontWeight !== 'undefined') {
const { fontWeight } = origin.props.style;
if (fontWeight === '100' || fontWeight === '200' || fontWeight === '300') {
useIndex = 1;
} else if (fontWeight === '400') {
useIndex = 2;
} else if (fontWeight === '500' || fontWeight === 'normal') {
useIndex = 3;
} else if (fontWeight === '600') {
useIndex = 4;
} else if (fontWeight === '700' || fontWeight === 'bold') {
useIndex = 5;
} else if (fontWeight === '800' || fontWeight === '900') {
useIndex = 6;
}
}
return React.cloneElement(origin, {
style: [settings[defaultIndex], Platform.OS === 'android' ? { fontFamily: 'Roboto' } : {}, origin.props.style, settings[useIndex]],
});
}
return origin;
};
Shouldn't this issue be open? It's definitely not desired behaviour if a fontWeight of '900' defaults to Roboto Regular on Android. This essentially means I have to either use the hack mentioned by @iamacup or use a custom font that includes all the font weights I need.
I don't really know if this is an issue or not then - this is the code i am using to 'fix' the fonts in my code base, this does not apply if you are using custom fonts and it is not using the roboto default which does not seem to support very many font weights out of the box.
iOS:
Android:
const oldRender = Text.render; const settings = [{ fontFamily: 'sans-serif-thin', fontWeight: 'normal', }, { fontFamily: 'sans-serif-light', fontWeight: 'normal', }, { fontFamily: 'sans-serif', fontWeight: 'normal', }, { fontFamily: 'sans-serif-medium', fontWeight: 'normal', }, { fontFamily: 'sans-serif', fontWeight: 'bold', }, { fontFamily: 'sans-serif-medium', fontWeight: 'bold', } ]; const defaultIndex = 2; Text.render = (...args) => { const origin = oldRender.call(this, ...args); if (Platform.OS === 'android') { let useIndex = defaultIndex; if (typeof origin.props.style !== 'undefined' && typeof origin.props.style.fontWeight !== 'undefined') { const { fontWeight } = origin.props.style; if (fontWeight === '100' || fontWeight === '200' || fontWeight === '300') { useIndex = 0; } else if (fontWeight === '400') { useIndex = 1; } else if (fontWeight === '500' || fontWeight === 'normal') { useIndex = 2; } else if (fontWeight === '600') { useIndex = 3; } else if (fontWeight === '700' || fontWeight === 'bold') { useIndex = 4; } else if (fontWeight === '800' || fontWeight === '900') { useIndex = 5; } } return React.cloneElement(origin, { style: [settings[defaultIndex], origin.props.style, settings[useIndex]], }); } return origin; };
@iamacup
Could you explain a bit more about your solution?
+1
Shouldn't this issue be open? It's definitely not desired behaviour if a fontWeight of '900' defaults to Roboto Regular on Android. This essentially means I have to either use the hack mentioned by @iamacup or use a custom font that includes all the font weights I need.
@iamacup Could you re-open this issue?
Done :)
@jorgemasta His solution is simulating the previous font weight rendering on android. I ended up using his solution to do something similar in our code base for our custom text component.
// @flow
import * as React from 'react';
import { StyleSheet, Text as RNText, Platform } from 'react-native';
type Props = {|
...
style: {} | number | $ReadOnlyArray<?{} | ?number | null>,
title: ?boolean,
medium: ?boolean,
italic?: ?boolean,
|};
const styles = StyleSheet.create({
...
medium: { fontWeight: '500' },
androidMedium: {
fontFamily: 'Roboto-Medium',
fontWeight: '500',
},
roboto100: {
fontFamily: 'Roboto-Thin',
},
roboto100Italic: {
fontFamily: 'Roboto-ThinItalic',
},
roboto300: {
fontFamily: 'Roboto-Light',
},
roboto300Italic: {
fontFamily: 'Roboto-LightItalic',
},
roboto400: {
fontFamily: 'Roboto-Regular',
},
roboto400Italic: {
fontFamily: 'Roboto-Italic',
},
roboto500: {
fontFamily: 'Roboto-Medium',
},
roboto500Italic: {
fontFamily: 'Roboto-MediumItalic',
},
roboto700: {
fontFamily: 'Roboto-Bold',
},
roboto700Italic: {
fontFamily: 'Roboto-BoldItalic',
},
roboto900: {
fontFamily: 'Roboto-Black',
},
roboto900Italic: {
fontFamily: 'Roboto-BlackItalic',
},
});
...
const androidGetFontWeightStyles = ({
title,
medium,
italic,
style,
}: Props): {} => {
if (title || medium) {
return styles.androidMedium;
}
// $FlowFixMe
if (style && style.fontWeight !== undefined) {
// $FlowFixMe
const isItalic = italic || style.fontStyle === 'italic';
const fontFamilyStyle =
styles[`roboto${style.fontWeight}${isItalic ? 'Italic' : ''}`];
if (fontFamilyStyle) {
return fontFamilyStyle;
}
}
return {};
};
const getFontWeightStyles = (props: Props): {} => {
if (Platform.OS === 'android') {
return androidGetFontWeightStyles(props);
}
if (props.title || props.medium) {
return styles.medium;
}
return {};
};
export default function Text(props: Props) {
...
const textStyles = [
...
getFontWeightStyles(props),
];
...
return (
<RNText style={textStyles}>
...
</RNText>
);
}
...
I introduced the change to add support for other font weights natively and fix parity with iOS. Custom font weight support wasn't available when RN emerged, but added later in SDK 25 or 26.
I wrote an npm package to handle mapping of custom fonts (eg. Roboto-Light, Roboto-Black) to fontWeights heavily modified from jacobcabantomski-ct's code.
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
?
Has it been fixed in RN 0.62.0?
Still an issue in 0.62.0
Same issue. RN 0.62.0 not fixed.
I quote https://github.com/facebook/react-native/pull/25341
I found that on Android we only support 2 fontWeight options, either normal or bold, even developer can set any numeric value. But iOS supports all possible numeric values. This PR tries to add support for all possible numeric values on Android, even if it's supported only on Android P(28) and above.
I will write a Pull Request to re-enable to old behaviour on Android API < 28 in ReactBaseTextShadowNode
@ReactProp(name = ViewProps.FONT_WEIGHT)
public void setFontWeight(@Nullable String fontWeightString) {
int fontWeightNumeric =
fontWeightString != null ? parseNumericFontWeight(fontWeightString) : -1;
int fontWeight = UNSET;
if (fontWeightNumeric >= 500 || "bold".equals(fontWeightString)) {
fontWeight = Typeface.BOLD;
} else if ("normal".equals(fontWeightString)
|| (fontWeightNumeric != -1 && fontWeightNumeric < 500)) {
fontWeight = Typeface.NORMAL;
}
as you can see in https://github.com/facebook/react-native/pull/25341 https://github.com/react-navigation/react-navigation/pull/7720 will have to update their logic to use this changes...
Additionally this is the result of https://github.com/facebook/react-native/pull/25341 with API 28
I am little bit confused, but I will write this pull request as I invested considerable amount of time reviewing all this discussions.
@iamacup
I don't understand your sentence below. Do you believe that I can present a pull request with such a change in behaviour?
Do you expect ReactNative to solve some of the limitations imposed by the Android Platform? Or you want me to do the change I described in https://github.com/facebook/react-native/issues/25696#issuecomment-642537998
Describe what you expected to happen:
When an unsupported font weight is higher than a supported, non standard font weight, the default should be picked as the fatter one not the skinny one.
I think you should close this issue in favour of https://github.com/facebook/react-native/issues/28854. Your requirement is not clear.
I quote issue https://github.com/facebook/react-native/issues/28854
Expected Results
font weight values should change font effect in android sdk 28 and above.
Expected result should look like this
I quote https://github.com/facebook/react-native/issues/26193#issuecomment-525028689
It's known issue due to Android limitations. Only way to use custom font weights is to create a custom font.
Android added support changing font weights programmatically, but won't work on older versions.
My pull request should fix this behaviour for Android Pie API 28.
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
Not fixed
Still not fixed
React native is great but its things like this that just makes it feel unfinished. 2 years has passed and it still isn't fixed. The problem is that it worked with older RN versions and now it's broken.
Why would developer depend or specify non-existing font weight. I would rather offer consistent UX by specifying font weights explicitly
Most helpful comment
Same issue. RN 0.62.0 not fixed.