From 8efd3572891d505f7409b64922644141bca7cc5b Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Thu, 11 Jun 2026 04:21:50 +0330 Subject: [PATCH] UNO refactor (stage 1): emerald felt theme + kit + full Home redesign - Theme: retint to lit emerald card-table felt + gold (body radial felt, green glass panels). New component kit in globals.css: glossy chunky 3D btn-gold + btn-green, .panel, .ribbon. Card backs pinned to classic navy. - Home fully redesigned UNO-style: nav rail + branding + two big 3D play buttons (gold online / green-glass vs-computer) + speed toggle; dropped the redundant 4 tiles (the rail covers them). Fits landscape (short: variants). Co-Authored-By: Claude Opus 4.8 --- src/app/globals.css | 90 ++++++++---- src/components/HomeScreen.tsx | 255 +++++++++++----------------------- 2 files changed, 146 insertions(+), 199 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 6c7d460..9044fc0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -7,26 +7,34 @@ @custom-variant tall (@media (min-height: 700px)); /* - FlatRender Hokm — "Persian Luxury" theme. - Deep navy/teal table, gold filigree accents, geometric motifs. + Barg-e Vasat — "Emerald Felt" theme (UNO-style, on-brand). + Lit emerald card-table felt + gold; dark-green glass panels, glossy 3D buttons. + The `navy-*` token names are kept (used app-wide) but now map to deep GREEN + surfaces — retinting here recolors every surface cohesively. */ :root { - --navy-950: #060c1f; - --navy-900: #0a142e; - --navy-800: #0e1c3f; - --navy-700: #14274f; - --teal-700: #0d6b6b; + --navy-950: #062a18; /* deepest surface */ + --navy-900: #0a3b25; /* panel base */ + --navy-800: #114e2e; /* raised surface */ + --navy-700: #1c7a4a; /* accent surface */ + --teal-700: #0d6b5c; --teal-500: #14b8a6; --teal-400: #2dd4bf; - --gold-600: #b8860b; + --gold-600: #9a6f0d; --gold-500: #d4af37; --gold-400: #e6c659; --gold-300: #f1da8a; - --cream: #f5ecd6; + --cream: #f6efdd; + + --felt-center: #1c7a4a; /* lit table center */ + --felt-mid: #114e2e; + --felt-edge: #07301c; + --green-btn: #2faa55; + --green-btn-lo: #1c6e36; --background: var(--navy-950); - --foreground: #eef2f8; + --foreground: #f3f7ef; } @theme inline { @@ -101,9 +109,8 @@ body { body { background: - radial-gradient(1200px 800px at 50% -10%, rgba(20, 184, 166, 0.12), transparent 60%), - radial-gradient(900px 700px at 50% 120%, rgba(212, 175, 55, 0.08), transparent 55%), - var(--navy-950); + radial-gradient(120% 90% at 50% 18%, var(--felt-center) 0%, var(--felt-mid) 52%, var(--felt-edge) 100%), + radial-gradient(900px 700px at 50% 120%, rgba(212, 175, 55, 0.10), transparent 55%); color: var(--foreground); font-family: var(--font-sans); -webkit-font-smoothing: antialiased; @@ -139,25 +146,56 @@ body { } .glass { - background: rgba(10, 20, 46, 0.72); + background: rgba(6, 42, 25, 0.78); backdrop-filter: blur(14px); - border: 1px solid rgba(212, 175, 55, 0.18); + border: 1px solid rgba(212, 175, 55, 0.22); } -.btn-gold { - background: linear-gradient(180deg, var(--gold-400), var(--gold-600)); - color: #2a1f04; +/* ── UNO-style component kit ────────────────────────────────────────── */ + +/* Chunky rounded panel: dark-green glass, gold hairline, inner top highlight. */ +.panel { + background: rgba(6, 42, 25, 0.82); + border: 1.5px solid rgba(212, 175, 55, 0.30); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 12px 28px rgba(0, 0, 0, 0.34); + backdrop-filter: blur(8px); +} + +/* Banner ribbon header (e.g. «زندگیِ حکم»). */ +.ribbon { + background: linear-gradient(180deg, var(--navy-700), var(--navy-900)); + border: 1.5px solid var(--gold-500); + color: var(--gold-300); font-weight: 700; - box-shadow: 0 8px 24px rgba(212, 175, 55, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.4); - transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease; + box-shadow: 0 5px 12px rgba(0, 0, 0, 0.35); } -.btn-gold:hover { - transform: translateY(-1px); - filter: brightness(1.05); - box-shadow: 0 12px 30px rgba(212, 175, 55, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.5); + +/* Glossy, chunky 3D gold button (own solid underside — no press-3d needed). */ +.btn-gold { + background: linear-gradient(180deg, var(--gold-300), var(--gold-400) 45%, var(--gold-500)); + color: #2a1f04; + font-weight: 800; + box-shadow: 0 6px 0 0 var(--gold-600), 0 9px 16px rgba(0, 0, 0, 0.34), inset 0 2px 0 rgba(255, 255, 255, 0.55); + transition: transform 0.08s ease, box-shadow 0.08s ease, filter 0.12s ease; } +.btn-gold:hover { filter: brightness(1.04); } .btn-gold:active { - transform: translateY(0); + transform: translateY(4px); + box-shadow: 0 2px 0 0 var(--gold-600), 0 3px 8px rgba(0, 0, 0, 0.3), inset 0 2px 0 rgba(255, 255, 255, 0.5); +} + +/* Glossy, chunky 3D green button (UNO secondary actions). */ +.btn-green { + background: linear-gradient(180deg, #6bd98a, var(--green-btn)); + color: #08220f; + font-weight: 800; + box-shadow: 0 6px 0 0 var(--green-btn-lo), 0 9px 16px rgba(0, 0, 0, 0.34), inset 0 2px 0 rgba(255, 255, 255, 0.4); + transition: transform 0.08s ease, box-shadow 0.08s ease, filter 0.12s ease; +} +.btn-green:hover { filter: brightness(1.04); } +.btn-green:active { + transform: translateY(4px); + box-shadow: 0 2px 0 0 var(--green-btn-lo), 0 3px 8px rgba(0, 0, 0, 0.3), inset 0 2px 0 rgba(255, 255, 255, 0.4); } /* Card face */ @@ -169,7 +207,7 @@ body { .card-back { background: repeating-linear-gradient(45deg, rgba(212, 175, 55, 0.22) 0 6px, transparent 6px 12px), - linear-gradient(160deg, var(--navy-700), var(--navy-900)); + linear-gradient(160deg, #14274f, #0a142e); border: 1px solid rgba(212, 175, 55, 0.45); box-shadow: 0 6px 14px rgba(0, 0, 0, 0.4), inset 0 0 0 2px rgba(212, 175, 55, 0.25); } diff --git a/src/components/HomeScreen.tsx b/src/components/HomeScreen.tsx index 8bf268b..f25ef25 100644 --- a/src/components/HomeScreen.tsx +++ b/src/components/HomeScreen.tsx @@ -9,10 +9,6 @@ import { LogIn, LogOut, Play, - ShoppingBag, - Trophy, - User, - Users, } from "lucide-react"; import { useEffect, useState } from "react"; import { Zap } from "lucide-react"; @@ -27,6 +23,7 @@ import { SPEED_TARGET_SCORE } from "@/lib/online/gamification"; import { SUIT_SYMBOL } from "@/lib/hokm/types"; import { cn } from "@/lib/cn"; import { TopBar } from "./online/TopBar"; +import { NavRail } from "./online/NavRail"; export function HomeScreen() { const { t, toggle, locale } = useI18n(); @@ -73,116 +70,104 @@ export function HomeScreen() { const Chevron = locale === "fa" ? ChevronLeft : ChevronRight; return ( -
+
-
-
- -
- {/* content: single column (portrait) → two columns (landscape) */} -
+ {/* content */} +
+ - {/* ===== column A: branding + play actions ===== */} -
- - {/* logo */} - - {/* logo + title on one row (no overflow); subtitle beneath the title */} -
-
- + {/* hero: branding + play actions (stacked portrait, side-by-side landscape) */} +
+ {/* branding */} + +
+
+ +
+
+

+ {t("app.title")} +

+

{t("app.subtitle")}

+
-
-

- {t("app.title")} -

-

{t("app.subtitle")}

+
+
-
-
- -
-
+ - {/* HERO: play online */} -
- {/* glow */} -
- - - - - - - {t("menu.online")} +
- - {/* vs computer */} -
- } - title={t("menu.vsComputer")} - desc={speed ? t("speed.desc") : t("menu.vsComputerDesc")} - onClick={playVsComputer} - /> - {/* Normal / Speed mode picker */} -
- + -
-
- {/* ===== end column A ===== */} -
- {/* ===== column B: tiles + footer ===== */} -
- - {/* tiles */} -
- } label={t("menu.profile")} tint="teal" onClick={() => nav("profile")} /> - } label={t("menu.friends")} tint="sky" onClick={() => nav(isAuthed ? "friends" : "auth")} /> - } label={t("menu.leaderboard")} tint="gold" onClick={() => nav("leaderboard")} /> - } label={t("menu.shop")} tint="rose" onClick={() => nav("shop")} /> + {/* Normal / Speed mode picker */} +
+ + +
+
-
- {/* footer */} -
+
{isAuthed ? (
- {/* ===== end column B ===== */} -
- {/* ===== end content columns ===== */} -
+ +
); } -function PrimaryCard({ - icon, - title, - desc, - onClick, - primary, -}: { - icon: React.ReactNode; - title: string; - desc: string; - onClick: () => void; - primary?: boolean; -}) { - return ( - - - {icon} - - - - {title} - - - {desc} - - - - ); -} - -const TILE_TINTS: Record = { - teal: "bg-teal-500/15 text-teal-400", - sky: "bg-sky-500/15 text-sky-400", - gold: "bg-gold-500/15 text-gold-400", - rose: "bg-rose-500/15 text-rose-400", -}; - -function Tile({ - icon, - label, - tint = "gold", - onClick, -}: { - icon: React.ReactNode; - label: string; - tint?: keyof typeof TILE_TINTS; - onClick: () => void; -}) { - return ( - - - {icon} - - {label} - - ); -} - function OnlinePlayers() { const { t, locale } = useI18n(); const [count, setCount] = useState(null);