feat(frontend): sync latest MOMO Pro prototype styling
All checks were successful
CD Pipeline / deploy (push) Successful in 2m18s

This commit is contained in:
OoO
2026-05-01 20:32:23 +08:00
parent 066cf1846f
commit b9d6186d68
8 changed files with 531 additions and 183 deletions

4
app.py
View File

@@ -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 防護函數

View File

@@ -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 # 用於模板顯示

View File

@@ -44,6 +44,8 @@
這樣做的好處是可以快速讓正式系統出現新版前端同時避免一次改動部署架構、認證、API、模板與容器。
`MOMO Pro/` 目錄定位為「最新前端原型來源」,不是正式 Flask runtime 依賴。正式落地時只抽取必要的設計 token、互動模式與版面結構到 `web/static/css/``templates/`、路由與測試;不把 prototype 的 React/Babel 工作檔直接部署成正式頁。待全站核心頁面同步完成後,再將 `MOMO Pro/` 歸檔或移除,避免專案長期保留兩套前端真相來源。
## 4. 分階段工作
### Phase 0設計系統落地

View File

@@ -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);

View File

@@ -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 @@
<div class="campaign-hero-content">
<div class="campaign-eyebrow">
<span class="campaign-eyebrow-label">CAMPAIGN</span>
<span class="momo-mono" style="font-size:11px;color:rgba(255,255,255,.72);">ID · {{ current_promo_page|upper }}</span>
<span class="momo-mono" style="font-size:11px;color:var(--momo-text-tertiary);">ID · {{ current_promo_page|upper }}</span>
</div>
<h1 class="campaign-title">{{ page_title }}</h1>
<div class="campaign-meta-grid momo-mono">

View File

@@ -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")

View File

@@ -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);
}

View File

@@ -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 只給 LABELuppercase 標籤),且必須 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. ShadowNothing 風幾乎不用陰影,改用線條) ===== */
--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. RadiusNothing 風偏方角,僅輕微圓角) ===== */
--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; }
}