wix/react-native-ui-lib

Dark mode view modifiers no longer working

dariuscosden opened this issue ยท 39 comments

Description

Dark mode no longer works with view modifiers. Regardless of device scheme, the view background colors will be "light".

Related to

  • Components
  • Demo
  • Docs
  • Typings

Steps to reproduce

  1. Load light and dark schemes using Colors.loadSchemes.
  2. Add background color modifier to any view
  3. Switch from light to dark color scheme on device
  4. Views stay light

More Info

I've traced it down to this commit, where the default scheme was changed from default to light. Currently I have patched the package to change it back to default and it works.

Environment

  • React Native: 0.68.2
  • React Native UI Lib: 6.18.0

Affected platforms

  • Android
  • iOS
  • Web

@dariusmandres
please add the following require in your app, in an initial place, before importing react-native-ui-lib at the first time.

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

@lidord-wix
Let's add this to our docs. Under Foundation -> Colors

@lidord-wix

I have tried that, it didn't work unfortunately. The dark mode is still not respected. I made sure to have it at the topmost level before anything else is imported.

Have you gotten this to work on your end? Was there anything else to keep in mind that could affect?

I investigated a little bit, 6.13.0 works perfectly but 6.14.0 breaks the color changes.

After adding this:

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

6.18.0 works great, but 6.19.0 breaks. Maybe something related to #2146?


Update: backgroundColor doesnt work. Rest of the props work.

The same happens to me with 6.20.* version. When downgrading to 6.18.*, View changes background color correctly when toggling appearance mode.

I created a new react-native app with version 6.20.* of react-native-ui-lib and when I call

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

at the very beginning point of the app, it works.
Your issue might happen because you import your app (where you use ui-lib) before you require the config. (imports are hoisted and called before requires).
I can suggest you the following solution:

  1. Add a new file to your project root called setup.js.
  2. Add the following line to your setup.js file:
require('react-native-ui-lib/config').setConfig({appScheme: 'default'});
  1. import the setup file on your index.js, and make sure it's your first import (or at least before importing your app).
    Please check that and let me know if it works for you

Thanks @lidord-wix I did the exact thing you mentioned:

index.js:

import './setup';
import 'react-native-gesture-handler';
import {registerRootComponent} from 'rnn-screens';

// ...

setup.ts:

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

and everything works, except background colors when appearance mode is updated:

Simulator Screen Recording - iPhone 13 - 2022-09-01 at 13 11 09

(using react-native-ui-lib's View: <View flex bg-bgColor>)

@sallar
do you load the bgColor using Colors.loadScheme()?
can you share a code snippet?

@lidord-wix yes,

const colors: DesignSystemColors = {
  primary: '#5383b8', // blue
  secondary: '#469c57', // green
  accent: '#fed330', // yellow
  blackish: Colors.rgba(20, 20, 20, 1),
  blackish2: Colors.rgba(50, 50, 50, 1),
  whitish: Colors.rgba(250, 250, 250, 1),
  whitish2: Colors.rgba(230, 230, 230, 1),
};

const themes: Record<AppearanceMode, ThemeColors> = {
  light: {
    textColor: colors.blackish,
    bgColor: colors.whitish,
    bg2Color: colors.whitish2,
  },
  dark: {
    textColor: colors.whitish,
    bgColor: colors.blackish,
    bg2Color: colors.blackish2,
  },
};

Colors.loadColors(colors);
Colors.loadSchemes(themes);

Another issue, <Text> also only works between appearance modes when I explicitly set the color myself: <Text marginB-s2 text60R textColor>, otherwise the default colors from react-native-ui-lib's design tokens don't come through.

so this works between scheme changes:
<Text marginB-s2 text60R textColor>

but this doesnt, and stays black:
<Text marginB-s2 text60R>

@sallar for now, after a scheme change you'll need to restart the app.
after the restart, the Text color should be in the right color.
for the background issue, where do you call this loadSchemes(themes)?

@lidord-wix if the app needs to reload then everything works as expected. all my comments regarding stuff not working are related to "dynamic" scheme changes.
do you think this will be fixed in the future?

@sallar we hope so, we're using react-native's platform color and we still have some issues there (mainly on android).

@lidord-wix thanks- if platformColor or dyanmicColorIOS is used then things should work at least on iOS

hey @lidord-wix @sallar

after digging into the code and using the power of console.logs, I've found out that when toggling appearance in system mode, themeProps are not being updated for the views which are currently displayed (also for components on other tabs). And if you push a new screen, then themeProps will be depended on system appearance.

the problem is in useThemeProps() hook which is actually a pure function and doesn't have any deps. I have added useColorScheme() hook from react-native to the useThemeProps(), so themeProps will be updated for views with updated theme values and color scheme.

I'm not sure if it's the best solution for this case in terms of performance and etc., as I don't know the whole structure of the library. Maybe @ethanshar can comment on this, and if it's okay, here is PR #2234.

Patch file - rnn-starter/patches/react-native-ui-lib+6.20.3.patch.
Working example - rnn-starter

Thanks @kanzitelli! I was actually testing this library with rnn-starter haha. But your patch works flawlessly. Thank you!
Just one thing, the default design tokens, eg, $textDefault still don't work without reloading, even with this change. But that doesn't matter. I'll try to not use any of them :(

hey @sallar! Great to hear that! It would be great if you could elaborate more on default design tokens. I couldn't find any information about them in RN UI Lib docs.

And thanks for using the rnn-starter haha. I've caught it on the video and it seems like you are using old version ๐Ÿ˜

@kanzitelli I will use the new rnn-starter ๐Ÿ‘
Design tokens are the colors that are used for light/dark mode by default in new versions of the library. For example Text component uses the colors from here:
https://github.com/wix/react-native-ui-lib/blob/master/src/style/designTokens.ts

for example when you use <Text> the default color is used inside the component: <Text $textDefault>

@sallar that's great!
I've never used design tokens tbh, they seem nice to use. The only import of designTokens is here and seems to be loading schemes correctly. Probably it needs to reloaded somewhere but needs more digging. I'll see what I can to help with this.

Seems to have been fixed as of 6.21.2 without any patch or extra configuration.

@sallar confirm?

Switching theme for View also does not work for me. Package version: 6.23.1. Components Text and Button work correctly, but the view does not respond to theme switching

Using Expo here. I did all of the suggested fixes however, even with an app reload, the colors still don't change at all when I change to dark mode on iOS. Any ideas?
package: 6.25.0
expo: 47.0.6
react: 18.1.0
react-native: 0.70.5

@DeveloperTheExplorer I was having the same issue, If you are using expo don't forget to add
"userInterfaceStyle": "automatic" to your app.json. Also it is crucial to install "expo-system-ui" package when using development builds.

Struggled through the documentation, simply adding the require to set the scheme to default did not work in a new bare Expo application, and since import statements are evaluated before requires, the setConfig() function does not work. The trick was to move the require into a new file, and import that file before anything else.

The following did work:

  1. create a file called setup.js besides the App.js file
  2. import the ./setup file before all the other imports in the App.js file.

setup.js

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

App.js line 1

import './setup';
sallar commented

It shouldn't be this hard to enable dark mode.

Dark mode

any support for dark mode, still I'm not able to use dark mode for the latest versions.

in my case if I didn't use any wrapper like Redux or other then this dark mode background is applying for View. If I use redux provider it's not applying the dark background.

Same here! Using "react-native-ui-lib": "7.2.1"

@MrX1068, @josegiufrida
Did you try the solution from this comment?

Yes tried that, but as I mentioned it is working fine. If i try to use redux provider it is not taking that dark theme.

Dark mode

any support for dark mode, still I'm not able to use dark mode for the latest versions.

in my case if I didn't use any wrapper like Redux or other then this dark mode background is applying for View. If I use redux provider it's not applying the dark background.

@MrX1068 can you share your project (or any example project) that I can run to reproduce?

@MrX1068, @josegiufrida Did you try the solution from this comment?

Yes, Im implementing that.

I think my problem it's related to this comment. I assumed that the changes would be applied without a restart.

@sallar for now, after a scheme change you'll need to restart the app. after the restart, the Text color should be in the right color. for the background issue, where do you call this loadSchemes(themes)?

When I reload the app, all works nice. It would be nice if this information is added to docs.

Struggled through the documentation, simply adding the require to set the scheme to default did not work in a new bare Expo application, and since import statements are evaluated before requires, the setConfig() function does not work. The trick was to move the require into a new file, and import that file before anything else.

The following did work:

  1. create a file called setup.js besides the App.js file
  2. import the ./setup file before all the other imports in the App.js file.

setup.js

require('react-native-ui-lib/config').setConfig({appScheme: 'default'});

App.js line 1

import './setup';

This worked for me, I am using the latest version as of today (7.5.1) and using expo + expo router.

I went and put the require('react-native-ui-lib/config').setConfig({appScheme: 'default'}); directly into my index.js, and now it looks like this:

// index.js
require('react-native-ui-lib/config').setConfig({appScheme: 'default'});
import "expo-router/entry";

Thanks @uyasarkocal for making me realise that I indeed needed to do serInterfaceStyle": "automatic" and of course @mr-menno for the well explained solution.

Does anyone have a work around for having to restart the app anytime you change themes? It's easier to change themes on IOS by just sliding down and pressing on the theme icon. Users will not know they have to restart the app to see the new theme and this is a little troubling.

I'd be glad if anyone has a way around this!

Hi @awalmubarak!
In the future, we'll use platformColor, which should solve this issue.
While we tried to do it in the past we had some issues on Android so we're waiting for a fix on React Native side.
Until that, you can use a UX solution for that - once the user changes his appearance on the device, you can show him a banner/alert saying that he needs to restart the app to see the changes.

@lidord-wix thanks for the response. I'll go with the UX solution for now. Is the future solution something that's already in progress. If not, is there a way I can contribute to help speedup this solution. I think this will be a big win

Thank you @awalmubarak!
We actually need to migrate an internal project we have to >= RN70 before we can do this step.
It has some bugs in older versions of react native.

For anyone still struggling with this: my solution for now is to do the theme switch in rnui, save it to local storage, then use the react-native-restart package to reload the app programmatically:

https://www.npmjs.com/package/react-native-restart

Would be nice in the future to dynamically change the theme though. So hopefully that comes soon.

After reading this thread and trying most solutions proposed here, nothing has worked on my detached expo project, so I think its time for me to abandon this library. The components look great but the documentation is not great (and it also lacks dark mode!) and to make things worse the APIs are not intuitive.

I don't see why this couldn't be abstracted into a single theme builder function with a more traditional context provider type of solution

// App.tsx
const theme = createTheme({
  mode: 'light', // or 'dark'
  lightColors: {
    tertiary: '#124789',
    accent: '#f98652',
    surface: '#0990763',
  },
  darkColors: {
    tertiary: '#124789',
    accent: '#908652',
    surface: '#0990763',
  },
  components:{
    Button:(props,theme)=>({
      containerStyle:{
        backgroundColor:theme.colors.tertiary
      }
    })
  }
});

// Wrap with ThemeProvider
const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Component />
    </ThemeProvider>
  );
};

still waiting for any working solution? I tried everything above, but nothing works