diff --git a/public/fonts/font_index.ts b/public/fonts/font_index.ts index 91ce81b..c88370e 100644 --- a/public/fonts/font_index.ts +++ b/public/fonts/font_index.ts @@ -7,6 +7,11 @@ export const america = localFont({ weight: "400", style: "normal", }, + // { + // path: "./america-medium.woff2", + // weight: "500", + // style: "normal", + // }, ], display: "swap", variable: "--font-america", diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 2b83529..b63e7db 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -2,8 +2,8 @@ import { LoginForm } from "@/features/auth/components/login-form"; export default function LoginPage() { return ( -
-
+
+
diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index fcbc345..0d21a44 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -2,8 +2,8 @@ import { SignupForm } from "@/features/auth/components/signup-form"; export default function Page() { return ( -
-
+
+
diff --git a/src/app/(landing-page)/page.tsx b/src/app/(landing-page)/page.tsx index 5ee532a..a1d3371 100644 --- a/src/app/(landing-page)/page.tsx +++ b/src/app/(landing-page)/page.tsx @@ -1,5 +1,4 @@ import { - RiArrowRightSLine, RiBankCard2Line, RiBarChartBoxLine, RiCalendarLine, @@ -211,7 +210,6 @@ export default async function Page() {
diff --git a/src/app/globals.css b/src/app/globals.css index 1d5c829..86e0b68 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -4,53 +4,44 @@ @theme { --spacing-custom-height-card: 30rem; - --spacing-8xl: 88rem; /* 1408px */ - --spacing-9xl: 96rem; /* 1536px */ + --spacing-8xl: 88rem; + --spacing-9xl: 96rem; } :root { - /* Base surfaces - warm cream with subtle orange undertone */ - --background: oklch(98.01% 0.00331 67.026); - --foreground: #201207; + --background: oklch(97.036% 0.00276 84.303); + --foreground: oklch(27% 0.008 45); --card: var(--background); - --card-foreground: #201207; - --popover: oklch(99.5% 0.004 80); - --popover-foreground: oklch(18% 0.02 45); + --card-foreground: var(--foreground); + --popover: oklch(100% 0 0); + --popover-foreground: var(--foreground); - /* Primary - rich terracotta orange */ - --primary: #f17a35; + --primary: oklch(72.085% 0.16286 50.705); --primary-foreground: oklch(98% 0.008 80); - /* Secondary - warm stone with subtle saturation */ - --secondary: oklch(94% 0.018 70); - --secondary-foreground: oklch(25% 0.025 45); + --secondary: oklch(96.2% 0.005 70); + --secondary-foreground: oklch(30% 0.01 45); - /* Muted - softer background variant */ - --muted: oklch(94.5% 0.014 75); - --muted-foreground: #44413c; + --muted: oklch(95% 0.0035 70); + --muted-foreground: oklch(50% 0.007 50); - /* Accent - complementary warm tone */ - --accent: oklch(94% 0.01 70); - --accent-foreground: #44413c; + --accent: oklch(94.8% 0.009 65); + --accent-foreground: var(--foreground); - /* Semantic states */ - --success: oklch(55.87% 0.12943 157.517); + --success: oklch(61.654% 0.14385 157.131); --success-foreground: oklch(98% 0.01 150); --warning: oklch(69.913% 0.1798 49.649); --warning-foreground: oklch(20% 0.04 85); --info: oklch(55% 0.17 250); --info-foreground: oklch(98% 0.01 250); - /* Destructive - accessible red */ --destructive: oklch(55% 0.22 27); --destructive-foreground: oklch(98% 0.005 30); - /* Borders and inputs - defined but subtle */ - --border: oklch(82% 0.012 75); - --input: oklch(82% 0.012 75); - --ring: oklch(69.18% 0.18855 38.353); + --border: oklch(84.567% 0.00583 84.468); + --input: oklch(84.567% 0.00583 84.468); + --ring: var(--primary); - /* Charts - 10 harmonious, distinct, accessible colors */ --chart-1: var(--color-emerald-500); --chart-2: var(--color-orange-500); --chart-3: var(--color-indigo-500); @@ -62,20 +53,17 @@ --chart-9: var(--color-cyan-500); --chart-10: var(--color-lime-500); - /* Sidebar - slight elevation from background */ - --sidebar: oklch(100% 0 0); - --sidebar-foreground: oklch(20% 0.02 45); - --sidebar-primary: oklch(25% 0.025 45); - --sidebar-primary-foreground: oklch(98% 0.008 80); - --sidebar-accent: oklch(96.563% 0.00504 67.275); - --sidebar-accent-foreground: oklch(22% 0.025 45); - --sidebar-border: oklch(69.18% 0.18855 38.353); - --sidebar-ring: oklch(69.18% 0.18855 38.353); + --sidebar: oklch(99.3% 0.0015 75); + --sidebar-foreground: var(--foreground); + --sidebar-primary: var(--primary); + --sidebar-primary-foreground: var(--primary-foreground); + --sidebar-accent: oklch(96.5% 0.004 70); + --sidebar-accent-foreground: var(--foreground); + --sidebar-border: oklch(91% 0.004 70); + --sidebar-ring: var(--primary); - /* Layout */ --radius: 0.5rem; - /* Shadows - warm tinted for cohesion */ --shadow-2xs: 0 1px 2px 0px oklch(35% 0.02 45 / 0.04); --shadow-xs: 0 1px 3px 0px oklch(35% 0.02 45 / 0.06); --shadow-sm: 0 1px 3px 0px oklch(35% 0.02 45 / 0.08), @@ -95,31 +83,25 @@ } .dark { - /* Base surfaces - warm dark with consistent hue family */ - --background: oklch(18.5% 0.002 70); - --foreground: oklch(92% 0.015 80); - --card: var(--background); - --card-foreground: oklch(92% 0.015 80); - --popover: oklch(24% 0.003 70); - --popover-foreground: oklch(92% 0.015 80); + --background: oklch(20.5% 0.004 55); + --foreground: oklch(93% 0.008 80); + --card: oklch(23% 0.004 55); + --card-foreground: var(--foreground); + --popover: oklch(25% 0.004 55); + --popover-foreground: var(--foreground); - /* Primary - vibrant terracotta stands out on dark */ - --primary: #fa6c26; - --primary-foreground: oklch(20% 0.002 70); + --primary: oklch(72.085% 0.16286 50.705); + --primary-foreground: oklch(16% 0.004 60); - /* Secondary - elevated surface */ - --secondary: oklch(22% 0.004 70); - --secondary-foreground: oklch(92% 0.015 80); + --secondary: oklch(26% 0.004 55); + --secondary-foreground: var(--foreground); - /* Muted - subtle surface variant */ - --muted: oklch(33.5% 0.005 70); - --muted-foreground: oklch(72% 0.004 70); + --muted: oklch(28.5% 0.0035 55); + --muted-foreground: oklch(73% 0.006 75); - /* Accent - subtle highlight */ - --accent: oklch(27% 0.004 70); - --accent-foreground: oklch(92% 0.015 80); + --accent: oklch(30% 0.005 55); + --accent-foreground: var(--foreground); - /* Semantic states */ --success: oklch(65% 0.19 150); --success-foreground: oklch(15% 0.02 150); --warning: oklch(69.913% 0.1798 49.649); @@ -127,16 +109,13 @@ --info: oklch(65% 0.17 250); --info-foreground: oklch(15% 0.02 250); - /* Destructive - accessible red for dark */ --destructive: oklch(62% 0.2 28); --destructive-foreground: oklch(98% 0.005 30); - /* Borders and inputs - visible but subtle */ - --border: oklch(37% 0.01 70); - --input: oklch(32% 0.005 70); - --ring: oklch(69.18% 0.18855 38.353); + --border: oklch(33% 0.004 55); + --input: oklch(30% 0.004 55); + --ring: var(--primary); - /* Charts - bright and distinct on dark */ --chart-1: var(--color-emerald-500); --chart-2: var(--color-orange-500); --chart-3: var(--color-indigo-500); @@ -148,20 +127,17 @@ --chart-9: var(--color-cyan-500); --chart-10: var(--color-lime-500); - /* Sidebar - slight separation from main */ - --sidebar: oklch(24% 0.003 70); - --sidebar-foreground: oklch(92% 0.015 80); - --sidebar-primary: oklch(69.18% 0.18855 38.353); - --sidebar-primary-foreground: oklch(13% 0.006 70); - --sidebar-accent: oklch(32% 0.004 70); - --sidebar-accent-foreground: oklch(92% 0.015 80); - --sidebar-border: oklch(26% 0.004 70); - --sidebar-ring: oklch(69.18% 0.18855 38.353); + --sidebar: oklch(18% 0.004 55); + --sidebar-foreground: var(--foreground); + --sidebar-primary: var(--primary); + --sidebar-primary-foreground: var(--primary-foreground); + --sidebar-accent: oklch(27% 0.004 55); + --sidebar-accent-foreground: var(--foreground); + --sidebar-border: oklch(31% 0.004 55); + --sidebar-ring: var(--primary); - /* Layout */ --radius: 0.5rem; - /* Shadows - deeper for dark mode */ --shadow-2xs: 0 1px 2px 0px oklch(0% 0 0 / 0.3); --shadow-xs: 0 1px 3px 0px oklch(0% 0 0 / 0.4); --shadow-sm: 0 1px 3px 0px oklch(0% 0 0 / 0.45), @@ -254,7 +230,6 @@ body { @apply bg-background text-foreground; - font-family: var(--font-america), sans-serif; } *::selection { @@ -283,7 +258,6 @@ mix-blend-mode: normal; } -/* Dialog animations */ @keyframes dialog-in { from { opacity: 0; @@ -332,7 +306,6 @@ animation: dialog-out 0.15s ease-in; } -/* Overdue blink: alternates two stacked labels with a smooth crossfade */ @keyframes blink-in { 0%, 40% { opacity: 1; } 50%, 90% { opacity: 0; } diff --git a/src/features/auth/components/auth-card-shell.tsx b/src/features/auth/components/auth-card-shell.tsx new file mode 100644 index 0000000..aca8613 --- /dev/null +++ b/src/features/auth/components/auth-card-shell.tsx @@ -0,0 +1,27 @@ +import type { PropsWithChildren } from "react"; +import { Card, CardContent } from "@/shared/components/ui/card"; +import { DotPattern } from "@/shared/components/ui/dot-pattern"; +import AuthSidebar from "./auth-sidebar"; + +export function AuthCardShell({ children }: PropsWithChildren) { + return ( + +
+ +
+
+ + +
{children}
+ +
+ + ); +} diff --git a/src/features/auth/components/auth-header.tsx b/src/features/auth/components/auth-header.tsx index 2164f33..a9fbcb3 100644 --- a/src/features/auth/components/auth-header.tsx +++ b/src/features/auth/components/auth-header.tsx @@ -2,14 +2,20 @@ import { cn } from "@/shared/utils/ui"; interface AuthHeaderProps { title: string; + description?: string; } -export function AuthHeader({ title }: AuthHeaderProps) { +export function AuthHeader({ title, description }: AuthHeaderProps) { return ( -
-

+
+

{title}

+ {description ? ( +

+ {description} +

+ ) : null}
); } diff --git a/src/features/auth/components/auth-sidebar.tsx b/src/features/auth/components/auth-sidebar.tsx index d154aaa..55176e4 100644 --- a/src/features/auth/components/auth-sidebar.tsx +++ b/src/features/auth/components/auth-sidebar.tsx @@ -1,12 +1,32 @@ +import { Logo } from "@/shared/components/logo"; +import { DotPattern } from "@/shared/components/ui/dot-pattern"; + function AuthSidebar() { return ( -
-
-
-

+
+
+ +
+
+
+ + +
+

Controle suas finanças com clareza e foco diário.

-

+

Centralize despesas, organize cartões e acompanhe metas mensais em um painel inteligente feito para o seu dia a dia.

diff --git a/src/features/auth/components/login-form.tsx b/src/features/auth/components/login-form.tsx index a55e430..0b33729 100644 --- a/src/features/auth/components/login-form.tsx +++ b/src/features/auth/components/login-form.tsx @@ -3,9 +3,7 @@ import { RiFingerprintLine, RiLoader4Line } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { type FormEvent, useEffect, useState } from "react"; import { toast } from "sonner"; -import { Logo } from "@/shared/components/logo"; import { Button } from "@/shared/components/ui/button"; -import { Card, CardContent } from "@/shared/components/ui/card"; import { Field, FieldDescription, @@ -16,13 +14,16 @@ import { import { Input } from "@/shared/components/ui/input"; import { authClient, googleSignInAvailable } from "@/shared/lib/auth/client"; import { cn } from "@/shared/utils/ui"; +import { AuthCardShell } from "./auth-card-shell"; import { AuthErrorAlert } from "./auth-error-alert"; import { AuthHeader } from "./auth-header"; -import AuthSidebar from "./auth-sidebar"; import { GoogleAuthButton } from "./google-auth-button"; type DivProps = React.ComponentProps<"div">; +const authLinkClassName = + "font-medium text-foreground/88 underline decoration-border underline-offset-4 transition-colors hover:text-foreground hover:decoration-foreground/30 focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"; + export function LoginForm({ className, ...props }: DivProps) { const router = useRouter(); const isGoogleAvailable = googleSignInAvailable; @@ -130,119 +131,117 @@ export function LoginForm({ className, ...props }: DivProps) { } return ( -
- - - -
- - +
+ + + + - + - - E-mail - setEmail(e.target.value)} - aria-invalid={!!error} - /> - + + E-mail + setEmail(e.target.value)} + aria-invalid={!!error} + /> + - -
- Senha -
- setPassword(e.target.value)} - aria-invalid={!!error} - /> -
+ +
+ Senha +
+ setPassword(e.target.value)} + aria-invalid={!!error} + /> +
+ + + + + + Ou continue com + + + + + + + {passkeySupported && ( + )} - - Ou continue com - + + Não tem uma conta?{" "} + + Inscreva-se + + - - - - - {passkeySupported && ( - - - - )} - - - Não tem uma conta?{" "} - - Inscreva-se - - -
- - - - - - - - - Voltar para o site - - + + + Voltar para a página inicial + + + + +
); } diff --git a/src/features/auth/components/signup-form.tsx b/src/features/auth/components/signup-form.tsx index 784fb4b..240ec5a 100644 --- a/src/features/auth/components/signup-form.tsx +++ b/src/features/auth/components/signup-form.tsx @@ -3,9 +3,7 @@ import { RiCheckLine, RiCloseLine, RiLoader4Line } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { type FormEvent, useState } from "react"; import { toast } from "sonner"; -import { Logo } from "@/shared/components/logo"; import { Button } from "@/shared/components/ui/button"; -import { Card, CardContent } from "@/shared/components/ui/card"; import { Field, FieldDescription, @@ -16,9 +14,9 @@ import { import { Input } from "@/shared/components/ui/input"; import { authClient, googleSignInAvailable } from "@/shared/lib/auth/client"; import { cn } from "@/shared/utils/ui"; +import { AuthCardShell } from "./auth-card-shell"; import { AuthErrorAlert } from "./auth-error-alert"; import { AuthHeader } from "./auth-header"; -import AuthSidebar from "./auth-sidebar"; import { GoogleAuthButton } from "./google-auth-button"; interface PasswordValidation { @@ -76,6 +74,9 @@ function PasswordRequirement({ met, label }: { met: boolean; label: string }) { type DivProps = React.ComponentProps<"div">; +const authLinkClassName = + "font-medium text-foreground/88 underline decoration-border underline-offset-4 transition-colors hover:text-foreground hover:decoration-foreground/30 focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"; + export function SignupForm({ className, ...props }: DivProps) { const router = useRouter(); const isGoogleAvailable = googleSignInAvailable; @@ -149,143 +150,141 @@ export function SignupForm({ className, ...props }: DivProps) { } return ( -
- - - -
- - +
+ + + + - + - - Nome completo - setFullname(e.target.value)} - aria-invalid={!!error} - /> - + + Nome completo + setFullname(e.target.value)} + aria-invalid={!!error} + /> + - - E-mail - setEmail(e.target.value)} - aria-invalid={!!error} - /> - + + E-mail + setEmail(e.target.value)} + aria-invalid={!!error} + /> + - - Senha - setPassword(e.target.value)} - aria-invalid={ - !!error || - (password.length > 0 && !passwordValidation.isValid) - } - maxLength={23} - /> - {password.length > 0 && ( -
- - - - - - -
+ + Senha + setPassword(e.target.value)} + aria-invalid={ + !!error || + (password.length > 0 && !passwordValidation.isValid) + } + maxLength={23} + /> + {password.length > 0 && ( +
+ + + + + + +
+ )} +
+ + + + - - - + + Ou continue com + - - Ou continue com - + + + - - - + + Já tem uma conta?{" "} + + Entrar + + - - Já tem uma conta?{" "} - - Entrar - - -
- - - - - - - - - Voltar para o site - - + + + Voltar para a página inicial + + + + +
); } diff --git a/src/shared/components/month-picker/month-navigation.tsx b/src/shared/components/month-picker/month-navigation.tsx index ab2bf3e..ecdeace 100644 --- a/src/shared/components/month-picker/month-navigation.tsx +++ b/src/shared/components/month-picker/month-navigation.tsx @@ -42,7 +42,7 @@ export default function MonthNavigation() { }; return ( - +
-
- {/* Logo */} +
+
+ +
+
+ +
- {/* Navigation */} - {/* Right-side actions */}
- {/* User avatar */}
diff --git a/src/shared/components/navigation/navbar/nav-styles.ts b/src/shared/components/navigation/navbar/nav-styles.ts index f65aef6..d736aaa 100644 --- a/src/shared/components/navigation/navbar/nav-styles.ts +++ b/src/shared/components/navigation/navbar/nav-styles.ts @@ -1,6 +1,6 @@ // Base para links diretos e triggers — pill arredondado export const linkBase = - "inline-flex h-8 items-center justify-center rounded-full px-3 text-sm font-medium transition-all lowercase"; + "inline-flex h-8 items-center justify-center rounded-md px-2 text-sm font-medium transition-all lowercase"; // Estado inativo: muted, hover suave sem underline export const linkIdle = "text-black/75 hover:bg-black/10 hover:text-black"; @@ -11,8 +11,8 @@ export const linkActive = "bg-black/10 text-black"; // Trigger do NavigationMenu — espelha linkBase + linkIdle, remove estilos padrão export const triggerClass = [ "h-8!", - "rounded-full!", - "px-3!", + "rounded-md!", + "px-2!", "py-0!", "text-sm!", "font-medium!", diff --git a/src/shared/components/ui/dot-pattern.tsx b/src/shared/components/ui/dot-pattern.tsx new file mode 100644 index 0000000..91fc8e2 --- /dev/null +++ b/src/shared/components/ui/dot-pattern.tsx @@ -0,0 +1,49 @@ +import type { ComponentProps } from "react"; +import { cn } from "@/shared/utils/ui"; + +type DotPatternProps = ComponentProps<"svg"> & { + width?: number; + height?: number; + x?: number; + y?: number; + cx?: number; + cy?: number; + cr?: number; +}; + +export function DotPattern({ + className, + width = 18, + height = 18, + x = 0, + y = 0, + cx = 1.5, + cy = 1.5, + cr = 1.5, + ...props +}: DotPatternProps) { + const patternId = `dot-pattern-${width}-${height}-${x}-${y}-${cx}-${cy}-${cr}`; + + return ( + + Dot pattern background + + + + + + + + ); +}