From f101af4872fa8f4a6e1b544365159b4c216264ab Mon Sep 17 00:00:00 2001 From: Puechberty Arthur Date: Mon, 20 Apr 2026 22:00:52 +0200 Subject: [PATCH] harmonisation du style dans tout le site --- apps/web/app/[locale]/dashboard/page.tsx | 7 +- apps/web/app/[locale]/layout.tsx | 6 +- apps/web/app/[locale]/login/page.tsx | 38 ++- apps/web/app/[locale]/page.tsx | 24 +- apps/web/app/globals.css | 384 +++++++---------------- apps/web/components/FeatureCard.tsx | 18 +- apps/web/components/Features.tsx | 12 +- apps/web/components/Footer.tsx | 27 +- apps/web/components/Hero.tsx | 114 +++---- apps/web/components/HowItWorks.tsx | 31 +- apps/web/components/LanguageSwitcher.tsx | 70 ++++- apps/web/components/Logo.tsx | 35 ++- apps/web/components/dashboard-client.tsx | 264 +++++++++++----- apps/web/components/ui/Badge.tsx | 39 +++ apps/web/components/ui/Button.tsx | 76 +++++ apps/web/components/ui/Card.tsx | 47 +++ apps/web/components/ui/Container.tsx | 12 + apps/web/components/ui/Input.tsx | 21 ++ apps/web/components/ui/Navbar.tsx | 63 ++++ apps/web/components/ui/PageWrapper.tsx | 40 +++ apps/web/components/ui/Switch.tsx | 72 +++++ apps/web/components/ui/cn.ts | 3 + apps/web/messages/en.json | 22 +- apps/web/messages/fr.json | 22 +- 24 files changed, 950 insertions(+), 497 deletions(-) create mode 100644 apps/web/components/ui/Badge.tsx create mode 100644 apps/web/components/ui/Button.tsx create mode 100644 apps/web/components/ui/Card.tsx create mode 100644 apps/web/components/ui/Container.tsx create mode 100644 apps/web/components/ui/Input.tsx create mode 100644 apps/web/components/ui/Navbar.tsx create mode 100644 apps/web/components/ui/PageWrapper.tsx create mode 100644 apps/web/components/ui/Switch.tsx create mode 100644 apps/web/components/ui/cn.ts diff --git a/apps/web/app/[locale]/dashboard/page.tsx b/apps/web/app/[locale]/dashboard/page.tsx index ae92b5b..66d99e8 100644 --- a/apps/web/app/[locale]/dashboard/page.tsx +++ b/apps/web/app/[locale]/dashboard/page.tsx @@ -1,4 +1,5 @@ import { DashboardClient } from "../../../components/dashboard-client"; +import PageWrapper from "../../../components/ui/PageWrapper"; const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:4000"; @@ -6,10 +7,10 @@ export const dynamic = "force-dynamic"; export default function DashboardPage() { return ( -
-
+ +
-
+ ); } diff --git a/apps/web/app/[locale]/layout.tsx b/apps/web/app/[locale]/layout.tsx index 0b0487a..e7aaaba 100644 --- a/apps/web/app/[locale]/layout.tsx +++ b/apps/web/app/[locale]/layout.tsx @@ -4,7 +4,6 @@ import { hasLocale, NextIntlClientProvider } from "next-intl"; import { getMessages } from "next-intl/server"; import { notFound } from "next/navigation"; -import LanguageSwitcher from "../../components/LanguageSwitcher"; import { routing, type AppLocale } from "../../i18n/routing"; import enMessages from "../../messages/en.json"; import frMessages from "../../messages/fr.json"; @@ -77,11 +76,8 @@ export default async function LocaleLayout({ return ( - + -
- -
{children}
diff --git a/apps/web/app/[locale]/login/page.tsx b/apps/web/app/[locale]/login/page.tsx index 1ef04ce..7b901d9 100644 --- a/apps/web/app/[locale]/login/page.tsx +++ b/apps/web/app/[locale]/login/page.tsx @@ -1,4 +1,8 @@ import { getT } from "../../../i18n/server"; +import { Badge } from "../../../components/ui/Badge"; +import { buttonClassName } from "../../../components/ui/Button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../../components/ui/Card"; +import PageWrapper from "../../../components/ui/PageWrapper"; const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:4000"; @@ -6,15 +10,31 @@ export default async function LoginPage() { const t = await getT(); return ( -
-
-

{t("login.eyebrow")}

-

{t("login.title")}

-

{t("login.description")}

- - {t("login.cta")} - + +
+ + + + {t("login.eyebrow")} + + {t("login.title")} + {t("login.description")} + + + + + {t("login.cta")} + + +
-
+ ); } diff --git a/apps/web/app/[locale]/page.tsx b/apps/web/app/[locale]/page.tsx index 3122b63..e991d96 100644 --- a/apps/web/app/[locale]/page.tsx +++ b/apps/web/app/[locale]/page.tsx @@ -2,20 +2,18 @@ import Features from "../../components/Features"; import Footer from "../../components/Footer"; import Hero from "../../components/Hero"; import HowItWorks from "../../components/HowItWorks"; +import PageWrapper from "../../components/ui/PageWrapper"; -export default function HomePage() { +export default async function HomePage() { return ( -
-
- - -
- - -
-
- -
-
+ } + > + + + + ); } diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index dacac51..2fa5cca 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -1,21 +1,46 @@ -:root { - --sand-50: #f7f2e8; - --sand-100: #efe3ce; - --ink-900: #1b1815; - --ink-700: #3f3933; - --sun-500: #ec6f08; - --sun-600: #cb5600; - --teal-600: #0f766e; - --teal-700: #0b5f58; - --danger-600: #b91c1c; - --card-shadow: 0 20px 60px rgba(33, 25, 17, 0.15); - --radius-lg: 20px; -} - @tailwind base; @tailwind components; @tailwind utilities; +:root { + color-scheme: dark; + --bg: #060c18; + --bg-elevated: #0a1324; + --surface: rgba(14, 24, 42, 0.78); + --surface-strong: rgba(22, 35, 59, 0.88); + --surface-subtle: rgba(140, 167, 205, 0.1); + --surface-overlay: rgba(6, 12, 24, 0.82); + --foreground: #e7eefb; + --foreground-muted: #9aabc8; + --primary: #4f8cff; + --primary-hover: #66a0ff; + --accent: #2dd4bf; + --danger: #ef5f7a; + --border-subtle: rgba(163, 184, 215, 0.26); + --border-muted: rgba(163, 184, 215, 0.18); + --shadow-soft: 0 18px 40px rgba(2, 6, 18, 0.4); +} + +@media (prefers-color-scheme: light) { + :root { + color-scheme: light; + --bg: #f5f8fe; + --bg-elevated: #ffffff; + --surface: rgba(255, 255, 255, 0.86); + --surface-strong: rgba(246, 250, 255, 0.96); + --surface-subtle: rgba(64, 102, 176, 0.08); + --surface-overlay: rgba(245, 248, 254, 0.8); + --foreground: #101a2f; + --foreground-muted: #4d6286; + --primary: #2f62d9; + --primary-hover: #3a73f7; + --accent: #0f9a88; + --danger: #d63d5c; + --border-subtle: rgba(47, 98, 217, 0.22); + --border-muted: rgba(16, 26, 47, 0.12); + --shadow-soft: 0 14px 32px rgba(26, 40, 66, 0.12); + } +} * { box-sizing: border-box; @@ -25,284 +50,112 @@ html, body { margin: 0; padding: 0; + min-height: 100%; } body { min-height: 100vh; + position: relative; font-family: var(--font-heading), sans-serif; - color: var(--ink-900); - background: - radial-gradient(circle at 15% 20%, rgba(15, 118, 110, 0.18), transparent 35%), - radial-gradient(circle at 80% 0%, rgba(236, 111, 8, 0.22), transparent 40%), - linear-gradient(140deg, var(--sand-50), #f8f7f4 55%, #fff3e3); + color: var(--foreground); + background: var(--bg); } -.locale-switcher-shell { +a { + color: inherit; + text-decoration: none; +} + +.ui-background { + pointer-events: none; position: fixed; - top: 1rem; - right: 1rem; - z-index: 80; + inset: 0; + z-index: -1; + background: + radial-gradient(circle at 18% 10%, color-mix(in srgb, var(--primary) 28%, transparent), transparent 44%), + radial-gradient(circle at 82% 0%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 36%), + linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 86%, var(--bg)), var(--bg)); } -.locale-switcher { - display: inline-flex; - align-items: center; - border-radius: 999px; - border: 1px solid rgba(255, 255, 255, 0.22); - background: rgba(23, 20, 16, 0.74); - padding: 0.2rem 0.55rem; - backdrop-filter: blur(8px); - box-shadow: 0 12px 36px rgba(0, 0, 0, 0.2); -} - -.locale-switcher select { - border: 0; - background: transparent; - color: #fff; - font-size: 0.84rem; - font-weight: 700; - font-family: var(--font-heading), sans-serif; - cursor: pointer; -} - -.locale-switcher select:focus { - outline: none; -} - -.locale-switcher select:disabled { - opacity: 0.7; - cursor: progress; -} - -.page-shell { - min-height: 100vh; - display: grid; - place-items: center; - padding: 2rem 1rem; -} - -.panel { - width: min(1100px, 100%); - background: linear-gradient(170deg, rgba(255, 255, 255, 0.95), rgba(255, 247, 237, 0.86)); - border: 1px solid rgba(203, 86, 0, 0.16); - border-radius: var(--radius-lg); - box-shadow: var(--card-shadow); - backdrop-filter: blur(6px); -} - -.panel-login { - width: min(560px, 100%); - padding: 2rem; -} - -.panel-dashboard { - padding: 1.75rem; -} - -.eyebrow { - margin: 0; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--teal-700); - font-size: 0.75rem; - font-weight: 700; +.ui-background::before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(to right, color-mix(in srgb, var(--foreground-muted) 10%, transparent) 1px, transparent 1px), + linear-gradient(to bottom, color-mix(in srgb, var(--foreground-muted) 10%, transparent) 1px, transparent 1px); + background-size: 42px 42px; + mask-image: radial-gradient(circle at 55% 42%, black, transparent 78%); + opacity: 0.2; } h1, +h2, +h3, +h4 { + margin: 0; + color: var(--foreground); + letter-spacing: -0.02em; +} + +h1 { + font-size: clamp(2rem, 3.8vw, 3.4rem); + font-weight: 700; + line-height: 1.08; +} + h2 { - margin: 0 0 0.5rem; + font-size: clamp(1.4rem, 2.3vw, 2rem); + font-weight: 650; + line-height: 1.15; } p { margin: 0; - color: var(--ink-700); + line-height: 1.65; + color: var(--foreground-muted); } -.stack { - display: grid; - gap: 1.25rem; -} - -.stack-tight { - display: grid; - gap: 0.75rem; -} - -.dashboard-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - flex-wrap: wrap; -} - -.button-primary, -.button-ghost, -.actions button { - border-radius: 12px; - border: 0; - padding: 0.7rem 1rem; - font-family: var(--font-heading), sans-serif; - font-weight: 700; - cursor: pointer; - transition: transform 120ms ease, background-color 120ms ease; -} - -.button-primary { - display: inline-flex; - align-items: center; - justify-content: center; - background: linear-gradient(140deg, var(--sun-500), var(--sun-600)); - color: white; - text-decoration: none; -} - -.button-primary:hover { - transform: translateY(-1px); -} - -.button-ghost, -.actions button { - background: rgba(15, 118, 110, 0.08); - color: var(--teal-700); -} - -.button-ghost:hover, -.actions button:hover { - background: rgba(15, 118, 110, 0.14); -} - -.card-grid { - display: grid; - grid-template-columns: 1fr; - gap: 1rem; -} - -.card { - border-radius: 14px; - border: 1px solid rgba(15, 118, 110, 0.18); - background: rgba(255, 255, 255, 0.88); - padding: 1rem; - display: grid; - gap: 0.75rem; +small { + color: var(--foreground-muted); } label { display: grid; - gap: 0.3rem; + gap: 0.45rem; + font-size: 0.85rem; font-weight: 600; - color: var(--ink-700); -} - -input { - width: 100%; - border: 1px solid rgba(62, 57, 51, 0.2); - border-radius: 10px; - padding: 0.65rem 0.75rem; - font: inherit; - background: #fff; -} - -input:focus { - outline: 2px solid rgba(236, 111, 8, 0.32); - border-color: var(--sun-600); -} - -.row-between { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; -} - -.bot-list { - margin: 0; - padding: 0; - list-style: none; - display: grid; - gap: 0.75rem; -} - -.bot-item { - border: 1px solid rgba(62, 57, 51, 0.14); - border-radius: 12px; - padding: 0.8rem; - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - flex-wrap: wrap; -} - -.bot-main { - display: grid; - gap: 0.25rem; -} - -.bot-name { - color: var(--ink-900); - font-size: 1.1rem; - font-weight: 700; -} - -.status { - display: inline-flex; - padding: 0.2rem 0.55rem; - border-radius: 999px; - width: fit-content; - font-size: 0.8rem; - font-weight: 700; -} - -.status-running { - background: rgba(15, 118, 110, 0.16); - color: var(--teal-700); -} - -.status-starting, -.status-stopping { - background: rgba(236, 111, 8, 0.16); - color: var(--sun-600); -} - -.status-stopped { - background: rgba(63, 57, 51, 0.12); - color: var(--ink-700); -} - -.status-error { - background: rgba(185, 28, 28, 0.13); - color: var(--danger-600); -} - -.actions { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; -} - -.error-banner, -.error-inline { - color: var(--danger-600); - font-weight: 600; -} - -.muted { - color: rgba(63, 57, 51, 0.85); + color: var(--foreground-muted); } .mono { - font-family: var(--font-mono), monospace; + font-family: var(--font-mono), ui-monospace, SFMono-Regular, Menlo, monospace; +} + +.section-kicker { + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--accent); + font-weight: 700; +} + +.section-title { + max-width: 18ch; +} + +.section-description { + max-width: 68ch; } .reveal-up { - animation: revealUp 500ms ease-out both; + animation: revealUp 420ms ease-out both; } @keyframes revealUp { from { opacity: 0; - transform: translateY(16px); + transform: translateY(14px); } to { @@ -311,23 +164,8 @@ input:focus { } } -@media (min-width: 920px) { - .card-grid { - grid-template-columns: 0.95fr 1.35fr; - } - - .panel-dashboard { - padding: 2rem; - } -} - @media (max-width: 640px) { - .locale-switcher-shell { - top: 0.7rem; - right: 0.7rem; - } - - .locale-switcher select { - font-size: 0.78rem; + .section-title { + max-width: 100%; } } diff --git a/apps/web/components/FeatureCard.tsx b/apps/web/components/FeatureCard.tsx index 54302ab..f54c28a 100644 --- a/apps/web/components/FeatureCard.tsx +++ b/apps/web/components/FeatureCard.tsx @@ -1,5 +1,7 @@ import { ReactNode } from "react"; +import { Card, CardContent } from "./ui/Card"; + export default function FeatureCard({ title, description, @@ -10,17 +12,17 @@ export default function FeatureCard({ icon: ReactNode; }) { return ( -
-
-
+ + +
{icon}
-
-

{title}

-

{description}

+
+

{title}

+

{description}

-
-
+ + ); } diff --git a/apps/web/components/Features.tsx b/apps/web/components/Features.tsx index e2b8730..204ba9b 100644 --- a/apps/web/components/Features.tsx +++ b/apps/web/components/Features.tsx @@ -9,7 +9,7 @@ export default async function Features() { title: t("features.items.autoCommands.title"), description: t("features.items.autoCommands.description"), icon: ( - + @@ -68,11 +68,13 @@ export default async function Features() { ]; return ( -
-

{t("features.title")}

-

{t("features.subtitle")}

+
+
+

{t("features.title")}

+

{t("features.subtitle")}

+
-
+
{items.map((it, idx) => ( ))} diff --git a/apps/web/components/Footer.tsx b/apps/web/components/Footer.tsx index 7ca598d..b0d2e7e 100644 --- a/apps/web/components/Footer.tsx +++ b/apps/web/components/Footer.tsx @@ -1,18 +1,31 @@ import { getT } from "../i18n/server"; +import { Container } from "./ui/Container"; + export default async function Footer() { const t = await getT(); return ( -