diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index b69e3b82af..1b3b790222 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -24,16 +24,13 @@ }, "common": { "hotkeysLabel": "Hotkeys", - "themeLabel": "Theme", + "darkMode": "Dark Mode", + "lightMode": "Light Mode", "languagePickerLabel": "Language", "reportBugLabel": "Report Bug", "githubLabel": "Github", "discordLabel": "Discord", "settingsLabel": "Settings", - "darkTheme": "Dark", - "lightTheme": "Light", - "greenTheme": "Green", - "oceanTheme": "Ocean", "langArabic": "العربية", "langEnglish": "English", "langDutch": "Nederlands", diff --git a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx index 5eea4bb940..1e86e0ce1b 100644 --- a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx +++ b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx @@ -3,17 +3,10 @@ import { createLocalStorageManager, extendTheme, } from '@chakra-ui/react'; -import { RootState } from 'app/store/store'; -import { useAppSelector } from 'app/store/storeHooks'; -import { ReactNode, useEffect } from 'react'; +import { ReactNode, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { theme as invokeAITheme } from 'theme/theme'; -import { greenTeaThemeColors } from 'theme/colors/greenTea'; -import { invokeAIThemeColors } from 'theme/colors/invokeAI'; -import { lightThemeColors } from 'theme/colors/lightTheme'; -import { oceanBlueColors } from 'theme/colors/oceanBlue'; - import '@fontsource-variable/inter'; import { MantineProvider } from '@mantine/core'; import { mantineTheme } from 'mantine-theme/theme'; @@ -24,29 +17,19 @@ type ThemeLocaleProviderProps = { children: ReactNode; }; -const THEMES = { - dark: invokeAIThemeColors, - light: lightThemeColors, - green: greenTeaThemeColors, - ocean: oceanBlueColors, -}; - const manager = createLocalStorageManager('@@invokeai-color-mode'); function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) { const { i18n } = useTranslation(); - const currentTheme = useAppSelector( - (state: RootState) => state.ui.currentTheme - ); - const direction = i18n.dir(); - const theme = extendTheme({ - ...invokeAITheme, - colors: THEMES[currentTheme as keyof typeof THEMES], - direction, - }); + const theme = useMemo(() => { + return extendTheme({ + ...invokeAITheme, + direction, + }); + }, [direction]); useEffect(() => { document.body.dir = direction; diff --git a/invokeai/frontend/web/src/common/components/IAICollapse.tsx b/invokeai/frontend/web/src/common/components/IAICollapse.tsx index ec23793741..5db26f3841 100644 --- a/invokeai/frontend/web/src/common/components/IAICollapse.tsx +++ b/invokeai/frontend/web/src/common/components/IAICollapse.tsx @@ -1,6 +1,14 @@ import { ChevronUpIcon } from '@chakra-ui/icons'; -import { Box, Collapse, Flex, Spacer, Switch } from '@chakra-ui/react'; +import { + Box, + Collapse, + Flex, + Spacer, + Switch, + useColorMode, +} from '@chakra-ui/react'; import { PropsWithChildren, memo } from 'react'; +import { mode } from 'theme/util/mode'; export type IAIToggleCollapseProps = PropsWithChildren & { label: string; @@ -11,6 +19,7 @@ export type IAIToggleCollapseProps = PropsWithChildren & { const IAICollapse = (props: IAIToggleCollapseProps) => { const { label, isOpen, onToggle, children, withSwitch = false } = props; + const { colorMode } = useColorMode(); return ( { px: 4, borderTopRadius: 'base', borderBottomRadius: isOpen ? 0 : 'base', - bg: isOpen ? 'base.750' : 'base.800', - color: 'base.100', + bg: isOpen + ? mode('base.200', 'base.750')(colorMode) + : mode('base.150', 'base.800')(colorMode), + color: mode('base.900', 'base.100')(colorMode), _hover: { - bg: isOpen ? 'base.700' : 'base.750', + bg: isOpen + ? mode('base.250', 'base.700')(colorMode) + : mode('base.200', 'base.750')(colorMode), }, fontSize: 'sm', fontWeight: 600, @@ -50,7 +63,13 @@ const IAICollapse = (props: IAIToggleCollapseProps) => { )} - + {children} diff --git a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx index bdf22c2df1..d0652dc8b9 100644 --- a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx @@ -5,6 +5,7 @@ import { Icon, IconButtonProps, Image, + useColorMode, } from '@chakra-ui/react'; import { useDraggable, useDroppable } from '@dnd-kit/core'; import { useCombinedRefs } from '@dnd-kit/utilities'; @@ -20,6 +21,7 @@ import { v4 as uuidv4 } from 'uuid'; import IAIDropOverlay from './IAIDropOverlay'; import { PostUploadAction } from 'services/api/thunks/image'; import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; +import { mode } from 'theme/util/mode'; type IAIDndImageProps = { image: ImageDTO | null | undefined; @@ -62,6 +64,7 @@ const IAIDndImage = (props: IAIDndImageProps) => { } = props; const dndId = useRef(uuidv4()); + const { colorMode } = useColorMode(); const { isOver, @@ -99,10 +102,10 @@ const IAIDndImage = (props: IAIDndImageProps) => { ? {} : { cursor: 'pointer', - bg: 'base.800', + bg: mode('base.200', 'base.800')(colorMode), _hover: { - bg: 'base.750', - color: 'base.300', + bg: mode('base.300', 'base.650')(colorMode), + color: mode('base.500', 'base.300')(colorMode), }, }; @@ -181,7 +184,7 @@ const IAIDndImage = (props: IAIDndImageProps) => { borderRadius: 'base', transitionProperty: 'common', transitionDuration: '0.1s', - color: 'base.500', + color: mode('base.500', 'base.500')(colorMode), ...uploadButtonStyles, }} {...getUploadButtonProps()} diff --git a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx index 79105c00d6..8ae54c30ab 100644 --- a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx @@ -1,6 +1,7 @@ -import { Flex, Text } from '@chakra-ui/react'; +import { Flex, Text, useColorMode } from '@chakra-ui/react'; import { motion } from 'framer-motion'; import { memo, useRef } from 'react'; +import { mode } from 'theme/util/mode'; import { v4 as uuidv4 } from 'uuid'; type Props = { @@ -11,6 +12,7 @@ type Props = { export const IAIDropOverlay = (props: Props) => { const { isOver, label = 'Drop' } = props; const motionId = useRef(uuidv4()); + const { colorMode } = useColorMode(); return ( { insetInlineStart: 0, w: 'full', h: 'full', - bg: 'base.900', + bg: mode('base.700', 'base.900')(colorMode), opacity: 0.7, borderRadius: 'base', alignItems: 'center', @@ -61,7 +63,9 @@ export const IAIDropOverlay = (props: Props) => { h: 'full', opacity: 1, borderWidth: 2, - borderColor: isOver ? 'base.200' : 'base.500', + borderColor: isOver + ? mode('base.50', 'base.200')(colorMode) + : mode('base.100', 'base.500')(colorMode), borderRadius: 'base', borderStyle: 'dashed', transitionProperty: 'common', @@ -75,7 +79,9 @@ export const IAIDropOverlay = (props: Props) => { fontSize: '2xl', fontWeight: 600, transform: isOver ? 'scale(1.1)' : 'scale(1)', - color: isOver ? 'base.100' : 'base.500', + color: isOver + ? mode('base.100', 'base.100')(colorMode) + : mode('base.200', 'base.500')(colorMode), transitionProperty: 'common', transitionDuration: '0.1s', }} diff --git a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx index 03a00d5b1c..4cff351aee 100644 --- a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx +++ b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx @@ -6,9 +6,10 @@ import { IconProps, Spinner, SpinnerProps, + useColorMode, } from '@chakra-ui/react'; -import { ReactElement } from 'react'; import { FaImage } from 'react-icons/fa'; +import { mode } from 'theme/util/mode'; type Props = FlexProps & { spinnerProps?: SpinnerProps; @@ -17,10 +18,11 @@ type Props = FlexProps & { export const IAIImageLoadingFallback = (props: Props) => { const { spinnerProps, ...rest } = props; const { sx, ...restFlexProps } = rest; + const { colorMode } = useColorMode(); return ( { const { sx: flexSx, ...restFlexProps } = props.flexProps ?? { sx: {} }; const { sx: iconSx, ...restIconProps } = props.iconProps ?? { sx: {} }; + const { colorMode } = useColorMode(); + return ( { > diff --git a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx index 90be25d04d..e62a7b34c3 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx @@ -1,6 +1,8 @@ -import { Tooltip } from '@chakra-ui/react'; +import { Tooltip, useColorMode, useToken } from '@chakra-ui/react'; import { MultiSelect, MultiSelectProps } from '@mantine/core'; +import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; import { memo } from 'react'; +import { mode } from 'theme/util/mode'; type IAIMultiSelectProps = MultiSelectProps & { tooltip?: string; @@ -8,71 +10,99 @@ type IAIMultiSelectProps = MultiSelectProps & { const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { const { searchable = true, tooltip, ...rest } = props; + const { + base50, + base100, + base200, + base300, + base400, + base500, + base600, + base700, + base800, + base900, + accent200, + accent300, + accent400, + accent500, + accent600, + } = useChakraThemeTokens(); + const [boxShadow] = useToken('shadows', ['dark-lg']); + const { colorMode } = useColorMode(); + return ( ({ label: { - color: 'var(--invokeai-colors-base-300)', + color: mode(base700, base300)(colorMode), fontWeight: 'normal', }, searchInput: { - '::placeholder': { - color: 'var(--invokeai-colors-base-700)', + ':placeholder': { + color: mode(base300, base700)(colorMode), }, }, input: { - backgroundColor: 'var(--invokeai-colors-base-900)', + backgroundColor: mode(base50, base900)(colorMode), borderWidth: '2px', - borderColor: 'var(--invokeai-colors-base-800)', - color: 'var(--invokeai-colors-base-100)', + borderColor: mode(base200, base800)(colorMode), + color: mode(base900, base100)(colorMode), paddingRight: 24, fontWeight: 600, - '&:hover': { borderColor: 'var(--invokeai-colors-base-700)' }, + '&:hover': { borderColor: mode(base300, base600)(colorMode) }, '&:focus': { - borderColor: 'var(--invokeai-colors-accent-600)', + borderColor: mode(accent300, accent600)(colorMode), + }, + '&:is(:focus, :hover)': { + borderColor: mode(base400, base500)(colorMode), }, '&:focus-within': { - borderColor: 'var(--invokeai-colors-accent-600)', + borderColor: mode(accent200, accent600)(colorMode), + }, + '&:disabled': { + backgroundColor: mode(base300, base700)(colorMode), + color: mode(base600, base400)(colorMode), }, }, value: { - backgroundColor: 'var(--invokeai-colors-base-800)', - color: 'var(--invokeai-colors-base-100)', + backgroundColor: mode(base200, base800)(colorMode), + color: mode(base900, base100)(colorMode), button: { - color: 'var(--invokeai-colors-base-100)', + color: mode(base900, base100)(colorMode), }, '&:hover': { - backgroundColor: 'var(--invokeai-colors-base-700)', + backgroundColor: mode(base300, base700)(colorMode), cursor: 'pointer', }, }, dropdown: { - backgroundColor: 'var(--invokeai-colors-base-800)', - borderColor: 'var(--invokeai-colors-base-700)', + backgroundColor: mode(base200, base800)(colorMode), + borderColor: mode(base200, base800)(colorMode), + boxShadow, }, item: { - backgroundColor: 'var(--invokeai-colors-base-800)', - color: 'var(--invokeai-colors-base-200)', + backgroundColor: mode(base200, base800)(colorMode), + color: mode(base800, base200)(colorMode), padding: 6, '&[data-hovered]': { - color: 'var(--invokeai-colors-base-100)', - backgroundColor: 'var(--invokeai-colors-base-750)', + color: mode(base900, base100)(colorMode), + backgroundColor: mode(base300, base700)(colorMode), }, '&[data-active]': { - backgroundColor: 'var(--invokeai-colors-base-750)', + backgroundColor: mode(base300, base700)(colorMode), '&:hover': { - color: 'var(--invokeai-colors-base-100)', - backgroundColor: 'var(--invokeai-colors-base-750)', + color: mode(base900, base100)(colorMode), + backgroundColor: mode(base300, base700)(colorMode), }, }, '&[data-selected]': { - color: 'var(--invokeai-colors-base-50)', - backgroundColor: 'var(--invokeai-colors-accent-650)', + color: mode(base900, base50)(colorMode), + backgroundColor: mode(accent300, accent600)(colorMode), fontWeight: 600, '&:hover': { - backgroundColor: 'var(--invokeai-colors-accent-600)', + backgroundColor: mode(accent400, accent500)(colorMode), }, }, }, @@ -80,7 +110,7 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { width: 24, padding: 20, button: { - color: 'var(--invokeai-colors-base-100)', + color: mode(base900, base100)(colorMode), }, }, })} diff --git a/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx index 5f02140904..0ca4c2c04c 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx @@ -1,6 +1,8 @@ -import { Tooltip } from '@chakra-ui/react'; +import { Tooltip, useColorMode, useToken } from '@chakra-ui/react'; import { Select, SelectProps } from '@mantine/core'; +import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; import { memo } from 'react'; +import { mode } from 'theme/util/mode'; export type IAISelectDataType = { value: string; @@ -14,61 +16,104 @@ type IAISelectProps = SelectProps & { const IAIMantineSelect = (props: IAISelectProps) => { const { searchable = true, tooltip, ...rest } = props; + const { + base50, + base100, + base200, + base300, + base400, + base500, + base600, + base700, + base800, + base900, + accent200, + accent300, + accent400, + accent500, + accent600, + } = useChakraThemeTokens(); + + const { colorMode } = useColorMode(); + + const [boxShadow] = useToken('shadows', ['dark-lg']); + return (