style(ui): polimento visual — tema, cards, dark mode e landing page

Raio de borda global 0.625rem → 0.7rem; ajustes finos em --card e --border.
DotPattern removido do layout, tela de auth e landing page.
Account-card redesenhado (cores de saldo, tooltip de flags de exclusão).
Budget-card, card-item, calendário (day-cell, event-modal) com layout revisado.
Auth-card-shell simplificado (sem glassmorphism/blob). Landing page com
mainFeatures + extraFeatures em grid único e dark mode nos botões de CTA.
Imagens de preview da landing atualizadas. CSS --data-7..10 removidas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-04-20 17:52:17 +00:00
parent 5d84ae928a
commit 6d81ff8b53
67 changed files with 612 additions and 737 deletions

View File

@@ -20,22 +20,13 @@ const getSingleParam = (
return Array.isArray(value) ? (value[0] ?? null) : value;
};
const capitalize = (value: string) =>
value.length === 0 ? value : value[0]?.toUpperCase() + value.slice(1);
export default async function Page({ searchParams }: PageProps) {
await connection();
const userId = await getUserId();
const resolvedSearchParams = searchParams ? await searchParams : undefined;
const periodoParam = getSingleParam(resolvedSearchParams, "periodo");
const {
period: selectedPeriod,
monthName: rawMonthName,
year,
} = parsePeriodParam(periodoParam);
const periodLabel = `${capitalize(rawMonthName)} ${year}`;
const { period: selectedPeriod } = parsePeriodParam(periodoParam);
const { budgets, categoriesOptions } = await fetchBudgetsForUser(
userId,
@@ -49,7 +40,6 @@ export default async function Page({ searchParams }: PageProps) {
budgets={budgets}
categories={categoriesOptions}
selectedPeriod={selectedPeriod}
periodLabel={periodLabel}
/>
</main>
);

View File

@@ -1,6 +1,6 @@
import { connection } from "next/server";
import { fetchCategoryHistory } from "@/features/dashboard/categories/category-history-queries";
import { CategoryHistoryWidget } from "@/features/dashboard/components/category-history-widget";
import { CategoryHistoryWidget } from "@/features/dashboard/components/widgets/category-history-widget";
import { getUser } from "@/shared/lib/auth/server";
import { getCurrentPeriod } from "@/shared/utils/period";

View File

@@ -40,7 +40,9 @@ export default async function Page({ searchParams }: PageProps) {
// Extract query params
const inicioParam = getSingleParam(resolvedSearchParams, "inicio");
const fimParam = getSingleParam(resolvedSearchParams, "fim");
const categoriasParam = getSingleParam(resolvedSearchParams, "categories");
const categoriasParam =
getSingleParam(resolvedSearchParams, "categorias") ??
getSingleParam(resolvedSearchParams, "categories");
// Calculate default period (last 6 months)
const currentPeriod = getCurrentPeriod();

View File

@@ -30,7 +30,6 @@ import { NavbarShell } from "@/shared/components/navigation/navbar/navbar-shell"
import { Badge } from "@/shared/components/ui/badge";
import { Button } from "@/shared/components/ui/button";
import { Card, CardContent } from "@/shared/components/ui/card";
import { DotPattern } from "@/shared/components/ui/dot-pattern";
import { getOptionalUserSession } from "@/shared/lib/auth/server";
export default async function Page() {
@@ -57,7 +56,7 @@ export default async function Page() {
<a
key={href}
href={href}
className="rounded-md px-2 py-1.5 text-sm font-medium text-black/75 hover:text-black hover:bg-black/10 transition-colors"
className="rounded-md px-2 py-1.5 text-sm font-medium text-black/75 hover:text-black hover:bg-black/10 transition-colors dark:text-white/75 dark:hover:text-white dark:hover:bg-white/10"
>
{label}
</a>
@@ -70,9 +69,9 @@ export default async function Page() {
(session?.user ? (
<Link prefetch href="/dashboard" className="hidden md:block">
<Button
variant="outline"
variant="navbar"
size="sm"
className="border-black/20 text-black/80 bg-transparent hover:bg-black/10 hover:text-black shadow-none"
className="border border-black/20 dark:border-white/20"
>
Dashboard
</Button>
@@ -83,7 +82,7 @@ export default async function Page() {
<Button
variant="ghost"
size="sm"
className="text-black/75 hover:bg-black/10 hover:text-black shadow-none"
className="text-black/75 hover:bg-black/10 hover:text-black shadow-none dark:text-white/75 dark:hover:bg-white/10 dark:hover:text-white"
>
Entrar
</Button>
@@ -91,7 +90,7 @@ export default async function Page() {
<Link href="/signup">
<Button
size="sm"
className="bg-black/10 border border-black/20 text-black shadow-none hover:bg-black/20 gap-2"
className="bg-black/10 border border-black/20 text-black shadow-none hover:bg-black/20 gap-2 dark:bg-white/10 dark:border-white/20 dark:text-white dark:hover:bg-white/20"
>
Começar
</Button>
@@ -107,18 +106,6 @@ export default async function Page() {
{/* Hero Section */}
<section className="relative overflow-hidden pt-14 md:pt-20 lg:pt-24 pb-0">
<div className="pointer-events-none absolute inset-x-0 top-0 h-80 overflow-hidden">
<DotPattern
width={20}
height={20}
cx={1.25}
cy={1.25}
cr={1.25}
className="text-primary/10 mask-[linear-gradient(to_bottom,black_0%,transparent_100%)]"
/>
<div className="absolute inset-0 bg-linear-to-b from-primary/6 to-transparent" />
</div>
<div className="max-w-8xl mx-auto px-4 relative">
<div className="mx-auto flex max-w-4xl flex-col items-center text-center gap-5 md:gap-6 pb-10 md:pb-14">
<Badge variant="outline">
@@ -265,72 +252,34 @@ export default async function Page() {
</AnimateOnScroll>
<AnimateOnScroll>
<div className="grid gap-4 md:gap-6 sm:grid-cols-2 lg:grid-cols-3">
{mainFeatures.map((feature) => (
<div className="grid gap-4 md:gap-4 sm:grid-cols-2 lg:grid-cols-3">
{[...mainFeatures, ...extraFeatures].map((feature) => (
<Card key={feature.title}>
<CardContent className="pt-5 pb-5 md:pt-6">
<div className="flex flex-col gap-3 md:gap-4">
<CardContent>
<div className="flex items-center gap-3 mb-3">
<div
className="flex h-11 w-11 md:h-12 md:w-12 items-center justify-center rounded-lg"
className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full"
style={{
backgroundColor: `color-mix(in oklch, ${feature.colorVar} 14%, transparent)`,
backgroundColor: `color-mix(in oklch, ${feature.colorVar} 20%, transparent)`,
}}
>
<feature.icon
className="size-[22px] md:size-6"
style={{ color: feature.colorVar }}
className="size-5"
style={{ color: "var(--foreground)" }}
/>
</div>
<div>
<h3 className="font-semibold text-base md:text-lg mb-1.5 md:mb-2">
{feature.title}
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
{feature.description}
</p>
</div>
<h3 className="font-semibold text-base leading-tight">
{feature.title}
</h3>
</div>
<p className="text-sm text-muted-foreground leading-relaxed">
{feature.description}
</p>
</CardContent>
</Card>
))}
</div>
</AnimateOnScroll>
<AnimateOnScroll>
<div className="mt-8 md:mt-12">
<h3 className="text-sm font-semibold text-center mb-4 md:mb-6 text-muted-foreground">
Também inclui
</h3>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{extraFeatures.map((feature) => (
<div
key={feature.title}
className="flex items-start gap-3 rounded-lg border bg-card p-3 md:p-4"
>
<div
className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md"
style={{
backgroundColor: `color-mix(in oklch, ${feature.colorVar} 14%, transparent)`,
}}
>
<feature.icon
className="size-[18px]"
style={{ color: feature.colorVar }}
/>
</div>
<div className="min-w-0">
<h4 className="font-semibold text-sm mb-0.5">
{feature.title}
</h4>
<p className="text-xs text-muted-foreground leading-relaxed">
{feature.description}
</p>
</div>
</div>
))}
</div>
</div>
</AnimateOnScroll>
</div>
</div>
</section>
@@ -396,14 +345,14 @@ export default async function Page() {
{pwaHighlights.map((item) => (
<li key={item.title} className="flex items-start gap-3">
<div
className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md"
className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full"
style={{
backgroundColor: `color-mix(in oklch, ${item.colorVar} 14%, transparent)`,
backgroundColor: `color-mix(in oklch, ${item.colorVar} 20%, transparent)`,
}}
>
<item.icon
className="size-[15px]"
style={{ color: item.colorVar }}
style={{ color: "var(--foreground)" }}
/>
</div>
<p className="text-sm">
@@ -438,17 +387,19 @@ export default async function Page() {
pré-lançamentos automaticamente para você revisar na inbox.
</p>
<ol className="space-y-3 mb-6">
{companionSteps.map((step, index) => (
{companionSteps.map((step) => (
<li key={step.title} className="flex items-start gap-3">
<span
className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-medium"
<div
className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full"
style={{
backgroundColor: `color-mix(in oklch, ${step.colorVar} 14%, transparent)`,
color: step.colorVar,
backgroundColor: `color-mix(in oklch, ${step.colorVar} 20%, transparent)`,
}}
>
{index + 1}
</span>
<step.icon
className="size-3.5"
style={{ color: "var(--foreground)" }}
/>
</div>
<p className="text-sm">
<span className="font-medium">{step.title}</span>
<span className="text-muted-foreground">
@@ -545,14 +496,14 @@ export default async function Page() {
<CardContent>
<div className="flex items-start gap-4">
<div
className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg"
className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full"
style={{
backgroundColor: `color-mix(in oklch, ${item.colorVar} 14%, transparent)`,
backgroundColor: `color-mix(in oklch, ${item.colorVar} 20%, transparent)`,
}}
>
<item.icon
className="size-6"
style={{ color: item.colorVar }}
style={{ color: "var(--foreground)" }}
/>
</div>
<div>
@@ -633,14 +584,14 @@ export default async function Page() {
<CardContent>
<div className="flex gap-3 md:gap-4">
<div
className="flex h-9 w-9 md:h-10 md:w-10 shrink-0 items-center justify-center rounded-lg"
className="flex h-9 w-9 md:h-10 md:w-10 shrink-0 items-center justify-center rounded-full"
style={{
backgroundColor: `color-mix(in oklch, ${item.colorVar} 14%, transparent)`,
backgroundColor: `color-mix(in oklch, ${item.colorVar} 20%, transparent)`,
}}
>
<item.icon
className="size-[18px] md:size-5"
style={{ color: item.colorVar }}
style={{ color: "var(--foreground)" }}
/>
</div>
<div>

View File

@@ -10,7 +10,7 @@
:root {
--background: oklch(97.412% 0.00332 67.032);
--foreground: oklch(27% 0.008 45);
--card: oklch(99% 0.002 67);
--card: oklch(100% 0 0);
--card-foreground: var(--foreground);
--popover: oklch(100% 0 0);
--popover-foreground: var(--foreground);
@@ -36,7 +36,7 @@
--destructive: oklch(55% 0.22 27);
--destructive-foreground: oklch(98% 0.005 30);
--border: oklch(90.274% 0.01362 60.342);
--border: oklch(92.323% 0.01276 63.703);
--input: var(--border);
--ring: var(--primary);
@@ -57,10 +57,6 @@
--data-4: oklch(74% 0.18 55); /* âmbar */
--data-5: oklch(78% 0.16 68); /* âmbar-dourado */
--data-6: oklch(76% 0.15 82); /* amarelo-quente */
--data-7: oklch(70% 0.17 95); /* amarelo-lima */
--data-8: oklch(65% 0.18 108); /* lima-verde */
--data-9: oklch(62% 0.17 120); /* verde-oliva claro */
--data-10: oklch(56% 0.15 10); /* terracota escuro */
--sidebar: oklch(99.3% 0.0015 75);
--sidebar-foreground: var(--foreground);
@@ -71,7 +67,7 @@
--sidebar-border: oklch(91% 0.004 70);
--sidebar-ring: var(--primary);
--radius: 0.625rem;
--radius: 0.7rem;
--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);
@@ -94,7 +90,7 @@
.dark {
--background: oklch(18% 0.004 55);
--foreground: oklch(93% 0.008 80);
--card: oklch(21.5% 0.004 55);
--card: oklch(21.531% 0.00369 48.293);
--card-foreground: var(--foreground);
--popover: oklch(24% 0.004 55);
--popover-foreground: var(--foreground);
@@ -120,7 +116,7 @@
--destructive: oklch(62% 0.2 28);
--destructive-foreground: oklch(98% 0.005 30);
--border: oklch(31% 0.004 55);
--border: oklch(28% 0.0035 55);
--input: var(--border);
--ring: var(--primary);
@@ -141,10 +137,6 @@
--data-4: oklch(81% 0.18 55);
--data-5: oklch(84% 0.16 68);
--data-6: oklch(82% 0.15 82);
--data-7: oklch(77% 0.17 95);
--data-8: oklch(72% 0.18 108);
--data-9: oklch(69% 0.17 120);
--data-10: oklch(63% 0.15 10);
--sidebar: oklch(15.5% 0.004 55);
--sidebar-foreground: var(--foreground);
@@ -155,7 +147,7 @@
--sidebar-border: oklch(30% 0.004 55);
--sidebar-ring: var(--primary);
--radius: 0.625rem;
--radius: 0.7rem;
--shadow-2xs: 0 1px 2px 0px oklch(0% 0 0 / 0.3);
--shadow-xs: 0 1px 3px 0px oklch(0% 0 0 / 0.4);