Implementing Dark Theme in Trip.com Mobile App: Design, iOS, Android, and React Native Solutions
This article details Trip.com’s comprehensive Dark Theme implementation across iOS, Android, and React Native, covering design principles, color mapping, illustration adjustments, platform‑specific adaptation techniques, code examples, and tooling that together reduced development effort while enhancing user experience and brand image.
Background: With iOS 13 and Android Q introducing system‑wide Dark Mode, Trip.com observed 66% of iOS 13 users enable it, prompting the team to adopt Dark Theme across the app.
Benefits: lower power consumption on OLED screens, consistent user experience, brand image improvement, and better accessibility for low‑vision users.
Visual design principles: (1) higher UI layers use lighter surface colors, (2) reduce saturation to avoid visual jitter, (3) ensure at least 4.5:1 contrast per WCAG AA.
Color mapping: a unified naming scheme links Light and Dark colors; examples of reduced‑saturation brand blue and contrast‑balanced backgrounds are shown.
Illustration system: assets adapt their colors according to theme, e.g., clothing colors become muted in Dark mode.
Implementation – iOS: the app supports adaptive mode and forced Light mode by toggling overrideUserInterfaceStyle on the key window. The switch logic is illustrated below.
App theme setting logic (illustrative diagram)Color adaptation uses colorWithDynamicProvider and resolvedColorWithTraitCollection for cases like CGColor. Image assets are registered in Images.xcassets and switched automatically via traitCollectionDidChange .
Implementation – Android: resources are placed in values and values‑night folders; the app toggles Dark mode with AppCompatDelegate.setDefaultNightMode(...) as shown.
// Open dark mode
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
// Close dark mode
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
// Follow system
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);Color usage in XML: android:textColor="@color/colorBrandingBlue" ; in code: ContextCompat.getColor(activity, R.color.colorBrandingBlue) . A script generates transparent variants.
Image adaptation uses matching files in drawable‑night directories or dynamic registration for non‑AppCompat contexts.
public class IBUDarkModeDelegate {
public static void applyNight(Context activity) {
Activity concreteActivity = null;
if (activity instanceof Activity) {
concreteActivity = (Activity) activity;
} else if (activity instanceof ThemedReactContext) {
concreteActivity = (Activity) ((ThemedReactContext) activity).getBaseContext();
}
if (concreteActivity != null) {
AppCompatDelegate appCompatDelegate = AppCompatDelegate.create(concreteActivity, new AppCompatCallback() {
public ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback) {
return null;
}
});
appCompatDelegate.applyDayNight();
}
}
}Implementation – React Native: a bridge provides synchronous theme retrieval and event listeners. Context API supplies IBUThemeContext and IBUThemeProvider for React components.
export const IBUThemeContext = React.createContext<'light' | 'dark'>('light');
export class IBUThemeProvider extends Component {
static theme = isInChromeDebugMode ? 'dark' : IBUTheme.getTheme();
constructor(props) {
super(props);
const theme = isInChromeDebugMode ? 'dark' : IBUTheme.getTheme();
IBUThemeProvider.theme = theme;
this.state = { theme };
}
render() {
const { theme } = this.state;
const { children } = this.props;
return
{children}
;
}
}Dynamic styles are created with a custom IBUDynamicStyleSheet that caches Light and Dark style objects and returns the appropriate one on each render.
type IBUNamedStyles
= { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
export function IBUDynamicStyleSheet
| IBUNamedStyles
>(
callback: () => T | IBUNamedStyles
) {
const cache: { light?: T; dark?: T } = {};
return (theme?: 'light' | 'dark'): T => {
const currentTheme = theme || IBUThemeProvider.theme;
let style = cache[currentTheme];
if (!style) {
style = StyleSheet.create(callback());
cache[currentTheme] = style;
}
return style;
};
}Tools & efficiency: a customized Sketch Measure plugin shows color names instead of hex values, allowing developers to copy the semantic name directly from design files, reducing lookup time.
Conclusion: By aligning design guidelines, implementation patterns, and tooling, the team delivered a full Dark Theme solution for iOS, Android, and React Native in two weeks with a single developer, improving user experience and brand perception.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.