diff --git a/web/components/ColorSwitcher.ce.vue b/web/components/ColorSwitcher.ce.vue new file mode 100644 index 000000000..bd074796e --- /dev/null +++ b/web/components/ColorSwitcher.ce.vue @@ -0,0 +1,74 @@ + + + + + + Color Theme Customization + Header Primary Text Color + + Header Secondary Text Color + + Header Background Color + + Dark Mode + + + + + diff --git a/web/components/HeaderOsVersion.ce.vue b/web/components/HeaderOsVersion.ce.vue index 49218a443..504c5cdb9 100644 --- a/web/components/HeaderOsVersion.ce.vue +++ b/web/components/HeaderOsVersion.ce.vue @@ -16,7 +16,6 @@ import { useUpdateOsStore } from '~/store/updateOs'; import { useUpdateOsActionsStore } from '~/store/updateOsActions'; import type { UserProfileLink } from '~/types/userProfile'; import type { UiBadgeProps, UiBadgePropsColor } from '~/types/ui/badge'; -import { useThemeStore } from '~/store/theme'; const { t } = useI18n(); @@ -67,16 +66,6 @@ const updateOsStatus = computed(() => { return null; }); - - -const themeStore = useThemeStore(); -const { darkMode, theme } = toRefs(themeStore); -const toggleDarkMode = () => { - const themeNameToSet = darkMode.value ? 'light' : 'black'; - if (theme.value) { - themeStore.setTheme({ ...theme.value, name: themeNameToSet }); - } -}; @@ -89,17 +78,13 @@ const toggleDarkMode = () => { {{ osVersion }} - - Toggle Dark / Light Theme - Current is: {{ darkMode ? 'Dark' : 'Light' }} - - { const ariaLablledById = computed((): string|undefined => props.title ? `ModalTitle-${Math.random()}`.replace('0.', '') : undefined); -/** - * @todo when providing custom colors for theme we should invert text-beta bg-alpha to text-alpha bg-beta - */ diff --git a/web/components/Notifications/Indicator.vue b/web/components/Notifications/Indicator.vue index 4de499b00..cf914a12f 100644 --- a/web/components/Notifications/Indicator.vue +++ b/web/components/Notifications/Indicator.vue @@ -39,16 +39,24 @@ const icon = computed<{ component: Component; color: string } | null>(() => { - - - - + + + + + + + diff --git a/web/components/UserProfile.ce.vue b/web/components/UserProfile.ce.vue index 9af4c9867..a426ec43a 100644 --- a/web/components/UserProfile.ce.vue +++ b/web/components/UserProfile.ce.vue @@ -97,21 +97,21 @@ onBeforeMount(() => { - + • - + - • + • {{ name }} @@ -155,7 +155,7 @@ onBeforeMount(() => { top: -10px; right: 42px; border-right: 11px solid transparent; - border-bottom: 11px solid var(--color-alpha); + border-bottom: 11px solid var(--color-headerTextPrimary); border-left: 11px solid transparent; } } diff --git a/web/components/UserProfile/DropdownTrigger.vue b/web/components/UserProfile/DropdownTrigger.vue index 8e3f64215..1c72e9ded 100644 --- a/web/components/UserProfile/DropdownTrigger.vue +++ b/web/components/UserProfile/DropdownTrigger.vue @@ -40,7 +40,7 @@ const title = computed((): string => { @@ -49,7 +49,7 @@ const title = computed((): string => { - + {{ text }} diff --git a/web/components/shadcn/sheet/index.ts b/web/components/shadcn/sheet/index.ts index 036777237..189902a84 100644 --- a/web/components/shadcn/sheet/index.ts +++ b/web/components/shadcn/sheet/index.ts @@ -10,7 +10,7 @@ export { default as SheetDescription } from './SheetDescription.vue' export { default as SheetFooter } from './SheetFooter.vue' export const sheetVariants = cva( - 'fixed z-50 gap-4 bg-beta shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500', + 'fixed z-50 gap-4 bg-alpha shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500', { variants: { side: { diff --git a/web/package.json b/web/package.json index f99d6b325..05b3071be 100644 --- a/web/package.json +++ b/web/package.json @@ -8,7 +8,7 @@ "lint:fix": "eslint . --fix", "type-check": "nuxi typecheck", "prebuild:dev": "./scripts/prebuild-webgui-set-env.sh .env.staging", - "build:dev": "npm run type-check && nuxt build && npm run manifest-ts && npm run deploy-to-unraid:dev", + "build:dev": "nuxt build && npm run manifest-ts && npm run deploy-to-unraid:dev", "postbuild:dev": "./scripts/postbuild-webgui-restore-env.sh", "prebuild:webgui": "./scripts/prebuild-webgui-set-env.sh", "build:webgui": "npm run type-check && nuxt build && npm run manifest-ts && npm run copy-to-webgui-repo", diff --git a/web/pages/index.vue b/web/pages/index.vue index 572cb41ee..1bc57136f 100644 --- a/web/pages/index.vue +++ b/web/pages/index.vue @@ -1,7 +1,6 @@ @@ -66,12 +56,10 @@ const toggleDarkMode = () => { - - Toggle Dark / Light Theme - Current is: {{ darkMode ? 'Dark' : 'Light' }} - + Vue Components UserProfileCe - + diff --git a/web/store/theme.ts b/web/store/theme.ts index f87decbd2..1cbb2e7de 100644 --- a/web/store/theme.ts +++ b/web/store/theme.ts @@ -1,5 +1,5 @@ -import { defineStore, createPinia, setActivePinia } from 'pinia'; import hexToRgba from 'hex-to-rgba'; +import { createPinia, defineStore, setActivePinia } from 'pinia'; /** * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components @@ -17,14 +17,46 @@ export interface Theme { textColor: string; } +interface ColorMode { + headerTextPrimary: string; + headerTextSecondary: string; + headerBackgroundColor: string; + alpha: string; + beta: string; + gamma: string; +} + +export const defaultColors: Record = { + dark: { + alpha: '#1c1c1c', // previously header custom text color + beta: '#f2f2f2', // previously header background color + gamma: '#999999', // previously header custom secondary text color + headerTextPrimary: '#1c1c1c', + headerBackgroundColor: '#f2f2f2', + headerTextSecondary: '#999999', + }, + light: { + alpha: '#f2f2f2', // previously header custom text color + beta: '#1c1b1b', // previously header background color + gamma: '#999999', // previously header custom secondary text color + headerTextPrimary: '#f2f2f2', + headerBackgroundColor: '#1c1b1b', + headerTextSecondary: '#999999', + }, +}; + export const useThemeStore = defineStore('theme', () => { // State const theme = ref(); // Getters - const darkMode = computed(() => (theme.value?.name === 'black' || theme.value?.name === 'azure') ?? false); + const darkMode = computed( + () => (theme.value?.name === 'black' || theme.value?.name === 'azure') ?? false + ); // used to swap the UPC text color when using the azure or gray theme const bannerGradient = computed(() => { - if (!theme.value?.banner || !theme.value?.bannerGradient) { return undefined; } + if (!theme.value?.banner || !theme.value?.bannerGradient) { + return undefined; + } const start = theme.value?.bgColor ? 'var(--color-customgradient-start)' : 'rgba(0, 0, 0, 0)'; const end = theme.value?.bgColor ? 'var(--color-customgradient-end)' : 'var(--color-beta)'; return `background-image: linear-gradient(90deg, ${start} 0, ${end} 30%);`; @@ -35,39 +67,44 @@ export const useThemeStore = defineStore('theme', () => { }; const setCssVars = () => { const body = document.body; - const defaultColors = { - darkTheme: { - alpha: '#1c1b1b', - beta: '#f2f2f2', - gamma: '#999999', - }, - lightTheme: { - alpha: '#f2f2f2', - beta: '#1c1b1b', - gamma: '#999999', - }, - }; - let { alpha, beta, gamma } = darkMode.value ? defaultColors.darkTheme : defaultColors.lightTheme; + + let { alpha, beta, gamma, headerTextPrimary, headerTextSecondary, headerBackgroundColor } = + darkMode.value ? defaultColors.dark : defaultColors.light; // overwrite with hex colors set in webGUI @ /Settings/DisplaySettings - if (theme.value?.textColor) { alpha = theme.value?.textColor; } + if (theme.value?.textColor) { + headerTextPrimary = theme.value?.textColor; + } if (theme.value?.bgColor) { - beta = theme.value?.bgColor; + headerBackgroundColor = theme.value.bgColor; body.style.setProperty('--color-customgradient-start', hexToRgba(beta, 0)); body.style.setProperty('--color-customgradient-end', hexToRgba(beta, 0.7)); } - if (theme.value?.metaColor) { gamma = theme.value?.metaColor; } + if (theme.value?.metaColor) { + headerTextSecondary = theme.value?.metaColor; + } body.style.setProperty('--color-alpha', alpha); body.style.setProperty('--color-beta', beta); body.style.setProperty('--color-gamma', gamma); + body.style.setProperty('--header-text-primary', headerTextPrimary); + body.style.setProperty('--header-text-secondary', headerTextSecondary); + body.style.setProperty('--header-background-color', headerBackgroundColor); body.style.setProperty('--color-gamma-opaque', hexToRgba(gamma, 0.25)); // box shadow body.style.setProperty('--shadow-beta', `0 25px 50px -12px ${hexToRgba(beta, 0.15)}`); body.style.setProperty('--ring-offset-shadow', `0 0 ${beta}`); body.style.setProperty('--ring-shadow', `0 0 ${beta}`); body.style.setProperty('--dev-test', `0 0 ${beta}`); + + if (darkMode.value) { + document.body.classList.add('dark'); + } else { + document.body.classList.remove('dark'); + } }; watch(theme, () => { + console.log(theme.value); + console.log('theme changed'); setCssVars(); }); diff --git a/web/store/updateOs.ts b/web/store/updateOs.ts index e5927f128..3002a0e39 100644 --- a/web/store/updateOs.ts +++ b/web/store/updateOs.ts @@ -69,7 +69,7 @@ export const useUpdateOsStore = defineStore('updateOs', () => { serverStore.setUpdateOsResponse(response as ServerUpdateOsResponse); checkForUpdatesLoading.value = false; } catch (error) { - throw new Error('[localCheckForUpdate] Error checking for updates'); + throw new Error("[localCheckForUpdate] Error checking for updates\n" + JSON.stringify(error)); } }; diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts index c62ec18e6..7c736887b 100644 --- a/web/tailwind.config.ts +++ b/web/tailwind.config.ts @@ -2,6 +2,7 @@ import 'dotenv/config'; import type { Config } from 'tailwindcss'; import type { PluginAPI } from 'tailwindcss/types/config'; + // @ts-expect-error - just trying to get this to build @fixme export default >{ darkMode: ['selector'], @@ -44,11 +45,6 @@ export default >{ 'grey-lightest': '#f2f2f2', white: '#ffffff', - - // New Color Palette - 'sidebar': '#f2f2f2', - 'sidebar-dark': '#1c1b1b', - // unraid colors 'yellow-accent': '#E9BF41', 'orange-dark': '#f15a2c', @@ -88,40 +84,42 @@ export default >{ beta: 'var(--color-beta)', gamma: 'var(--color-gamma)', 'gamma-opaque': 'var(--color-gamma-opaque)', - - // shadcn specific - border: 'hsl(0 0% 89.8%)', - input: 'hsl(0 0% 89.8%)', - ring: 'hsl(0 0% 3.9%)', - background: 'hsl(0 0% 100%)', - foreground: 'hsl(0 0% 3.9%)', + 'header-text-primary': 'var(--header-text-primary)', + 'header-text-secondary': 'var(--header-text-secondary)', + 'header-background-color': 'var(--header-background-color)', + // ShadCN + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', primary: { - DEFAULT: 'hsl(0 0% 9%)', - foreground: 'hsl(0 0% 98%)', + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', }, secondary: { - DEFAULT: 'hsl(0 0% 96.1%)', - foreground: 'hsl(0 0% 9%)', + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', }, destructive: { - DEFAULT: 'hsl(0 84.2% 60.2%)', - foreground: 'hsl(0 0% 98%)', + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', }, muted: { - DEFAULT: 'hsl(0 0% 96.1%)', - foreground: 'hsl(0 0% 45.1%)', + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', }, accent: { - DEFAULT: 'hsl(0 0% 96.1%)', - foreground: 'hsl(0 0% 9%)', + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', }, popover: { - DEFAULT: 'hsl(0 0% 100%)', - foreground: 'hsl(0 0% 3.9%)', + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', }, card: { - DEFAULT: 'hsl(0 0% 100%)', - foreground: 'hsl(0 0% 3.9%)', + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', }, }, // Unfortunately due to webGUI CSS setting base HTML font-size to .65% or something we must use pixel values for web components @@ -319,4 +317,4 @@ export default >{ newFontSize: process.env.VITE_TAILWIND_BASE_FONT_SIZE ?? 10, }), ], -}; +}; \ No newline at end of file