Base dark theme implementation

This commit is contained in:
Patrick Fic
2025-10-23 13:44:33 -07:00
parent b6271b8ef2
commit f170192008
22 changed files with 308 additions and 176 deletions

View File

@@ -4,6 +4,7 @@
"slug": "imexmobile", "slug": "imexmobile",
"version": "1.8.0", "version": "1.8.0",
"scheme": "imex-mobile-scheme", "scheme": "imex-mobile-scheme",
"userInterfaceStyle": "automatic",
"extra": { "extra": {
"expover": "1", "expover": "1",
"eas": { "eas": {

View File

@@ -2,6 +2,11 @@ import { checkUserSession } from "@/redux/user/user.actions";
import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors"; import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors";
import { ApolloProvider } from "@apollo/client"; import { ApolloProvider } from "@apollo/client";
import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import {
DarkTheme,
DefaultTheme,
ThemeProvider,
} from "@react-navigation/native";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
import { import {
Icon, Icon,
@@ -9,7 +14,7 @@ import {
NativeTabs, NativeTabs,
VectorIcon, VectorIcon,
} from "expo-router/unstable-native-tabs"; } from "expo-router/unstable-native-tabs";
import { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { ActivityIndicator, Platform, View } from "react-native"; import { ActivityIndicator, Platform, View } from "react-native";
import { Provider as PaperProvider } from "react-native-paper"; import { Provider as PaperProvider } from "react-native-paper";
@@ -17,45 +22,55 @@ import { connect, Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react"; import { PersistGate } from "redux-persist/integration/react";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { client } from "../graphql/client"; import { client } from "../graphql/client";
import { useTheme as usePaperTheme } from "../hooks/useTheme";
import { persistor, store } from "../redux/store"; import { persistor, store } from "../redux/store";
import "../translations/i18n"; import "../translations/i18n";
import theme from "../util/theme";
function AuthenticatedLayout() { function AuthenticatedLayout() {
const { t } = useTranslation(); const { t } = useTranslation();
const paperTheme = usePaperTheme();
return ( return (
<NativeTabs minimizeBehavior="onScrollDown" disableTransparentOnScrollEdge> <ThemeProvider
<NativeTabs.Trigger name="jobs"> value={paperTheme.theme === "dark" ? DarkTheme : DefaultTheme}
<Label>{t("joblist.labels.activejobs")}</Label> >
<NativeTabs
minimizeBehavior="onScrollDown"
disableTransparentOnScrollEdge
>
<NativeTabs.Trigger name="jobs">
<Label>{t("joblist.labels.activejobs")}</Label>
{Platform.select({ {Platform.select({
ios: <Icon sf="checklist" drawable="custom_android_drawable" />, ios: <Icon sf="checklist" drawable="custom_android_drawable" />,
android: ( android: (
<Icon <Icon
src={<VectorIcon family={MaterialIcons} name="checklist" />} src={<VectorIcon family={MaterialIcons} name="checklist" />}
/> />
), ),
})} })}
</NativeTabs.Trigger> </NativeTabs.Trigger>
<NativeTabs.Trigger name="settings"> <NativeTabs.Trigger name="settings">
{Platform.select({ {Platform.select({
ios: <Icon sf="gear" drawable="custom_android_drawable" />, ios: <Icon sf="gear" drawable="custom_android_drawable" />,
android: ( android: (
<Icon src={<VectorIcon family={MaterialIcons} name="settings" />} /> <Icon
), src={<VectorIcon family={MaterialIcons} name="settings" />}
})} />
<Label>{t("settings.titles.settings")}</Label> ),
</NativeTabs.Trigger> })}
<NativeTabs.Trigger name="search" role="search"> <Label>{t("settings.titles.settings")}</Label>
{Platform.select({ </NativeTabs.Trigger>
//ios: <Icon sf="checklist" drawable="custom_android_drawable" />, <NativeTabs.Trigger name="search" role="search">
android: ( {Platform.select({
<Icon src={<VectorIcon family={MaterialIcons} name="search" />} /> //ios: <Icon sf="checklist" drawable="custom_android_drawable" />,
), android: (
})} <Icon src={<VectorIcon family={MaterialIcons} name="search" />} />
<Label>Search</Label> ),
</NativeTabs.Trigger> })}
</NativeTabs> <Label>Search</Label>
</NativeTabs.Trigger>
</NativeTabs>
</ThemeProvider>
); );
} }
@@ -79,21 +94,38 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch: any) => ({
checkUserSession: () => dispatch(checkUserSession()), checkUserSession: () => dispatch(checkUserSession()),
}); });
function AppContent({ currentUser, checkUserSession, bodyshop }) { function AppContent({ currentUser, checkUserSession, bodyshop }: any) {
useEffect(() => { useEffect(() => {
checkUserSession(); checkUserSession();
}, []); }, [checkUserSession]);
if (currentUser.authorized === null) { if (currentUser.authorized === null) {
return <LoadingLayout />; return (
<ThemedLayout>
<LoadingLayout />
</ThemedLayout>
);
} }
if (currentUser.authorized) { if (currentUser.authorized) {
return <AuthenticatedLayout />; return (
<ThemedLayout>
<AuthenticatedLayout />
</ThemedLayout>
);
} }
return <UnauthenticatedLayout />; return (
<ThemedLayout>
<UnauthenticatedLayout />
</ThemedLayout>
);
}
function ThemedLayout({ children }: { children: React.ReactNode }) {
const themeToApply = usePaperTheme();
return <PaperProvider theme={themeToApply}>{children}</PaperProvider>;
} }
const ConnectedAppContent = connect( const ConnectedAppContent = connect(
mapStateToProps, mapStateToProps,
@@ -105,9 +137,7 @@ export default function AppLayout() {
<Provider store={store}> <Provider store={store}>
<PersistGate persistor={persistor}> <PersistGate persistor={persistor}>
<ApolloProvider client={client}> <ApolloProvider client={client}>
<PaperProvider theme={theme}> <ConnectedAppContent />
<ConnectedAppContent />
</PaperProvider>
</ApolloProvider> </ApolloProvider>
</PersistGate> </PersistGate>
</Provider> </Provider>

View File

@@ -1,8 +1,7 @@
import { Stack, useRouter } from "expo-router"; import { Stack } from "expo-router";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function JobsStack() { function JobsStack() {
const router = useRouter();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Stack <Stack
@@ -17,15 +16,6 @@ function JobsStack() {
options={{ options={{
headerShown: false, headerShown: false,
title: t("joblist.titles.jobtab"), title: t("joblist.titles.jobtab"),
// headerSearchBarOptions: {
// placement: "automatic",
// placeholder: "Search",
// onChangeText: (event) => {
// router.setParams({
// search: event?.nativeEvent?.text,
// });
// },
// },
}} }}
/> />
<Stack.Screen <Stack.Screen

View File

@@ -1,6 +1,7 @@
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import React from "react"; import React from "react";
import { Text, View } from "react-native"; import { View } from "react-native";
import { Text } from "react-native-paper";
export default function DataLabelComponent({ export default function DataLabelComponent({
label, label,
@@ -17,7 +18,7 @@ export default function DataLabelComponent({
const { key, ...rest } = restProps; const { key, ...rest } = restProps;
return ( return (
<View key={key} {...rest} style={{ margin: 4, ...restProps.style }}> <View key={key} {...rest} style={{ margin: 4, ...restProps.style }}>
<Text style={{ color: "slategray" }}>{label}</Text> <Text>{label}</Text>
<Text>{theContent}</Text> <Text>{theContent}</Text>
</View> </View>
); );

View File

@@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Text } from "react-native";
import { Button, Card } from "react-native-paper"; import { Button, Card, Text } from "react-native-paper";
export default function ErrorDisplay({ errorMessage, error, onDismiss }) { export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -14,11 +14,11 @@ export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
error || error ||
"An unknown error has occured."} "An unknown error has occured."}
</Text> </Text>
{onDismiss ? ( {onDismiss ? (
<Card.Actions> <Card.Actions>
<Button onPress={onDismiss}>{t("general.labels.dismiss")}</Button> <Button onPress={onDismiss}>{t("general.labels.dismiss")}</Button>
</Card.Actions> </Card.Actions>
) : null} ) : null}
</Card.Content> </Card.Content>
</Card> </Card>
); );

View File

@@ -6,12 +6,11 @@ import {
FlatList, FlatList,
Image, Image,
RefreshControl, RefreshControl,
Text,
TouchableOpacity, TouchableOpacity,
View, View,
} from "react-native"; } from "react-native";
import ImageView from "react-native-image-viewing"; import ImageView from "react-native-image-viewing";
import { ActivityIndicator } from "react-native-paper"; import { ActivityIndicator, Text } from "react-native-paper";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import env from "../../env"; import env from "../../env";

View File

@@ -5,8 +5,8 @@ import { useGlobalSearchParams } from "expo-router";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FlatList, RefreshControl, Text, View } from "react-native"; import { FlatList, RefreshControl, View } from "react-native";
import { ActivityIndicator, Card } from "react-native-paper"; import { ActivityIndicator, Card, Text } from "react-native-paper";
import ErrorDisplay from "../error/error-display"; import ErrorDisplay from "../error/error-display";
export default function JobNotes() { export default function JobNotes() {

View File

@@ -3,14 +3,8 @@ import { useQuery } from "@apollo/client";
import { useLocalSearchParams } from "expo-router"; import { useLocalSearchParams } from "expo-router";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import { RefreshControl, ScrollView, StyleSheet, View } from "react-native";
RefreshControl, import { ActivityIndicator, Card, Text, useTheme } from "react-native-paper";
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
import { ActivityIndicator, Card, useTheme } from "react-native-paper";
import DataLabelComponent from "../data-label/data-label"; import DataLabelComponent from "../data-label/data-label";
export default function JobTombstone() { export default function JobTombstone() {
@@ -82,14 +76,9 @@ export default function JobTombstone() {
/> />
<DataLabelComponent <DataLabelComponent
label={t("objects.jobs.fields.vehicle")} label={t("objects.jobs.fields.vehicle")}
content={ content={`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
<View> job.v_model_desc || ""
<Text>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${ } - ${job.v_vin}`}
job.v_model_desc || ""
}`}</Text>
<Text>{job.v_vin}</Text>
</View>
}
/> />
</View> </View>
<View style={localStyles.twoColumnCardColumn}> <View style={localStyles.twoColumnCardColumn}>

View File

@@ -58,9 +58,7 @@ function JobListItemComponent({ openImagePicker, item }) {
style={[ style={[
styles.glassCard, styles.glassCard,
{ {
backgroundColor: theme.dark backgroundColor: theme.colors.primaryContainer,
? "rgba(30,30,30,0.55)"
: "rgba(255,255,255,0.55)",
borderColor: theme.colors.outlineVariant, borderColor: theme.colors.outlineVariant,
}, },
]} ]}

View File

@@ -11,6 +11,7 @@ import { Button, Card, Divider, List, Text } from "react-native-paper";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { ThemeSelector } from "../theme-selector/theme-selector";
import UploadDeleteSwitch from "./upload-delete-switch"; import UploadDeleteSwitch from "./upload-delete-switch";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -24,6 +25,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(Tab);
function Tab({ bodyshop, currentUser }) { function Tab({ bodyshop, currentUser }) {
const { t } = useTranslation(); const { t } = useTranslation();
const handleClearStorage = () => { const handleClearStorage = () => {
Alert.alert( Alert.alert(
"Clear Local Cache", "Clear Local Cache",
@@ -47,7 +49,7 @@ function Tab({ bodyshop, currentUser }) {
}; };
return ( return (
<SafeAreaView> <SafeAreaView style={{ flex: 1 }}>
<ScrollView contentContainerStyle={styles.container}> <ScrollView contentContainerStyle={styles.container}>
<Text variant="headlineMedium" style={styles.title}> <Text variant="headlineMedium" style={styles.title}>
Settings Settings
@@ -59,7 +61,6 @@ function Tab({ bodyshop, currentUser }) {
<List.Section> <List.Section>
<View style={styles.inlineRow}> <View style={styles.inlineRow}>
<Text style={styles.switchLabel}> <Text style={styles.switchLabel}>
{" "}
{t("mediabrowser.labels.deleteafterupload")} {t("mediabrowser.labels.deleteafterupload")}
</Text> </Text>
<UploadDeleteSwitch /> <UploadDeleteSwitch />
@@ -97,6 +98,18 @@ function Tab({ bodyshop, currentUser }) {
</Card.Actions> </Card.Actions>
</Card> </Card>
<Card style={styles.section}>
<Card.Title title="Theme" />
<Card.Content>
<List.Section>
<View style={styles.inlineRow}>
<ThemeSelector />
</View>
</List.Section>
</Card.Content>
<Card.Actions></Card.Actions>
</Card>
<Card style={styles.section}> <Card style={styles.section}>
<Card.Content> <Card.Content>
<Text style={styles.paragraph}> <Text style={styles.paragraph}>

View File

@@ -0,0 +1,37 @@
import { setTheme } from "@/redux/user/user.actions";
import { selectTheme } from "@/redux/user/user.selectors";
import React from "react";
import { SegmentedButtons } from "react-native-paper";
import { useDispatch, useSelector } from "react-redux";
/**
* Example component showing how to use the useTheme hook
* and how to dispatch theme changes
*/
export const ThemeSelector = () => {
const dispatch = useDispatch();
const currentTheme = useSelector(selectTheme);
const handleThemeChange = (theme: "light" | "dark" | "system") => {
dispatch(setTheme(theme));
};
return (
<SegmentedButtons
value={currentTheme}
onValueChange={handleThemeChange}
buttons={[
{
value: "light",
label: "Light",
},
{
value: "dark",
label: "Dark",
},
{ value: "system", label: "System" },
]}
/>
);
};

View File

@@ -1,5 +1,4 @@
import { clearUploadError } from "@/redux/photos/photos.actions"; import { clearUploadError } from "@/redux/photos/photos.actions";
import theme from "@/util/theme";
import { formatBytes } from "@/util/uploadUtils"; import { formatBytes } from "@/util/uploadUtils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { StyleSheet, View } from "react-native"; import { StyleSheet, View } from "react-native";
@@ -92,7 +91,7 @@ const styles = StyleSheet.create({
display: "flex", display: "flex",
marginLeft: 12, marginLeft: 12,
marginRight: 12, marginRight: 12,
backgroundColor: theme.colors.elevation.level3, //backgroundColor: theme.colors.elevation.level3,
borderRadius: 20, borderRadius: 20,
paddingTop: 12, paddingTop: 12,
shadowColor: "#000", shadowColor: "#000",

1
hooks/index.ts Normal file
View File

@@ -0,0 +1 @@
export { useTheme } from "./useTheme";

View File

@@ -1 +0,0 @@
export { useColorScheme } from 'react-native';

View File

@@ -1,21 +0,0 @@
import { useEffect, useState } from 'react';
import { useColorScheme as useRNColorScheme } from 'react-native';
/**
* To support static rendering, this value needs to be re-calculated on the client side for web
*/
export function useColorScheme() {
const [hasHydrated, setHasHydrated] = useState(false);
useEffect(() => {
setHasHydrated(true);
}, []);
const colorScheme = useRNColorScheme();
if (hasHydrated) {
return colorScheme;
}
return 'light';
}

View File

@@ -1,21 +0,0 @@
/**
* Learn more about light and dark modes:
* https://docs.expo.dev/guides/color-schemes/
*/
import { Colors } from '@/constants/theme';
import { useColorScheme } from '@/hooks/use-color-scheme';
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}

57
hooks/useTheme.ts Normal file
View File

@@ -0,0 +1,57 @@
import { selectTheme } from "@/redux/user/user.selectors";
import { darkTheme, lightTheme } from "@/util/theme";
import { useColorScheme } from "react-native";
import { useSelector } from "react-redux";
type ThemePreference = "light" | "dark" | "system";
/**
* Custom hook that returns the appropriate theme based on user preference
*
* This hook automatically selects the correct theme (light or dark) based on:
* - User's explicit preference (light/dark)
* - System appearance when "system" is selected
* - Defaults to system theme if no preference is set
*
* @returns The theme object compatible with React Native Paper
*
* @example
* ```typescript
* import { useTheme } from "@/hooks/useTheme";
*
* const MyComponent = () => {
* const theme = useTheme();
*
* return (
* <View style={{ backgroundColor: theme.colors.background }}>
* <Text style={{ color: theme.colors.onBackground }}>
* Themed content
* </Text>
* </View>
* );
* };
* ```
*/
export const useTheme = () => {
const userThemePreference: ThemePreference = useSelector(selectTheme);
const systemColorScheme = useColorScheme();
// Determine which theme to use based on user preference
let selectedTheme;
switch (userThemePreference) {
case "light":
selectedTheme = lightTheme;
break;
case "dark":
selectedTheme = darkTheme;
break;
case "system":
default:
// Use system preference when user selects "system" or as fallback
selectedTheme = systemColorScheme === "dark" ? darkTheme : lightTheme;
break;
}
return selectedTheme;
};

View File

@@ -98,3 +98,7 @@ export const validatePasswordResetFailure = (error) => ({
type: UserActionTypes.VALIDATE_PASSWORD_RESET_FAILURE, type: UserActionTypes.VALIDATE_PASSWORD_RESET_FAILURE,
payload: error, payload: error,
}); });
export const setTheme = (theme) => ({
type: UserActionTypes.SET_THEME,
payload: theme,
});

View File

@@ -7,6 +7,7 @@ const INITIAL_STATE = {
bodyshop: null, bodyshop: null,
signingIn: false, signingIn: false,
error: null, error: null,
theme: "system"
}; };
const userReducer = (state = INITIAL_STATE, action) => { const userReducer = (state = INITIAL_STATE, action) => {
@@ -48,6 +49,8 @@ const userReducer = (state = INITIAL_STATE, action) => {
case UserActionTypes.SET_SHOP_DETAILS: case UserActionTypes.SET_SHOP_DETAILS:
return { ...state, bodyshop: action.payload }; return { ...state, bodyshop: action.payload };
case UserActionTypes.SET_THEME:
return { ...state, theme: action.payload };
case UserActionTypes.SIGN_IN_FAILURE: case UserActionTypes.SIGN_IN_FAILURE:
case UserActionTypes.SIGN_OUT_FAILURE: case UserActionTypes.SIGN_OUT_FAILURE:
case UserActionTypes.EMAIL_SIGN_UP_FAILURE: case UserActionTypes.EMAIL_SIGN_UP_FAILURE:

View File

@@ -31,3 +31,8 @@ export const selectSigningIn = createSelector(
[selectUser], [selectUser],
(user) => user.signingIn (user) => user.signingIn
); );
export const selectTheme = createSelector(
[selectUser],
(user) => user.theme
);

View File

@@ -26,5 +26,6 @@ const UserActionTypes = {
VALIDATE_PASSWORD_RESET_START: "VALIDATE_PASSWORD_RESET_START", VALIDATE_PASSWORD_RESET_START: "VALIDATE_PASSWORD_RESET_START",
VALIDATE_PASSWORD_RESET_SUCCESS: "VALIDATE_PASSWORD_RESET_SUCCESS", VALIDATE_PASSWORD_RESET_SUCCESS: "VALIDATE_PASSWORD_RESET_SUCCESS",
VALIDATE_PASSWORD_RESET_FAILURE: "VALIDATE_PASSWORD_RESET_FAILURE", VALIDATE_PASSWORD_RESET_FAILURE: "VALIDATE_PASSWORD_RESET_FAILURE",
SET_THEME: "SET_THEME",
}; };
export default UserActionTypes; export default UserActionTypes;

View File

@@ -1,45 +1,92 @@
export default //Custom values were used as the overrides did not work. const lightTheme = {
{ theme: 'light',
colors: { colors: {
primary: "#1890ff", primary: "#1890ff",
onPrimary: "#ffffff", onPrimary: "#ffffff",
primaryContainer: "#e1e1e1ff", primaryContainer: "#e1e1e1ff",
onPrimaryContainer: "#001c3a", onPrimaryContainer: "#001c3a",
secondary: "#545f71", secondary: "#545f71",
onSecondary: "#ffffff", onSecondary: "#ffffff",
secondaryContainer: "#d8e3f8", secondaryContainer: "#d8e3f8",
onSecondaryContainer: "#111c2b", onSecondaryContainer: "#111c2b",
tertiary: "#00658d", tertiary: "#00658d",
onTertiary: "#ffffff", onTertiary: "#ffffff",
tertiaryContainer: "#c6e7ff", tertiaryContainer: "#c6e7ff",
onTertiaryContainer: "#001e2d", onTertiaryContainer: "#001e2d",
error: "#ba1a1a", error: "#ba1a1a",
onError: "#ffffff", onError: "#ffffff",
errorContainer: "#ffdad6", errorContainer: "#ffdad6",
onErrorContainer: "#410002", onErrorContainer: "#410002",
background: "#fdfcff", background: "#fdfcff",
onBackground: "#1a1c1e", onBackground: "#1a1c1e",
surface: "#fdfcff", surface: "#fdfcff",
onSurface: "#1a1c1e", onSurface: "#1a1c1e",
surfaceVariant: "#dededeff", surfaceVariant: "#dededeff",
onSurfaceVariant: "#43474e", onSurfaceVariant: "#43474e",
outline: "#74777f", outline: "#74777f",
outlineVariant: "#c3c6cf", outlineVariant: "#c3c6cf",
shadow: "#000000", shadow: "#000000",
scrim: "#000000", scrim: "#000000",
inverseSurface: "#2f3033", inverseSurface: "#2f3033",
inverseOnSurface: "#f1f0f4", inverseOnSurface: "#f1f0f4",
inversePrimary: "#a5c8ff", inversePrimary: "#a5c8ff",
elevation: { elevation: {
level0: "transparent", level0: "transparent",
level1: "#f1f1f1ff", level1: "#f1f1f1ff",
level2: "#e9eff9", level2: "#e9eff9",
level3: "#e1ebf6", level3: "#e1ebf6",
level4: "#dfe9f5", level4: "#dfe9f5",
level5: "#dae6f4", level5: "#dae6f4",
},
surfaceDisabled: "rgba(26, 28, 30, 0.12)",
onSurfaceDisabled: "rgba(26, 28, 30, 0.38)",
backdrop: "rgba(45, 49, 56, 0.4)",
}, },
}; surfaceDisabled: "rgba(26, 28, 30, 0.12)",
onSurfaceDisabled: "rgba(26, 28, 30, 0.38)",
backdrop: "rgba(45, 49, 56, 0.4)",
},
};
const darkTheme = {
theme: 'dark',
colors: {
primary: "#a5c8ff",
onPrimary: "#001c3a",
primaryContainer: "#2c3e50",
onPrimaryContainer: "#d6e8ff",
secondary: "#bcc6db",
onSecondary: "#262f40",
secondaryContainer: "#3d4756",
onSecondaryContainer: "#d8e3f8",
tertiary: "#7dd3fc",
onTertiary: "#001e2d",
tertiaryContainer: "#004d66",
onTertiaryContainer: "#c6e7ff",
error: "#ffb4ab",
onError: "#690005",
errorContainer: "#93000a",
onErrorContainer: "#ffdad6",
background: "#0f1419",
onBackground: "#e4e1e6",
surface: "#0f1419",
onSurface: "#e4e1e6",
surfaceVariant: "#43474e",
onSurfaceVariant: "#c3c6cf",
outline: "#8d9199",
outlineVariant: "#43474e",
shadow: "#000000",
scrim: "#000000",
inverseSurface: "#e4e1e6",
inverseOnSurface: "#2f3033",
inversePrimary: "#1890ff",
elevation: {
level0: "transparent",
level1: "#1a1f2e",
level2: "#212837",
level3: "#293141",
level4: "#2b3344",
level5: "#2e3748",
},
surfaceDisabled: "rgba(228, 225, 230, 0.12)",
onSurfaceDisabled: "rgba(228, 225, 230, 0.38)",
backdrop: "rgba(45, 49, 56, 0.4)",
},
};
export { darkTheme, lightTheme };