163 lines
5.8 KiB
Vue
163 lines
5.8 KiB
Vue
<script setup lang="ts">
|
|
const colorMode = useColorMode()
|
|
const isDark = computed({
|
|
get () {
|
|
return colorMode.value === 'dark'
|
|
},
|
|
set () {
|
|
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
|
}
|
|
})
|
|
|
|
const themeOptions = [
|
|
{ id: 'light', label: 'Light', description: 'Clean and bright interface' },
|
|
{ id: 'dark', label: 'Dark', description: 'Easy on the eyes in low light' },
|
|
]
|
|
|
|
const themeGradients: Record<string, string> = {
|
|
light: 'from-sky-100 via-blue-50 to-indigo-100',
|
|
dark: 'from-slate-700 via-slate-800 to-slate-900',
|
|
}
|
|
|
|
const themeIcons: Record<string, string> = {
|
|
light: 'i-heroicons-sun',
|
|
dark: 'i-heroicons-moon'
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-6">
|
|
<div>
|
|
<p class="text-sm leading-relaxed text-[var(--text-muted)]">
|
|
Choose a theme for the entire application. Colors, inputs, buttons, cards, sidebar, and top bar all follow your choice.
|
|
Your preference is saved to this browser.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Theme grid -->
|
|
<div class="grid gap-4 sm:grid-cols-2">
|
|
<button
|
|
v-for="opt in themeOptions"
|
|
:key="opt.id"
|
|
type="button"
|
|
class="app-theme-card text-left"
|
|
:class="colorMode === opt.id ? 'app-theme-card-selected' : ''"
|
|
@click="colorMode.preference = opt.id"
|
|
>
|
|
<!-- Header -->
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div class="flex items-center gap-2.5">
|
|
<div
|
|
class="flex h-8 w-8 items-center justify-center rounded-lg"
|
|
:class="colorMode === opt.id ? 'bg-[var(--brand-soft)] text-[var(--brand)]' : 'bg-[var(--badge-muted-bg)] text-[var(--text-muted)]'"
|
|
>
|
|
<UIcon :name="themeIcons[opt.id]" class="h-4 w-4" />
|
|
</div>
|
|
<div>
|
|
<p class="font-semibold text-[var(--text-primary)]">{{ opt.label }}</p>
|
|
<p class="mt-0.5 text-xs text-[var(--text-muted)]">{{ opt.description }}</p>
|
|
</div>
|
|
</div>
|
|
<Transition
|
|
enter-active-class="transition duration-200 ease-out"
|
|
enter-from-class="opacity-0 scale-75"
|
|
enter-to-class="opacity-100 scale-100"
|
|
leave-active-class="transition duration-150 ease-in"
|
|
leave-from-class="opacity-100"
|
|
leave-to-class="opacity-0 scale-75"
|
|
>
|
|
<UIcon
|
|
v-if="colorMode === opt.id"
|
|
name="i-heroicons-check-circle-solid"
|
|
class="h-6 w-6 shrink-0 text-[var(--brand)]"
|
|
/>
|
|
</Transition>
|
|
</div>
|
|
|
|
<!-- Gradient preview bar -->
|
|
<div
|
|
class="mt-4 h-8 overflow-hidden rounded-lg bg-gradient-to-r"
|
|
:class="themeGradients[opt.id]"
|
|
/>
|
|
|
|
<!-- Component preview (scoped to this theme) -->
|
|
<div
|
|
class="mt-3 rounded-lg border border-[var(--sidebar-border)] bg-[var(--page-bg)] p-3"
|
|
:class="opt.id === 'dark' ? 'dark' : ''"
|
|
>
|
|
<div class="space-y-3">
|
|
<!-- Buttons row -->
|
|
<div>
|
|
<p class="mb-2 text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)]">Buttons</p>
|
|
<div class="flex flex-wrap gap-1.5">
|
|
<UButton size="xs" color="primary">Primary</UButton>
|
|
<UButton size="xs" color="primary" variant="soft">Soft</UButton>
|
|
<UButton size="xs" color="primary" variant="outline">Outline</UButton>
|
|
<UButton size="xs" color="neutral" variant="ghost">Ghost</UButton>
|
|
</div>
|
|
</div>
|
|
<!-- Input preview -->
|
|
<div>
|
|
<p class="mb-2 text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)]">Input</p>
|
|
<div class="max-w-[200px]">
|
|
<UInput size="xs" placeholder="Search policies..." icon="i-heroicons-magnifying-glass" disabled />
|
|
</div>
|
|
</div>
|
|
<!-- Badges row -->
|
|
<div>
|
|
<p class="mb-2 text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)]">Badges</p>
|
|
<div class="flex flex-wrap gap-1.5">
|
|
<UBadge color="primary" variant="soft" size="xs">Active</UBadge>
|
|
<UBadge color="success" variant="soft" size="xs">Bound</UBadge>
|
|
<UBadge color="warning" variant="soft" size="xs">Pending</UBadge>
|
|
<UBadge color="error" variant="soft" size="xs">Overdue</UBadge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Extra info -->
|
|
<div class="rounded-xl border border-[var(--card-border)] bg-[var(--surface)] p-4 shadow-sm">
|
|
<div class="flex gap-3">
|
|
<UIcon name="i-heroicons-information-circle" class="mt-0.5 h-5 w-5 shrink-0 text-[var(--brand)]" />
|
|
<div class="text-sm text-[var(--text-muted)]">
|
|
<p>
|
|
Theme applies instantly to all pages including sidebar navigation, cards, inputs, buttons, badges, and KPI panels.
|
|
You can also quickly switch themes from the
|
|
<UIcon name="i-heroicons-sun" class="inline h-3.5 w-3.5" />
|
|
/
|
|
<UIcon name="i-heroicons-moon" class="inline h-3.5 w-3.5" />
|
|
icon in the top bar.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.app-theme-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
padding: 1rem;
|
|
border-radius: 0.75rem;
|
|
border: 1px solid var(--card-border);
|
|
background: var(--surface);
|
|
transition: all 0.15s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.app-theme-card:hover {
|
|
border-color: rgba(1, 105, 111, 0.2);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.app-theme-card-selected {
|
|
border-color: var(--brand);
|
|
background: rgba(1, 105, 111, 0.02);
|
|
}
|
|
</style>
|