From 72679cca59e6eedf6bd39066ff01528da9ec852d Mon Sep 17 00:00:00 2001 From: EstrellaXD Date: Fri, 23 Jan 2026 14:54:26 +0100 Subject: [PATCH 01/10] feat(webui): complete UI redesign with design system, dark mode, and accessibility Implement a comprehensive UI overhaul using CSS custom properties for theming, scoped SCSS for all components and pages, dark/light mode toggle with flash prevention, page transitions, ARIA accessibility attributes, and responsive layout fixes. Fix VueUse auto-import configuration and dev proxy target. Co-Authored-By: Claude Opus 4.5 --- webui/index.html | 15 +- webui/src/App.vue | 88 +++-- webui/src/components/ab-bangumi-card.vue | 269 +++++++++++---- webui/src/components/ab-container.vue | 66 +++- webui/src/components/ab-fold-panel.vue | 87 +++-- webui/src/components/ab-label.vue | 20 +- webui/src/components/ab-popup.vue | 37 ++- webui/src/components/ab-search-bar.vue | 85 +++-- webui/src/components/ab-status-bar.vue | 171 +++++++--- webui/src/components/basic/ab-add.vue | 105 ++++-- .../src/components/basic/ab-button-multi.vue | 184 +++++----- webui/src/components/basic/ab-button.vue | 85 +++-- webui/src/components/basic/ab-checkbox.vue | 85 ++++- webui/src/components/basic/ab-page-title.vue | 39 ++- webui/src/components/basic/ab-search.vue | 139 +++++--- webui/src/components/basic/ab-select.vue | 96 ++++-- webui/src/components/basic/ab-status.vue | 57 +++- webui/src/components/basic/ab-switch.vue | 64 ++-- webui/src/components/basic/ab-tag.vue | 94 +++--- webui/src/components/layout/ab-sidebar.vue | 313 +++++++++++++----- webui/src/components/layout/ab-topbar.vue | 98 +++++- webui/src/hooks/useDarkMode.ts | 49 +++ webui/src/pages/index.vue | 38 ++- webui/src/pages/index/bangumi.vue | 73 ++-- webui/src/pages/index/config.vue | 98 +++--- webui/src/pages/index/downloader.vue | 52 ++- webui/src/pages/index/log.vue | 200 ++++++++--- webui/src/pages/index/player.vue | 51 ++- webui/src/pages/index/rss.vue | 28 +- webui/src/pages/login.vue | 75 ++++- webui/src/style/global.scss | 46 ++- webui/src/style/mixin.scss | 2 +- webui/src/style/transition.scss | 67 +++- webui/src/style/var.scss | 84 ++++- webui/types/dts/auto-imports.d.ts | 188 +---------- webui/unocss.config.ts | 59 +++- webui/vite.config.ts | 15 +- 37 files changed, 2363 insertions(+), 959 deletions(-) create mode 100644 webui/src/hooks/useDarkMode.ts diff --git a/webui/index.html b/webui/index.html index 356c7f49..6b8f6f66 100644 --- a/webui/index.html +++ b/webui/index.html @@ -4,11 +4,24 @@ - + + + + AutoBangumi +
diff --git a/webui/src/App.vue b/webui/src/App.vue index 66506e88..21e28b3a 100644 --- a/webui/src/App.vue +++ b/webui/src/App.vue @@ -1,40 +1,88 @@ diff --git a/webui/src/components/ab-bangumi-card.vue b/webui/src/components/ab-bangumi-card.vue index 0fb2adee..50675f5d 100644 --- a/webui/src/components/ab-bangumi-card.vue +++ b/webui/src/components/ab-bangumi-card.vue @@ -16,48 +16,36 @@ defineEmits(['click']); diff --git a/webui/src/components/basic/ab-checkbox.vue b/webui/src/components/basic/ab-checkbox.vue index ceb21da7..1fcbe3d6 100644 --- a/webui/src/components/basic/ab-checkbox.vue +++ b/webui/src/components/basic/ab-checkbox.vue @@ -15,28 +15,22 @@ const checked = defineModel({ default: false }); + + diff --git a/webui/src/components/basic/ab-page-title.vue b/webui/src/components/basic/ab-page-title.vue index cb1546eb..48c84ebe 100644 --- a/webui/src/components/basic/ab-page-title.vue +++ b/webui/src/components/basic/ab-page-title.vue @@ -10,8 +10,41 @@ withDefaults( + + diff --git a/webui/src/components/basic/ab-search.vue b/webui/src/components/basic/ab-search.vue index f07f03c8..297c685d 100644 --- a/webui/src/components/basic/ab-search.vue +++ b/webui/src/components/basic/ab-search.vue @@ -1,7 +1,6 @@ diff --git a/webui/src/components/basic/ab-select.vue b/webui/src/components/basic/ab-select.vue index b1bed9c8..2ba5e7d5 100644 --- a/webui/src/components/basic/ab-select.vue +++ b/webui/src/components/basic/ab-select.vue @@ -64,26 +64,17 @@ watchEffect(() => { + + diff --git a/webui/src/components/basic/ab-status.vue b/webui/src/components/basic/ab-status.vue index 0c01710e..322d2eac 100644 --- a/webui/src/components/basic/ab-status.vue +++ b/webui/src/components/basic/ab-status.vue @@ -12,14 +12,59 @@ withDefaults( + + diff --git a/webui/src/components/basic/ab-switch.vue b/webui/src/components/basic/ab-switch.vue index 63d41e01..de365f1d 100644 --- a/webui/src/components/basic/ab-switch.vue +++ b/webui/src/components/basic/ab-switch.vue @@ -8,54 +8,44 @@ const checked = defineModel('checked', { - diff --git a/webui/src/components/basic/ab-tag.vue b/webui/src/components/basic/ab-tag.vue index 5b11de9a..32409419 100644 --- a/webui/src/components/basic/ab-tag.vue +++ b/webui/src/components/basic/ab-tag.vue @@ -1,5 +1,5 @@ diff --git a/webui/src/components/layout/ab-sidebar.vue b/webui/src/components/layout/ab-sidebar.vue index 9f184239..32bd5e1c 100644 --- a/webui/src/components/layout/ab-sidebar.vue +++ b/webui/src/components/layout/ab-sidebar.vue @@ -6,8 +6,10 @@ import { Log, Logout, MenuUnfold, + Moon, Play, SettingTwo, + Sun, } from '@icon-park/vue-next'; import InlineSvg from 'vue-inline-svg'; @@ -24,14 +26,15 @@ const { t } = useMyI18n(); const { logout } = useAuth(); const route = useRoute(); const { isMobile } = useBreakpointQuery(); +const { isDark, toggle: toggleDark } = useDarkMode(); const show = ref(props.open); const toggle = () => (show.value = !show.value); const RSS = h( 'span', - { class: ['rel', 'left-2'] }, - h(InlineSvg, { src: './images/RSS.svg' }) + { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '20px', height: '20px' } }, + h(InlineSvg, { src: './images/RSS.svg', width: '16', height: '16' }) ); const items = [ @@ -86,21 +89,13 @@ function Exit() {
- - {!isMobile.value &&
{t('sidebar.logout')}
} + + {!isMobile.value && show.value && }
); } @@ -111,91 +106,261 @@ const mobileItems = computed(() => items.filter((i) => i.id !== 4)); + + diff --git a/webui/src/components/layout/ab-topbar.vue b/webui/src/components/layout/ab-topbar.vue index a9947f83..272430ed 100644 --- a/webui/src/components/layout/ab-topbar.vue +++ b/webui/src/components/layout/ab-topbar.vue @@ -67,12 +67,12 @@ const items = [ }, ]; +const { isDark } = useDarkMode(); const onSearchFocus = ref(false); function addSearchResult(bangumi: BangumiRule) { showAddRSS.value = true; searchRule.value = bangumi; - console.log('searchRule', searchRule.value); } watch(showAddRSS, (val) => { @@ -94,33 +94,28 @@ onUnmounted(() => { + + diff --git a/webui/src/hooks/useDarkMode.ts b/webui/src/hooks/useDarkMode.ts new file mode 100644 index 00000000..79bb8942 --- /dev/null +++ b/webui/src/hooks/useDarkMode.ts @@ -0,0 +1,49 @@ +import { computed, ref, watch } from 'vue'; +import { createSharedComposable, usePreferredDark } from '@vueuse/core'; + +type ThemeMode = 'light' | 'dark' | 'system'; + +export const useDarkMode = createSharedComposable(() => { + const prefersDark = usePreferredDark(); + const stored = localStorage.getItem('theme') as ThemeMode | null; + const mode = ref(stored || 'system'); + + const isDark = computed(() => { + if (mode.value === 'system') return prefersDark.value; + return mode.value === 'dark'; + }); + + function applyTheme() { + const html = document.documentElement; + if (isDark.value) { + html.classList.add('dark'); + } else { + html.classList.remove('dark'); + } + } + + function setMode(newMode: ThemeMode) { + mode.value = newMode; + if (newMode === 'system') { + localStorage.removeItem('theme'); + } else { + localStorage.setItem('theme', newMode); + } + } + + function toggle() { + setMode(isDark.value ? 'light' : 'dark'); + } + + watch(isDark, applyTheme, { immediate: true }); + watch(prefersDark, () => { + if (mode.value === 'system') applyTheme(); + }); + + return { + mode, + isDark, + setMode, + toggle, + }; +}); diff --git a/webui/src/pages/index.vue b/webui/src/pages/index.vue index 53f6f88e..96670d1b 100644 --- a/webui/src/pages/index.vue +++ b/webui/src/pages/index.vue @@ -7,18 +7,22 @@ definePage({ diff --git a/webui/src/components/setting/config-download.vue b/webui/src/components/setting/config-download.vue index 927d4968..0267cb44 100644 --- a/webui/src/components/setting/config-download.vue +++ b/webui/src/components/setting/config-download.vue @@ -65,7 +65,7 @@ const items: SettingItem[] = [