Open svbutko opened 4 years ago
There is a similar discussion about the same issue but it's focusing on colours rather than styles itself.
And to avoid massive changes in StyleSheet
, the before mentioned proposal, under the hood, can simply split and merge styles the same way as described in details and return the according style based on the theme (more like a workaround).
This approach will also work in cases if new themes (e.g high contrast) will be available and won't break the already existed code base.
I believe Platform.select
is able to work because it can be determined either when the app is first loaded or maybe it's resolved at build time by Metro
Appearance.select
would not be able to work the same way since the dark-mode preference could change during runtime. To handle that, you'd probably have to move to calculating your styles within the component
I believe
Platform.select
is able to work because it can be determined either when the app is first loaded or maybe it's resolved at build time by Metro
Appearance.select
would not be able to work the same way since the dark-mode preference could change during runtime. To handle that, you'd probably have to move to calculating your styles within the component
Probably incorrectly described it in the details. I understand the way it works and I'm mentioning Platform.select
only to show the way it looks in the context of applying styles, not the implementation itself but the approach to applying different styles within a style
Perhaps a new top-level StyleSheet.createThemed
?
const themedStyles = StyleSheet.createThemed({
dark: { ... },
light: { ... },
customTheme: { ... },
shared: { ... },
});
then a generic utility function, and a light/dark theme specific hook, so
function MyComponent {
const styles = useThemedStyles(themedStyles);
return <View style={styles.container} />;
}
function MyOtherComponent {
const customTheme: void | 'customTheme' | 'default' = useCustomTheme();
const styles = getThemedStyle(customTheme, themedStyles);
return <View style={styles.container} />;
The custom themes would be merged into (overwriting) the shared
styles, and if styles don't exist for a theme (like default
), the shared
styles would just be used instead.
Another option that would be much more flexible would be to make it functional:
const themedStyles = StyleSheet.createThemed((theme: 'light' | 'dark') => ({
container: {
flex: 1,
backgroundColor: ThemeColors.surfaceBackground[theme],
},
});
function MyComponent {
const styles = useThemedStyles(themedStyles);
return <View style={styles.container} />;
}
StyleSheet.createThemed
could either return the function and useThemedStyle
could compute the styles at runtime, or StyleSheet.createThemed
could pre-compute the themes for each theme option ahead of time, e.g. in a babel transform or some other mechanism to generate static styles, e.g. for react-native-web.
@sahrens We did something much simpler at Twitter. StyleSheet.create
was wrapped in user-space so that it can take a function or an object:
const styles = StyleSheet.create((theme) => ({
root: {
backgroundColor: theme.backgroundColor
}
}));
Then you can use styles as normal. It also made it trivial to codemod existing code that used a static theme as done in the OP, without any changes to function bodies.
The benefit of doing this in user space is it isn't tied to just themes either. You can pass any dynamic data to the styles definitions if you want. There's a small cost on web to doing too much of this, which is runtime duplication of style objects for all the variants.
I wonder if RN could support something similar to how CSS custom properties are used for theming on the web, as this means the values (behind an imported theme object) can be dynamically changed without needing any changes to how styles are defined and without needing to re-render the React tree (on web at least).
RN could support something similar to how CSS custom properties are used for theming on the web
@necolas This is how PlatformColor
works. Essentially like css variables:
StyleSheet.create({
root: {
backgroundColor: PlatformColor('myAppCustomColor') // or use the system ones like Apple's PlatformColor('labelColor')
}
});
And then myAppCustomColor
would be defined at the app level and could be swapped without any JS needing to run.
If you change the underlying values at runtime that could be an approach to theming too. But on web you can more than colors
The way I understand StyleSheet
is an API only for defining style objects. It doesn't concern itself with any aspect of the state or environment of the application (layout, theme, platform, network, etc). All that can be built over StyleSheet
.
A basic way to work with the current theme (OP request) could be done like this: https://gist.github.com/necolas/5b421ca860ed98eabc5fd2b9bc6d1136
Or you can go wild like this: https://github.com/vitalets/react-native-extended-stylesheet
The way I currently build themed apps (for light, dark only but code prepared for more theme) is that I build a static stylesheet & another bundle of themed stylesheets that can dynamically switched ("current" available but also all if needed: sometimes you want some components using an "hardcoded theme" (eg: some cards in "dark").
In practise I call a styles
object in my components (standard RN styles), and one other themedStyles
that comes from a hook useTheme
. It's pretty easy to implement, probably not perfect and verbose, but very easy to follow & adjust.
The themed styles are defined in a single places & looks like (random examples)
This is a way to do that imo, doesn't really require any new apis in RN. You can do the same kind of implementation to adjust styles to the platform (eg: if you follow apple hig or google materiel design).
A library is probably more suited to handle "platform specific styles".
Introduction
Current implementation of theming doesn't allow developers to assign theme-dependant changes inside
StyleSheet
Details
React Native has a built-in support for theming which is a great addition itself but at the moment it only gives developers information about what theme device has currently and based on this, developer can decide what styles to use in components which can be done different ways, but it always brings to something similar to this:
So now in order to have different styles working, a developer needs to manually check current theme and apply the styles, and in case if user has a lot of components it creates a lot of clutter and separates the styles by different constants (dark, light).
Besides, styles could have a lot of properties and in order to avoid copying and pasting them, one need to create one more constant style (i.e. commonStyles) and merge it with dark and light ones.
To “avoid” (mitigate) this comparison and “improve” merging, something like this can be done:
What if instead creating separate styles and merging them, and in most of the cases get rid of
Appearance.getColorScheme() == “light"
comparison, developers could define their theme styles insideStyleSheet.create
similar way we have platform dependant code usingPlatform.select
?So that it could look something like this:
Discussion points
Platform.select
mechanism to have theme-dependant selection insideStyleSheet
style