Closed aswinmohanme closed 3 years ago
i'm not sure if this is supported, but you could in theory write custom configs for your color pallets in a tailwind.config.js:
colors: {
light: {}
dark: {}
}
and then access whichever one like:${theme}-bg-primary
colors: { light: {} dark: {} }
and then access whichever one like:
${theme}-bg-primary
Finally found the solution based on this. I used react-native-dynamic
to create a hook to change the theme based on the device theme.
mport { create } from "tailwind-rn"
import styles from "./styles.json"
import { useDynamicValue } from "react-native-dynamic"
const { tailwind, getColor } = create(styles)
function themedStyle(variant) {
return function (classNames: string) {
return tailwind(classNames.replaceAll("theme", variant))
}
}
// Usage: style = useStyle();
// style('bg-theme-primary') which would be converted either to dark or light based on device theme
const useStyle = function () {
const variant = useDynamicValue("light", "dark")
return themedStyle(variant)
}
export { tailwind, getColor, useStyle }
I've modified the above to make use of a global stylesheet
import { create } from 'tailwind-rn';
import styles from 'themes/styles/styles.json'; // generated by tailwind-rn
import defaults from 'themes/styles/defaults'; // global stylesheet
import { useDynamicValue } from 'react-native-dynamic';
const { tailwind, getColor } = create(styles);
export { tailwind, getColor };
function themedStyle(variant: string, scopedStyles: object = {}) {
const rootStyles = { ...defaults, ...scopedStyles };
function parse(...args: Array<object | string>) {
let derivedStyles = {};
args.map((definition: string | object) => {
if (typeof definition === 'string') {
const classes = definition.split(' ');
const tailwindClasses = classes
.filter((cls: string) => !rootStyles[cls])
.join(' ');
let classesInString = {};
classes
.filter((cls: string) => rootStyles[cls])
.map((cls: string) => {
if (Array.isArray(rootStyles[cls])) {
classesInString = {
...classesInString,
...parse.apply(null, rootStyles[cls]),
};
} else {
classesInString = { ...classesInString, ...rootStyles[cls] };
}
});
derivedStyles = {
...derivedStyles,
...tailwind(tailwindClasses.replace(/theme/gim, variant)),
...classesInString,
};
} else {
derivedStyles = {
...derivedStyles,
...definition,
};
}
});
return derivedStyles;
}
return parse;
}
export function useTheme(scopedStyles?: object) {
const variant = useDynamicValue('light', 'dark');
return themedStyle(variant, scopedStyles);
}
you can use this in your hooks like before with added features such as
useTheme('bg-theme-primary classInDefaultsJs text-xl')
useTheme('bg-theme-primary', 'classInDefaultsJs', { padding: 2 })
{
"classInDefaultsJs": { borderWidth: 3 },
"inherited": ["classInDefaultsJs", "bg-white", { margin: 2 }]
}
and reference that as useTheme('inherited')
and will get all the values from the above
import { useTheme } from 'themes/theme';
const styles = {
textInput: {
borderWidth: 2,
},
};
export default () => useTheme(styles);
and use it as
import useTheme from './style';
export const Input = (props: TextInputProps) => {
const theme = useTheme();
return <TextInput {...props} style={theme('p-4 textInput')} />;
};
thanks @aswinmohanme for the great idea.
I had handle Dark/Light theme on my app and thanks to @aswinmohanme and @jamoy idea i build a solution that ended up pretty close with the official way of writing it: bg-white dark:bg-black
etc..
here a glance of it:
<View style={tw("p-4 bg-gray-100 dark:bg-black")}>
<Text style={tw("text-blue dark:text-white")}>DarkMode:</Text>
</View>
I added a function before the tailwind
and getColor
functions to handle the dark:
string depending on the current color scheme.
dark:
.dark:
string so it replaces the previous definition. Here is the hook code: useTailwind.js
import React, { createContext, useState, useContext } from "react";
import { Appearance } from "react-native";
import { create } from "tailwind-rn";
import styles from "../styles.json";
const { tailwind, getColor } = create(styles);
export default tailwind;
export { getColor };
const TailwindContext = createContext();
function handleThemeClasses(classes, isDarkMode) {
const regExp = isDarkMode ? /dark:/g : /dark:\S+/g;
return classes.replace(regExp, "").replace(/\s\s/g, " ").trim();
}
function useTailwind() {
const context = useContext(TailwindContext);
if (!context) throw new Error(`useTailwind must be used within a TailwindProvider`);
const [currentColorScheme, setCurrentColorScheme] = context;
const isDarkMode = currentColorScheme == "dark";
return {
isDarkMode,
setDarkMode: (isDark) => setCurrentColorScheme(isDark ? "dark" : "light"),
tw: (classes) => tailwind(handleThemeClasses(classes, isDarkMode)),
getColor: (colors) => getColor(handleThemeClasses(colors, isDarkMode)),
};
}
function TailwindProvider({ children }) {
const contextValue = useState(Appearance.getColorScheme());
return <TailwindContext.Provider value={contextValue}>{children}</TailwindContext.Provider>;
}
export { TailwindProvider, useTailwind };
and an usage example:
import React from "react";
import { ScrollView, View, Switch } from "react-native";
import { Text, Divider } from "react-native-elements";
import { useTailwind } from "../theme/tailwind";
export default function ConfigScreen() {
const { isDarkMode, setDarkMode, tw, getColor } = useTailwind();
return (
<ScrollView contentContainerStyle={tw("flex-1 p-4 bg-gray-100 dark:bg-black")}>
<View style={tw("flex-row px-2")}>
<Text h3 h3Style={tw("flex-1 dark:text-white")}>DarkMode:</Text>
<Switch value={isDarkMode} onValueChange={() => setDarkMode(!isDarkMode)} />
</View>
<Divider style={tw("my-4")} />
<Text h2 h2Style={tw("dark:text-white")}>
CurrentMode: {isDarkMode ? "dark" : "light"}
</Text>
<Divider style={tw("my-4")} />
<Text h4 h4Style={tw("dark:text-white mb-2")}>
getColor('green dark:yellow'):
</Text>
<View style={{ backgroundColor: getColor("green-500 dark:yellow-500") }}>
<Text style={tw("m-3")}>{getColor("green-500 dark:yellow-500")}</Text>
</View>
</ScrollView>
);
}
https://user-images.githubusercontent.com/307759/112774208-4d41d000-900f-11eb-84b4-935d82c3b8dc.mov
hope it helps!
First of all thanks for making such an awesome library, really love using Tailwind everywhere :smiley:
I'm going to start work on a new app, but it would require theming. Are there any pointers on how to support user switchable themes using this library?
I thought about creating a separate function that wraps the
tailwind
function and which then would replacebg-primary
with the current selected theme, but that seems error-prone. Any suggestions are welcome.I found this library https://www.npmjs.com/package/rn-themed-tailwind but it is nowhere near as mature as this one.