Add theme selection, dark/light mode selection
This commit is contained in:
@ -1,11 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { Search } from 'lucide-vue-next';
|
||||
import ModeToggle from './ModeToggle.vue';
|
||||
import ThemeSelector from './ThemeSelector.vue';
|
||||
|
||||
const colorMode = useColorMode()
|
||||
const logoPath = computed(() => {
|
||||
return colorMode.value === 'dark' ? 'logo-dark.png' : 'logo.png'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-center w-full p-4 gap-8 border-b">
|
||||
<div>
|
||||
<NuxtImg src="logo.jpg" width="48" height="48" />
|
||||
<ClientOnly>
|
||||
<NuxtImg :src="logoPath" width="48" height="48" />
|
||||
</ClientOnly>
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="ghost">
|
||||
@ -18,13 +27,15 @@ import { Search } from 'lucide-vue-next';
|
||||
Forum
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex gap-4">
|
||||
<InputGroup>
|
||||
<InputGroupInput placeholder="Search..." />
|
||||
<InputGroupAddon>
|
||||
<Search />
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
<ModeToggle />
|
||||
<ThemeSelector />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
62
app/components/ui/internal/HeroSection.vue
Normal file
62
app/components/ui/internal/HeroSection.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center gap-4 mt-32">
|
||||
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight text-balance">
|
||||
The next-generation anime platform
|
||||
</h1>
|
||||
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight text-center max-w-2/5">
|
||||
Track, share, and discover your favorite anime and manga with Anyame
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="grid grid-cols-2 gap-16">
|
||||
<div class="flex max-w-96 gap-8">
|
||||
<NuxtImg src="discover-icon.svg" width="96" height="96" />
|
||||
<div>
|
||||
<h4 class="scroll-m-20 text-xl font-semibold tracking-tight">
|
||||
Discover your obsessions
|
||||
</h4>
|
||||
<p class="font-semibold text-sm text-muted-foreground">
|
||||
What are your highest rated genres or most watched voice actors?
|
||||
Follow your watching habits over time with in-depth statistics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex max-w-96 gap-8">
|
||||
<NuxtImg src="anywhere-icon.svg" width="96" height="96" />
|
||||
<div>
|
||||
<h4 class="scroll-m-20 text-xl font-semibold tracking-tight">
|
||||
Bring Anyame anywhere
|
||||
</h4>
|
||||
<p class="font-semibold text-sm text-muted-foreground">
|
||||
Keep track of your progress on-the-go with one of many Anyame apps across iOS, Android,
|
||||
macOS, and Windows.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex max-w-96 gap-8">
|
||||
<NuxtImg src="conversation-icon.svg" width="96" height="96" />
|
||||
<div>
|
||||
<h4 class="scroll-m-20 text-xl font-semibold tracking-tight">
|
||||
Join the conversation
|
||||
</h4>
|
||||
<p class="font-semibold text-sm text-muted-foreground">
|
||||
Share your thoughts with our thriving community, make friends, socialize, and receive
|
||||
recommendations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex max-w-96 gap-8">
|
||||
<NuxtImg src="tweak-icon.svg" width="96" height="96" />
|
||||
<div>
|
||||
<h4 class="scroll-m-20 text-xl font-semibold tracking-tight">
|
||||
Tweak it to your liking
|
||||
</h4>
|
||||
<p class="font-semibold text-sm text-muted-foreground">
|
||||
Customize your scoring system, title format, color scheme, and much more! Also, we have a
|
||||
dark mode.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
22
app/components/ui/internal/ModeToggle.vue
Normal file
22
app/components/ui/internal/ModeToggle.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Moon, Sun } from 'lucide-vue-next';
|
||||
|
||||
const colorMode = useColorMode()
|
||||
|
||||
function toggleMode() {
|
||||
if (colorMode.preference === 'system' || colorMode.preference === 'light') {
|
||||
colorMode.preference = 'dark'
|
||||
} else {
|
||||
colorMode.preference = 'light'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button variant="outline" @click="toggleMode">
|
||||
<Moon class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Sun class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</template>
|
||||
149
app/components/ui/internal/ThemeSelector.vue
Normal file
149
app/components/ui/internal/ThemeSelector.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<Select v-model="selectedPreset" @update:model-value="handleThemeChange">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="Select theme..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Themes</SelectLabel>
|
||||
|
||||
<!-- Theme Categories -->
|
||||
<SelectLabel class="pl-8 text-xs font-normal text-muted-foreground">
|
||||
Classic
|
||||
</SelectLabel>
|
||||
<SelectItem v-for="theme in classicThemes" :key="theme" :value="theme">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-3 w-3 rounded-full" :class="themeColor(theme)" />
|
||||
<span>{{ formatThemeName(theme) }}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
|
||||
<SelectLabel class="pl-8 text-xs font-normal text-muted-foreground mt-2">
|
||||
Vibrant
|
||||
</SelectLabel>
|
||||
<SelectItem v-for="theme in vibrantThemes" :key="theme" :value="theme">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-3 w-3 rounded-full" :class="themeColor(theme)" />
|
||||
<span>{{ formatThemeName(theme) }}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
|
||||
<SelectLabel class="pl-8 text-xs font-normal text-muted-foreground mt-2">
|
||||
Special
|
||||
</SelectLabel>
|
||||
<SelectItem v-for="theme in specialThemes" :key="theme" :value="theme">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-3 w-3 rounded-full" :class="themeColor(theme)" />
|
||||
<span>{{ formatThemeName(theme) }}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import type { ThemePreset } from '@/composables/useTheme'
|
||||
import type { AcceptableValue } from 'reka-ui'
|
||||
|
||||
const { themePreset, setThemePreset } = useTheme()
|
||||
|
||||
const selectedPreset = ref<ThemePreset>(themePreset.value)
|
||||
|
||||
const classicThemes: ThemePreset[] = [
|
||||
'default', 'modern', 'nature', 'mocha', 'graphite',
|
||||
'notebook', 'ocean', 'pastel', 'retro', 'sage',
|
||||
'vintage', 'mono'
|
||||
]
|
||||
|
||||
const vibrantThemes: ThemePreset[] = [
|
||||
'cyberpunk', 'claymorphism', 'bold-tech', 'bubblegum',
|
||||
'candyland', 'neo', 'quantum', 'softpop', 'solardusk',
|
||||
'starry', 'sunset', 'tangerine', 'violet', 'amber',
|
||||
'amethyst'
|
||||
]
|
||||
|
||||
const specialThemes: ThemePreset[] = [
|
||||
'darkmatter', 'cleanslate', 'eleganyluxury', 'kodama',
|
||||
'midnight', 'catpuccin', 'claude', 'cosmicnight', 'doom64',
|
||||
'caffeine', 'northern', 'perpetuity', 'supabase', 't3chat',
|
||||
'twitter', 'vercel'
|
||||
]
|
||||
|
||||
const handleThemeChange = (value: AcceptableValue) => {
|
||||
console.log(value);
|
||||
setThemePreset(value as ThemePreset)
|
||||
}
|
||||
|
||||
watch(() => themePreset.value, (newPreset) => {
|
||||
selectedPreset.value = newPreset
|
||||
})
|
||||
|
||||
const formatThemeName = (theme: string) => {
|
||||
return theme
|
||||
.replace(/-/g, ' ')
|
||||
.replace(/\b\w/g, l => l.toUpperCase())
|
||||
}
|
||||
|
||||
const themeColor = (theme: ThemePreset) => {
|
||||
const colors: Record<string, string> = {
|
||||
'default': 'bg-blue-500',
|
||||
'modern': 'bg-slate-600',
|
||||
'mono': 'bg-zinc-600',
|
||||
'cleanslate': 'bg-slate-300',
|
||||
'notebook': 'bg-amber-100 border border-amber-300',
|
||||
'vintage': 'bg-amber-800',
|
||||
'graphite': 'bg-gray-700',
|
||||
|
||||
'darkmatter': 'bg-gray-900',
|
||||
'midnight': 'bg-blue-900',
|
||||
'cosmicnight': 'bg-indigo-950',
|
||||
'doom64': 'bg-purple-900',
|
||||
|
||||
'cyberpunk': 'bg-pink-500',
|
||||
'bold-tech': 'bg-rose-600',
|
||||
'neo': 'bg-emerald-400',
|
||||
'quantum': 'bg-teal-500',
|
||||
|
||||
'nature': 'bg-green-600',
|
||||
'sage': 'bg-emerald-600',
|
||||
'northern': 'bg-sky-700',
|
||||
'ocean': 'bg-cyan-500',
|
||||
|
||||
'claymorphism': 'bg-orange-300',
|
||||
'softpop': 'bg-fuchsia-300',
|
||||
|
||||
'eleganyluxury': 'bg-amber-900',
|
||||
'amethyst': 'bg-purple-700',
|
||||
'claude': 'bg-violet-800',
|
||||
|
||||
'kodama': 'bg-lime-400',
|
||||
'catpuccin': 'bg-mauve-500',
|
||||
'bubblegum': 'bg-pink-300',
|
||||
|
||||
'mocha': 'bg-amber-800',
|
||||
'caffeine': 'bg-brown-700',
|
||||
'tangerine': 'bg-orange-500',
|
||||
'candyland': 'bg-rose-400',
|
||||
|
||||
'sunset': 'bg-orange-500',
|
||||
'solardusk': 'bg-red-700',
|
||||
'starry': 'bg-indigo-700',
|
||||
'perpetuity': 'bg-cyan-700',
|
||||
|
||||
'pastel': 'bg-pink-200',
|
||||
'retro': 'bg-yellow-400',
|
||||
|
||||
'supabase': 'bg-green-400',
|
||||
'twitter': 'bg-sky-500',
|
||||
'vercel': 'bg-black',
|
||||
't3chat': 'bg-blue-600',
|
||||
|
||||
'amber': 'bg-amber-500',
|
||||
'violet': 'bg-violet-600',
|
||||
}
|
||||
|
||||
return colors[theme] || 'bg-gray-400'
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user