diff --git a/k-notes-frontend/index.html b/k-notes-frontend/index.html index 7d5cc58..21fc5e6 100644 --- a/k-notes-frontend/index.html +++ b/k-notes-frontend/index.html @@ -4,7 +4,7 @@ - + K-Notes - Modern Note-Taking App | Organize Your Ideas diff --git a/k-notes-frontend/src/aero-theme.css b/k-notes-frontend/src/aero-theme.css new file mode 100644 index 0000000..157f0a2 --- /dev/null +++ b/k-notes-frontend/src/aero-theme.css @@ -0,0 +1,335 @@ +/* + * Frutiger Aero Theme for shadcn + Tailwind v4 + * Drop this file + a background image into any shadcn project. + * Import it in your index.css: @import "./aero-theme.css"; + */ + +/* ── Aero color overrides ─────────────────────────────────────── */ + +:root { + --background: transparent; + --foreground: oklch(0.985 0 0); + --card: rgba(255, 255, 255, 0.08); + --card-foreground: oklch(0.985 0 0); + --popover: rgba(20, 20, 30, 0.85); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.852 0.199 91.936); + --primary-foreground: #fff; + --secondary: rgba(255, 255, 255, 0.08); + --secondary-foreground: oklch(0.985 0 0); + --muted: rgba(255, 255, 255, 0.06); + --muted-foreground: oklch(1 0 0); + --accent: rgba(255, 255, 255, 0.1); + --accent-foreground: oklch(0.985 0 0); + --destructive: rgba(200, 60, 60, 0.65); + --border: rgba(255, 255, 255, 0.15); + --input: rgba(255, 255, 255, 0.1); + --ring: oklch(0.852 0.199 91.936 / 0.4); + --chart-1: oklch(0.852 0.199 91.936); + --chart-2: oklch(0.89 0.13 91.936); + --chart-3: oklch(0.93 0.07 91.936); + --chart-4: rgba(255, 255, 255, 0.4); + --chart-5: rgba(255, 255, 255, 0.2); + --sidebar: rgba(0, 0, 0, 0.4); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.852 0.199 91.936); + --sidebar-primary-foreground: #fff; + --sidebar-accent: rgba(255, 255, 255, 0.1); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: rgba(255, 255, 255, 0.1); + --sidebar-ring: oklch(0.852 0.199 91.936 / 0.4); + + --aero-primary: oklch(0.852 0.199 91.936); + --aero-primary-mid: oklch(0.89 0.13 91.936); + --aero-primary-light: oklch(0.93 0.07 91.936); + --aero-primary-glow: oklch(0.852 0.199 91.936 / 0.3); + --aero-glass-bg: rgba(255, 255, 255, 0.12); + --aero-glass-border: rgba(255, 255, 255, 0.2); + --aero-glass-shadow: 0 8px 32px oklch(0.852 0.199 91.936 / 0.1); + --aero-glass-inset: inset 0 1px 0 rgba(255, 255, 255, 0.4); + --aero-blur: 12px; +} + +/* Force dark — override any .dark block from shadcn defaults */ +.dark { + --background: transparent; + --foreground: oklch(0.985 0 0); + --card: rgba(255, 255, 255, 0.08); + --card-foreground: oklch(0.985 0 0); + --popover: rgba(20, 20, 30, 0.85); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.852 0.199 91.936); + --primary-foreground: #fff; + --secondary: rgba(255, 255, 255, 0.08); + --secondary-foreground: oklch(0.985 0 0); + --muted: rgba(255, 255, 255, 0.06); + --muted-foreground: oklch(1 0 0); + --accent: rgba(255, 255, 255, 0.1); + --accent-foreground: oklch(0.985 0 0); + --destructive: rgba(200, 60, 60, 0.65); + --border: rgba(255, 255, 255, 0.15); + --input: rgba(255, 255, 255, 0.1); + --ring: oklch(0.852 0.199 91.936 / 0.4); + --chart-1: oklch(0.852 0.199 91.936); + --chart-2: oklch(0.89 0.13 91.936); + --chart-3: oklch(0.93 0.07 91.936); + --chart-4: rgba(255, 255, 255, 0.4); + --chart-5: rgba(255, 255, 255, 0.2); + --sidebar: rgba(0, 0, 0, 0.4); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.852 0.199 91.936); + --sidebar-primary-foreground: #fff; + --sidebar-accent: rgba(255, 255, 255, 0.1); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: rgba(255, 255, 255, 0.1); + --sidebar-ring: oklch(0.852 0.199 91.936 / 0.4); +} + +/* ── Background treatment ─────────────────────────────────────── */ + +body { + background: url("./assets/background2.webp") center / cover no-repeat fixed; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); +} + +body::before { + content: ""; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.2); + z-index: 0; + pointer-events: none; +} + +body > #root { + position: relative; + z-index: 1; +} + +/* ── Utility classes ──────────────────────────────────────────── */ + +@utility glass { + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + background: var(--aero-glass-bg); + border: 1px solid var(--aero-glass-border); +} + +@utility glass-card { + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + background: var(--aero-glass-bg); + border: 1px solid var(--aero-glass-border); + box-shadow: var(--aero-glass-shadow), var(--aero-glass-inset); + position: relative; + overflow: hidden; +} + +@utility glass-heavy { + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + background: rgba(0, 0, 0, 0.55); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +@utility aero-glow { + text-shadow: 0 0 8px var(--aero-primary-glow), 0 0 2px var(--aero-primary); +} + +@utility aero-pill { + border-radius: 999px; + border: 1px solid var(--aero-glass-border); + background: transparent; +} + +/* ── Glass card gradient overlay ──────────────────────────────── */ + +.glass-card::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent); + border-radius: inherit; + pointer-events: none; + z-index: 1; +} + +/* ── Hover lift (pointer devices only) ────────────────────────── */ + +@media (hover: hover) { + .aero-lift { + transition: transform 0.2s, box-shadow 0.2s; + } + .aero-lift:hover { + transform: translateY(-2px); + box-shadow: 0 12px 40px var(--aero-primary-glow), var(--aero-glass-inset); + } +} + +/* ── shadcn data-slot overrides ───────────────────────────────── */ + +[data-slot="card"] { + background: var(--aero-glass-bg); + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + border-color: var(--aero-glass-border); + box-shadow: var(--aero-glass-shadow), var(--aero-glass-inset); + --tw-ring-color: transparent; + --tw-ring-shadow: none; +} + +[data-slot="dialog-overlay"] { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +[data-slot="dialog-content"] { + background: rgba(20, 20, 30, 0.8); + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + border: 1px solid rgba(255, 255, 255, 0.15); + --tw-ring-color: transparent; + --tw-ring-shadow: none; +} + +[data-slot="sheet-overlay"] { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +[data-slot="sheet-content"] { + background: rgba(20, 20, 30, 0.85); + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + border-color: rgba(255, 255, 255, 0.15); +} + +[data-slot="input"] { + background: rgba(255, 255, 255, 0.08); + border-color: var(--aero-glass-border); +} + +[data-slot="input"]:focus { + border-color: var(--aero-primary); + box-shadow: 0 0 0 3px oklch(0.852 0.199 91.936 / 0.2); +} + +/* Primary button — gold gradient with Aero bevel */ +[data-slot="button"][data-variant="default"] { + background: linear-gradient( + 135deg, + var(--aero-primary-mid) 0%, + var(--aero-primary) 60%, + oklch(0.72 0.199 91.936) 100% + ); + color: #fff; + border: none; + box-shadow: 0 4px 16px var(--aero-primary-glow), + inset 0 1px 0 rgba(255, 255, 255, 0.35); + position: relative; + overflow: hidden; +} + +[data-slot="button"][data-variant="default"]::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 0.25), + transparent + ); + border-radius: inherit; + pointer-events: none; +} + +@media (hover: hover) { + [data-slot="button"][data-variant="default"]:hover { + transform: translateY(-1px); + box-shadow: 0 6px 24px var(--aero-primary-glow), + inset 0 1px 0 rgba(255, 255, 255, 0.35); + } +} + +/* Aqua dome pill — stronger vertical gradient + taller shine for pill-shaped buttons */ +[data-slot="button"].aero-aqua-btn { + background: linear-gradient( + to bottom, + oklch(0.93 0.13 91.936) 0%, + oklch(0.852 0.199 91.936) 50%, + oklch(0.68 0.199 91.936) 100% + ); + box-shadow: 0 5px 20px var(--aero-primary-glow), + inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +[data-slot="button"].aero-aqua-btn::after { + height: 58%; + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 0.55), + transparent + ); +} + +@media (hover: hover) { + [data-slot="button"].aero-aqua-btn:hover { + box-shadow: 0 7px 28px var(--aero-primary-glow), + inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + } +} + +/* Destructive button — glassmorphic red */ +[data-slot="button"][data-variant="destructive"] { + background: rgba(200, 60, 60, 0.65); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + border: 1px solid rgba(220, 80, 80, 0.3); +} + +/* Ghost/outline buttons — subtle glass on hover */ +[data-slot="button"][data-variant="ghost"]:hover, +[data-slot="button"][data-variant="outline"]:hover { + background: rgba(255, 255, 255, 0.1); +} + +/* Drawer content (used by log-sheet, wrapup, etc.) */ +[data-slot="drawer-content"] { + background: rgba(20, 20, 30, 0.9); + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); +} + +[data-slot="drawer-overlay"] { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +/* Sonner toast */ +[data-sonner-toaster] [data-sonner-toast] { + background: rgba(20, 20, 30, 0.85); + backdrop-filter: blur(var(--aero-blur)); + -webkit-backdrop-filter: blur(var(--aero-blur)); + border-color: rgba(255, 255, 255, 0.15); + color: oklch(0.985 0 0); +} + +/* Chart label readability */ +[data-slot="chart"] .recharts-cartesian-axis-tick text { + fill: rgba(255, 255, 255, 0.85) !important; +} + +/* Star glow for filled amber stars */ +.aero-star-filled { + filter: drop-shadow(0 0 4px var(--aero-primary-glow)) + drop-shadow(0 0 1px var(--aero-primary)); +} diff --git a/k-notes-frontend/src/assets/background.avif b/k-notes-frontend/src/assets/background.avif new file mode 100644 index 0000000..4913844 Binary files /dev/null and b/k-notes-frontend/src/assets/background.avif differ diff --git a/k-notes-frontend/src/assets/background.webp b/k-notes-frontend/src/assets/background.webp new file mode 100644 index 0000000..9e699cd Binary files /dev/null and b/k-notes-frontend/src/assets/background.webp differ diff --git a/k-notes-frontend/src/assets/background2.webp b/k-notes-frontend/src/assets/background2.webp new file mode 100644 index 0000000..8d11707 Binary files /dev/null and b/k-notes-frontend/src/assets/background2.webp differ diff --git a/k-notes-frontend/src/components/bulk-actions-bar.tsx b/k-notes-frontend/src/components/bulk-actions-bar.tsx index d788388..b974a7f 100644 --- a/k-notes-frontend/src/components/bulk-actions-bar.tsx +++ b/k-notes-frontend/src/components/bulk-actions-bar.tsx @@ -34,7 +34,7 @@ export function BulkActionsBar() { }; return ( -
+
{t("{{count}} selected", { count: selectedIds.size })} @@ -55,7 +55,7 @@ export function BulkActionsBar() { variant="ghost" size="sm" onClick={handleDeleteAll} - className="gap-2 text-destructive hover:text-destructive hover:bg-destructive/10" + className="gap-2 text-red-400 hover:text-red-300 hover:bg-red-500/20" > {t("Delete")} diff --git a/k-notes-frontend/src/components/create-note-dialog.tsx b/k-notes-frontend/src/components/create-note-dialog.tsx index 857aeb4..d918695 100644 --- a/k-notes-frontend/src/components/create-note-dialog.tsx +++ b/k-notes-frontend/src/components/create-note-dialog.tsx @@ -41,7 +41,7 @@ export function CreateNoteDialog({ trigger, open: controlledOpen, onOpenChange } }; const defaultTrigger = ( - diff --git a/k-notes-frontend/src/components/note-card.tsx b/k-notes-frontend/src/components/note-card.tsx index fa644c0..6e5a027 100644 --- a/k-notes-frontend/src/components/note-card.tsx +++ b/k-notes-frontend/src/components/note-card.tsx @@ -92,17 +92,18 @@ export function NoteCard({ note }: NoteCardProps) { }); } - const colorClass = getNoteColor(note.color); + const { glass, borderClass } = getNoteColor(note.color); return ( <> !isBulkMode && setViewOpen(true)} > {/* Bulk selection checkbox */} diff --git a/k-notes-frontend/src/components/note-form.tsx b/k-notes-frontend/src/components/note-form.tsx index dc171b8..1a3dbe7 100644 --- a/k-notes-frontend/src/components/note-form.tsx +++ b/k-notes-frontend/src/components/note-form.tsx @@ -100,11 +100,12 @@ export function NoteForm({ defaultValues, onSubmit, isLoading, submitLabel = "Sa
field.onChange(color.name)} - className={`w-8 h-8 rounded-full cursor-pointer border-2 transition-all ${color.value.split(" ")[0] // Take background class - } ${field.value === color.name + style={{ background: color.swatch }} + className={`w-8 h-8 rounded-full cursor-pointer border-2 transition-all ${ + field.value === color.name ? "border-primary scale-110" : "border-transparent hover:scale-105" - }`} + }`} title={color.label} /> ))} diff --git a/k-notes-frontend/src/components/providers.tsx b/k-notes-frontend/src/components/providers.tsx index 5fb25ab..215a772 100644 --- a/k-notes-frontend/src/components/providers.tsx +++ b/k-notes-frontend/src/components/providers.tsx @@ -15,7 +15,7 @@ const queryClient = new QueryClient({ export function Providers({ children }: { children: ReactNode }) { return ( - + {children} diff --git a/k-notes-frontend/src/index.css b/k-notes-frontend/src/index.css index 6bbd33d..4aad54f 100644 --- a/k-notes-frontend/src/index.css +++ b/k-notes-frontend/src/index.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @import "tw-animate-css"; @plugin "@tailwindcss/typography"; +@import "./aero-theme.css"; @custom-variant dark (&:is(.dark *)); @@ -50,71 +51,6 @@ :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); } @layer base { diff --git a/k-notes-frontend/src/lib/constants.ts b/k-notes-frontend/src/lib/constants.ts index 80fc2b6..b30ccd0 100644 --- a/k-notes-frontend/src/lib/constants.ts +++ b/k-notes-frontend/src/lib/constants.ts @@ -1,17 +1,19 @@ const NOTE_COLORS = [ - { name: "DEFAULT", value: "bg-background border-border", label: "Default" }, - { name: "RED", value: "bg-red-50 border-red-200 dark:bg-red-950 dark:border-red-900", label: "Red" }, - { name: "ORANGE", value: "bg-orange-50 border-orange-200 dark:bg-orange-950 dark:border-orange-900", label: "Orange" }, - { name: "YELLOW", value: "bg-yellow-50 border-yellow-200 dark:bg-yellow-950 dark:border-yellow-900", label: "Yellow" }, - { name: "GREEN", value: "bg-green-50 border-green-200 dark:bg-green-950 dark:border-green-900", label: "Green" }, - { name: "TEAL", value: "bg-teal-50 border-teal-200 dark:bg-teal-950 dark:border-teal-900", label: "Teal" }, - { name: "BLUE", value: "bg-blue-50 border-blue-200 dark:bg-blue-950 dark:border-blue-900", label: "Blue" }, - { name: "INDIGO", value: "bg-indigo-50 border-indigo-200 dark:bg-indigo-950 dark:border-indigo-900", label: "Indigo" }, + { name: "DEFAULT", label: "Default", glass: null, borderClass: "", swatch: "rgba(255,255,255,0.25)" }, + { name: "RED", label: "Red", glass: "rgba(239, 68, 68, 0.22)", borderClass: "border-red-400/50", swatch: "rgb(239, 68, 68)" }, + { name: "ORANGE", label: "Orange", glass: "rgba(249, 115, 22, 0.22)", borderClass: "border-orange-400/50", swatch: "rgb(249, 115, 22)" }, + { name: "YELLOW", label: "Yellow", glass: "rgba(234, 179, 8, 0.22)", borderClass: "border-yellow-400/50", swatch: "rgb(234, 179, 8)" }, + { name: "GREEN", label: "Green", glass: "rgba(34, 197, 94, 0.22)", borderClass: "border-green-400/50", swatch: "rgb(34, 197, 94)" }, + { name: "TEAL", label: "Teal", glass: "rgba(20, 184, 166, 0.22)", borderClass: "border-teal-400/50", swatch: "rgb(20, 184, 166)" }, + { name: "BLUE", label: "Blue", glass: "rgba(59, 130, 246, 0.22)", borderClass: "border-blue-400/50", swatch: "rgb(59, 130, 246)" }, + { name: "INDIGO", label: "Indigo", glass: "rgba(99, 102, 241, 0.22)", borderClass: "border-indigo-400/50", swatch: "rgb(99, 102, 241)" }, ]; -export function getNoteColor(colorName: string | undefined): string { +export function getNoteColor(colorName: string | undefined): { glass: string | null; borderClass: string } { const color = NOTE_COLORS.find(c => c.name === colorName); - return color ? color.value : NOTE_COLORS[0].value; + return color + ? { glass: color.glass, borderClass: color.borderClass } + : { glass: null, borderClass: "" }; } export { NOTE_COLORS }; diff --git a/k-notes-frontend/src/pages/register.tsx b/k-notes-frontend/src/pages/register.tsx index a5f35d0..23be79b 100644 --- a/k-notes-frontend/src/pages/register.tsx +++ b/k-notes-frontend/src/pages/register.tsx @@ -33,6 +33,11 @@ export default function RegisterPage() { const { t } = useTranslation(); const [settingsOpen, setSettingsOpen] = useState(false); + const form = useForm({ + resolver: zodResolver(registerSchema), + defaultValues: { email: "", password: "", confirmPassword: "" }, + }); + useEffect(() => { if (!isConfigLoading && config?.allow_registration === false) { toast.error(t("Registration is currently disabled")); @@ -44,11 +49,6 @@ export default function RegisterPage() { return null; } - const form = useForm({ - resolver: zodResolver(registerSchema), - defaultValues: { email: "", password: "", confirmPassword: "" }, - }); - const onSubmit = (data: RegisterFormValues) => { register({ email: data.email, password: data.password }, { onError: (error: any) => {