feat: Establish core application layout, global styles, and theme mode management.

This commit is contained in:
supalerk-ar66 2026-01-26 14:03:56 +07:00
parent a2ce1d79a2
commit 0e095b35c5
4 changed files with 79 additions and 30 deletions

View file

@ -1113,3 +1113,33 @@ ul {
color: var(--text-secondary);
margin-bottom: 24px;
}
/* =========================
Discovery page: Quasar dark-mode overrides (scoped)
ไม่กระทบหน้าอื่น เพราะล็อคด้วย .discovery-page
========================= */
/* Dark mode: input background + border */
html.dark .discovery-page .input-dark-override .q-field__control {
background-color: #0f172a !important; /* slate-900 */
border-color: rgba(255, 255, 255, 0.10) !important;
}
/* Dark mode: input text + icons */
html.dark .discovery-page .input-dark-override .q-field__native,
html.dark .discovery-page .input-dark-override .q-field__input,
html.dark .discovery-page .input-dark-override .q-field__prefix,
html.dark .discovery-page .input-dark-override .q-field__suffix,
html.dark .discovery-page .input-dark-override .q-select__dropdown-icon,
html.dark .discovery-page .input-dark-override .q-field__append .q-icon {
color: #ffffff !important;
}
/* Dark mode: placeholder */
html.dark .discovery-page .input-dark-override .q-placeholder {
color: #94a3b8 !important; /* slate-400 */
}
/* Dark mode: category label ให้ชัดขึ้น (แก้กรณี class ไม่ติด) */
html.dark .discovery-page .category-item .q-item__label {
color: #e2e8f0 !important; /* slate-200 */
}

View file

@ -12,36 +12,13 @@ const { currentUser, logout } = useAuth()
const { t } = useI18n()
const $q = useQuasar()
const isDarkMode = ref(false)
// Use centralized theme management
const { isDark, set } = useThemeMode()
const isHydrated = ref(false)
const applyTheme = (value: boolean) => {
// Tailwind dark mode
document.documentElement.classList.toggle('dark', value)
// Quasar dark mode
$q.dark.set(value)
// persist
localStorage.setItem('theme', value ? 'dark' : 'light')
}
const toggleDarkMode = (val: boolean) => {
isDarkMode.value = val
applyTheme(val)
}
// Sync Dark Mode state on mount
onMounted(() => {
isHydrated.value = true
const savedTheme = localStorage.getItem('theme')
const preferredDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false
isDarkMode.value = savedTheme
? savedTheme === 'dark'
: preferredDark
applyTheme(isDarkMode.value)
})
// User Initials
@ -88,19 +65,19 @@ const handleLogout = async () => {
class="hover:bg-slate-100 dark:hover:bg-white/10 transition-colors"
>
<q-item-section>
<q-item-label class="font-bold text-sm">{{ item.label }}</q-item-label>
<q-item-label class="font-bold text-sm text-slate-800 dark:text-white">{{ item.label }}</q-item-label>
</q-item-section>
</q-item>
<q-item class="hover:bg-slate-100 dark:hover:bg-white/10 transition-colors">
<q-item-section>
<q-item-label class="font-bold text-sm">{{ $t('userMenu.darkMode') }}</q-item-label>
<q-item-label class="font-bold text-sm text-slate-800 dark:text-white">{{ $t('userMenu.darkMode') }}</q-item-label>
</q-item-section>
<q-item-section side>
<q-toggle
v-if="isHydrated"
:model-value="isDarkMode"
@update:model-value="toggleDarkMode"
:model-value="isDark"
@update:model-value="set"
color="blue"
keep-color
/>

View file

@ -0,0 +1,39 @@
import { useQuasar } from 'quasar'
export const useThemeMode = () => {
const $q = useQuasar()
// deterministic on SSR: default = light
const isDark = useState<boolean>('theme:isDark', () => false)
const applyTheme = (value: boolean) => {
if (!process.client) return
document.documentElement.classList.toggle('dark', value)
$q.dark.set(value)
localStorage.setItem('theme', value ? 'dark' : 'light')
}
const initTheme = () => {
if (!process.client) return
const saved = localStorage.getItem('theme') // 'dark' | 'light' | null
const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false
const value = saved ? saved === 'dark' : prefersDark
isDark.value = value
applyTheme(value)
}
onMounted(initTheme)
watch(isDark, (v) => applyTheme(v))
const toggle = () => {
isDark.value = !isDark.value
}
const set = (v: boolean) => {
isDark.value = v
}
return { isDark, toggle, set }
}

View file

@ -10,6 +10,9 @@ const leftDrawerOpen = ref(false)
const toggleLeftDrawer = () => {
leftDrawerOpen.value = !leftDrawerOpen.value
}
// Initialize global theme management
useThemeMode()
</script>
<template>