Issue
I'm trying to create multiple themes for a monorepo project (managed with yarn workspaces) with expo for Mobile (managed workflow) and reactjs for Web (using Vite and both with a typescript template) with a shared folder, which would have the themes configuration, but I can not find any good information about how to do it, any solution or source is appreciated. Here is how my tailwind.config.js
files are set in each platform/project.
For Mobile:
/** @type {import('tailwindcss').Config} */
module.exports = {
presets: [require('@expo-monorepo/shared/tailwind.config')],
content: [
'./index.{js,jsx,ts,tsx}',
'./App.{js,jsx,ts,tsx}',
'./src/**/*.{js,jsx,ts,tsx}',
'../Shared/**/*.{js,jsx,ts,tsx}',
],
theme: {
extend: {},
},
plugins: [require('nativewind/tailwind/native')],
};
Shared Folder:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [],
theme: {
extend: {
colors: {
'pastel-green': {
50: '#f1fdf0',
100: '#dbfddb',
200: '#bbf8ba',
300: '#85f184',
400: '#63e663',
500: '#1ec91f',
600: '#13a613',
700: '#138214',
800: '#146716',
900: '#135415',
950: '#042f06',
},
candlelight: {
50: '#feffe7',
100: '#fcffc1',
200: '#fdff86',
300: '#fffa41',
400: '#ffee0d',
500: '#ffdf00',
600: '#d1a500',
700: '#a67602',
800: '#895c0a',
900: '#744b0f',
950: '#442804',
},
},
},
},
plugins: [],
};
For Web:
/** @type {import('tailwindcss').Config} */
export default {
presets: [require('@expo-monorepo/shared/tailwind.config')],
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
I've tried to create a theme inside the shared folder using nativewind
's NativeWindStyleSheet API. Already tried tailwindcss-themer
plugin and tailwindcss/plugin
. Maybe I just don't know how to configure them for mobile, but I was expecting to have the themes that I want, for example christmas
, halloween
, my-theme
, etc, when I use colorScheme
from useColorScheme
.
Solution
I've made multiple themes in another project just by searching on the internet. I've found that there will be a new version of NativeWind which supports multiple themes (NativeWind v4). I've done it with an expo project using expo-router.
Here is all the documentation that you need to do it in the following order:
- https://docs.expo.dev/tutorial/create-your-first-app/
- https://docs.expo.dev/routing/installation/#manual-installation
- https://www.nativewind.dev/v4/getting-started/expo-router
After all the configuration following the documentation, I added some custom color variables inside the tailwind.config.js
:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./app/**/*.{js,jsx,ts,tsx}', './Themes/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {
colors: {
primary: 'var(--color-primary)',
secondary: 'var(--color-secondary)',
outstand: 'var(--color-outstand)',
},
},
},
plugins: [],
};
After that, I created a Themes
folder with the following files:
index.tsx
import { useState, useCallback, createContext, useContext } from 'react';
import { View, ViewProps } from 'react-native';
import { StatusBarTheme, Themes, ThemesVariant } from './theme-config';
import clsx from 'clsx';
import { StatusBar } from 'expo-status-bar';
type ThemeContextValues = {
theme: ThemesVariant;
};
const ThemeProviderValues = createContext<ThemeContextValues>({
theme: 'light',
});
export function useThemeContextValues() {
return useContext(ThemeProviderValues);
}
type ThemeContextActions = {
handleThemeSwitch: (newTheme: ThemesVariant) => void;
};
const ThemeProviderActions = createContext<ThemeContextActions>(
{} as ThemeContextActions
);
export function useThemeContextActions() {
return useContext(ThemeProviderActions);
}
type ThemeProps = ViewProps;
export function Theme(props: ThemeProps) {
const [theme, setTheme] = useState<ThemesVariant>('light');
const handleThemeSwitch = useCallback((newTheme: ThemesVariant) => {
setTheme(newTheme);
}, []);
return (
<View style={Themes[theme]} className={clsx('flex-1', props.className)}>
<ThemeProviderValues.Provider value={{ theme }}>
<ThemeProviderActions.Provider value={{ handleThemeSwitch }}>
<StatusBar
style={StatusBarTheme[theme].style}
backgroundColor={StatusBarTheme[theme].background}
/>
{props.children}
</ThemeProviderActions.Provider>
</ThemeProviderValues.Provider>
</View>
);
}
theme-config.ts
import { StatusBarStyle } from 'expo-status-bar';
import { vars } from 'nativewind';
export type ThemesVariant = 'light' | 'xmas' | 'dark' | 'halloween';
export const Themes = {
light: vars({
'--color-primary': '#000000',
'--color-secondary': '#ffffffff',
'--color-outstand': '#2288dd',
}),
dark: vars({
'--color-primary': '#ffffff',
'--color-secondary': '#000000',
'--color-outstand': '#552288',
}),
xmas: vars({
'--color-primary': '#fff',
'--color-secondary': '#3225de',
'--color-outstand': '#0ca90c',
}),
halloween: vars({
'--color-primary': '#000000',
'--color-secondary': '#5522dd',
'--color-outstand': '#ffcc00',
}),
};
type StatusBarThemeStyle = {
[keys in ThemesVariant]: {
style: StatusBarStyle;
background: string;
};
};
export const StatusBarTheme: StatusBarThemeStyle = {
light: {
style: 'dark',
background: '#fff',
},
dark: {
style: 'light',
background: '#000',
},
xmas: {
style: 'light',
background: '#3225de',
},
halloween: {
style: 'dark',
background: '#52d',
},
};
and ThemeSwitcher.tsx
import { Pressable, Text, View } from 'react-native';
import { useThemeContextActions } from '.';
export function ThemeSwitcher() {
const { handleThemeSwitch } = useThemeContextActions();
return (
<View className="p-5 flex-row flex-wrap gap-y-5 w-full justify-evenly">
<Pressable
onPress={() => handleThemeSwitch('light')}
className="p-2 rounded-lg items-center bg-outstand justify-center w-40 h-36 shadow-lg shadow-black"
>
<Text className="text-lg font-semibold text-primary">Light</Text>
</Pressable>
<Pressable
onPress={() => handleThemeSwitch('dark')}
className="p-2 rounded-lg items-center bg-outstand justify-center w-40 h-36 shadow-lg shadow-black"
>
<Text className="text-lg font-semibold text-primary">Dark</Text>
</Pressable>
<Pressable
onPress={() => handleThemeSwitch('xmas')}
className="p-2 rounded-lg items-center bg-outstand justify-center w-40 h-36 shadow-lg shadow-black"
>
<Text className="text-lg font-semibold text-primary">Christmas</Text>
</Pressable>
<Pressable
onPress={() => handleThemeSwitch('halloween')}
className="p-2 rounded-lg items-center bg-outstand justify-center w-40 h-36 shadow-lg shadow-black"
>
<Text className="text-lg font-semibold text-primary">Halloween</Text>
</Pressable>
</View>
);
}
My app/index.tsx
file, looks like this:
import { Text, View } from 'react-native';
import '../global.css';
import { Theme } from '../Themes';
import { ThemeSwitcher } from '../Themes/ThemeSwitcher';
export default function App() {
return (
<Theme>
<View className="flex-1 items-center justify-center bg-secondary">
<Text className="text-primary text-lg font-semibold">
Open up App.tsx to start working on your app!
</Text>
<ThemeSwitcher />
</View>
</Theme>
);
}
If you want to see all the files, you can check in my repository where I have this project right here.
Answered By - Carlos Saraiva
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.