From b9d6186d68134ef78b3b08d47fff6a5567cc17d1 Mon Sep 17 00:00:00 2001 From: OoO Date: Fri, 1 May 2026 20:32:23 +0800 Subject: [PATCH] feat(frontend): sync latest MOMO Pro prototype styling --- app.py | 4 +- config.py | 2 +- docs/guides/frontend_upgrade_roadmap.md | 2 + templates/dashboard_v2.html | 34 ++ templates/edm_dashboard_v2.html | 128 +++++-- tests/test_frontend_v2_assets.py | 35 ++ web/static/css/ewoooc-shell.css | 28 +- web/static/css/ewoooc-tokens.css | 481 +++++++++++++++++------- 8 files changed, 531 insertions(+), 183 deletions(-) diff --git a/app.py b/app.py index e8af78a..5baa4f3 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-05-01 V10.67: Show AI product pick evidence gaps on dashboard -SYSTEM_VERSION = "V10.67" +# 🚩 2026-05-01 V10.68: Sync latest MOMO Pro prototype styling to production frontend +SYSTEM_VERSION = "V10.68" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/config.py b/config.py index c350986..cdeda4c 100644 --- a/config.py +++ b/config.py @@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.67" +SYSTEM_VERSION = "V10.68" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/guides/frontend_upgrade_roadmap.md b/docs/guides/frontend_upgrade_roadmap.md index 75a7fb5..b378aab 100644 --- a/docs/guides/frontend_upgrade_roadmap.md +++ b/docs/guides/frontend_upgrade_roadmap.md @@ -44,6 +44,8 @@ 這樣做的好處是可以快速讓正式系統出現新版前端,同時避免一次改動部署架構、認證、API、模板與容器。 +`MOMO Pro/` 目錄定位為「最新前端原型來源」,不是正式 Flask runtime 依賴。正式落地時只抽取必要的設計 token、互動模式與版面結構到 `web/static/css/`、`templates/`、路由與測試;不把 prototype 的 React/Babel 工作檔直接部署成正式頁。待全站核心頁面同步完成後,再將 `MOMO Pro/` 歸檔或移除,避免專案長期保留兩套前端真相來源。 + ## 4. 分階段工作 ### Phase 0:設計系統落地 diff --git a/templates/dashboard_v2.html b/templates/dashboard_v2.html index fd1cc16..e086c8d 100644 --- a/templates/dashboard_v2.html +++ b/templates/dashboard_v2.html @@ -17,12 +17,25 @@ } .dashboard-section-label .num { + display: inline-flex; + align-items: center; + gap: 10px; color: var(--momo-text-tertiary); font-size: 11px; font-weight: 800; letter-spacing: 0.08em; } + .dashboard-section-label .num::after { + display: inline-block; + width: 56px; + height: 6px; + content: ""; + background-image: radial-gradient(circle, var(--momo-text-tertiary) 1px, transparent 1px); + background-size: 6px 6px; + opacity: 0.5; + } + .dashboard-section-label .title { color: var(--momo-text-primary); font-size: 13px; @@ -46,11 +59,27 @@ } .dashboard-kpi { + position: relative; min-width: 0; padding: 20px 24px; + overflow: hidden; border-right: 1px solid var(--momo-border-light); } + .dashboard-kpi::before { + position: absolute; + inset: 0; + content: ""; + background-image: radial-gradient(circle, rgba(42, 37, 32, 0.12) 1px, transparent 1px); + background-size: 8px 8px; + opacity: 0.28; + pointer-events: none; + } + + .dashboard-kpi > * { + position: relative; + } + .dashboard-kpi:last-child { border-right: 0; } @@ -60,6 +89,11 @@ background: var(--momo-ink); } + .dashboard-kpi.is-accent::before { + background-image: radial-gradient(circle, rgba(250, 247, 240, 0.18) 1px, transparent 1px); + opacity: 0.55; + } + .dashboard-kpi-label { margin-bottom: 10px; color: var(--momo-text-tertiary); diff --git a/templates/edm_dashboard_v2.html b/templates/edm_dashboard_v2.html index 070abd7..f8bfdff 100644 --- a/templates/edm_dashboard_v2.html +++ b/templates/edm_dashboard_v2.html @@ -68,34 +68,51 @@ position: relative; min-height: 220px; overflow: hidden; - padding: 24px; - color: #fff; - background: linear-gradient(160deg, #dc2626 0%, #ea580c 52%, #f97316 100%); + padding: 28px 32px; + color: var(--momo-text-primary); + background: var(--momo-bg-paper); + border: 1px solid var(--momo-border-light); border-radius: 8px; + --campaign-accent: var(--momo-warm-caramel); + } + + .campaign-hero.is-edm { + --campaign-accent: var(--momo-warm-caramel); } .campaign-hero.is-festival { - background: linear-gradient(160deg, #7c3aed 0%, #6d28d9 52%, #5b21b6 100%); + --campaign-accent: var(--momo-warm-honey); } .campaign-hero.is-mothers-day { - background: linear-gradient(160deg, #db2777 0%, #be185d 52%, #9d174d 100%); + --campaign-accent: var(--momo-warm-rust); } .campaign-hero.is-valentine-520 { - background: linear-gradient(160deg, #e11d48 0%, #be123c 52%, #9f1239 100%); + --campaign-accent: var(--momo-warm-mahogany); } .campaign-hero.is-labor-day { - background: linear-gradient(160deg, #0891b2 0%, #0e7490 52%, #155e75 100%); + --campaign-accent: var(--momo-warm-earth); } .campaign-hero::before { position: absolute; inset: 0; content: ""; - background-image: radial-gradient(circle, rgba(255, 255, 255, 0.10) 1px, transparent 1px); - background-size: 12px 12px; + background-image: radial-gradient(circle, rgba(42, 37, 32, 0.12) 1px, transparent 1px); + background-size: 8px 8px; + opacity: 0.6; + } + + .campaign-hero::after { + position: absolute; + top: 0; + left: 0; + width: 64px; + height: 4px; + content: ""; + background: var(--campaign-accent); } .campaign-hero-content { @@ -106,6 +123,16 @@ gap: 16px; } + .campaign-hero-content::before { + position: absolute; + top: -28px; + left: -32px; + width: 4px; + height: 64px; + content: ""; + background: var(--campaign-accent); + } + .campaign-eyebrow { display: flex; align-items: center; @@ -114,21 +141,24 @@ .campaign-eyebrow-label { padding: 3px 10px; - border: 1px solid rgba(255, 255, 255, 0.26); - border-radius: var(--momo-radius-pill); - background: rgba(255, 255, 255, 0.16); + color: var(--campaign-accent); + border: 1px solid var(--campaign-accent); + border-radius: 2px; + background: var(--momo-bg-surface); font-size: 10px; font-weight: 800; - letter-spacing: 0.08em; + letter-spacing: 0.12em; text-transform: uppercase; } .campaign-title { margin: 0; - font-size: 36px; + color: var(--momo-text-primary); + font-family: var(--momo-font-display); + font-size: 40px; font-weight: 800; - letter-spacing: 0; - line-height: 1.12; + letter-spacing: -0.025em; + line-height: 1.05; } .campaign-meta-grid { @@ -140,11 +170,15 @@ .campaign-meta-label { margin-bottom: 2px; - color: rgba(255, 255, 255, 0.62); + color: var(--momo-text-tertiary); + font-size: 10px; + font-weight: 800; + letter-spacing: 0.10em; + text-transform: uppercase; } .campaign-meta-value { - color: #fff; + color: var(--momo-text-primary); font-weight: 800; } @@ -161,18 +195,18 @@ gap: 6px; min-height: 32px; padding: 8px 14px; - color: #fff; - background: rgba(255, 255, 255, 0.16); - border: 1px solid rgba(255, 255, 255, 0.28); + color: var(--momo-text-primary); + background: var(--momo-bg-surface); + border: 1px solid var(--momo-border-light); border-radius: 4px; font-size: 13px; font-weight: 800; } .campaign-action.is-light { - color: var(--momo-ink); - background: #fff; - border-color: #fff; + color: var(--momo-text-inverse); + background: var(--momo-ink); + border-color: var(--momo-ink); } .campaign-kpi-panel { @@ -197,22 +231,35 @@ .campaign-kpi-grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 14px; - padding: 20px; + gap: 0; + padding: 0; flex: 1; } .campaign-kpi { - padding: 12px; - background: var(--momo-bg-paper); - border: 1px solid var(--momo-border-light); - border-radius: 6px; + padding: 20px; + background: transparent; + border-right: 1px solid var(--momo-border-light); + border-bottom: 1px solid var(--momo-border-light); + border-radius: 0; + } + + .campaign-kpi:nth-child(2n) { + border-right: 0; + } + + .campaign-kpi:nth-last-child(-n+2) { + border-bottom: 0; } .campaign-kpi-label { margin-bottom: 4px; color: var(--momo-text-secondary); - font-size: 11px; + font-family: var(--momo-font-family-mono); + font-size: 10px; + font-weight: 800; + letter-spacing: 0.10em; + text-transform: uppercase; } .campaign-kpi-value { @@ -243,12 +290,25 @@ } .campaign-section-label .num { + display: inline-flex; + align-items: center; + gap: 10px; color: var(--momo-text-tertiary); font-size: 11px; font-weight: 800; letter-spacing: 0.08em; } + .campaign-section-label .num::after { + display: inline-block; + width: 56px; + height: 6px; + content: ""; + background-image: radial-gradient(circle, var(--momo-text-tertiary) 1px, transparent 1px); + background-size: 6px 6px; + opacity: 0.5; + } + .campaign-section-label .title { color: var(--momo-text-primary); font-size: 13px; @@ -364,8 +424,8 @@ .campaign-table th { padding: 11px 14px; - color: var(--momo-text-tertiary); - background: var(--momo-bg-paper); + color: var(--momo-text-inverse); + background: var(--momo-ink); border-bottom: 1px solid var(--momo-border-light); font-family: var(--momo-font-family-mono); font-size: 10px; @@ -625,7 +685,7 @@
CAMPAIGN - ID · {{ current_promo_page|upper }} + ID · {{ current_promo_page|upper }}

{{ page_title }}

diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 0f2cf0c..ec812ef 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -40,6 +40,41 @@ def test_frontend_v2_shell_uses_real_runtime_context(): assert all(marker not in combined for marker in forbidden_markers) +def test_frontend_v2_syncs_latest_momo_pro_prototype_tokens_and_shell(): + tokens = (ROOT / "web/static/css/ewoooc-tokens.css").read_text(encoding="utf-8") + shell = (ROOT / "web/static/css/ewoooc-shell.css").read_text(encoding="utf-8") + + assert "MOMO Pro × Nothing × Claude" in tokens + assert "--momo-warm-caramel" in tokens + assert "--momo-tag-honey-bg" in tokens + assert "--momo-text-body" in tokens + assert "--momo-accent-strong" in tokens + assert "--momo-border-strong" in tokens + assert "--momo-sidebar-width" in tokens + assert ".momo-app button:not(.btn):not(.btn-close)" in tokens + + assert "background: #1f1a14;" in shell + assert "rgba(250, 247, 240, 0.08)" in shell + assert "var(--momo-text-inverse)" in shell + + +def test_campaign_v2_uses_latest_warm_hero_without_fake_data(): + template = (ROOT / "templates/edm_dashboard_v2.html").read_text(encoding="utf-8") + + assert "--campaign-accent: var(--momo-warm-caramel)" in template + assert "--campaign-accent: var(--momo-warm-honey)" in template + assert "--campaign-accent: var(--momo-warm-rust)" in template + assert "background: var(--momo-bg-paper)" in template + assert "radial-gradient(circle, rgba(42, 37, 32, 0.12) 1px, transparent 1px)" in template + assert ".campaign-hero::after" in template + assert "font-family: var(--momo-font-display)" in template + assert "linear-gradient(160deg" not in template + assert "#7c3aed" not in template + assert "#0891b2" not in template + assert "mock" not in template.lower() + assert "假商品" not in template + + def test_dashboard_v2_is_production_default_and_uses_real_dashboard_data(): route_source = (ROOT / "routes/dashboard_routes.py").read_text(encoding="utf-8") dashboard = (ROOT / "templates/dashboard_v2.html").read_text(encoding="utf-8") diff --git a/web/static/css/ewoooc-shell.css b/web/static/css/ewoooc-shell.css index 3e38ab4..cec73ee 100644 --- a/web/static/css/ewoooc-shell.css +++ b/web/static/css/ewoooc-shell.css @@ -20,8 +20,8 @@ body.momo-v2-body { display: flex; flex-direction: column; overflow: hidden; - background: var(--momo-bg-paper); - border-right: 1px solid var(--momo-border-light); + background: #1f1a14; + border-right: 1px solid rgba(250, 247, 240, 0.08); } .momo-sidebar-logo { @@ -30,9 +30,9 @@ body.momo-v2-body { gap: 10px; height: var(--momo-topbar-height); padding: 0 20px; - color: var(--momo-text-primary); + color: var(--momo-text-inverse); text-decoration: none; - border-bottom: 1px solid var(--momo-border-light); + border-bottom: 1px solid rgba(250, 247, 240, 0.08); } .momo-logo-mark { @@ -44,8 +44,8 @@ body.momo-v2-body { grid-template-rows: repeat(3, 1fr); gap: 1.5px; padding: 5px; - color: var(--momo-text-inverse); - background: var(--momo-ink); + color: var(--momo-ink); + background: var(--momo-text-inverse); border-radius: 2px; } @@ -65,7 +65,7 @@ body.momo-v2-body { } .momo-brand-name { - color: var(--momo-text-primary); + color: var(--momo-text-inverse); font-size: 18px; font-weight: 800; letter-spacing: -0.02em; @@ -73,7 +73,7 @@ body.momo-v2-body { .momo-brand-subtitle { margin-top: 3px; - color: var(--momo-text-secondary); + color: rgba(250, 247, 240, 0.55); } .momo-nav { @@ -91,14 +91,14 @@ body.momo-v2-body { align-items: center; gap: 8px; padding: 14px 12px 8px; - color: var(--momo-text-tertiary); + color: rgba(250, 247, 240, 0.40); } .momo-nav-group-title::after { content: ""; flex: 1; height: 1px; - background: var(--momo-border-light); + background: rgba(250, 247, 240, 0.08); } .momo-nav-link { @@ -110,7 +110,7 @@ body.momo-v2-body { padding: 9px 12px; border: 1px solid transparent; border-radius: var(--momo-radius-md); - color: var(--momo-text-primary); + color: rgba(250, 247, 240, 0.72); font-size: var(--momo-font-size-sm); font-weight: var(--momo-font-weight-medium); text-decoration: none; @@ -118,8 +118,8 @@ body.momo-v2-body { } .momo-nav-link:hover { - color: var(--momo-text-primary); - background: var(--momo-accent-soft); + color: var(--momo-text-inverse); + background: rgba(250, 247, 240, 0.06); } .momo-nav-link.is-active { @@ -447,7 +447,7 @@ body.momo-v2-body { padding: 0; overflow: hidden; color: transparent; - border: 2px solid var(--momo-bg-paper); + border: 2px solid #1f1a14; border-radius: 50%; transform: translate(12px, -10px); } diff --git a/web/static/css/ewoooc-tokens.css b/web/static/css/ewoooc-tokens.css index 8fa1f98..2b529cd 100644 --- a/web/static/css/ewoooc-tokens.css +++ b/web/static/css/ewoooc-tokens.css @@ -1,193 +1,410 @@ /** - * EwoooC Frontend V2 tokens. - * Source of truth: MOMO Pro/design-tokens.css. + * MOMO Pro × Nothing × Claude 設計 Token v2.0 + * — Nothing 的點陣骨架(黑白、像素、工業) + * — Claude 的暖米基底(#f0eee9、焦糖橘 #c96442) */ :root { - --momo-bg-body: #ebe6dc; - --momo-bg-surface: #faf7f0; - --momo-bg-elevated: #fdfaf3; - --momo-bg-subtle: #e2dccf; - --momo-bg-muted: #cfc7b5; - --momo-bg-paper: #f3eee2; + /* ===== 1. 色彩 ===== */ + /* Claude 暖系基底(紙張 / 米色)— 加深暖度 */ + --momo-bg-body: #ebe6dc; /* Claude 米色頁面底(加深暖度)*/ + --momo-bg-surface: #faf7f0; /* 卡片改為米白,不純白 */ + --momo-bg-elevated: #fdfaf3; + --momo-bg-subtle: #e2dccf; /* 米色微深 */ + --momo-bg-muted: #cfc7b5; + --momo-bg-paper: #f3eee2; /* 卡片紙張感 */ - --momo-ink: #2a2520; - --momo-ink-strong: #1a1612; - --momo-ink-soft: #3d362f; - --momo-line: #2a2520; - --momo-line-soft: rgba(42, 37, 32, 0.18); - --momo-line-faint: rgba(42, 37, 32, 0.10); + /* Nothing 黑(用暖墨色,不純黑)*/ + --momo-ink: #2a2520; /* 暖墨色 */ + --momo-ink-strong: #1a1612; + --momo-ink-soft: #3d362f; + --momo-line: #2a2520; + --momo-line-soft: rgba(42,37,32,0.18); + --momo-line-faint: rgba(42,37,32,0.10); - --momo-accent: #c96442; - --momo-accent-50: #fbf2ef; - --momo-accent-100: #f5e1d9; - --momo-accent-200: #ecc3b3; - --momo-accent-500: #c96442; - --momo-accent-600: #b1543a; - --momo-accent-700: #8f4530; - --momo-accent-soft: rgba(201, 100, 66, 0.12); + /* Claude 焦糖橘(accent) */ + --momo-accent: #c96442; /* 主 accent */ + --momo-accent-50: #fbf2ef; + --momo-accent-100: #f5e1d9; + --momo-accent-200: #ecc3b3; + --momo-accent-500: #c96442; + --momo-accent-600: #b1543a; + --momo-accent-700: #8f4530; + --momo-accent-soft: rgba(201,100,66,0.12); + --momo-accent-strong: var(--momo-accent-700); - --momo-primary: var(--momo-accent); - --momo-primary-50: var(--momo-accent-50); - --momo-primary-100: var(--momo-accent-100); - --momo-primary-200: var(--momo-accent-200); - --momo-primary-500: var(--momo-accent-500); - --momo-primary-600: var(--momo-accent-600); - --momo-primary-700: var(--momo-accent-700); + /* ===== EwoooC 暖色家族(全站運用) ===== + * 全部留在暖色域(紅/橘/金/土),不混入冷色 + * 用法:活動頁 / 標籤色 / 圖表分類色 / 各區段視覺主軸 + */ + --momo-warm-caramel: #c96442; /* 焦糖橘 — 主 accent / 限時搶購 */ + --momo-warm-honey: #b88416; /* 蜂蜜金 — 1.1 狂歡 / 警示 */ + --momo-warm-rust: #b5342f; /* 暖紅 — 母親節 / danger */ + --momo-warm-mahogany: #8f4530; /* 深焦糖 — 520 情人節 / 強調 */ + --momo-warm-earth: #8a5a2b; /* 焦土 — 勞動節 / 中性暖 */ + /* 對應淡色(背景 / 軟標籤用) */ + --momo-warm-caramel-soft: rgba(201,100,66,0.12); + --momo-warm-honey-soft: rgba(184,132,22,0.12); + --momo-warm-rust-soft: rgba(181,52,47,0.12); + --momo-warm-mahogany-soft:rgba(143,69,48,0.12); + --momo-warm-earth-soft: rgba(138,90,43,0.12); - --momo-success: #2a7a3f; - --momo-success-bg: #e3ebd9; - --momo-success-border: #c5d4b0; - --momo-success-text: #1f5a2d; - --momo-danger: #b5342f; - --momo-danger-bg: #f0d8d4; - --momo-danger-border: #d9b1ac; - --momo-danger-text: #7d2520; - --momo-warning: #b88416; - --momo-warning-bg: #f3e7c4; - --momo-warning-border: #d9c590; - --momo-warning-text: #6e500e; - --momo-info: #2d5d80; - --momo-info-bg: #d8e2ea; - --momo-info-border: #b5c5d2; - --momo-info-text: #1d3e54; + /* ===== 標籤色系統(Tag / Chip / Badge 統一規範) ===== + * 規則: + * 1. 全部留在暖色域(caramel/honey/rust/mahogany/earth + 中性 ink) + * 2. 一個標籤 = 一組 (bg + border + text),三者一起用,不要混用 + * 3. 文字色一定是 *-text,已對 *-bg 做過 WCAG AA 對比驗證 + * 4. 不要任意指定 #色碼到標籤上 + * + * 用途分配(語義化,不是看心情挑色): + * caramel → 主強調 / 推薦 / 焦點品 / accent 動作 + * honey → 警示 / AI 挑品 / 待處理 / 提醒 + * rust → 危險 / 漲價 / 異常 / 警報 + * mahogany→ 次強調 / 已選 / 已標記 + * earth → 中性分類 / 一般標籤 / 商品分類 + * ink → 平台標 / 狀態標 / mono 標籤(PChome 領先 / MOMO 領先) + * muted → 弱資訊 / 待比對 / 已下架 + */ - --momo-text-primary: #2a2520; - --momo-text-secondary: #645c52; - --momo-text-tertiary: #9b9081; - --momo-text-disabled: #c4baa8; - --momo-text-inverse: #faf7f0; - --momo-text-link: #c96442; - --momo-text-link-hover: #8f4530; + /* caramel — 主焦點 */ + --momo-tag-caramel-bg: #f5e1d9; + --momo-tag-caramel-border: #ecc3b3; + --momo-tag-caramel-text: #7a3520; /* AA on bg */ - --momo-border: #2a2520; - --momo-border-light: rgba(42, 37, 32, 0.16); - --momo-border-dark: #2a2520; - --momo-border-focus: #c96442; - --momo-divider: rgba(42, 37, 32, 0.12); - --momo-bg-overlay: rgba(26, 26, 26, 0.70); - --momo-bg-backdrop: rgba(26, 26, 26, 0.30); + /* honey — 警示 / AI */ + --momo-tag-honey-bg: #f3e7c4; + --momo-tag-honey-border: #d9c590; + --momo-tag-honey-text: #6e500e; /* AA on bg */ - --momo-font-display: "JetBrains Mono", "Space Mono", "SF Mono", Menlo, Consolas, monospace; - --momo-font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", sans-serif; - --momo-font-family-mono: "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace; + /* rust — 漲價 / 異常 */ + --momo-tag-rust-bg: #f0d8d4; + --momo-tag-rust-border: #d9b1ac; + --momo-tag-rust-text: #7d2520; /* AA on bg */ - --momo-font-size-xs: 0.75rem; - --momo-font-size-sm: 0.8125rem; - --momo-font-size-base: 0.9375rem; - --momo-font-size-lg: 1.0625rem; - --momo-font-size-xl: 1.625rem; - --momo-font-size-2xl: 2.25rem; + /* mahogany — 次強調 */ + --momo-tag-mahogany-bg: #ecdcd4; + --momo-tag-mahogany-border:#d4b8ab; + --momo-tag-mahogany-text: #5e2e20; - --momo-font-weight-normal: 400; - --momo-font-weight-medium: 500; - --momo-font-weight-semibold: 600; - --momo-font-weight-bold: 700; + /* earth — 中性分類(最常用,不搶眼)*/ + --momo-tag-earth-bg: #ede4d2; + --momo-tag-earth-border: #d4c5a3; + --momo-tag-earth-text: #5a3f1c; - --momo-line-height-tight: 1.15; - --momo-line-height-base: 1.5; - --momo-line-height-loose: 1.7; + /* ink — 平台 / mono */ + --momo-tag-ink-bg: #2a2520; + --momo-tag-ink-border: #2a2520; + --momo-tag-ink-text: #faf7f0; - --momo-space-1: 0.25rem; - --momo-space-2: 0.5rem; - --momo-space-3: 0.75rem; - --momo-space-4: 1rem; - --momo-space-5: 1.5rem; - --momo-space-6: 2rem; - --momo-space-7: 3rem; - --momo-space-8: 4rem; + /* muted — 弱資訊 */ + --momo-tag-muted-bg: #e2dccf; + --momo-tag-muted-border: rgba(42,37,32,0.16); + --momo-tag-muted-text: #645c52; - --momo-shadow-sm: 0 0 0 1px rgba(26, 26, 26, 0.08); - --momo-shadow-md: 0 0 0 1px rgba(26, 26, 26, 0.10); - --momo-shadow-lg: 0 12px 40px -8px rgba(26, 26, 26, 0.18), 0 0 0 1px rgba(26, 26, 26, 0.10); - --momo-shadow-colored: 0 0 0 2px rgba(201, 100, 66, 0.25); + /* success / 降價(綠色保留,但去飽和對齊暖底)*/ + --momo-tag-success-bg: #dde6cf; + --momo-tag-success-border: #c2d0a6; + --momo-tag-success-text: #1f5a2d; - --momo-radius-sm: 0.125rem; - --momo-radius-md: 0.25rem; - --momo-radius-lg: 0.375rem; - --momo-radius-pill: 50rem; - --momo-radius-circle: 50%; + /* ===== 字體層級規範(避免眼睛疲勞) ===== + * 原則: + * 1. 主要內文 ≥ 13px (0.8125rem);說明文 ≥ 12px (0.75rem) 但只用於 mono / 數據 + * 2. 11px / 10px 只給 LABEL(uppercase 標籤),且必須 letter-spacing ≥ 0.06em + * 3. 顏色用 text-primary / secondary / tertiary 三層;tertiary 不放重點資訊 + * 4. 數據 (mono) 用 tnum 等寬,避免跳動 + * 5. line-height:標題 1.2 / 內文 1.5 / 鬆散段落 1.7 + */ + --momo-text-display: 2rem; /* 32px - hero 大數字 */ + --momo-text-headline: 1.5rem; /* 24px - 區塊大數字 */ + --momo-text-title: 1.0625rem; /* 17px - 卡片標題 */ + --momo-text-body: 0.875rem; /* 14px - 內文(提升 1px,減少疲勞)*/ + --momo-text-body-sm: 0.8125rem; /* 13px - 表格內文最小值 */ + --momo-text-meta: 0.75rem; /* 12px - mono 數據 / 次要說明 */ + --momo-text-label: 0.6875rem; /* 11px - LABEL only */ + --momo-text-label-tiny: 0.625rem; /* 10px - 角落標籤 only */ - --momo-duration-fast: 0.12s; - --momo-duration-normal: 0.2s; - --momo-duration-slow: 0.4s; - --momo-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); - --momo-ease-out: cubic-bezier(0, 0, 0.2, 1); + /* 沿用 primary 命名以相容(指向 accent) */ + --momo-primary: var(--momo-accent); + --momo-primary-50: var(--momo-accent-50); + --momo-primary-100: var(--momo-accent-100); + --momo-primary-200: var(--momo-accent-200); + --momo-primary-300: var(--momo-accent-200); + --momo-primary-400: var(--momo-accent-500); + --momo-primary-500: var(--momo-accent-500); + --momo-primary-600: var(--momo-accent-600); + --momo-primary-700: var(--momo-accent-700); + --momo-primary-800: var(--momo-accent-700); + --momo-primary-900: #5e2e20; + + /* 導航(Nothing 黑) */ + --momo-nav-start: #1a1a1a; + --momo-nav-end: #000000; + --momo-nav-text: #ffffff; + --momo-nav-text-muted: rgba(255,255,255,0.55); + --momo-nav-hover: rgba(255,255,255,0.08); + --momo-nav-active: rgba(255,255,255,0.14); + + /* 漸層 → Nothing 風幾乎不用,保留低調黑灰漸層 */ + --momo-gradient-primary: #1a1a1a; + --momo-gradient-nav: linear-gradient(180deg, #1a1a1a 0%, #000 100%); + --momo-gradient-success: #2a7a3f; + --momo-gradient-danger: #b5342f; + --momo-gradient-warning: #b88416; + --momo-gradient-info: #2d5d80; + --momo-gradient-subtle: linear-gradient(180deg, #f7f5ef 0%, #ebe8e1 100%); + + /* 狀態色(去飽和化、配合米色底) */ + --momo-success: #2a7a3f; + --momo-success-bg: #e3ebd9; + --momo-success-border: #c5d4b0; + --momo-success-text: #1f5a2d; + --momo-danger: #b5342f; + --momo-danger-bg: #f0d8d4; + --momo-danger-border: #d9b1ac; + --momo-danger-text: #7d2520; + --momo-warning: #b88416; + --momo-warning-bg: #f3e7c4; + --momo-warning-border: #d9c590; + --momo-warning-text: #6e500e; + --momo-info: #2d5d80; + --momo-info-bg: #d8e2ea; + --momo-info-border: #b5c5d2; + --momo-info-text: #1d3e54; + + /* 文字(暖墨)*/ + --momo-text-primary: #2a2520; + --momo-text-secondary: #645c52; + --momo-text-tertiary: #9b9081; + --momo-text-disabled: #c4baa8; + --momo-text-inverse: #faf7f0; + --momo-text-link: #c96442; + --momo-text-link-hover: #8f4530; + + /* 邊框(暖色調線)*/ + --momo-border: #2a2520; + --momo-border-light: rgba(42,37,32,0.16); + --momo-border-strong: rgba(42,37,32,0.42); + --momo-border-dark: #2a2520; + --momo-border-focus: #c96442; + --momo-divider: rgba(42,37,32,0.12); + + /* Overlay */ + --momo-bg-overlay: rgba(26, 26, 26, 0.7); + --momo-bg-backdrop: rgba(26, 26, 26, 0.3); + + /* ===== 2. Typography ===== */ + /* 標題:JetBrains Mono / Space Mono — 帶等寬機械感(替代 Ndot) */ + --momo-font-display: + "JetBrains Mono", "Space Mono", "SF Mono", Menlo, Consolas, monospace; + /* 內文:中英混排 */ + --momo-font-family: + "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", + "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", + sans-serif; + --momo-font-family-base: var(--momo-font-family); + /* 等寬:數據 */ + --momo-font-family-mono: + "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace; + + --momo-font-size-xs: 0.75rem; + --momo-font-size-sm: 0.8125rem; + --momo-font-size-base: 0.9375rem; + --momo-font-size-lg: 1.0625rem; + --momo-font-size-xl: 1.625rem; + --momo-font-size-2xl: 2.25rem; + + --momo-font-weight-normal: 400; + --momo-font-weight-medium: 500; + --momo-font-weight-semibold: 600; + --momo-font-weight-bold: 700; + + --momo-line-height-tight: 1.15; + --momo-line-height-base: 1.5; + --momo-line-height-loose: 1.7; + + /* ===== 3. Spacing ===== */ + --momo-space-1: 0.25rem; + --momo-space-2: 0.5rem; + --momo-space-3: 0.75rem; + --momo-space-4: 1rem; + --momo-space-5: 1.5rem; + --momo-space-6: 2rem; + --momo-space-7: 3rem; + --momo-space-8: 4rem; + + /* ===== 4. Shadow(Nothing 風幾乎不用陰影,改用線條) ===== */ + --momo-shadow-sm: 0 0 0 1px rgba(26,26,26,0.08); + --momo-shadow-md: 0 0 0 1px rgba(26,26,26,0.10); + --momo-shadow-lg: 0 12px 40px -8px rgba(26,26,26,0.18), 0 0 0 1px rgba(26,26,26,0.10); + --momo-shadow-colored: 0 0 0 2px rgba(201,100,66,0.25); + --momo-shadow-inner: inset 0 1px 2px 0 rgba(26,26,26,0.08); + + /* ===== 5. Radius(Nothing 風偏方角,僅輕微圓角) ===== */ + --momo-radius-sm: 0.125rem; /* 2px */ + --momo-radius-md: 0.25rem; /* 4px */ + --momo-radius-lg: 0.375rem; /* 6px */ + --momo-radius-pill: 50rem; + --momo-radius-circle: 50%; + + /* ===== 6. Transition ===== */ + --momo-duration-fast: 0.12s; + --momo-duration-normal: 0.2s; + --momo-duration-slow: 0.4s; + --momo-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --momo-ease-out: cubic-bezier(0, 0, 0.2, 1); + --momo-ease-in: cubic-bezier(0.4, 0, 1, 1); --momo-transition-base: color var(--momo-duration-fast) var(--momo-ease-in-out), background-color var(--momo-duration-fast) var(--momo-ease-in-out), border-color var(--momo-duration-fast) var(--momo-ease-in-out), box-shadow var(--momo-duration-fast) var(--momo-ease-in-out); - --momo-sidebar-width: 240px; + /* ===== 7. z-index ===== */ + --momo-z-base: 1; + --momo-z-dropdown: 1000; + --momo-z-sticky: 1020; + --momo-z-fixed: 1030; + --momo-z-modal-backdrop: 1040; + --momo-z-modal: 1050; + --momo-z-popover: 1060; + --momo-z-tooltip: 1070; + --momo-z-toast: 1080; + --momo-sidebar-width: 260px; --momo-sidebar-collapsed-width: 72px; - --momo-topbar-height: 64px; -} - -.momo-app, -.momo-app * { - box-sizing: border-box; + --momo-topbar-height: 64px; } +/* 全域 */ .momo-app { - min-height: 100vh; - background: var(--momo-bg-body); - color: var(--momo-text-primary); font-family: var(--momo-font-family); font-size: var(--momo-font-size-base); line-height: var(--momo-line-height-base); + color: var(--momo-text-primary); + background-color: var(--momo-bg-body); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + letter-spacing: -0.005em; +} +.momo-app *, .momo-app *::before, .momo-app *::after { box-sizing: border-box; } +.momo-app button:not(.btn):not(.btn-close) { + font-family: inherit; cursor: pointer; border: none; background: none; padding: 0; color: inherit; +} +.momo-app input, .momo-app select, .momo-app textarea { + font-family: inherit; font-size: inherit; color: inherit; } +/* Display class for big numbers / titles in Nothing-mono */ .momo-display { font-family: var(--momo-font-display); font-feature-settings: "tnum", "ss01"; + letter-spacing: -0.02em; } - .momo-mono { font-family: var(--momo-font-family-mono); font-feature-settings: "tnum"; } - .momo-label { font-family: var(--momo-font-display); font-size: 10px; - font-weight: 600; + font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; } +/* Dot matrix 背景圖案(Nothing 招牌點陣) */ .momo-dot-bg { - background-image: radial-gradient(circle, rgba(26, 26, 26, 0.12) 1px, transparent 1px); + background-image: radial-gradient(circle, rgba(255,255,255,0.18) 1px, transparent 1px); + background-size: 8px 8px; +} +.momo-dot-bg-dark { + background-image: radial-gradient(circle, rgba(26,26,26,0.12) 1px, transparent 1px); background-size: 8px 8px; } -.momo-scroll::-webkit-scrollbar { - width: 8px; - height: 8px; +/* 滾動條 */ +.momo-scroll::-webkit-scrollbar { width: 8px; height: 8px; } +.momo-scroll::-webkit-scrollbar-track { background: transparent; } +.momo-scroll::-webkit-scrollbar-thumb { background: rgba(26,26,26,0.18); border-radius: var(--momo-radius-pill); } +.momo-scroll::-webkit-scrollbar-thumb:hover { background: rgba(26,26,26,0.32); } + +/* 動畫 */ +@keyframes momo-fade-in { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } } +@keyframes momo-slide-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } } +@keyframes momo-pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } } + +/* ===== 商品看板響應式 ===== */ +.dash-page { container-type: inline-size; } +@container (max-width: 900px) { + .dash-kpi-num { font-size: 30px !important; } + .dash-focus { grid-template-columns: 1fr 1fr !important; } +} +@container (max-width: 720px) { + .dash-kpis { grid-template-columns: repeat(2, 1fr) !important; } + .dash-kpis > div { border-right: none !important; border-bottom: 1px solid var(--momo-border-light) !important; } + .dash-kpis > div:nth-child(odd) { border-right: 1px solid var(--momo-border-light) !important; } + .dash-kpis > div:nth-child(n+3) { border-bottom: none !important; } + .dash-kpi-num { font-size: 28px !important; } + .dash-focus { grid-template-columns: 1fr !important; } +} +@media (max-width: 900px) { + .dash-focus { grid-template-columns: 1fr 1fr !important; } +} +@media (max-width: 720px) { + .dash-kpis { grid-template-columns: repeat(2, 1fr) !important; } + .dash-focus { grid-template-columns: 1fr !important; } } -.momo-scroll::-webkit-scrollbar-track { - background: transparent; +/* ===== 活動看板響應式 ===== */ +.camp-page { container-type: inline-size; } +@container (max-width: 900px) { + .camp-hero { padding: 22px 24px !important; } + .camp-hero-title { font-size: 32px !important; } + .camp-hero-meta { gap: 20px !important; } + .camp-hero-total { font-size: 28px !important; } +} +@container (max-width: 720px) { + .camp-hero { padding: 20px 20px !important; } + .camp-hero-title { font-size: 26px !important; } + .camp-hero-meta-row { flex-direction: column !important; align-items: flex-start !important; gap: 14px !important; } + .camp-hero-total-wrap { margin-left: 0 !important; text-align: left !important; } + .camp-hero-total { font-size: 24px !important; } + .camp-kpis { grid-template-columns: repeat(2, 1fr) !important; } + .camp-kpis > div { border-right: none !important; border-bottom: 1px solid var(--momo-border-light) !important; } + .camp-kpis > div:nth-child(odd) { border-right: 1px solid var(--momo-border-light) !important; } + .camp-kpis > div:nth-child(n+3) { border-bottom: none !important; } + .camp-kpi-num { font-size: 28px !important; } + .camp-th-cat, .camp-td-cat { display: none !important; } + .camp-table th, .camp-table td { padding: 12px 12px !important; } +} +@media (max-width: 900px) { + .camp-hero { padding: 22px 24px !important; } + .camp-hero-title { font-size: 32px !important; } +} +@media (max-width: 720px) { + .camp-hero-meta-row { flex-direction: column !important; align-items: flex-start !important; gap: 14px !important; } + .camp-hero-total-wrap { margin-left: 0 !important; text-align: left !important; } + .camp-kpis { grid-template-columns: repeat(2, 1fr) !important; } + .camp-th-cat, .camp-td-cat { display: none !important; } } -.momo-scroll::-webkit-scrollbar-thumb { - background: rgba(26, 26, 26, 0.18); - border-radius: var(--momo-radius-pill); +/* Topbar responsive */ +.momo-topbar { container-type: inline-size; } +@container (max-width: 1024px) { + .momo-schedule-pill { display: none !important; } } - -.momo-scroll::-webkit-scrollbar-thumb:hover { - background: rgba(26, 26, 26, 0.32); +@container (max-width: 880px) { + .momo-user-meta { display: none !important; } } - -@keyframes momo-pulse-dot { - 0%, - 100% { - opacity: 1; - } - - 50% { - opacity: 0.35; - } +@container (max-width: 720px) { + .momo-search-text { display: none !important; } +} +/* fallback:若瀏覽器不支援 container query,仍用 viewport */ +@media (max-width: 1024px) { + .momo-schedule-pill { display: none !important; } +} +@media (max-width: 880px) { + .momo-user-meta { display: none !important; } +} +@media (max-width: 720px) { + .momo-search-text { display: none !important; } }