feat: move theme picker to its own component and improve contrast on login screen

This commit is contained in:
tonyaellie
2025-08-23 18:05:00 +00:00
parent 377c6c6e0d
commit 6fcd10d796
6 changed files with 89 additions and 59 deletions

View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { themes } from "~~/lib/data/themes";
import { useTheme } from "~/composables/use-theme";
const { setTheme } = useTheme();
</script>
<template>
<div class="homebox grid grid-cols-1 gap-4 font-sans sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
<div
v-for="theme in themes"
:key="theme.value"
:class="'theme-' + theme.value"
class="overflow-hidden rounded-lg border outline-2 outline-offset-2"
:data-theme="theme.value"
:data-set-theme="theme.value"
data-act-class="outline"
@click="setTheme(theme.value)"
>
<div :data-theme="theme.value" class="w-full cursor-pointer bg-background-accent text-foreground">
<div class="grid grid-cols-5 grid-rows-3">
<div class="col-start-1 row-start-1 bg-background"></div>
<div class="col-start-1 row-start-2 bg-sidebar"></div>
<div class="col-start-1 row-start-3 bg-background-accent"></div>
<div class="col-span-4 col-start-2 row-span-3 row-start-1 flex flex-col gap-1 bg-background p-2">
<div class="font-bold">{{ theme.label }}</div>
<div class="flex flex-wrap gap-1">
<div class="flex size-5 items-center justify-center rounded bg-primary lg:size-6">
<div class="text-sm font-bold text-primary-foreground">A</div>
</div>
<div class="flex size-5 items-center justify-center rounded bg-secondary lg:size-6">
<div class="text-sm font-bold text-secondary-foreground">A</div>
</div>
<div class="flex size-5 items-center justify-center rounded bg-accent lg:size-6">
<div class="text-sm font-bold text-accent-foreground">A</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped></style>

View File

@@ -15,6 +15,7 @@
import "@vuepic/vue-datepicker/dist/main.css"; import "@vuepic/vue-datepicker/dist/main.css";
import * as datelib from "~/lib/datelib/datelib"; import * as datelib from "~/lib/datelib/datelib";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { darkThemes } from "~/lib/data/themes";
const emit = defineEmits(["update:modelValue", "update:text"]); const emit = defineEmits(["update:modelValue", "update:text"]);
@@ -34,7 +35,7 @@
}, },
}); });
const isDark = useIsDark(); const isDark = useIsThemeInList(darkThemes);
const formatDate = (date: Date | string | number) => fmtDate(date, "human", "date"); const formatDate = (date: Date | string | number) => fmtDate(date, "human", "date");

View File

@@ -1,5 +1,5 @@
import type { ComputedRef } from "vue"; import type { ComputedRef } from "vue";
import type { DaisyTheme } from "~~/lib/data/themes"; import { type DaisyTheme } from "~~/lib/data/themes";
export interface UseTheme { export interface UseTheme {
theme: ComputedRef<DaisyTheme>; theme: ComputedRef<DaisyTheme>;
@@ -42,27 +42,11 @@ export function useTheme(): UseTheme {
return { theme, setTheme }; return { theme, setTheme };
} }
export function useIsDark() { export function useIsThemeInList(list: DaisyTheme[]) {
const theme = useTheme(); const theme = useTheme();
const darkthemes = [
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"forest",
"aqua",
"black",
"luxury",
"dracula",
"business",
"night",
"coffee",
];
return computed(() => { return computed(() => {
return darkthemes.includes(theme.theme.value); return list.includes(theme.theme.value);
}); });
} }

View File

@@ -153,3 +153,19 @@ export const themes: ThemeOption[] = [
value: "winter", value: "winter",
}, },
]; ];
export const darkThemes: DaisyTheme[] = [
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"forest",
"aqua",
"black",
"luxury",
"dracula",
"business",
"night",
"coffee",
];

View File

@@ -54,6 +54,20 @@
} }
}); });
const isEvilAccentTheme = useIsThemeInList([
"bumblebee",
"corporate",
"forest",
"pastel",
"wireframe",
"black",
"dracula",
"autumn",
"acid",
]);
const isEvilForegroundTheme = useIsThemeInList(["light", "aqua", "fantasy", "autumn", "night"]);
const isLofiTheme = useIsThemeInList(["lofi"]);
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -166,14 +180,19 @@
</svg> </svg>
</div> </div>
<div> <div>
<header class="mx-auto p-4 text-accent sm:flex sm:items-end sm:p-6 lg:p-14"> <header
class="mx-auto p-4 sm:flex sm:items-end sm:p-6 lg:p-14"
:class="{ 'text-accent': !isEvilAccentTheme, 'text-white': isLofiTheme }"
>
<div class="z-10"> <div class="z-10">
<h2 class="mt-1 flex text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl"> <h2 class="mt-1 flex text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
HomeB HomeB
<AppLogo class="-mb-4 w-12" /> <AppLogo class="-mb-4 w-12" />
x x
</h2> </h2>
<p class="ml-1 text-lg text-foreground">{{ $t("index.tagline") }}</p> <p class="ml-1 text-lg" :class="{ 'text-foreground': !isEvilForegroundTheme, 'text-white': isLofiTheme }">
{{ $t("index.tagline") }}
</p>
</div> </div>
<TooltipProvider :delay-duration="0"> <TooltipProvider :delay-duration="0">
<div class="z-10 ml-auto mt-6 flex items-center gap-4 sm:mt-0"> <div class="z-10 ml-auto mt-6 flex items-center gap-4 sm:mt-0">

View File

@@ -2,7 +2,6 @@
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { toast } from "@/components/ui/sonner"; import { toast } from "@/components/ui/sonner";
import type { Detail } from "~~/components/global/DetailsSection/types"; import type { Detail } from "~~/components/global/DetailsSection/types";
import { themes } from "~~/lib/data/themes";
import type { CurrenciesCurrency, NotifierCreate, NotifierOut } from "~~/lib/api/types/data-contracts"; import type { CurrenciesCurrency, NotifierCreate, NotifierOut } from "~~/lib/api/types/data-contracts";
import MdiLoading from "~icons/mdi/loading"; import MdiLoading from "~icons/mdi/loading";
import MdiAccount from "~icons/mdi/account"; import MdiAccount from "~icons/mdi/account";
@@ -21,6 +20,7 @@
import LanguageSelector from "~/components/App/LanguageSelector.vue"; import LanguageSelector from "~/components/App/LanguageSelector.vue";
import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "@/components/ui/tooltip";
import { DialogID } from "~/components/ui/dialog-provider/utils"; import { DialogID } from "~/components/ui/dialog-provider/utils";
import ThemePicker from "~/components/App/ThemePicker.vue";
const { t } = useI18n(); const { t } = useI18n();
@@ -105,8 +105,6 @@
toast.success(t("profile.toast.group_updated")); toast.success(t("profile.toast.group_updated"));
} }
const { setTheme } = useTheme();
const auth = useAuthContext(); const auth = useAuthContext();
const details = computed(() => { const details = computed(() => {
@@ -516,40 +514,7 @@
{{ $t("profile.display_legacy_header", { currentValue: preferences.displayLegacyHeader }) }} {{ $t("profile.display_legacy_header", { currentValue: preferences.displayLegacyHeader }) }}
</Button> </Button>
</div> </div>
<div class="homebox grid grid-cols-1 gap-4 font-sans sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5"> <ThemePicker />
<div
v-for="theme in themes"
:key="theme.value"
:class="'theme-' + theme.value"
class="overflow-hidden rounded-lg border outline-2 outline-offset-2"
:data-theme="theme.value"
:data-set-theme="theme.value"
data-act-class="outline"
@click="setTheme(theme.value)"
>
<div :data-theme="theme.value" class="w-full cursor-pointer bg-background-accent text-foreground">
<div class="grid grid-cols-5 grid-rows-3">
<div class="col-start-1 row-start-1 bg-background"></div>
<div class="col-start-1 row-start-2 bg-sidebar"></div>
<div class="col-start-1 row-start-3 bg-background-accent"></div>
<div class="col-span-4 col-start-2 row-span-3 row-start-1 flex flex-col gap-1 bg-background p-2">
<div class="font-bold">{{ theme.label }}</div>
<div class="flex flex-wrap gap-1">
<div class="flex size-5 items-center justify-center rounded bg-primary lg:size-6">
<div class="text-sm font-bold text-primary-foreground">A</div>
</div>
<div class="flex size-5 items-center justify-center rounded bg-secondary lg:size-6">
<div class="text-sm font-bold text-secondary-foreground">A</div>
</div>
<div class="flex size-5 items-center justify-center rounded bg-accent lg:size-6">
<div class="text-sm font-bold text-accent-foreground">A</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</BaseCard> </BaseCard>