mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
feat: add multiple color themes to appearance settings
This commit is contained in:
@@ -1,47 +1,275 @@
|
||||
//
|
||||
// Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com)
|
||||
//
|
||||
// This file is part of the Bichon Email Archiving Project
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useEffect } from 'react'
|
||||
import { Moon, Sun, Check } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { IconMoon, IconSun } from '@tabler/icons-react'
|
||||
import { useTheme } from '@/context/theme-context'
|
||||
import { Theme, useTheme } from '@/context/theme-context'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const themeColors: Record<Theme, string> = {
|
||||
'light': '#ffffff',
|
||||
'dark': '#020817',
|
||||
'rose-light': '#fb7185',
|
||||
'rose-dark': '#881337',
|
||||
'orange-light': '#fb923c',
|
||||
'orange-dark': '#9a3412',
|
||||
'green-light': '#4ade80',
|
||||
'green-dark': '#166534',
|
||||
'yellow-light': '#facc15',
|
||||
'yellow-dark': '#a16207',
|
||||
'blue-light': '#60a5fa',
|
||||
'blue-dark': '#1e40af',
|
||||
}
|
||||
|
||||
const LIGHT_THEMES = [
|
||||
{
|
||||
value: 'light',
|
||||
labelKey: 'theme.default',
|
||||
background: '#ffffff',
|
||||
color: '#0f172a',
|
||||
muted: '#cbd5e1',
|
||||
},
|
||||
{
|
||||
value: 'rose-light',
|
||||
labelKey: 'theme.rose',
|
||||
background: '#fff1f2',
|
||||
color: '#e11d48',
|
||||
muted: '#fda4af',
|
||||
},
|
||||
{
|
||||
value: 'orange-light',
|
||||
labelKey: 'theme.orange',
|
||||
background: '#fff7ed',
|
||||
color: '#ea580c',
|
||||
muted: '#fdba74',
|
||||
},
|
||||
{
|
||||
value: 'green-light',
|
||||
labelKey: 'theme.green',
|
||||
background: '#f0fdf4',
|
||||
color: '#16a34a',
|
||||
muted: '#86efac',
|
||||
},
|
||||
{
|
||||
value: 'yellow-light',
|
||||
labelKey: 'theme.yellow',
|
||||
background: '#fefce8',
|
||||
color: '#ca8a04',
|
||||
muted: '#fde047',
|
||||
},
|
||||
{
|
||||
value: 'blue-light',
|
||||
labelKey: 'theme.blue',
|
||||
background: '#eff6ff',
|
||||
color: '#2563eb',
|
||||
muted: '#bfdbfe',
|
||||
},
|
||||
] as const
|
||||
|
||||
const DARK_THEMES = [
|
||||
{
|
||||
value: 'dark',
|
||||
labelKey: 'theme.default',
|
||||
background: '#020817',
|
||||
color: '#94a3b8',
|
||||
muted: '#334155',
|
||||
},
|
||||
{
|
||||
value: 'rose-dark',
|
||||
labelKey: 'theme.rose',
|
||||
background: '#1f1115',
|
||||
color: '#e11d48',
|
||||
muted: '#881337',
|
||||
},
|
||||
{
|
||||
value: 'orange-dark',
|
||||
labelKey: 'theme.orange',
|
||||
background: '#1c120d',
|
||||
color: '#f97316',
|
||||
muted: '#9a3412',
|
||||
},
|
||||
{
|
||||
value: 'green-dark',
|
||||
labelKey: 'theme.green',
|
||||
background: '#0d1b12',
|
||||
color: '#22c55e',
|
||||
muted: '#166534',
|
||||
},
|
||||
{
|
||||
value: 'yellow-dark',
|
||||
labelKey: 'theme.yellow',
|
||||
background: '#1c1608',
|
||||
color: '#facc15',
|
||||
muted: '#a16207',
|
||||
},
|
||||
{
|
||||
value: 'blue-dark',
|
||||
labelKey: 'theme.blue',
|
||||
background: '#172554',
|
||||
color: '#60a5fa',
|
||||
muted: '#1e3a8a',
|
||||
}
|
||||
] as const
|
||||
|
||||
type ThemeOption =
|
||||
| (typeof LIGHT_THEMES)[number]
|
||||
| (typeof DARK_THEMES)[number]
|
||||
|
||||
function ThemePreview({
|
||||
background,
|
||||
primary,
|
||||
muted,
|
||||
}: {
|
||||
background: string
|
||||
primary: string
|
||||
muted: string
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className='flex h-10 w-20 shrink-0 flex-col gap-1.5 rounded-md border p-1.5'
|
||||
style={{
|
||||
backgroundColor: background,
|
||||
borderColor: muted,
|
||||
}}
|
||||
>
|
||||
<div className='flex gap-1'>
|
||||
<span className='h-1 w-1 rounded-full' style={{ backgroundColor: muted }} />
|
||||
<span className='h-1 w-1 rounded-full' style={{ backgroundColor: muted }} />
|
||||
<span className='h-1 w-1 rounded-full' style={{ backgroundColor: primary }} />
|
||||
</div>
|
||||
<div className='h-1.5 w-12 rounded-sm' style={{ backgroundColor: primary }} />
|
||||
<div className='h-1 w-16 rounded-sm opacity-70' style={{ backgroundColor: muted }} />
|
||||
<div className='h-1 w-10 rounded-sm opacity-50' style={{ backgroundColor: muted }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ThemeItem({
|
||||
item,
|
||||
active,
|
||||
onSelect,
|
||||
}: {
|
||||
item: ThemeOption
|
||||
active: boolean
|
||||
onSelect: (v: Theme) => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => onSelect(item.value as Theme)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-lg px-2 py-1.5 transition-colors',
|
||||
active ? 'bg-accent' : 'hover:bg-accent/50'
|
||||
)}
|
||||
>
|
||||
<ThemePreview
|
||||
background={item.background}
|
||||
primary={item.color}
|
||||
muted={item.muted}
|
||||
/>
|
||||
|
||||
<span className='flex-1 text-left text-xs font-medium'>
|
||||
{t(item.labelKey)}
|
||||
</span>
|
||||
|
||||
<Check
|
||||
className={cn(
|
||||
'size-4 shrink-0 transition-opacity',
|
||||
active ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function ThemeColumn({
|
||||
icon,
|
||||
label,
|
||||
items,
|
||||
currentTheme,
|
||||
onSelect,
|
||||
}: {
|
||||
icon: React.ReactNode
|
||||
label: string
|
||||
items: readonly ThemeOption[]
|
||||
currentTheme: Theme
|
||||
onSelect: (v: Theme) => void
|
||||
}) {
|
||||
return (
|
||||
<div className='space-y-1'>
|
||||
<div className='mb-2 flex items-center gap-1.5 px-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/60'>
|
||||
{icon}
|
||||
{label}
|
||||
</div>
|
||||
|
||||
{items.map((item) => (
|
||||
<ThemeItem
|
||||
key={item.value}
|
||||
item={item}
|
||||
active={currentTheme === item.value}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ThemeSwitch() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isDark = theme.includes('dark')
|
||||
|
||||
useEffect(() => {
|
||||
const themeColor = theme === 'dark' ? '#020817' : '#fff'
|
||||
const metaThemeColor = document.querySelector("meta[name='theme-color']")
|
||||
if (metaThemeColor) metaThemeColor.setAttribute('content', themeColor)
|
||||
}, [theme])
|
||||
|
||||
const toggleTheme = useCallback(() => {
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark')
|
||||
document
|
||||
.querySelector("meta[name='theme-color']")
|
||||
?.setAttribute('content', themeColors[theme])
|
||||
}, [theme])
|
||||
|
||||
return (
|
||||
<Button variant='ghost' size='icon' className='scale-95' onClick={toggleTheme}>
|
||||
<IconSun className='size-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0' />
|
||||
<IconMoon className='absolute size-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100' />
|
||||
<span className='sr-only'>Toggle theme</span>
|
||||
</Button>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant='ghost' size='icon' className='rounded-full'>
|
||||
{isDark ? (
|
||||
<Moon className='size-5' />
|
||||
) : (
|
||||
<Sun className='size-5' />
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent align='end' className='w-[420px] p-3'>
|
||||
<p className='mb-3 px-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/60'>
|
||||
{t('theme.appearance')}
|
||||
</p>
|
||||
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<ThemeColumn
|
||||
icon={<Sun className='size-3.5' />}
|
||||
label={t('theme.light')}
|
||||
items={LIGHT_THEMES}
|
||||
currentTheme={theme}
|
||||
onSelect={setTheme}
|
||||
/>
|
||||
|
||||
<div className='border-l border-border/40 pl-3'>
|
||||
<ThemeColumn
|
||||
icon={<Moon className='size-3.5' />}
|
||||
label={t('theme.dark')}
|
||||
items={DARK_THEMES}
|
||||
currentTheme={theme}
|
||||
onSelect={setTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
type Theme = 'dark' | 'light'
|
||||
export type Theme = 'light' | 'dark' | 'rose-light' | 'rose-dark' | 'orange-light'
|
||||
| 'orange-dark' | 'green-light' | 'green-dark' | 'yellow-light' | 'yellow-dark' | 'blue-light' | 'blue-dark'
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode
|
||||
@@ -41,7 +42,7 @@ const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
defaultTheme = 'dark',
|
||||
defaultTheme = 'light',
|
||||
storageKey = 'vite-ui-theme',
|
||||
...props
|
||||
}: ThemeProviderProps) {
|
||||
@@ -51,7 +52,8 @@ export function ThemeProvider({
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement
|
||||
root.classList.remove('light', 'dark')
|
||||
root.classList.remove('light', 'dark', 'rose-light', 'rose-dark', 'orange-light',
|
||||
'orange-dark', 'green-light', 'green-dark', 'yellow-light', 'yellow-dark', 'blue-light', 'blue-dark')
|
||||
root.classList.add(theme)
|
||||
localStorage.setItem(storageKey, theme)
|
||||
}, [theme, storageKey])
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
//
|
||||
// Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com)
|
||||
//
|
||||
// This file is part of the Bichon Email Archiving Project
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import { z } from 'zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { CaretSortIcon, CheckIcon } from '@radix-ui/react-icons'
|
||||
@@ -32,8 +13,8 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||
import { useTheme } from '@/context/theme-context'
|
||||
import { RadioGroup } from '@/components/ui/radio-group'
|
||||
import { useTheme, Theme } from '@/context/theme-context'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -42,7 +23,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { update_user } from '@/api/users/api'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
import { AxiosError } from 'axios'
|
||||
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const languages = [
|
||||
{ value: 'ar', label: 'العربية' },
|
||||
@@ -65,8 +46,28 @@ const languages = [
|
||||
{ value: 'zh-tw', label: '繁體中文' },
|
||||
]
|
||||
|
||||
const LIGHT_THEMES = [
|
||||
{ value: 'light', labelKey: 'theme.default', background: '#ffffff', color: '#0f172a', muted: '#cbd5e1' },
|
||||
{ value: 'rose-light', labelKey: 'theme.rose', background: '#fff1f2', color: '#e11d48', muted: '#fda4af' },
|
||||
{ value: 'orange-light', labelKey: 'theme.orange', background: '#fff7ed', color: '#ea580c', muted: '#fdba74' },
|
||||
{ value: 'green-light', labelKey: 'theme.green', background: '#f0fdf4', color: '#16a34a', muted: '#86efac' },
|
||||
{ value: 'yellow-light', labelKey: 'theme.yellow', background: '#fefce8', color: '#ca8a04', muted: '#fde047' },
|
||||
{ value: 'blue-light', labelKey: 'theme.blue', background: '#eff6ff', color: '#2563eb', muted: '#bfdbfe' },
|
||||
] as const
|
||||
|
||||
const DARK_THEMES = [
|
||||
{ value: 'dark', labelKey: 'theme.default', background: '#020817', color: '#94a3b8', muted: '#334155' },
|
||||
{ value: 'rose-dark', labelKey: 'theme.rose', background: '#1f1115', color: '#e11d48', muted: '#881337' },
|
||||
{ value: 'orange-dark', labelKey: 'theme.orange', background: '#1c120d', color: '#f97316', muted: '#9a3412' },
|
||||
{ value: 'green-dark', labelKey: 'theme.green', background: '#0d1b12', color: '#22c55e', muted: '#166534' },
|
||||
{ value: 'yellow-dark', labelKey: 'theme.yellow', background: '#1c1608', color: '#facc15', muted: '#a16207' },
|
||||
{ value: 'blue-dark', labelKey: 'theme.blue', background: '#172554', color: '#60a5fa', muted: '#1e3a8a' }
|
||||
] as const
|
||||
|
||||
const ALL_THEME_VALUES = [...LIGHT_THEMES.map(t => t.value), ...DARK_THEMES.map(t => t.value)] as [string, ...string[]]
|
||||
|
||||
const appearanceSchema = (t: (key: string) => string) => z.object({
|
||||
theme: z.enum(['light', 'dark'], {
|
||||
theme: z.enum(ALL_THEME_VALUES, {
|
||||
required_error: t('settings.appearance.validation.theme.required'),
|
||||
}),
|
||||
language: z.string({
|
||||
@@ -76,19 +77,30 @@ const appearanceSchema = (t: (key: string) => string) => z.object({
|
||||
|
||||
type AppearanceFormValues = z.infer<ReturnType<typeof appearanceSchema>>
|
||||
|
||||
function ThemePreview({ background, primary, muted }: { background: string; primary: string; muted: string }) {
|
||||
return (
|
||||
<div className='flex h-12 w-full flex-col gap-1.5 rounded-md border p-2 shadow-sm' style={{ backgroundColor: background, borderColor: 'transparent' }}>
|
||||
<div className='flex gap-1'>
|
||||
<span className='h-1.5 w-1.5 rounded-full' style={{ backgroundColor: muted }} />
|
||||
<span className='h-1.5 w-1.5 rounded-full' style={{ backgroundColor: primary }} />
|
||||
</div>
|
||||
<div className='h-1.5 w-3/4 rounded-sm' style={{ backgroundColor: primary }} />
|
||||
<div className='h-1.5 w-1/2 rounded-sm opacity-50' style={{ backgroundColor: muted }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AppearanceForm() {
|
||||
const { data: user } = useCurrentUser();
|
||||
const queryClient = useQueryClient();
|
||||
const { t, i18n } = useTranslation();
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
const { data: user } = useCurrentUser()
|
||||
const queryClient = useQueryClient()
|
||||
const { t, i18n } = useTranslation()
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
const form = useForm<AppearanceFormValues>({
|
||||
resolver: zodResolver(appearanceSchema(t)),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
theme: (theme as 'light' | 'dark') || 'light',
|
||||
theme: (theme as any) || 'light',
|
||||
language: i18n.language || 'en',
|
||||
},
|
||||
})
|
||||
@@ -108,21 +120,18 @@ export function AppearanceForm() {
|
||||
description: (err.response?.data as any)?.message || err.message,
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
function onSubmit(data: AppearanceFormValues) {
|
||||
i18n.changeLanguage(data.language);
|
||||
setTheme(data.theme);
|
||||
mutation.mutate(data);
|
||||
i18n.changeLanguage(data.language)
|
||||
setTheme(data.theme as Theme)
|
||||
mutation.mutate(data)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-6xl ml-0 px-4">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-8 w-full max-w-screen-xl mx-auto px-4 md:px-6"
|
||||
>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-10 w-full max-w-screen-xl mx-auto px-4 md:px-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='language'
|
||||
@@ -182,66 +191,61 @@ export function AppearanceForm() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='theme'
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>{t('settings.appearance.field.theme')}</FormLabel>
|
||||
<FormDescription>
|
||||
{t('settings.appearance.description.theme')}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
<FormItem className="space-y-4">
|
||||
<div>
|
||||
<FormLabel className="text-base">{t('settings.appearance.field.theme')}</FormLabel>
|
||||
<FormDescription>{t('settings.appearance.description.theme')}</FormDescription>
|
||||
</div>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
onValueChange={(v) => {
|
||||
setTheme(v as Theme);
|
||||
field.onChange(v)
|
||||
}}
|
||||
defaultValue={field.value}
|
||||
className='grid max-w-md grid-cols-2 gap-8 pt-2'
|
||||
className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4 pt-2'
|
||||
>
|
||||
<FormItem>
|
||||
<FormLabel className='[&:has([data-state=checked])>div]:border-primary cursor-pointer'>
|
||||
<FormControl>
|
||||
<RadioGroupItem value='light' className='sr-only' />
|
||||
</FormControl>
|
||||
<div className='items-center rounded-md border-2 border-muted p-1 hover:border-accent'>
|
||||
<div className='space-y-2 rounded-sm bg-[#ecedef] p-2'>
|
||||
<div className='space-y-2 rounded-md bg-white p-2 shadow-sm'>
|
||||
<div className='h-2 w-[80px] rounded-lg bg-[#ecedef]' />
|
||||
<div className='h-2 w-[100px] rounded-lg bg-[#ecedef]' />
|
||||
</div>
|
||||
<div className='flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm'>
|
||||
<div className='h-4 w-4 rounded-full bg-[#ecedef]' />
|
||||
<div className='h-2 w-[100px] rounded-lg bg-[#ecedef]' />
|
||||
{[...LIGHT_THEMES, ...DARK_THEMES].map((item) => {
|
||||
const selected = field.value === item.value
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={item.value}
|
||||
onClick={() => {
|
||||
setTheme(item.value as Theme)
|
||||
field.onChange(item.value)
|
||||
}}
|
||||
className={cn(
|
||||
"cursor-pointer overflow-hidden border-2 transition-all hover:shadow-md",
|
||||
selected ? "border-primary shadow-md" : "border-muted opacity-80"
|
||||
)}
|
||||
>
|
||||
<div className="p-2">
|
||||
<ThemePreview
|
||||
background={item.background}
|
||||
primary={item.color}
|
||||
muted={item.muted}
|
||||
/>
|
||||
|
||||
<div className="mt-2 flex items-center justify-between">
|
||||
<span className="text-[11px] font-medium uppercase truncate">
|
||||
{t(item.labelKey)}
|
||||
</span>
|
||||
|
||||
{selected && (
|
||||
<CheckIcon className="h-3.5 w-3.5 text-primary" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className='block w-full p-2 text-center font-normal'>
|
||||
{t('settings.appearance.theme.light')}
|
||||
</span>
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<FormLabel className='[&:has([data-state=checked])>div]:border-primary cursor-pointer'>
|
||||
<FormControl>
|
||||
<RadioGroupItem value='dark' className='sr-only' />
|
||||
</FormControl>
|
||||
<div className='items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground'>
|
||||
<div className='space-y-2 rounded-sm bg-slate-950 p-2'>
|
||||
<div className='space-y-2 rounded-md bg-slate-800 p-2 shadow-sm'>
|
||||
<div className='h-2 w-[80px] rounded-lg bg-slate-400' />
|
||||
<div className='h-2 w-[100px] rounded-lg bg-slate-400' />
|
||||
</div>
|
||||
<div className='flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm'>
|
||||
<div className='h-4 w-4 rounded-full bg-slate-400' />
|
||||
<div className='h-2 w-[100px] rounded-lg bg-slate-400' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className='block w-full p-2 text-center font-normal'>
|
||||
{t('settings.appearance.theme.dark')}
|
||||
</span>
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</RadioGroup>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
+2
-91
@@ -1,101 +1,12 @@
|
||||
@import "./theme.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 215 28% 17%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 215 28% 17%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 215 28% 17%;
|
||||
|
||||
--border: 214 32% 88%;
|
||||
--input: 214 32% 88%;
|
||||
|
||||
--primary: 221 83% 53%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 221 83% 53%;
|
||||
--radius: 0.35rem;
|
||||
|
||||
--chart-1: 221 83% 53%;
|
||||
--chart-2: 200 90% 40%;
|
||||
--chart-3: 35 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
|
||||
--sidebar-background: 210 40% 98%;
|
||||
--sidebar-foreground: 215 28% 17%;
|
||||
--sidebar-primary: 221 83% 53%;
|
||||
--sidebar-primary-foreground: 210 40% 98%;
|
||||
--sidebar-accent: 214 32% 91%;
|
||||
--sidebar-accent-foreground: 215 28% 17%;
|
||||
--sidebar-border: 214 32% 88%;
|
||||
--sidebar-ring: 221 83% 53%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 215 20% 12%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 215 20% 16%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 215 20% 16%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
|
||||
--muted: 215 20% 22%;
|
||||
--muted-foreground: 215 15% 70%;
|
||||
|
||||
--border: 215 20% 28%;
|
||||
--input: 215 20% 28%;
|
||||
|
||||
--primary: 217 90% 70%;
|
||||
--primary-foreground: 215 20% 12%;
|
||||
|
||||
--secondary: 215 20% 22%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--accent: 215 20% 22%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 70% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 217 90% 70%;
|
||||
|
||||
--chart-1: 217 90% 70%;
|
||||
--chart-2: 200 90% 60%;
|
||||
--chart-3: 35 90% 65%;
|
||||
--chart-4: 280 75% 75%;
|
||||
--chart-5: 340 85% 70%;
|
||||
|
||||
--sidebar-background: 215 20% 10%;
|
||||
--sidebar-foreground: 210 40% 98%;
|
||||
--sidebar-primary: 217 90% 70%;
|
||||
--sidebar-primary-foreground: 215 20% 12%;
|
||||
--sidebar-accent: 215 20% 22%;
|
||||
--sidebar-accent-foreground: 210 40% 98%;
|
||||
--sidebar-border: 215 20% 28%;
|
||||
--sidebar-ring: 217 90% 70%;
|
||||
}
|
||||
|
||||
|
||||
/* styles.css */
|
||||
.CollapsibleContent {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
/* theme.css */
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 215 28% 17%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 215 28% 17%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 215 28% 17%;
|
||||
|
||||
--border: 214 32% 88%;
|
||||
--input: 214 32% 88%;
|
||||
|
||||
--primary: 221 83% 53%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 221 83% 53%;
|
||||
--radius: 0.35rem;
|
||||
|
||||
--chart-1: 221 83% 53%;
|
||||
--chart-2: 200 90% 40%;
|
||||
--chart-3: 35 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 215 20% 12%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 215 20% 16%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 215 20% 16%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
|
||||
--muted: 215 20% 22%;
|
||||
--muted-foreground: 215 15% 70%;
|
||||
|
||||
--border: 215 20% 28%;
|
||||
--input: 215 20% 28%;
|
||||
|
||||
--primary: 217 90% 70%;
|
||||
--primary-foreground: 215 20% 12%;
|
||||
|
||||
--secondary: 215 20% 22%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--accent: 215 20% 22%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 70% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 217 90% 70%;
|
||||
|
||||
--chart-1: 217 90% 70%;
|
||||
--chart-2: 200 90% 60%;
|
||||
--chart-3: 35 90% 65%;
|
||||
--chart-4: 280 75% 75%;
|
||||
--chart-5: 340 85% 70%;
|
||||
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.yellow-light {
|
||||
--radius: 0.5rem;
|
||||
--background: 180 0% 100%;
|
||||
--foreground: 49 5% 15%;
|
||||
--muted: 106 10% 97%;
|
||||
--muted-foreground: 58 5% 55%;
|
||||
--popover: 180 0% 100%;
|
||||
--popover-foreground: 49 5% 15%;
|
||||
--card: 180 0% 100%;
|
||||
--card-foreground: 49 5% 15%;
|
||||
--border: 49 3% 92%;
|
||||
--input: 49 3% 92%;
|
||||
--primary: 48 100% 76%;
|
||||
--primary-foreground: 54 20% 28%;
|
||||
--secondary: 106 10% 97%;
|
||||
--secondary-foreground: 56 10% 22%;
|
||||
--accent: 106 10% 97%;
|
||||
--accent-foreground: 56 10% 22%;
|
||||
--destructive: 2 65% 55%;
|
||||
--destructive-foreground: 106 10% 98%;
|
||||
--ring: 49 5% 15%;
|
||||
--chart-1: 15 65% 55%;
|
||||
--chart-2: 183 45% 50%;
|
||||
--chart-3: 225 35% 35%;
|
||||
--chart-4: 88 55% 75%;
|
||||
--chart-5: 45 60% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.yellow-dark {
|
||||
--radius: 0.5rem;
|
||||
--background: 49 10% 20%;
|
||||
--foreground: 106 10% 98%;
|
||||
--muted: 38 10% 25%;
|
||||
--muted-foreground: 56 10% 74%;
|
||||
--popover: 49 10% 23%;
|
||||
--popover-foreground: 106 10% 98%;
|
||||
--card: 49 10% 23%;
|
||||
--card-foreground: 106 10% 98%;
|
||||
--border: 36 10% 30%;
|
||||
--input: 36 10% 30%;
|
||||
--primary: 48 100% 76%;
|
||||
--primary-foreground: 54 20% 28%;
|
||||
--secondary: 38 10% 25%;
|
||||
--secondary-foreground: 106 10% 98%;
|
||||
--accent: 38 10% 25%;
|
||||
--accent-foreground: 106 10% 98%;
|
||||
--destructive: 2 60% 45%;
|
||||
--destructive-foreground: 106 10% 98%;
|
||||
--ring: 66 65% 60%;
|
||||
--chart-1: 262 55% 65%;
|
||||
--chart-2: 165 40% 70%;
|
||||
--chart-3: 60 50% 75%;
|
||||
--chart-4: 312 50% 65%;
|
||||
--chart-5: 6 55% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.rose-light {
|
||||
--radius: 0.5rem;
|
||||
--background: 180 0% 100%;
|
||||
--foreground: 285 5% 15%;
|
||||
--muted: 286 1% 97%;
|
||||
--muted-foreground: 286 7% 55%;
|
||||
--popover: 180 0% 100%;
|
||||
--popover-foreground: 285 5% 15%;
|
||||
--card: 180 0% 100%;
|
||||
--card-foreground: 285 5% 15%;
|
||||
--border: 286 3% 92%;
|
||||
--input: 286 3% 92%;
|
||||
--primary: 350 55% 45%;
|
||||
--primary-foreground: 355 25% 97%;
|
||||
--secondary: 286 1% 97%;
|
||||
--secondary-foreground: 286 5% 21%;
|
||||
--accent: 286 1% 97%;
|
||||
--accent-foreground: 286 5% 21%;
|
||||
--destructive: 2 65% 55%;
|
||||
--destructive-foreground: 180 0% 98.5%;
|
||||
--ring: 350 55% 45%;
|
||||
--chart-1: 15 65% 55%;
|
||||
--chart-2: 183 45% 50%;
|
||||
--chart-3: 225 35% 35%;
|
||||
--chart-4: 88 55% 75%;
|
||||
--chart-5: 45 60% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.rose-dark {
|
||||
--radius: 0.5rem;
|
||||
--background: 49 5% 20.5%;
|
||||
--foreground: 180 0% 96%;
|
||||
--muted: 168 5% 31%;
|
||||
--muted-foreground: 286 7% 75%;
|
||||
--popover: 180 3% 24%;
|
||||
--popover-foreground: 180 0% 96%;
|
||||
--card: 56 6% 25%;
|
||||
--card-foreground: 180 0% 96%;
|
||||
--border: 286 5% 33%;
|
||||
--input: 286 5% 33%;
|
||||
--primary: 350 55% 45%;
|
||||
--primary-foreground: 355 25% 97%;
|
||||
--secondary: 286 5% 31%;
|
||||
--secondary-foreground: 180 0% 98.5%;
|
||||
--accent: 34 5% 31%;
|
||||
--accent-foreground: 180 0% 98.5%;
|
||||
--destructive: 2 60% 45%;
|
||||
--destructive-foreground: 350 50% 97%;
|
||||
--ring: 350 60% 50%;
|
||||
--chart-1: 262 55% 65%;
|
||||
--chart-2: 165 40% 70%;
|
||||
--chart-3: 60 50% 75%;
|
||||
--chart-4: 312 50% 65%;
|
||||
--chart-5: 6 55% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.orange-light {
|
||||
--radius: 0.5rem;
|
||||
--background: 180 0% 100%;
|
||||
--foreground: 49 5% 15%;
|
||||
--muted: 106 1% 97%;
|
||||
--muted-foreground: 58 5% 55%;
|
||||
--popover: 180 0% 100%;
|
||||
--popover-foreground: 49 5% 15%;
|
||||
--card: 180 0% 100%;
|
||||
--card-foreground: 49 5% 15%;
|
||||
--border: 49 3% 92%;
|
||||
--input: 49 3% 92%;
|
||||
--primary: 25 85% 58%;
|
||||
--primary-foreground: 106 1% 98.5%;
|
||||
--secondary: 106 1% 97%;
|
||||
--secondary-foreground: 56 10% 22%;
|
||||
--accent: 106 1% 97%;
|
||||
--accent-foreground: 56 10% 22%;
|
||||
--destructive: 2 65% 55%;
|
||||
--destructive-foreground: 106 1% 98.5%;
|
||||
--ring: 25 85% 58%;
|
||||
--chart-1: 15 65% 55%;
|
||||
--chart-2: 183 45% 50%;
|
||||
--chart-3: 225 35% 35%;
|
||||
--chart-4: 88 55% 75%;
|
||||
--chart-5: 45 60% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.orange-dark {
|
||||
--radius: 0.5rem;
|
||||
--background: 49 10% 20.5%;
|
||||
--foreground: 106 1% 98.5%;
|
||||
--muted: 34 10% 29%;
|
||||
--muted-foreground: 56 10% 74%;
|
||||
--popover: 49 10% 23.5%;
|
||||
--popover-foreground: 106 1% 98.5%;
|
||||
--card: 49 10% 23.5%;
|
||||
--card-foreground: 106 1% 98.5%;
|
||||
--border: 34 10% 32%;
|
||||
--input: 34 10% 32%;
|
||||
--primary: 22 80% 55%;
|
||||
--primary-foreground: 106 1% 98.5%;
|
||||
--secondary: 34 10% 29%;
|
||||
--secondary-foreground: 106 1% 98.5%;
|
||||
--accent: 34 10% 29%;
|
||||
--accent-foreground: 106 1% 98.5%;
|
||||
--destructive: 5 65% 55%;
|
||||
--destructive-foreground: 106 1% 98.5%;
|
||||
--ring: 22 75% 62%;
|
||||
--chart-1: 262 55% 65%;
|
||||
--chart-2: 165 40% 70%;
|
||||
--chart-3: 60 50% 75%;
|
||||
--chart-4: 312 50% 65%;
|
||||
--chart-5: 6 55% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.green-light {
|
||||
--radius: 0.5rem;
|
||||
--background: 180 0% 100%;
|
||||
--foreground: 285 5% 14%;
|
||||
--muted: 286 1% 97%;
|
||||
--muted-foreground: 286 7% 55%;
|
||||
--popover: 180 0% 100%;
|
||||
--popover-foreground: 285 5% 14%;
|
||||
--card: 180 0% 100%;
|
||||
--card-foreground: 285 5% 14%;
|
||||
--border: 286 3% 92%;
|
||||
--input: 286 3% 92%;
|
||||
--primary: 149 55% 48%;
|
||||
--primary-foreground: 12 25% 97%;
|
||||
--secondary: 286 1% 97%;
|
||||
--secondary-foreground: 286 5% 21%;
|
||||
--accent: 286 1% 97%;
|
||||
--accent-foreground: 286 5% 21%;
|
||||
--destructive: 2 65% 55%;
|
||||
--destructive-foreground: 180 0% 98.5%;
|
||||
--ring: 149 55% 48%;
|
||||
--chart-1: 15 65% 55%;
|
||||
--chart-2: 183 45% 50%;
|
||||
--chart-3: 225 35% 35%;
|
||||
--chart-4: 88 55% 75%;
|
||||
--chart-5: 45 60% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.green-dark {
|
||||
--radius: 0.5rem;
|
||||
--background: 49 10% 20.5%;
|
||||
--foreground: 180 0% 96%;
|
||||
--muted: 168 5% 31%;
|
||||
--muted-foreground: 286 7% 75%;
|
||||
--popover: 180 3% 24%;
|
||||
--popover-foreground: 180 0% 96%;
|
||||
--card: 56 6% 25%;
|
||||
--card-foreground: 180 0% 96%;
|
||||
--border: 286 5% 33%;
|
||||
--input: 286 5% 33%;
|
||||
--primary: 150 65% 65%;
|
||||
--primary-foreground: 153 35% 20%;
|
||||
--secondary: 286 5% 33%;
|
||||
--secondary-foreground: 180 0% 98.5%;
|
||||
--accent: 34 5% 31%;
|
||||
--accent-foreground: 180 0% 98.5%;
|
||||
--destructive: 2 60% 45%;
|
||||
--destructive-foreground: 17 50% 97%;
|
||||
--ring: 150 55% 55%;
|
||||
--chart-1: 262 55% 65%;
|
||||
--chart-2: 165 40% 70%;
|
||||
--chart-3: 60 50% 75%;
|
||||
--chart-4: 312 50% 65%;
|
||||
--chart-5: 6 55% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.blue-light {
|
||||
--radius: 0.5rem;
|
||||
--background: 180 0% 100%;
|
||||
--foreground: 222 47% 11%;
|
||||
--muted: 210 20% 96%;
|
||||
--muted-foreground: 215 25% 40%;
|
||||
--popover: 180 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--card: 180 0% 100%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--border: 214 32% 91%;
|
||||
--input: 214 32% 91%;
|
||||
--primary: 221 83% 53%; /* 经典的 Shadcn 蓝色 */
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--accent: 210 40% 96%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--ring: 221 83% 53%;
|
||||
--chart-1: 221 83% 53%;
|
||||
--chart-2: 199 89% 48%;
|
||||
--chart-3: 225 35% 35%;
|
||||
--chart-4: 201 96% 32%;
|
||||
--chart-5: 190 90% 40%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
.blue-dark {
|
||||
--radius: 0.5rem;
|
||||
--background: 222 47% 11%;
|
||||
--foreground: 213 31% 91%;
|
||||
--muted: 223 47% 11%;
|
||||
--muted-foreground: 215 20% 65%;
|
||||
--popover: 222 47% 11%;
|
||||
--popover-foreground: 213 31% 91%;
|
||||
--card: 222 47% 11%;
|
||||
--card-foreground: 213 31% 91%;
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
--primary: 217 91% 60%;
|
||||
--primary-foreground: 222 47% 11%;
|
||||
--secondary: 222 47% 11%;
|
||||
--secondary-foreground: 213 31% 91%;
|
||||
--accent: 216 34% 17%;
|
||||
--accent-foreground: 213 31% 91%;
|
||||
--destructive: 0 63% 31%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--ring: 224 76% 48%;
|
||||
--chart-1: 221 83% 53%;
|
||||
--chart-2: 165 40% 70%;
|
||||
--chart-3: 60 50% 75%;
|
||||
--chart-4: 210 50% 65%;
|
||||
--chart-5: 200 55% 65%;
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user