From 6de98b7d8497c196303ac324bf7b005c40641074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9?= Date: Fri, 8 May 2026 19:44:17 +0200 Subject: [PATCH 01/88] (ai) first home page non-auth --- apps/frontend/app/globals.css | 217 ++++++++- apps/frontend/app/layout.tsx | 114 ++++- apps/frontend/app/page.tsx | 82 +--- apps/frontend/components.json | 21 + .../components/brand/section-label.tsx | 23 + apps/frontend/components/brand/star-mark.tsx | 29 ++ apps/frontend/components/brand/wordmark.tsx | 19 + apps/frontend/components/home/deployment.tsx | 205 +++++++++ .../home/features/block-editor-preview.tsx | 196 ++++++++ .../components/home/features/index.tsx | 87 ++++ .../home/features/leaderboard-preview.tsx | 179 ++++++++ .../components/home/features/quiz-preview.tsx | 176 ++++++++ .../components/home/features/roadmap-rail.tsx | 60 +++ apps/frontend/components/home/hero.tsx | 106 +++++ .../components/home/join-or-create.tsx | 168 +++++++ apps/frontend/components/home/open-source.tsx | 125 +++++ apps/frontend/components/home/personas.tsx | 112 +++++ apps/frontend/components/home/site-footer.tsx | 99 ++++ apps/frontend/components/home/sovereignty.tsx | 98 ++++ apps/frontend/components/home/top-nav.tsx | 87 ++++ apps/frontend/components/reveal-on-scroll.tsx | 29 ++ apps/frontend/components/ui/badge.tsx | 43 ++ apps/frontend/components/ui/button.tsx | 63 +++ apps/frontend/components/ui/card.tsx | 119 +++++ apps/frontend/components/ui/icon.tsx | 29 ++ apps/frontend/components/ui/tabs.tsx | 58 +++ apps/frontend/lib/icons.ts | 90 ++++ apps/frontend/lib/reveal.ts | 9 + apps/frontend/lib/utils.ts | 6 + apps/frontend/package.json | 18 +- apps/frontend/pnpm-lock.yaml | 426 ++++++++++++++++++ 31 files changed, 3000 insertions(+), 93 deletions(-) create mode 100644 apps/frontend/components.json create mode 100644 apps/frontend/components/brand/section-label.tsx create mode 100644 apps/frontend/components/brand/star-mark.tsx create mode 100644 apps/frontend/components/brand/wordmark.tsx create mode 100644 apps/frontend/components/home/deployment.tsx create mode 100644 apps/frontend/components/home/features/block-editor-preview.tsx create mode 100644 apps/frontend/components/home/features/index.tsx create mode 100644 apps/frontend/components/home/features/leaderboard-preview.tsx create mode 100644 apps/frontend/components/home/features/quiz-preview.tsx create mode 100644 apps/frontend/components/home/features/roadmap-rail.tsx create mode 100644 apps/frontend/components/home/hero.tsx create mode 100644 apps/frontend/components/home/join-or-create.tsx create mode 100644 apps/frontend/components/home/open-source.tsx create mode 100644 apps/frontend/components/home/personas.tsx create mode 100644 apps/frontend/components/home/site-footer.tsx create mode 100644 apps/frontend/components/home/sovereignty.tsx create mode 100644 apps/frontend/components/home/top-nav.tsx create mode 100644 apps/frontend/components/reveal-on-scroll.tsx create mode 100644 apps/frontend/components/ui/badge.tsx create mode 100644 apps/frontend/components/ui/button.tsx create mode 100644 apps/frontend/components/ui/card.tsx create mode 100644 apps/frontend/components/ui/icon.tsx create mode 100644 apps/frontend/components/ui/tabs.tsx create mode 100644 apps/frontend/lib/icons.ts create mode 100644 apps/frontend/lib/reveal.ts create mode 100644 apps/frontend/lib/utils.ts diff --git a/apps/frontend/app/globals.css b/apps/frontend/app/globals.css index a2dc41e..9370588 100644 --- a/apps/frontend/app/globals.css +++ b/apps/frontend/app/globals.css @@ -1,26 +1,215 @@ @import "tailwindcss"; +/* ============================================================ + Brand color system + Validated palette — see CLAUDE.md (2026-05-04). + Primary accent: #F28022 — used as accent only, never large fill. + ============================================================ */ + :root { - --background: #ffffff; - --foreground: #171717; + /* Brand surfaces & ink */ + --color-canvas-raw: #f5f5f7; + --color-surface-raw: #ffffff; + --color-surface-2-raw: #fafafc; + --color-ink-raw: #1d1d1f; + --color-ink-2-raw: #6e6e73; + --color-ink-3-raw: #aeaeb2; + --color-line-raw: rgba(0, 0, 0, 0.08); + --color-line-strong-raw: rgba(0, 0, 0, 0.13); + + /* Brand accent — single source of truth for #F28022 */ + --color-brand-raw: #f28022; + --color-brand-soft-raw: #fde9d8; + --color-brand-deep-raw: #c8651a; + + /* Code surfaces (cool, never warm) */ + --color-code-bg-raw: #111318; + --color-code-fg-raw: #cdd6f4; + + /* Navy alt theme reference */ + --color-navy-raw: #0b1220; + --color-navy-2-raw: #111d32; + + /* shadcn radius */ + --radius: 0.875rem; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + /* ---- Brand tokens (used directly across the site) ---- */ + --color-canvas: var(--color-canvas-raw); + --color-surface: var(--color-surface-raw); + --color-surface-2: var(--color-surface-2-raw); + --color-ink: var(--color-ink-raw); + --color-ink-2: var(--color-ink-2-raw); + --color-ink-3: var(--color-ink-3-raw); + --color-line: var(--color-line-raw); + --color-line-strong: var(--color-line-strong-raw); + --color-brand: var(--color-brand-raw); + --color-brand-soft: var(--color-brand-soft-raw); + --color-brand-deep: var(--color-brand-deep-raw); + --color-code-bg: var(--color-code-bg-raw); + --color-code-fg: var(--color-code-fg-raw); + --color-navy: var(--color-navy-raw); + --color-navy-2: var(--color-navy-2-raw); + + /* Backwards-compat alias: `accent` was the previous name for brand orange. + Keeping it alive so legacy classNames (text-accent, bg-accent) still resolve. */ + --color-accent: var(--color-brand-raw); + --color-accent-soft: var(--color-brand-soft-raw); + --color-accent-deep: var(--color-brand-deep-raw); + + /* ---- shadcn semantic tokens (mapped to the brand) ---- */ + --color-background: var(--color-canvas-raw); + --color-foreground: var(--color-ink-raw); + --color-card: var(--color-surface-raw); + --color-card-foreground: var(--color-ink-raw); + --color-popover: var(--color-surface-raw); + --color-popover-foreground: var(--color-ink-raw); + --color-primary: var(--color-brand-raw); + --color-primary-foreground: #ffffff; + --color-secondary: var(--color-surface-raw); + --color-secondary-foreground: var(--color-ink-raw); + --color-muted: var(--color-canvas-raw); + --color-muted-foreground: var(--color-ink-2-raw); + --color-destructive: #ef4444; + --color-destructive-foreground: #ffffff; + --color-border: var(--color-line-strong-raw); + --color-input: var(--color-line-strong-raw); + --color-ring: var(--color-brand-raw); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --font-sans: var(--font-outfit), -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; + --font-display: var(--font-outfit), -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; + --font-mono: ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, monospace; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +:root { + color-scheme: light; +} + +html { + scroll-behavior: smooth; + scroll-padding-top: 88px; } body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--color-canvas-raw); + color: var(--color-ink-raw); + font-family: var(--font-sans); + font-feature-settings: "ss01", "cv11"; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +::selection { + background: var(--color-brand-raw); + color: #fff; +} + +/* ============================================================ + Display utilities + ============================================================ */ + +.font-display { + font-family: var(--font-display); + font-weight: 900; + letter-spacing: -0.035em; + line-height: 0.94; +} + +.font-mono { + font-family: var(--font-mono); +} + +.bg-dots { + background-image: radial-gradient(rgba(0, 0, 0, 0.08) 1px, transparent 1px); + background-size: 24px 24px; + background-position: -12px -12px; +} + +.bg-grid { + background-image: linear-gradient( + to right, + rgba(0, 0, 0, 0.04) 1px, + transparent 1px + ), + linear-gradient(to bottom, rgba(0, 0, 0, 0.04) 1px, transparent 1px); + background-size: 64px 64px; +} + +[data-reveal] { + opacity: 0; + transform: translateY(18px); + transition: + opacity 700ms cubic-bezier(0.22, 1, 0.36, 1), + transform 700ms cubic-bezier(0.22, 1, 0.36, 1); + transition-delay: var(--reveal-delay, 0ms); +} + +[data-reveal].is-visible { + opacity: 1; + transform: translateY(0); +} + +@keyframes blink { + 0%, + 49% { + opacity: 1; + } + 50%, + 100% { + opacity: 0; + } +} + +.cursor-blink { + display: inline-block; + width: 0.55em; + height: 1.05em; + background: currentColor; + margin-left: 0.18em; + vertical-align: -0.18em; + animation: blink 1.06s step-end infinite; +} + +@keyframes float-soft { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } +} + +.float-soft { + animation: float-soft 6s ease-in-out infinite; +} + +.no-scrollbar::-webkit-scrollbar { + display: none; +} +.no-scrollbar { + scrollbar-width: none; +} + +@media (prefers-reduced-motion: reduce) { + [data-reveal] { + opacity: 1; + transform: none; + transition: none; + } + .cursor-blink, + .float-soft { + animation: none; + } + html { + scroll-behavior: auto; + } } diff --git a/apps/frontend/app/layout.tsx b/apps/frontend/app/layout.tsx index 976eb90..fd3363d 100644 --- a/apps/frontend/app/layout.tsx +++ b/apps/frontend/app/layout.tsx @@ -1,33 +1,111 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import type { Metadata, Viewport } from "next"; +import { Outfit } from "next/font/google"; + +import { config } from "@fortawesome/fontawesome-svg-core"; +import "@fortawesome/fontawesome-svg-core/styles.css"; + import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); +// Prevent FA from injecting its own CSS (we already imported it above). +config.autoAddCss = false; -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const outfit = Outfit({ + variable: "--font-outfit", subsets: ["latin"], + weight: ["300", "400", "500", "600", "700", "800", "900"], + display: "swap", }); +const SITE_URL = "https://codestar.dev"; + export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + metadataBase: new URL(SITE_URL), + title: { + default: "CodeStar — La plateforme e-learning souveraine, hébergée chez vous", + template: "%s · CodeStar", + }, + description: + "CodeStar est une plateforme e-learning open source, sous licence GPL v3. Déployez votre instance via Docker. Vos données ne quittent jamais votre serveur.", + applicationName: "CodeStar", + keywords: [ + "e-learning open source", + "LMS self-hosted", + "plateforme apprentissage GPL v3", + "alternative Moodle", + "souveraineté numérique éducation", + "Docker LMS", + "éditeur de cours par blocs", + "classement apprenants", + "quiz pédagogique", + "CodeStar", + ], + authors: [ + { name: "CodeStar Project", url: "https://github.com/CodeStar-Project" }, + ], + creator: "CodeStar Project", + publisher: "CodeStar Project", + category: "education", + alternates: { canonical: SITE_URL }, + openGraph: { + type: "website", + locale: "fr_FR", + url: SITE_URL, + siteName: "CodeStar", + title: "CodeStar — L'e-learning souverain, sur vos serveurs", + description: + "Plateforme e-learning open source, déployée chez vous via Docker. Sous licence GPL v3. Vos données restent chez vous.", + }, + twitter: { + card: "summary_large_image", + title: "CodeStar — L'e-learning souverain", + description: + "Plateforme e-learning open source, hébergée chez vous, sous licence GPL v3.", + }, + robots: { + index: true, + follow: true, + googleBot: { index: true, follow: true, "max-image-preview": "large" }, + }, + icons: { icon: "/favicon.ico" }, +}; + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#f5f5f7" }, + { media: "(prefers-color-scheme: dark)", color: "#0b1220" }, + ], + width: "device-width", + initialScale: 1, + colorScheme: "light", +}; + +const jsonLd = { + "@context": "https://schema.org", + "@type": "SoftwareApplication", + name: "CodeStar", + url: SITE_URL, + description: + "Plateforme e-learning open source, self-hosted, sous licence GPL v3.", + applicationCategory: "EducationalApplication", + operatingSystem: "Linux, macOS, Windows (WSL2)", + offers: { "@type": "Offer", price: "0", priceCurrency: "EUR" }, + license: "https://www.gnu.org/licenses/gpl-3.0.html", + codeRepository: "https://github.com/CodeStar-Project", + inLanguage: "fr-FR", }; export default function RootLayout({ children, -}: Readonly<{ - children: React.ReactNode; -}>) { +}: Readonly<{ children: React.ReactNode }>) { return ( - - {children} + + +