Introduction
iOS 13 introduced Dark Mode, a feature that lets users choose between a system-wide light or dark theme. At that time many people saw this more as a gimmick. Soon after, Whatsapp, Facebook Messenger, Youtube, or Gmail came up with both light and dark versions of their iOS apps.
Start from here
Before designing your app, make sure you read Apple Human Interface Guidelines. I will not go into design details but here is a great resource.
Key takeaways:
- iOS provides dynamic system colors that automatically adapt to light or dark modes.
- your app should comply with the appearance mode people choose in Settings.
Let's build an app
I will create a new application using React Native CLI. You can follow along or check the final version on Github.
npx react-native init DarkModeExample
Make sure you remove generated code from App.js and replace it with the following
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
const App = () => (
<View style={styles.container}>
<Text>Hello World</Text>
</View>
);
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
});
export default App;
Now we are ready to customize the app.
1. Launch Screen
Let's start the customization with the Launch Screen. This is the splash screen which appears for a few moments when the application is launched.
Go to LaunchSreen.storyboard and make sure you change the background color of the View to SystemBackground. SystemBackground is pure white for the light theme and pure black for the dark one. I also changed the color of the "DarkModeExample" text to System Orange Color.
To see the result, on your simulator, go to Settings->Developer->Appearance, switch between dark and light appearance, and open the app. Depending on what you selected, the LaunchScreen should change accordingly.
2. Add a login screen
For demonstration purposes, we will design a login screen.
Many UI kits and libraries offered theming capabilities even before Dark Mode landed on iOS. Most of them rely on React Context to provide this kind of functionality.
React Native 0.63 introduced PlatformColor. PlatformColor lets you access native colors on the target platform by supplying the native color’s corresponding string value.
backgroundColor: PlatformColor('systemBackground')
systemBackground is a native iOS color. More than that it is dynamic which means its value is #fff for the light theme and #000 for the dark one. The color will automatically change when the theme is changed by the user in Settings.
Now let's update the App.js file:
import React from 'react';
import {
KeyboardAvoidingView,
Platform,
PlatformColor,
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<KeyboardAvoidingView
style={styles.contentContainer}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.form}>
<TextInput
paddingLeft={10}
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
placeholder="Email"
placeholderTextColor={Platform.select({
ios: PlatformColor('secondaryLabel'),
android: 'white',
})}
style={styles.input}
/>
<TextInput
paddingLeft={10}
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
placeholderTextColor={Platform.select({
ios: PlatformColor('secondaryLabel'),
})}
style={styles.input}
/>
<View>
<Pressable style={styles.loginButton}>
<Text style={styles.label}>Login</Text>
</Pressable>
</View>
</View>
</KeyboardAvoidingView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
...Platform.select({
ios: {backgroundColor: PlatformColor('systemBackground')},
default: {
backgroundColor: '#000000ff',
},
}),
},
contentContainer: {
flex: 1,
maxHeight: '90%',
flexDirection: 'column',
justifyContent: 'space-evenly',
alignItems: 'center',
...Platform.select({
ios: {backgroundColor: PlatformColor('systemBackground')},
default: {
backgroundColor: '#000000ff',
},
}),
},
form: {
width: '90%',
justifyContent: 'space-between',
borderRadius: 5,
},
input: {
height: 40,
marginTop: 10,
fontWeight: '500',
borderWidth: 0.3,
borderRadius: 5,
...Platform.select({
ios: {
color: PlatformColor('labelColor'),
backgroundColor: PlatformColor('tertiarySystemBackground'),
borderColor: PlatformColor('separator'),
},
default: {
backgroundColor: '#1c1c1eff',
borderColor: '#54545899',
},
}),
},
loginButton: {
width: '100%',
justifyContent: 'center',
borderRadius: 5,
height: 40,
marginTop: 40,
...Platform.select({
ios: {backgroundColor: PlatformColor('systemBlue')},
android: {backgroundColor: '#0a84ffff'},
default: {
backgroundColor: '#0a84ffff',
},
}),
},
label: {
fontWeight: '600',
color: 'white',
width: '100%',
fontSize: 20,
textAlign: 'center',
},
});
export default App;
Note: Chances are this will not be responsive and won't look good on every screen size
Switch again between light and dark themes and see how colors are automatically updated.
As you can see I used PlatformColor to get different iOS native colors. For a full list check this.
PlatformColor('systemBlue');
These colors are only available on iOS 13+ so for earlier versions of iOS or for the Android platform we should provide alternative values. This can be accomplished using PlatformSelect.
...Platform.select({
ios: {backgroundColor: PlatformColor('systemBlue')},
android: {backgroundColor: '#0a84ffff'},
default: {
backgroundColor: '#0a84ffff',
},
})
3. Add dynamic logo
The only missing part of our login screen is the logo which is usually an image. There is a pretty high chance our logo will not look good on both black and white backgrounds. To fix this we will need a light and a dark version for the logo.
First, we will create the following folder structure
DarkModeExample
│
│
│
└───src
└───assets
│ │ logo_dark.png
│ │ logo_light.png
│ │
└─── components
│ LogoComponent.js
│
You can get the images from my Github repository.
Now let's implement the LogoComponent.
import React from 'react';
import {useColorScheme, Image} from 'react-native';
const LogoComponent = (props) => {
const colorScheme = useColorScheme();
return colorScheme === 'dark' ? (
<Image source={require('./../assets/logo_dark.png')} style={{...props}} />
) : (
<Image source={require('./../assets/logo_light.png')} style={{...props}} />
);
};
export default LogoComponent;
As you can see, React Native provides useColorScheme() hook which returns the active color scheme as a string. Based on that we return the proper version of the logo.
All we need to do is to import the LogoComponent in our App.js and render it just above the form.
Conclusion
You made it here! Thank you! At this point, you should be able to implement Dark Mode into your React Native app 🚀
Top comments (1)
Great guide, thank you!