From 12ddaba23140915ef9c2661eda331793aa727ebe Mon Sep 17 00:00:00 2001 From: kvtodev Date: Tue, 14 Jul 2026 11:52:56 +0800 Subject: [PATCH 1/3] extract color schema --- .../shared-memory/decision-log.md | 2 +- ...项目基线】当å‰äº§å“与工程约æŸ-2026-05-15.md | 2 +- packages/shared/src/theme.css | 538 +++++++++++++++++ packages/shared/src/theme.test.ts | 45 ++ src/index.css | 539 +----------------- src/index.test.ts | 20 +- 6 files changed, 602 insertions(+), 544 deletions(-) create mode 100644 packages/shared/src/theme.css create mode 100644 packages/shared/src/theme.test.ts diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 15f59ed04..35dcfa189 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2632,7 +2632,7 @@ # 2026-05-20 陶泥儿主视觉é…色回收为暖白/陶土橙 - èƒŒæ™¯ï¼šç”¨æˆ·è¦æ±‚åªæ›¿æ¢äº§å“å„界é¢çš„ UI é¢œè‰²ï¼Œä¸æ”¹å¸ƒå±€ï¼Œå¹¶ä»¥ä¸¤å¼ é™¶æ³¥å„¿ä¸»è§†è§‰å›¾ä½œä¸ºé…è‰²ä¾æ®ã€‚ -- 决策:平å°äº®è‰²ä¸»é¢˜çš„主色回收到暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›åŽå°ç®¡ç†ä¹ŸåŒæ­¥åˆ‡æ¢åˆ°åŒä¸€æš–橙体系。主题å˜é‡ä¼˜å…ˆé€šè¿‡ `src/index.css` çš„ `--platform-*` token 统一控制,零散组件åªåšå¿…è¦çš„局部替æ¢ã€‚ +- 决策:平å°äº®è‰²ä¸»é¢˜çš„主色回收到暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›åŽå°ç®¡ç†ä¹ŸåŒæ­¥åˆ‡æ¢åˆ°åŒä¸€æš–橙体系。主题å˜é‡ä¼˜å…ˆé€šè¿‡ `packages/shared/src/platform-theme.css` çš„ `--platform-*` token 统一控制,零散组件åªåšå¿…è¦çš„局部替æ¢ã€‚ - å½±å“范围:主站平å°å£³å±‚ã€å¸¸ç”¨è¡¨å• / 按钮 / å¡ç‰‡ / 背景ã€åŽå°ç®¡ç† UIã€ä¸šåŠ¡è¿›åº¦æ¡å’Œå°æ¸¸æˆç»“æžœæ¡çš„通用强调色。 - éªŒè¯æ–¹å¼ï¼šä¼˜å…ˆæ£€æŸ¥ `src/index.css` 与 `apps/admin-web/src/styles/admin.css` 是å¦è¿˜å­˜åœ¨æ—§ç²‰è‰²ä¸»è‰²ï¼›å†ç”¨ç¼–ç æ£€æŸ¥å’Œå¯æ‰§è¡Œçš„æœ¬åœ° typecheck / build 验è¯ã€‚ - å…³è”æ–‡æ¡£ï¼š`docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md`。 diff --git a/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md b/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md index 8665e27cf..74366143f 100644 --- a/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md +++ b/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md @@ -108,7 +108,7 @@ server-rs + Axum + SpacetimeDB 12. “我的â€é¡µæ³¥ç‚¹ä½™é¢ã€ç´¯è®¡æ¸¸çŽ©ã€å·²çŽ©æ¸¸æˆä¸‰å¼ ç»Ÿè®¡å¡åªå±•示å„自标签和值,三个统计 icon 使用å°å°ºå¯¸æ™®é€š UI æ¡£ä½ï¼Œå†…容䏿¢è¡Œï¼Œä¸åœ¨ç»Ÿè®¡åŒºåº•éƒ¨å±•ç¤ºâ€œæ›´æ–°äºŽâ€æ—¶é—´ï¼›ç§»åŠ¨ç«¯æ˜µç§°ã€å¸¸ç”¨åŠŸèƒ½å’Œæ³•å¾‹ä¿¡æ¯ä¹Ÿåº”ä¿æŒ `10px` 到 `14px` 的普通 UI å­—å·åŒºé—´ï¼Œé¿å…å±•ç¤ºçº§å­—å·æŒ¤åŽ‹å†…å®¹ã€‚ 13. 移动端“我的â€é¡µéœ€è¦å…¼å®¹çª„å±ï¼šå¤´åƒ / 昵称 / é™¶æ³¥å·ã€ä¸‰å¼ ç»Ÿè®¡å¡ã€äº”项常用功能和法律信æ¯éƒ½å¿…须能在底部固定 TabBar 上方完整滚动露出,ä¸å¾—与底部 dockã€åˆ˜æµ· safe-area 或相邻 UI å…ƒç´ é®æŒ¡é‡å ã€‚ 14. RPG ç­‰è¿è¡Œæ€çš„æˆ˜æ–—飘字ã€è¡€é‡å˜åŒ–å’Œå³æ—¶å馈必须在暗色ã€å™ªå£°é«˜çš„åœºæ™¯èƒŒæ™¯ä¸Šä¿æŒå¯è¯»ï¼šä½¿ç”¨é«˜äº®æ–‡å­—ã€æ·±è‰²æè¾¹ã€å¼ºé˜´å½±æˆ–å°é¢ç§¯åŠé€æ˜Žåº•,ä¸åªä¾èµ–红/绿文字本身表达伤害或治疗。 -15. å¹³å°äº®è‰² UI é…色以陶泥儿主视觉为准:暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›æ–°å¢žç•Œé¢ä¼˜å…ˆå¤ç”¨ `src/index.css` çš„ `--platform-*` 主题å˜é‡å’Œ `apps/admin-web/src/styles/admin.css` çš„åŒç³»è‰²å€¼ï¼Œä¸å†å¼•入粉红ã€è“绿等独立主色方案。 +15. å¹³å°äº®è‰² UI é…色以陶泥儿主视觉为准:暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›æ–°å¢žç•Œé¢ä¼˜å…ˆå¤ç”¨ `packages/shared/src/platform-theme.css` çš„ `--platform-*` 主题å˜é‡å’Œ `apps/admin-web/src/styles/admin.css` çš„åŒç³»è‰²å€¼ï¼Œä¸å†å¼•入粉红ã€è“绿等独立主色方案。 ## æ–‡æ¡ˆä¸Žç¼–ç  diff --git a/packages/shared/src/theme.css b/packages/shared/src/theme.css new file mode 100644 index 000000000..4ffe8d340 --- /dev/null +++ b/packages/shared/src/theme.css @@ -0,0 +1,538 @@ +.platform-theme, +.platform-theme * { + font-family: 'Inter', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'PingFang SC', + 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif !important; +} + +.platform-theme { + color: var(--platform-text-strong); +} + +.platform-theme--light { + color-scheme: light; + --platform-accent: #c7653d; + --platform-body-fill: radial-gradient( + circle at 8% 0%, + rgba(240, 203, 169, 0.24), + transparent 26% + ), + radial-gradient(circle at 92% 8%, rgba(226, 171, 134, 0.2), transparent 22%), + radial-gradient( + circle at 54% 100%, + rgba(204, 117, 76, 0.08), + transparent 30% + ), + linear-gradient(180deg, #fffdf9 0%, #fdf9f5 54%, #f8efe7 100%); + --platform-panel-shadow: 0 22px 60px rgba(112, 57, 30, 0.1), + 0 8px 22px rgba(255, 255, 255, 0.82); + --platform-panel-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.985), + rgba(253, 248, 243, 0.96) + ); + --platform-panel-fill-soft: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.96), + rgba(250, 243, 236, 0.9) + ); + --platform-hero-fill: linear-gradient( + 135deg, + rgba(244, 226, 211, 0.96), + rgba(238, 208, 183, 0.9) + ); + --platform-hero-glow-a: rgba(255, 255, 255, 0.42); + --platform-hero-glow-b: rgba(228, 149, 91, 0.18); + --platform-hero-overlay-strong: linear-gradient( + 135deg, + rgba(238, 208, 183, 0.66), + rgba(204, 117, 76, 0.18) + ); + --platform-hero-overlay-soft: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.34), + rgba(248, 233, 219, 0.22) + ); + --platform-surface-border: rgba(226, 203, 184, 0.88); + --platform-surface-hover-border: rgba(204, 117, 76, 0.42); + --platform-shell-glow-1: rgba(255, 255, 255, 0.34); + --platform-shell-glow-2: rgba(238, 208, 183, 0.22); + --platform-shell-glow-3: rgba(210, 132, 93, 0.12); + --platform-surface-glow-a: rgba(240, 203, 169, 0.18); + --platform-surface-glow-b: rgba(204, 117, 76, 0.1); + --platform-text-strong: #3d1f10; + --platform-text-base: #6f5848; + --platform-text-soft: #988476; + --platform-text-muted: #a38f80; + --platform-brand-logo-title: #4a220f; + --platform-brand-logo-subtitle: #c7653d; + --platform-brand-logo-shadow: #caa48b; + --platform-line-soft: rgba(226, 203, 184, 0.72); + --platform-subpanel-fill: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.94), + rgba(250, 243, 236, 0.9) + ); + --platform-subpanel-border: rgba(225, 204, 187, 0.82); + --platform-warm-border: rgba(204, 117, 76, 0.28); + --platform-warm-bg: rgba(234, 204, 179, 0.32); + --platform-warm-text: #b6623f; + --platform-cool-border: rgba(199, 117, 76, 0.24); + --platform-cool-bg: rgba(238, 208, 183, 0.26); + --platform-cool-text: #b76038; + --platform-neutral-border: rgba(226, 203, 184, 0.44); + --platform-neutral-bg: rgba(255, 253, 250, 0.7); + --platform-neutral-text: #7b6150; + --platform-button-primary-border: rgba(182, 98, 63, 0.32); + --platform-button-primary-solid: #c7653d; + --platform-button-primary-fill: linear-gradient(135deg, #df7f40, #b95d3a); + --platform-button-primary-text: #fffaf5; + --platform-button-secondary-fill: rgba(255, 253, 250, 0.78); + --platform-button-secondary-text: #4b2412; + --platform-button-ghost-fill: rgba(255, 253, 250, 0.56); + --platform-button-ghost-text: #755a49; + --platform-button-danger-border: rgba(185, 75, 58, 0.22); + --platform-button-danger-fill: rgba(255, 237, 229, 0.94); + --platform-button-danger-text: #a6402f; + --platform-unread-dot-fill: #8b5a3d; + --platform-unread-dot-glow: rgba(139, 90, 61, 0.34); + --platform-success-border: rgba(73, 144, 96, 0.24); + --platform-success-bg: rgba(237, 248, 239, 0.92); + --platform-success-text: #2f7b46; + --platform-icon-fill: rgba(255, 253, 250, 0.68); + --platform-icon-border: rgba(226, 203, 184, 0.5); + --platform-icon-text: #7b5c49; + --platform-nav-fill: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.96), + rgba(250, 243, 236, 0.92) + ); + --platform-nav-active-fill: linear-gradient( + 180deg, + rgba(238, 208, 183, 0.48), + rgba(204, 117, 76, 0.16) + ); + --platform-nav-active-border: rgba(204, 117, 76, 0.34); + --platform-nav-active-shadow: 0 10px 24px rgba(182, 98, 63, 0.12); + --platform-modal-fill: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.97), + rgba(250, 243, 236, 0.95) + ); + --platform-modal-border: rgba(255, 255, 255, 0.58); + --platform-desktop-shell-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.99), + rgba(253, 249, 245, 0.985) + ); + --platform-desktop-shell-border: rgba(230, 213, 199, 0.94); + --platform-desktop-shell-inner-border: rgba(233, 218, 205, 0.92); + --platform-desktop-topbar-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.95), + rgba(253, 249, 245, 0.92) + ); + --platform-desktop-panel-fill: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.95), + rgba(250, 243, 236, 0.91) + ); + --platform-desktop-panel-border: rgba(226, 203, 184, 0.88); + --platform-desktop-hover-shadow: 0 16px 30px rgba(112, 57, 30, 0.12); + --platform-overlay-fill: linear-gradient( + 180deg, + rgba(240, 203, 169, 0.14), + rgba(255, 255, 255, 0.58) + ); + --platform-track-border: rgba(226, 203, 184, 0.48); + --platform-track-fill: rgba(255, 253, 250, 0.9); + --platform-page-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.9), + rgba(250, 243, 236, 0.8) + ); + --platform-page-border: rgba(233, 218, 205, 0.88); + --platform-input-fill: rgba(255, 253, 250, 0.94); + --platform-input-fill-focus: rgba(255, 254, 252, 0.96); + --platform-input-highlight: rgba(255, 255, 255, 0.9); + --platform-input-focus-ring: rgba(204, 117, 76, 0.15); + --platform-nav-item-text: #80695a; + --platform-nav-item-text-active: #3d1f10; + --platform-nav-item-hover-fill: rgba(253, 248, 243, 0.94); + --platform-nav-item-icon-fill: rgba(250, 243, 236, 1); + --platform-nav-item-icon-text: #7b5c49; + --platform-nav-item-icon-active-fill: rgba(255, 254, 252, 0.98); + --platform-nav-item-icon-active-text: #c7653d; + --platform-nav-icon-active-shadow: 0 12px 24px rgba(182, 98, 63, 0.16); + --platform-bottom-nav-fill: linear-gradient(180deg, #fffefa, #fff9ef); + --platform-bottom-nav-border: rgba(224, 181, 139, 0.58); + --platform-bottom-nav-divider: rgba(225, 185, 145, 0.52); + --platform-bottom-nav-text: #5b2b19; + --platform-bottom-nav-muted-text: #7a4b37; + --platform-bottom-nav-active-text: #f15a17; + --platform-bottom-nav-primary-fill: linear-gradient(180deg, #ff7a23, #f04d12); + --platform-bottom-nav-primary-shadow: 0 12px 24px rgba(240, 77, 18, 0.28); + --platform-bottom-nav-primary-text: #fffaf2; + --platform-profile-hero-fill: linear-gradient( + 180deg, + rgba(255, 254, 252, 0.96), + rgba(250, 239, 229, 0.9) + ); + --platform-profile-hero-border: rgba(255, 255, 255, 0.56); + --platform-profile-hero-shadow: 0 20px 56px rgba(112, 57, 30, 0.16); + --platform-profile-avatar-fill: linear-gradient( + 135deg, + rgba(223, 127, 64, 0.96), + rgba(181, 91, 56, 0.92) + ); + --platform-profile-avatar-shadow: 0 14px 30px rgba(182, 98, 63, 0.22); + --platform-profile-chip-fill: rgba(255, 253, 250, 0.88); + --platform-profile-chip-hover-fill: rgba(255, 254, 252, 0.96); + --platform-profile-chip-text: #755a49; + --platform-profile-action-fill: linear-gradient(135deg, #df7f40, #b95d3a); + --platform-profile-action-solid: #c7653d; + --platform-profile-action-text: #fffaf5; + --platform-profile-action-shadow: 0 14px 30px rgba(182, 98, 63, 0.22); + --platform-card-overlay-soft: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.08), + rgba(250, 243, 236, 0.82) + ); + --platform-card-overlay-strong: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.16), + rgba(248, 239, 231, 0.92) + ); + --platform-card-overlay-deep: radial-gradient( + circle at top left, + rgba(255, 255, 255, 0.2), + transparent 30% + ), + radial-gradient(circle at right, rgba(228, 149, 91, 0.14), transparent 28%), + linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(248, 239, 231, 0.9)); + --platform-recommend-runtime-fill: var(--platform-panel-fill); + --platform-recommend-runtime-border: rgba(226, 203, 184, 0.42); + --platform-recommend-runtime-shadow: 0 18px 44px rgba(112, 57, 30, 0.12), + inset 0 0 0 1px rgba(255, 255, 255, 0.58); + --platform-recommend-runtime-state-fill: radial-gradient( + circle at 50% 18%, + rgba(204, 117, 76, 0.12), + transparent 34% + ), + linear-gradient( + 180deg, + rgba(255, 255, 255, 0.98), + rgba(250, 243, 236, 0.94) + ); + --platform-recommend-runtime-state-text: var(--platform-text-strong); + --puzzle-runtime-shell-fill: var(--platform-body-fill); + --puzzle-runtime-stage-fill: radial-gradient( + circle at 50% 18%, + rgba(204, 117, 76, 0.13), + transparent 30% + ), + radial-gradient( + circle at 18% 82%, + rgba(240, 203, 169, 0.18), + transparent 28% + ), + linear-gradient(180deg, #fffdf9 0%, #fbf5ed 58%, #f4e5d7 100%); + --puzzle-runtime-grid-line: rgba(112, 57, 30, 0.06); + --puzzle-runtime-text-strong: var(--platform-text-strong); + --puzzle-runtime-text-base: var(--platform-text-base); + --puzzle-runtime-text-soft: var(--platform-text-soft); + --puzzle-runtime-surface-fill: rgba(255, 253, 250, 0.76); + --puzzle-runtime-surface-fill-strong: rgba(255, 253, 250, 0.9); + --puzzle-runtime-surface-border: rgba(226, 203, 184, 0.48); + --puzzle-runtime-board-fill: rgba(255, 253, 250, 0.68); + --puzzle-runtime-board-border: rgba(204, 117, 76, 0.28); + --puzzle-runtime-board-shadow: 0 30px 80px rgba(112, 57, 30, 0.14); + --puzzle-runtime-piece-fill: rgba(255, 253, 250, 0.74); + --puzzle-runtime-piece-border: transparent; + --puzzle-runtime-piece-empty-fill: rgba(238, 208, 183, 0.34); + --puzzle-runtime-piece-empty-text: rgba(111, 88, 72, 0.4); + --puzzle-runtime-piece-selected-fill: linear-gradient( + 135deg, + #df7f40, + #b95d3a + ); + --puzzle-runtime-piece-selected-text: #fffaf5; + --puzzle-runtime-piece-selected-border: transparent; + --puzzle-runtime-next-card-overlay: rgba(74, 34, 15, 0.06); + --puzzle-runtime-control-fill: rgba(255, 253, 250, 0.72); + --puzzle-runtime-control-hover-fill: rgba(204, 117, 76, 0.1); + --puzzle-runtime-primary-fill: var(--platform-button-primary-fill); + --puzzle-runtime-primary-text: var(--platform-button-primary-text); + --puzzle-runtime-primary-shadow: var(--platform-profile-action-shadow); + --puzzle-runtime-accent-text: var(--platform-cool-text); + --puzzle-runtime-cool-text: #6e8d42; + --puzzle-runtime-danger-fill: rgba(255, 237, 229, 0.9); + --puzzle-runtime-danger-text: #a6402f; + --puzzle-runtime-backdrop-fill: rgba(61, 31, 16, 0.34); + --puzzle-runtime-dialog-fill: radial-gradient( + circle at 12% 0%, + rgba(204, 117, 76, 0.18), + transparent 36% + ), + linear-gradient( + 180deg, + rgba(255, 254, 252, 0.98), + rgba(250, 243, 236, 0.95) + ); + --puzzle-runtime-dialog-border: rgba(204, 117, 76, 0.3); + --puzzle-runtime-table-fill: rgba(255, 253, 250, 0.62); + --puzzle-runtime-table-row-fill: rgba(204, 117, 76, 0.12); + --puzzle-runtime-next-card-fill: rgba(255, 253, 250, 0.66); + --puzzle-runtime-next-card-hover-fill: rgba(204, 117, 76, 0.1); +} + +.platform-theme--dark { + color-scheme: dark; + --platform-accent: #5b6cff; + --platform-body-fill: radial-gradient( + circle at top, + rgba(129, 140, 248, 0.2), + transparent 30% + ), + radial-gradient( + circle at top right, + rgba(59, 130, 246, 0.12), + transparent 24% + ), + radial-gradient( + circle at bottom left, + rgba(34, 211, 238, 0.08), + transparent 26% + ), + linear-gradient(180deg, #13151c 0%, #090b11 100%); + --platform-panel-shadow: 0 28px 88px rgba(5, 8, 28, 0.5); + --platform-panel-fill: linear-gradient( + 180deg, + rgba(22, 20, 52, 0.95), + rgba(7, 9, 21, 0.98) + ); + --platform-panel-fill-soft: linear-gradient( + 180deg, + rgba(122, 92, 255, 0.12), + rgba(255, 255, 255, 0.03) + ); + --platform-hero-fill: radial-gradient( + circle at top left, + rgba(129, 140, 248, 0.24), + transparent 34% + ), + radial-gradient(circle at right, rgba(34, 211, 238, 0.12), transparent 32%), + linear-gradient(180deg, rgba(17, 22, 66, 0.95), rgba(8, 10, 24, 0.98)); + --platform-hero-glow-a: rgba(129, 140, 248, 0.18); + --platform-hero-glow-b: rgba(34, 211, 238, 0.1); + --platform-surface-border: rgba(160, 169, 255, 0.12); + --platform-surface-hover-border: rgba(255, 255, 255, 0.16); + --platform-shell-glow-1: rgba(129, 140, 248, 0.2); + --platform-shell-glow-2: rgba(59, 130, 246, 0.12); + --platform-shell-glow-3: rgba(34, 211, 238, 0.08); + --platform-surface-glow-a: rgba(129, 140, 248, 0.18); + --platform-surface-glow-b: rgba(34, 211, 238, 0.1); + --platform-text-strong: #ffffff; + --platform-text-base: rgb(228 228 231); + --platform-text-soft: rgb(161 161 170); + --platform-text-muted: rgb(161 161 170); + --platform-brand-logo-title: #fff7dc; + --platform-brand-logo-subtitle: #9fe7ff; + --platform-brand-logo-shadow: #040814; + --platform-line-soft: rgba(255, 255, 255, 0.1); + --platform-subpanel-fill: rgba(255, 255, 255, 0.05); + --platform-subpanel-border: rgba(255, 255, 255, 0.1); + --platform-warm-border: rgba(167, 139, 250, 0.24); + --platform-warm-bg: rgba(109, 40, 217, 0.18); + --platform-warm-text: rgb(237 233 254); + --platform-cool-border: rgba(103, 232, 249, 0.22); + --platform-cool-bg: rgba(8, 145, 178, 0.14); + --platform-cool-text: rgb(236 254 255); + --platform-neutral-border: rgba(255, 255, 255, 0.08); + --platform-neutral-bg: rgba(255, 255, 255, 0.05); + --platform-neutral-text: rgb(228 228 231); + --platform-button-primary-border: rgba(129, 140, 248, 0.3); + --platform-button-primary-solid: #5b6cff; + --platform-button-primary-fill: linear-gradient(135deg, #5b6cff, #3dd9ff); + --platform-button-primary-text: rgb(238 248 255); + --platform-button-secondary-fill: rgba(255, 255, 255, 0.05); + --platform-button-secondary-text: rgb(244 244 245); + --platform-button-ghost-fill: rgba(255, 255, 255, 0.03); + --platform-button-ghost-text: rgb(212 212 216); + --platform-button-danger-border: rgba(251, 113, 133, 0.2); + --platform-button-danger-fill: rgba(244, 63, 94, 0.1); + --platform-button-danger-text: rgb(255 228 230); + --platform-unread-dot-fill: #d6a27c; + --platform-unread-dot-glow: rgba(214, 162, 124, 0.24); + --platform-success-border: rgba(52, 211, 153, 0.24); + --platform-success-bg: rgba(16, 185, 129, 0.1); + --platform-success-text: rgb(220 252 231); + --platform-icon-fill: rgba(255, 255, 255, 0.05); + --platform-icon-border: rgba(255, 255, 255, 0.1); + --platform-icon-text: rgb(212 212 216); + --platform-nav-fill: linear-gradient( + 180deg, + rgba(109, 40, 217, 0.12), + rgba(255, 255, 255, 0.03) + ); + --platform-nav-active-fill: linear-gradient( + 180deg, + rgba(91, 108, 255, 0.2), + rgba(61, 217, 255, 0.08) + ); + --platform-nav-active-border: rgba(160, 169, 255, 0.24); + --platform-nav-active-shadow: 0 12px 28px rgba(8, 14, 42, 0.4); + --platform-modal-fill: linear-gradient( + 180deg, + rgba(16, 18, 46, 0.98), + rgba(7, 8, 19, 0.98) + ); + --platform-modal-border: rgba(160, 169, 255, 0.12); + --platform-desktop-shell-fill: linear-gradient( + 180deg, + rgba(8, 8, 30, 0.98), + rgba(5, 6, 18, 0.99) + ); + --platform-desktop-shell-border: rgba(160, 169, 255, 0.14); + --platform-desktop-shell-inner-border: rgba(255, 255, 255, 0.03); + --platform-desktop-topbar-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.05), + rgba(255, 255, 255, 0.03) + ); + --platform-desktop-panel-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.05), + rgba(255, 255, 255, 0.02) + ); + --platform-desktop-panel-border: rgba(160, 169, 255, 0.12); + --platform-desktop-hover-shadow: 0 16px 28px rgba(8, 11, 38, 0.2); + --platform-overlay-fill: rgba(5, 8, 28, 0.72); + --platform-track-border: rgba(255, 255, 255, 0.12); + --platform-track-fill: rgba(255, 255, 255, 0.08); + --platform-page-fill: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.04), + rgba(255, 255, 255, 0.02) + ); + --platform-page-border: rgba(255, 255, 255, 0.06); + --platform-input-fill: rgba(255, 255, 255, 0.05); + --platform-input-fill-focus: rgba(255, 255, 255, 0.08); + --platform-input-highlight: rgba(255, 255, 255, 0.06); + --platform-input-focus-ring: rgba(91, 108, 255, 0.22); + --platform-nav-item-text: rgb(161 161 170); + --platform-nav-item-text-active: rgb(238 248 255); + --platform-nav-item-hover-fill: rgba(91, 108, 255, 0.08); + --platform-nav-item-icon-fill: rgba(255, 255, 255, 0.06); + --platform-nav-item-icon-text: rgb(161 161 170); + --platform-nav-item-icon-active-fill: linear-gradient( + 180deg, + rgba(91, 108, 255, 0.24), + rgba(61, 217, 255, 0.12) + ); + --platform-nav-item-icon-active-text: rgb(238 248 255); + --platform-nav-icon-active-shadow: 0 12px 24px rgba(8, 14, 42, 0.42); + --platform-bottom-nav-fill: linear-gradient(180deg, #fffefa, #fff9ef); + --platform-bottom-nav-border: rgba(224, 181, 139, 0.64); + --platform-bottom-nav-divider: rgba(225, 185, 145, 0.56); + --platform-bottom-nav-text: #5b2b19; + --platform-bottom-nav-muted-text: #7a4b37; + --platform-bottom-nav-active-text: #f15a17; + --platform-bottom-nav-primary-fill: linear-gradient(180deg, #ff7a23, #f04d12); + --platform-bottom-nav-primary-shadow: 0 12px 24px rgba(240, 77, 18, 0.3); + --platform-bottom-nav-primary-text: #fffaf2; + --platform-profile-hero-fill: linear-gradient( + 180deg, + rgba(20, 24, 58, 0.96), + rgba(8, 10, 24, 0.98) + ); + --platform-profile-hero-border: rgba(160, 169, 255, 0.14); + --platform-profile-hero-shadow: 0 24px 70px rgba(5, 8, 28, 0.42); + --platform-profile-avatar-fill: linear-gradient( + 135deg, + rgba(91, 108, 255, 0.94), + rgba(61, 217, 255, 0.78) + ); + --platform-profile-avatar-shadow: 0 14px 32px rgba(61, 217, 255, 0.16); + --platform-profile-chip-fill: rgba(255, 255, 255, 0.08); + --platform-profile-chip-hover-fill: rgba(255, 255, 255, 0.14); + --platform-profile-chip-text: rgb(228 228 231); + --platform-profile-action-fill: linear-gradient(135deg, #5b6cff, #3dd9ff); + --platform-profile-action-solid: #5b6cff; + --platform-profile-action-text: rgb(238 248 255); + --platform-profile-action-shadow: 0 14px 32px rgba(91, 108, 255, 0.22); + --platform-card-overlay-soft: linear-gradient( + 180deg, + rgba(8, 10, 14, 0.06), + rgba(8, 10, 14, 0.74) + ); + --platform-card-overlay-strong: linear-gradient( + 180deg, + rgba(8, 10, 14, 0.14), + rgba(8, 10, 14, 0.9) + ); + --platform-card-overlay-deep: radial-gradient( + circle at top left, + rgba(255, 255, 255, 0.14), + transparent 30% + ), + radial-gradient(circle at right, rgba(255, 205, 178, 0.14), transparent 28%), + linear-gradient(180deg, rgba(8, 10, 14, 0.22), rgba(8, 10, 14, 0.9)); + --platform-recommend-runtime-fill: #030303; + --platform-recommend-runtime-border: rgba(255, 255, 255, 0.08); + --platform-recommend-runtime-shadow: inset 0 0 0 1px + rgba(255, 255, 255, 0.025), + 0 18px 44px rgba(0, 0, 0, 0.18); + --platform-recommend-runtime-state-fill: #030303; + --platform-recommend-runtime-state-text: rgba(255, 255, 255, 0.92); + --puzzle-runtime-shell-fill: #020617; + --puzzle-runtime-stage-fill: radial-gradient( + circle at 50% 20%, + rgba(251, 191, 36, 0.18), + transparent 28% + ), + radial-gradient( + circle at 20% 80%, + rgba(249, 115, 22, 0.16), + transparent 26% + ), + linear-gradient(180deg, #2d160e, #020617); + --puzzle-runtime-grid-line: rgba(255, 255, 255, 0.04); + --puzzle-runtime-text-strong: #ffffff; + --puzzle-runtime-text-base: rgba(255, 255, 255, 0.82); + --puzzle-runtime-text-soft: rgba(255, 255, 255, 0.58); + --puzzle-runtime-surface-fill: rgba(0, 0, 0, 0.34); + --puzzle-runtime-surface-fill-strong: rgba(0, 0, 0, 0.5); + --puzzle-runtime-surface-border: rgba(255, 255, 255, 0.12); + --puzzle-runtime-board-fill: rgba(255, 255, 255, 0.08); + --puzzle-runtime-board-border: rgba(255, 255, 255, 0.16); + --puzzle-runtime-board-shadow: 0 30px 80px rgba(0, 0, 0, 0.35); + --puzzle-runtime-piece-fill: rgba(255, 255, 255, 0.12); + --puzzle-runtime-piece-border: transparent; + --puzzle-runtime-piece-empty-fill: rgba(0, 0, 0, 0.18); + --puzzle-runtime-piece-empty-text: rgba(255, 255, 255, 0.2); + --puzzle-runtime-piece-selected-fill: rgba(251, 191, 36, 0.84); + --puzzle-runtime-piece-selected-text: #020617; + --puzzle-runtime-piece-selected-border: transparent; + --puzzle-runtime-next-card-overlay: rgba(0, 0, 0, 0.1); + --puzzle-runtime-control-fill: rgba(0, 0, 0, 0.36); + --puzzle-runtime-control-hover-fill: rgba(255, 255, 255, 0.1); + --puzzle-runtime-primary-fill: #fde68a; + --puzzle-runtime-primary-text: #020617; + --puzzle-runtime-primary-shadow: 0 14px 36px rgba(251, 191, 36, 0.26); + --puzzle-runtime-accent-text: #fde68a; + --puzzle-runtime-cool-text: #cffafe; + --puzzle-runtime-danger-fill: rgba(239, 68, 68, 0.2); + --puzzle-runtime-danger-text: #fee2e2; + --puzzle-runtime-backdrop-fill: rgba(2, 6, 23, 0.68); + --puzzle-runtime-dialog-fill: radial-gradient( + circle at 14% 0%, + rgba(251, 191, 36, 0.16), + transparent 36% + ), + linear-gradient(180deg, rgba(29, 17, 12, 0.96), rgba(2, 6, 23, 0.96)); + --puzzle-runtime-dialog-border: rgba(251, 191, 36, 0.22); + --puzzle-runtime-table-fill: rgba(255, 255, 255, 0.06); + --puzzle-runtime-table-row-fill: rgba(251, 191, 36, 0.14); + --puzzle-runtime-next-card-fill: rgba(255, 255, 255, 0.06); + --puzzle-runtime-next-card-hover-fill: rgba(251, 191, 36, 0.1); +} diff --git a/packages/shared/src/theme.test.ts b/packages/shared/src/theme.test.ts new file mode 100644 index 000000000..04400bb29 --- /dev/null +++ b/packages/shared/src/theme.test.ts @@ -0,0 +1,45 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import {describe, expect, it} from 'vitest'; + +const themePath = path.resolve( + process.cwd(), + 'packages/shared/src/platform-theme.css', +); + +function getCssBlock(source: string, selector: string) { + const selectorIndex = source.indexOf(selector); + expect(selectorIndex, `${selector} should exist`).toBeGreaterThanOrEqual(0); + + const openBraceIndex = source.indexOf('{', selectorIndex); + let depth = 0; + for (let index = openBraceIndex; index < source.length; index += 1) { + const char = source[index]; + if (char === '{') { + depth += 1; + } else if (char === '}') { + depth -= 1; + if (depth === 0) { + return source.slice(openBraceIndex + 1, index); + } + } + } + + throw new Error(`${selector} block is not closed`); +} + +describe('shared platform theme', () => { + it('keeps the light and dark semantic color contracts', () => { + const css = fs.readFileSync(themePath, 'utf8'); + const light = getCssBlock(css, '.platform-theme--light'); + const dark = getCssBlock(css, '.platform-theme--dark'); + + expect(light).toContain('--platform-accent: #c7653d;'); + expect(light).toContain('linear-gradient(135deg, #df7f40, #b95d3a)'); + expect(light).toContain('--platform-text-strong: #3d1f10;'); + expect(dark).toContain('--platform-accent: #5b6cff;'); + expect(dark).toContain('linear-gradient(135deg, #5b6cff, #3dd9ff)'); + expect(dark).toContain('--platform-text-strong: #ffffff;'); + }); +}); diff --git a/src/index.css b/src/index.css index 8df8a0ccb..5b699bda3 100644 --- a/src/index.css +++ b/src/index.css @@ -1,5 +1,6 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Noto+Serif+SC:wght@400;700&display=swap'); @import 'tailwindcss'; +@import '@/packages/shared/src/theme.css'; @source not "../dist"; @source not "../dist_check"; @source not "../dist_check_final"; @@ -957,544 +958,6 @@ html[data-mobile-keyboard-open='true'] #root { 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif !important; } -.platform-theme, -.platform-theme * { - font-family: 'Inter', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'PingFang SC', - 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif !important; -} - -.platform-theme { - color: var(--platform-text-strong); -} - -.platform-theme--light { - color-scheme: light; - --platform-accent: #c7653d; - --platform-body-fill: radial-gradient( - circle at 8% 0%, - rgba(240, 203, 169, 0.24), - transparent 26% - ), - radial-gradient(circle at 92% 8%, rgba(226, 171, 134, 0.2), transparent 22%), - radial-gradient( - circle at 54% 100%, - rgba(204, 117, 76, 0.08), - transparent 30% - ), - linear-gradient(180deg, #fffdf9 0%, #fdf9f5 54%, #f8efe7 100%); - --platform-panel-shadow: 0 22px 60px rgba(112, 57, 30, 0.1), - 0 8px 22px rgba(255, 255, 255, 0.82); - --platform-panel-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.985), - rgba(253, 248, 243, 0.96) - ); - --platform-panel-fill-soft: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.96), - rgba(250, 243, 236, 0.9) - ); - --platform-hero-fill: linear-gradient( - 135deg, - rgba(244, 226, 211, 0.96), - rgba(238, 208, 183, 0.9) - ); - --platform-hero-glow-a: rgba(255, 255, 255, 0.42); - --platform-hero-glow-b: rgba(228, 149, 91, 0.18); - --platform-hero-overlay-strong: linear-gradient( - 135deg, - rgba(238, 208, 183, 0.66), - rgba(204, 117, 76, 0.18) - ); - --platform-hero-overlay-soft: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.34), - rgba(248, 233, 219, 0.22) - ); - --platform-surface-border: rgba(226, 203, 184, 0.88); - --platform-surface-hover-border: rgba(204, 117, 76, 0.42); - --platform-shell-glow-1: rgba(255, 255, 255, 0.34); - --platform-shell-glow-2: rgba(238, 208, 183, 0.22); - --platform-shell-glow-3: rgba(210, 132, 93, 0.12); - --platform-surface-glow-a: rgba(240, 203, 169, 0.18); - --platform-surface-glow-b: rgba(204, 117, 76, 0.1); - --platform-text-strong: #3d1f10; - --platform-text-base: #6f5848; - --platform-text-soft: #988476; - --platform-text-muted: #a38f80; - --platform-brand-logo-title: #4a220f; - --platform-brand-logo-subtitle: #c7653d; - --platform-brand-logo-shadow: #caa48b; - --platform-line-soft: rgba(226, 203, 184, 0.72); - --platform-subpanel-fill: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.94), - rgba(250, 243, 236, 0.9) - ); - --platform-subpanel-border: rgba(225, 204, 187, 0.82); - --platform-warm-border: rgba(204, 117, 76, 0.28); - --platform-warm-bg: rgba(234, 204, 179, 0.32); - --platform-warm-text: #b6623f; - --platform-cool-border: rgba(199, 117, 76, 0.24); - --platform-cool-bg: rgba(238, 208, 183, 0.26); - --platform-cool-text: #b76038; - --platform-neutral-border: rgba(226, 203, 184, 0.44); - --platform-neutral-bg: rgba(255, 253, 250, 0.7); - --platform-neutral-text: #7b6150; - --platform-button-primary-border: rgba(182, 98, 63, 0.32); - --platform-button-primary-solid: #c7653d; - --platform-button-primary-fill: linear-gradient(135deg, #df7f40, #b95d3a); - --platform-button-primary-text: #fffaf5; - --platform-button-secondary-fill: rgba(255, 253, 250, 0.78); - --platform-button-secondary-text: #4b2412; - --platform-button-ghost-fill: rgba(255, 253, 250, 0.56); - --platform-button-ghost-text: #755a49; - --platform-button-danger-border: rgba(185, 75, 58, 0.22); - --platform-button-danger-fill: rgba(255, 237, 229, 0.94); - --platform-button-danger-text: #a6402f; - --platform-unread-dot-fill: #8b5a3d; - --platform-unread-dot-glow: rgba(139, 90, 61, 0.34); - --platform-success-border: rgba(73, 144, 96, 0.24); - --platform-success-bg: rgba(237, 248, 239, 0.92); - --platform-success-text: #2f7b46; - --platform-icon-fill: rgba(255, 253, 250, 0.68); - --platform-icon-border: rgba(226, 203, 184, 0.5); - --platform-icon-text: #7b5c49; - --platform-nav-fill: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.96), - rgba(250, 243, 236, 0.92) - ); - --platform-nav-active-fill: linear-gradient( - 180deg, - rgba(238, 208, 183, 0.48), - rgba(204, 117, 76, 0.16) - ); - --platform-nav-active-border: rgba(204, 117, 76, 0.34); - --platform-nav-active-shadow: 0 10px 24px rgba(182, 98, 63, 0.12); - --platform-modal-fill: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.97), - rgba(250, 243, 236, 0.95) - ); - --platform-modal-border: rgba(255, 255, 255, 0.58); - --platform-desktop-shell-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.99), - rgba(253, 249, 245, 0.985) - ); - --platform-desktop-shell-border: rgba(230, 213, 199, 0.94); - --platform-desktop-shell-inner-border: rgba(233, 218, 205, 0.92); - --platform-desktop-topbar-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.95), - rgba(253, 249, 245, 0.92) - ); - --platform-desktop-panel-fill: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.95), - rgba(250, 243, 236, 0.91) - ); - --platform-desktop-panel-border: rgba(226, 203, 184, 0.88); - --platform-desktop-hover-shadow: 0 16px 30px rgba(112, 57, 30, 0.12); - --platform-overlay-fill: linear-gradient( - 180deg, - rgba(240, 203, 169, 0.14), - rgba(255, 255, 255, 0.58) - ); - --platform-track-border: rgba(226, 203, 184, 0.48); - --platform-track-fill: rgba(255, 253, 250, 0.9); - --platform-page-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.9), - rgba(250, 243, 236, 0.8) - ); - --platform-page-border: rgba(233, 218, 205, 0.88); - --platform-input-fill: rgba(255, 253, 250, 0.94); - --platform-input-fill-focus: rgba(255, 254, 252, 0.96); - --platform-input-highlight: rgba(255, 255, 255, 0.9); - --platform-input-focus-ring: rgba(204, 117, 76, 0.15); - --platform-nav-item-text: #80695a; - --platform-nav-item-text-active: #3d1f10; - --platform-nav-item-hover-fill: rgba(253, 248, 243, 0.94); - --platform-nav-item-icon-fill: rgba(250, 243, 236, 1); - --platform-nav-item-icon-text: #7b5c49; - --platform-nav-item-icon-active-fill: rgba(255, 254, 252, 0.98); - --platform-nav-item-icon-active-text: #c7653d; - --platform-nav-icon-active-shadow: 0 12px 24px rgba(182, 98, 63, 0.16); - --platform-bottom-nav-fill: linear-gradient(180deg, #fffefa, #fff9ef); - --platform-bottom-nav-border: rgba(224, 181, 139, 0.58); - --platform-bottom-nav-divider: rgba(225, 185, 145, 0.52); - --platform-bottom-nav-text: #5b2b19; - --platform-bottom-nav-muted-text: #7a4b37; - --platform-bottom-nav-active-text: #f15a17; - --platform-bottom-nav-primary-fill: linear-gradient(180deg, #ff7a23, #f04d12); - --platform-bottom-nav-primary-shadow: 0 12px 24px rgba(240, 77, 18, 0.28); - --platform-bottom-nav-primary-text: #fffaf2; - --platform-profile-hero-fill: linear-gradient( - 180deg, - rgba(255, 254, 252, 0.96), - rgba(250, 239, 229, 0.9) - ); - --platform-profile-hero-border: rgba(255, 255, 255, 0.56); - --platform-profile-hero-shadow: 0 20px 56px rgba(112, 57, 30, 0.16); - --platform-profile-avatar-fill: linear-gradient( - 135deg, - rgba(223, 127, 64, 0.96), - rgba(181, 91, 56, 0.92) - ); - --platform-profile-avatar-shadow: 0 14px 30px rgba(182, 98, 63, 0.22); - --platform-profile-chip-fill: rgba(255, 253, 250, 0.88); - --platform-profile-chip-hover-fill: rgba(255, 254, 252, 0.96); - --platform-profile-chip-text: #755a49; - --platform-profile-action-fill: linear-gradient(135deg, #df7f40, #b95d3a); - --platform-profile-action-solid: #c7653d; - --platform-profile-action-text: #fffaf5; - --platform-profile-action-shadow: 0 14px 30px rgba(182, 98, 63, 0.22); - --platform-card-overlay-soft: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.08), - rgba(250, 243, 236, 0.82) - ); - --platform-card-overlay-strong: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.16), - rgba(248, 239, 231, 0.92) - ); - --platform-card-overlay-deep: radial-gradient( - circle at top left, - rgba(255, 255, 255, 0.2), - transparent 30% - ), - radial-gradient(circle at right, rgba(228, 149, 91, 0.14), transparent 28%), - linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(248, 239, 231, 0.9)); - --platform-recommend-runtime-fill: var(--platform-panel-fill); - --platform-recommend-runtime-border: rgba(226, 203, 184, 0.42); - --platform-recommend-runtime-shadow: 0 18px 44px rgba(112, 57, 30, 0.12), - inset 0 0 0 1px rgba(255, 255, 255, 0.58); - --platform-recommend-runtime-state-fill: radial-gradient( - circle at 50% 18%, - rgba(204, 117, 76, 0.12), - transparent 34% - ), - linear-gradient( - 180deg, - rgba(255, 255, 255, 0.98), - rgba(250, 243, 236, 0.94) - ); - --platform-recommend-runtime-state-text: var(--platform-text-strong); - --puzzle-runtime-shell-fill: var(--platform-body-fill); - --puzzle-runtime-stage-fill: radial-gradient( - circle at 50% 18%, - rgba(204, 117, 76, 0.13), - transparent 30% - ), - radial-gradient( - circle at 18% 82%, - rgba(240, 203, 169, 0.18), - transparent 28% - ), - linear-gradient(180deg, #fffdf9 0%, #fbf5ed 58%, #f4e5d7 100%); - --puzzle-runtime-grid-line: rgba(112, 57, 30, 0.06); - --puzzle-runtime-text-strong: var(--platform-text-strong); - --puzzle-runtime-text-base: var(--platform-text-base); - --puzzle-runtime-text-soft: var(--platform-text-soft); - --puzzle-runtime-surface-fill: rgba(255, 253, 250, 0.76); - --puzzle-runtime-surface-fill-strong: rgba(255, 253, 250, 0.9); - --puzzle-runtime-surface-border: rgba(226, 203, 184, 0.48); - --puzzle-runtime-board-fill: rgba(255, 253, 250, 0.68); - --puzzle-runtime-board-border: rgba(204, 117, 76, 0.28); - --puzzle-runtime-board-shadow: 0 30px 80px rgba(112, 57, 30, 0.14); - --puzzle-runtime-piece-fill: rgba(255, 253, 250, 0.74); - --puzzle-runtime-piece-border: transparent; - --puzzle-runtime-piece-empty-fill: rgba(238, 208, 183, 0.34); - --puzzle-runtime-piece-empty-text: rgba(111, 88, 72, 0.4); - --puzzle-runtime-piece-selected-fill: linear-gradient( - 135deg, - #df7f40, - #b95d3a - ); - --puzzle-runtime-piece-selected-text: #fffaf5; - --puzzle-runtime-piece-selected-border: transparent; - --puzzle-runtime-next-card-overlay: rgba(74, 34, 15, 0.06); - --puzzle-runtime-control-fill: rgba(255, 253, 250, 0.72); - --puzzle-runtime-control-hover-fill: rgba(204, 117, 76, 0.1); - --puzzle-runtime-primary-fill: var(--platform-button-primary-fill); - --puzzle-runtime-primary-text: var(--platform-button-primary-text); - --puzzle-runtime-primary-shadow: var(--platform-profile-action-shadow); - --puzzle-runtime-accent-text: var(--platform-cool-text); - --puzzle-runtime-cool-text: #6e8d42; - --puzzle-runtime-danger-fill: rgba(255, 237, 229, 0.9); - --puzzle-runtime-danger-text: #a6402f; - --puzzle-runtime-backdrop-fill: rgba(61, 31, 16, 0.34); - --puzzle-runtime-dialog-fill: radial-gradient( - circle at 12% 0%, - rgba(204, 117, 76, 0.18), - transparent 36% - ), - linear-gradient( - 180deg, - rgba(255, 254, 252, 0.98), - rgba(250, 243, 236, 0.95) - ); - --puzzle-runtime-dialog-border: rgba(204, 117, 76, 0.3); - --puzzle-runtime-table-fill: rgba(255, 253, 250, 0.62); - --puzzle-runtime-table-row-fill: rgba(204, 117, 76, 0.12); - --puzzle-runtime-next-card-fill: rgba(255, 253, 250, 0.66); - --puzzle-runtime-next-card-hover-fill: rgba(204, 117, 76, 0.1); -} - -.platform-theme--dark { - color-scheme: dark; - --platform-accent: #5b6cff; - --platform-body-fill: radial-gradient( - circle at top, - rgba(129, 140, 248, 0.2), - transparent 30% - ), - radial-gradient( - circle at top right, - rgba(59, 130, 246, 0.12), - transparent 24% - ), - radial-gradient( - circle at bottom left, - rgba(34, 211, 238, 0.08), - transparent 26% - ), - linear-gradient(180deg, #13151c 0%, #090b11 100%); - --platform-panel-shadow: 0 28px 88px rgba(5, 8, 28, 0.5); - --platform-panel-fill: linear-gradient( - 180deg, - rgba(22, 20, 52, 0.95), - rgba(7, 9, 21, 0.98) - ); - --platform-panel-fill-soft: linear-gradient( - 180deg, - rgba(122, 92, 255, 0.12), - rgba(255, 255, 255, 0.03) - ); - --platform-hero-fill: radial-gradient( - circle at top left, - rgba(129, 140, 248, 0.24), - transparent 34% - ), - radial-gradient(circle at right, rgba(34, 211, 238, 0.12), transparent 32%), - linear-gradient(180deg, rgba(17, 22, 66, 0.95), rgba(8, 10, 24, 0.98)); - --platform-hero-glow-a: rgba(129, 140, 248, 0.18); - --platform-hero-glow-b: rgba(34, 211, 238, 0.1); - --platform-surface-border: rgba(160, 169, 255, 0.12); - --platform-surface-hover-border: rgba(255, 255, 255, 0.16); - --platform-shell-glow-1: rgba(129, 140, 248, 0.2); - --platform-shell-glow-2: rgba(59, 130, 246, 0.12); - --platform-shell-glow-3: rgba(34, 211, 238, 0.08); - --platform-surface-glow-a: rgba(129, 140, 248, 0.18); - --platform-surface-glow-b: rgba(34, 211, 238, 0.1); - --platform-text-strong: #ffffff; - --platform-text-base: rgb(228 228 231); - --platform-text-soft: rgb(161 161 170); - --platform-text-muted: rgb(161 161 170); - --platform-brand-logo-title: #fff7dc; - --platform-brand-logo-subtitle: #9fe7ff; - --platform-brand-logo-shadow: #040814; - --platform-line-soft: rgba(255, 255, 255, 0.1); - --platform-subpanel-fill: rgba(255, 255, 255, 0.05); - --platform-subpanel-border: rgba(255, 255, 255, 0.1); - --platform-warm-border: rgba(167, 139, 250, 0.24); - --platform-warm-bg: rgba(109, 40, 217, 0.18); - --platform-warm-text: rgb(237 233 254); - --platform-cool-border: rgba(103, 232, 249, 0.22); - --platform-cool-bg: rgba(8, 145, 178, 0.14); - --platform-cool-text: rgb(236 254 255); - --platform-neutral-border: rgba(255, 255, 255, 0.08); - --platform-neutral-bg: rgba(255, 255, 255, 0.05); - --platform-neutral-text: rgb(228 228 231); - --platform-button-primary-border: rgba(129, 140, 248, 0.3); - --platform-button-primary-solid: #5b6cff; - --platform-button-primary-fill: linear-gradient(135deg, #5b6cff, #3dd9ff); - --platform-button-primary-text: rgb(238 248 255); - --platform-button-secondary-fill: rgba(255, 255, 255, 0.05); - --platform-button-secondary-text: rgb(244 244 245); - --platform-button-ghost-fill: rgba(255, 255, 255, 0.03); - --platform-button-ghost-text: rgb(212 212 216); - --platform-button-danger-border: rgba(251, 113, 133, 0.2); - --platform-button-danger-fill: rgba(244, 63, 94, 0.1); - --platform-button-danger-text: rgb(255 228 230); - --platform-unread-dot-fill: #d6a27c; - --platform-unread-dot-glow: rgba(214, 162, 124, 0.24); - --platform-success-border: rgba(52, 211, 153, 0.24); - --platform-success-bg: rgba(16, 185, 129, 0.1); - --platform-success-text: rgb(220 252 231); - --platform-icon-fill: rgba(255, 255, 255, 0.05); - --platform-icon-border: rgba(255, 255, 255, 0.1); - --platform-icon-text: rgb(212 212 216); - --platform-nav-fill: linear-gradient( - 180deg, - rgba(109, 40, 217, 0.12), - rgba(255, 255, 255, 0.03) - ); - --platform-nav-active-fill: linear-gradient( - 180deg, - rgba(91, 108, 255, 0.2), - rgba(61, 217, 255, 0.08) - ); - --platform-nav-active-border: rgba(160, 169, 255, 0.24); - --platform-nav-active-shadow: 0 12px 28px rgba(8, 14, 42, 0.4); - --platform-modal-fill: linear-gradient( - 180deg, - rgba(16, 18, 46, 0.98), - rgba(7, 8, 19, 0.98) - ); - --platform-modal-border: rgba(160, 169, 255, 0.12); - --platform-desktop-shell-fill: linear-gradient( - 180deg, - rgba(8, 8, 30, 0.98), - rgba(5, 6, 18, 0.99) - ); - --platform-desktop-shell-border: rgba(160, 169, 255, 0.14); - --platform-desktop-shell-inner-border: rgba(255, 255, 255, 0.03); - --platform-desktop-topbar-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.05), - rgba(255, 255, 255, 0.03) - ); - --platform-desktop-panel-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.05), - rgba(255, 255, 255, 0.02) - ); - --platform-desktop-panel-border: rgba(160, 169, 255, 0.12); - --platform-desktop-hover-shadow: 0 16px 28px rgba(8, 11, 38, 0.2); - --platform-overlay-fill: rgba(5, 8, 28, 0.72); - --platform-track-border: rgba(255, 255, 255, 0.12); - --platform-track-fill: rgba(255, 255, 255, 0.08); - --platform-page-fill: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.04), - rgba(255, 255, 255, 0.02) - ); - --platform-page-border: rgba(255, 255, 255, 0.06); - --platform-input-fill: rgba(255, 255, 255, 0.05); - --platform-input-fill-focus: rgba(255, 255, 255, 0.08); - --platform-input-highlight: rgba(255, 255, 255, 0.06); - --platform-input-focus-ring: rgba(91, 108, 255, 0.22); - --platform-nav-item-text: rgb(161 161 170); - --platform-nav-item-text-active: rgb(238 248 255); - --platform-nav-item-hover-fill: rgba(91, 108, 255, 0.08); - --platform-nav-item-icon-fill: rgba(255, 255, 255, 0.06); - --platform-nav-item-icon-text: rgb(161 161 170); - --platform-nav-item-icon-active-fill: linear-gradient( - 180deg, - rgba(91, 108, 255, 0.24), - rgba(61, 217, 255, 0.12) - ); - --platform-nav-item-icon-active-text: rgb(238 248 255); - --platform-nav-icon-active-shadow: 0 12px 24px rgba(8, 14, 42, 0.42); - --platform-bottom-nav-fill: linear-gradient(180deg, #fffefa, #fff9ef); - --platform-bottom-nav-border: rgba(224, 181, 139, 0.64); - --platform-bottom-nav-divider: rgba(225, 185, 145, 0.56); - --platform-bottom-nav-text: #5b2b19; - --platform-bottom-nav-muted-text: #7a4b37; - --platform-bottom-nav-active-text: #f15a17; - --platform-bottom-nav-primary-fill: linear-gradient(180deg, #ff7a23, #f04d12); - --platform-bottom-nav-primary-shadow: 0 12px 24px rgba(240, 77, 18, 0.3); - --platform-bottom-nav-primary-text: #fffaf2; - --platform-profile-hero-fill: linear-gradient( - 180deg, - rgba(20, 24, 58, 0.96), - rgba(8, 10, 24, 0.98) - ); - --platform-profile-hero-border: rgba(160, 169, 255, 0.14); - --platform-profile-hero-shadow: 0 24px 70px rgba(5, 8, 28, 0.42); - --platform-profile-avatar-fill: linear-gradient( - 135deg, - rgba(91, 108, 255, 0.94), - rgba(61, 217, 255, 0.78) - ); - --platform-profile-avatar-shadow: 0 14px 32px rgba(61, 217, 255, 0.16); - --platform-profile-chip-fill: rgba(255, 255, 255, 0.08); - --platform-profile-chip-hover-fill: rgba(255, 255, 255, 0.14); - --platform-profile-chip-text: rgb(228 228 231); - --platform-profile-action-fill: linear-gradient(135deg, #5b6cff, #3dd9ff); - --platform-profile-action-solid: #5b6cff; - --platform-profile-action-text: rgb(238 248 255); - --platform-profile-action-shadow: 0 14px 32px rgba(91, 108, 255, 0.22); - --platform-card-overlay-soft: linear-gradient( - 180deg, - rgba(8, 10, 14, 0.06), - rgba(8, 10, 14, 0.74) - ); - --platform-card-overlay-strong: linear-gradient( - 180deg, - rgba(8, 10, 14, 0.14), - rgba(8, 10, 14, 0.9) - ); - --platform-card-overlay-deep: radial-gradient( - circle at top left, - rgba(255, 255, 255, 0.14), - transparent 30% - ), - radial-gradient(circle at right, rgba(255, 205, 178, 0.14), transparent 28%), - linear-gradient(180deg, rgba(8, 10, 14, 0.22), rgba(8, 10, 14, 0.9)); - --platform-recommend-runtime-fill: #030303; - --platform-recommend-runtime-border: rgba(255, 255, 255, 0.08); - --platform-recommend-runtime-shadow: inset 0 0 0 1px - rgba(255, 255, 255, 0.025), - 0 18px 44px rgba(0, 0, 0, 0.18); - --platform-recommend-runtime-state-fill: #030303; - --platform-recommend-runtime-state-text: rgba(255, 255, 255, 0.92); - --puzzle-runtime-shell-fill: #020617; - --puzzle-runtime-stage-fill: radial-gradient( - circle at 50% 20%, - rgba(251, 191, 36, 0.18), - transparent 28% - ), - radial-gradient( - circle at 20% 80%, - rgba(249, 115, 22, 0.16), - transparent 26% - ), - linear-gradient(180deg, #2d160e, #020617); - --puzzle-runtime-grid-line: rgba(255, 255, 255, 0.04); - --puzzle-runtime-text-strong: #ffffff; - --puzzle-runtime-text-base: rgba(255, 255, 255, 0.82); - --puzzle-runtime-text-soft: rgba(255, 255, 255, 0.58); - --puzzle-runtime-surface-fill: rgba(0, 0, 0, 0.34); - --puzzle-runtime-surface-fill-strong: rgba(0, 0, 0, 0.5); - --puzzle-runtime-surface-border: rgba(255, 255, 255, 0.12); - --puzzle-runtime-board-fill: rgba(255, 255, 255, 0.08); - --puzzle-runtime-board-border: rgba(255, 255, 255, 0.16); - --puzzle-runtime-board-shadow: 0 30px 80px rgba(0, 0, 0, 0.35); - --puzzle-runtime-piece-fill: rgba(255, 255, 255, 0.12); - --puzzle-runtime-piece-border: transparent; - --puzzle-runtime-piece-empty-fill: rgba(0, 0, 0, 0.18); - --puzzle-runtime-piece-empty-text: rgba(255, 255, 255, 0.2); - --puzzle-runtime-piece-selected-fill: rgba(251, 191, 36, 0.84); - --puzzle-runtime-piece-selected-text: #020617; - --puzzle-runtime-piece-selected-border: transparent; - --puzzle-runtime-next-card-overlay: rgba(0, 0, 0, 0.1); - --puzzle-runtime-control-fill: rgba(0, 0, 0, 0.36); - --puzzle-runtime-control-hover-fill: rgba(255, 255, 255, 0.1); - --puzzle-runtime-primary-fill: #fde68a; - --puzzle-runtime-primary-text: #020617; - --puzzle-runtime-primary-shadow: 0 14px 36px rgba(251, 191, 36, 0.26); - --puzzle-runtime-accent-text: #fde68a; - --puzzle-runtime-cool-text: #cffafe; - --puzzle-runtime-danger-fill: rgba(239, 68, 68, 0.2); - --puzzle-runtime-danger-text: #fee2e2; - --puzzle-runtime-backdrop-fill: rgba(2, 6, 23, 0.68); - --puzzle-runtime-dialog-fill: radial-gradient( - circle at 14% 0%, - rgba(251, 191, 36, 0.16), - transparent 36% - ), - linear-gradient(180deg, rgba(29, 17, 12, 0.96), rgba(2, 6, 23, 0.96)); - --puzzle-runtime-dialog-border: rgba(251, 191, 36, 0.22); - --puzzle-runtime-table-fill: rgba(255, 255, 255, 0.06); - --puzzle-runtime-table-row-fill: rgba(251, 191, 36, 0.14); - --puzzle-runtime-next-card-fill: rgba(255, 255, 255, 0.06); - --puzzle-runtime-next-card-hover-fill: rgba(251, 191, 36, 0.1); -} .platform-brand-logo { display: inline-flex; diff --git a/src/index.test.ts b/src/index.test.ts index 9584033c9..dae786b6e 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -7,6 +7,13 @@ function readIndexCss() { return fs.readFileSync(path.resolve(process.cwd(), 'src/index.css'), 'utf8'); } +function readPlatformThemeCss() { + return fs.readFileSync( + path.resolve(process.cwd(), 'packages/shared/src/platform-theme.css'), + 'utf8', + ); +} + function getCssBlock(source: string, selector: string) { const selectorIndex = source.indexOf(selector); expect(selectorIndex, `${selector} should exist in src/index.css`).toBeGreaterThanOrEqual(0); @@ -194,11 +201,16 @@ describe('index stylesheet unread dots', () => { it('uses warm brown tokens for draft unread markers instead of red literals', () => { const css = readIndexCss(); + const themeCss = readPlatformThemeCss(); - expect(css).toContain('--platform-unread-dot-fill: #8b5a3d;'); - expect(css).toContain('--platform-unread-dot-glow: rgba(139, 90, 61, 0.34);'); - expect(css).toContain('--platform-unread-dot-fill: #d6a27c;'); - expect(css).toContain('--platform-unread-dot-glow: rgba(214, 162, 124, 0.24);'); + expect(themeCss).toContain('--platform-unread-dot-fill: #8b5a3d;'); + expect(themeCss).toContain( + '--platform-unread-dot-glow: rgba(139, 90, 61, 0.34);', + ); + expect(themeCss).toContain('--platform-unread-dot-fill: #d6a27c;'); + expect(themeCss).toContain( + '--platform-unread-dot-glow: rgba(214, 162, 124, 0.24);', + ); for (const selector of [ '.creation-work-card__unread-dot', -- 2.52.0 From 9ecf0a2d91cb250563306f1aa2cb1ef817dfdc88 Mon Sep 17 00:00:00 2001 From: kvtodev Date: Tue, 14 Jul 2026 13:21:19 +0800 Subject: [PATCH 2/3] update theme path references in tests --- packages/shared/src/theme.test.ts | 2 +- src/index.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/theme.test.ts b/packages/shared/src/theme.test.ts index 04400bb29..35e63d76d 100644 --- a/packages/shared/src/theme.test.ts +++ b/packages/shared/src/theme.test.ts @@ -5,7 +5,7 @@ import {describe, expect, it} from 'vitest'; const themePath = path.resolve( process.cwd(), - 'packages/shared/src/platform-theme.css', + 'packages/shared/src/theme.css', ); function getCssBlock(source: string, selector: string) { diff --git a/src/index.test.ts b/src/index.test.ts index dae786b6e..124430a01 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -9,7 +9,7 @@ function readIndexCss() { function readPlatformThemeCss() { return fs.readFileSync( - path.resolve(process.cwd(), 'packages/shared/src/platform-theme.css'), + path.resolve(process.cwd(), 'packages/shared/src/theme.css'), 'utf8', ); } -- 2.52.0 From 40a92c0a7a5dff5a8864034d6108c32ddef5caa5 Mon Sep 17 00:00:00 2001 From: kvtodev Date: Tue, 14 Jul 2026 16:27:42 +0800 Subject: [PATCH 3/3] specially treat font to make sure it will not override --- docs/project-memory/shared-memory/decision-log.md | 2 +- docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md | 2 +- packages/shared/src/theme.css | 8 ++------ packages/shared/src/theme.test.ts | 8 ++++++++ src/index.css | 5 +++++ src/index.test.ts | 14 ++++++++++++++ 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 3303af1db..f9d8c6e98 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2649,7 +2649,7 @@ # 2026-05-20 陶泥儿主视觉é…色回收为暖白/陶土橙 - èƒŒæ™¯ï¼šç”¨æˆ·è¦æ±‚åªæ›¿æ¢äº§å“å„界é¢çš„ UI é¢œè‰²ï¼Œä¸æ”¹å¸ƒå±€ï¼Œå¹¶ä»¥ä¸¤å¼ é™¶æ³¥å„¿ä¸»è§†è§‰å›¾ä½œä¸ºé…è‰²ä¾æ®ã€‚ -- 决策:平å°äº®è‰²ä¸»é¢˜çš„主色回收到暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›åŽå°ç®¡ç†ä¹ŸåŒæ­¥åˆ‡æ¢åˆ°åŒä¸€æš–橙体系。主题å˜é‡ä¼˜å…ˆé€šè¿‡ `packages/shared/src/platform-theme.css` çš„ `--platform-*` token 统一控制,零散组件åªåšå¿…è¦çš„局部替æ¢ã€‚ +- 决策:平å°äº®è‰²ä¸»é¢˜çš„主色回收到暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›åŽå°ç®¡ç†ä¹ŸåŒæ­¥åˆ‡æ¢åˆ°åŒä¸€æš–橙体系。主题å˜é‡å’Œå¹³å°å­—体栈优先通过 `packages/shared/src/theme.css` çš„ `--platform-*` token 统一控制,具体全局字体选择器留在消费端原有层å ä½ç½®ï¼Œé›¶æ•£ç»„ä»¶åªåšå¿…è¦çš„局部替æ¢ã€‚ - å½±å“范围:主站平å°å£³å±‚ã€å¸¸ç”¨è¡¨å• / 按钮 / å¡ç‰‡ / 背景ã€åŽå°ç®¡ç† UIã€ä¸šåŠ¡è¿›åº¦æ¡å’Œå°æ¸¸æˆç»“æžœæ¡çš„通用强调色。 - éªŒè¯æ–¹å¼ï¼šä¼˜å…ˆæ£€æŸ¥ `src/index.css` 与 `apps/admin-web/src/styles/admin.css` 是å¦è¿˜å­˜åœ¨æ—§ç²‰è‰²ä¸»è‰²ï¼›å†ç”¨ç¼–ç æ£€æŸ¥å’Œå¯æ‰§è¡Œçš„æœ¬åœ° typecheck / build 验è¯ã€‚ - å…³è”æ–‡æ¡£ï¼š`docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md`。 diff --git a/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md b/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md index 74366143f..36ff0a5d0 100644 --- a/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md +++ b/docs/ã€é¡¹ç›®åŸºçº¿ã€‘当å‰äº§å“与工程约æŸ-2026-05-15.md @@ -108,7 +108,7 @@ server-rs + Axum + SpacetimeDB 12. “我的â€é¡µæ³¥ç‚¹ä½™é¢ã€ç´¯è®¡æ¸¸çŽ©ã€å·²çŽ©æ¸¸æˆä¸‰å¼ ç»Ÿè®¡å¡åªå±•示å„自标签和值,三个统计 icon 使用å°å°ºå¯¸æ™®é€š UI æ¡£ä½ï¼Œå†…容䏿¢è¡Œï¼Œä¸åœ¨ç»Ÿè®¡åŒºåº•éƒ¨å±•ç¤ºâ€œæ›´æ–°äºŽâ€æ—¶é—´ï¼›ç§»åŠ¨ç«¯æ˜µç§°ã€å¸¸ç”¨åŠŸèƒ½å’Œæ³•å¾‹ä¿¡æ¯ä¹Ÿåº”ä¿æŒ `10px` 到 `14px` 的普通 UI å­—å·åŒºé—´ï¼Œé¿å…å±•ç¤ºçº§å­—å·æŒ¤åŽ‹å†…å®¹ã€‚ 13. 移动端“我的â€é¡µéœ€è¦å…¼å®¹çª„å±ï¼šå¤´åƒ / 昵称 / é™¶æ³¥å·ã€ä¸‰å¼ ç»Ÿè®¡å¡ã€äº”项常用功能和法律信æ¯éƒ½å¿…须能在底部固定 TabBar 上方完整滚动露出,ä¸å¾—与底部 dockã€åˆ˜æµ· safe-area 或相邻 UI å…ƒç´ é®æŒ¡é‡å ã€‚ 14. RPG ç­‰è¿è¡Œæ€çš„æˆ˜æ–—飘字ã€è¡€é‡å˜åŒ–å’Œå³æ—¶å馈必须在暗色ã€å™ªå£°é«˜çš„åœºæ™¯èƒŒæ™¯ä¸Šä¿æŒå¯è¯»ï¼šä½¿ç”¨é«˜äº®æ–‡å­—ã€æ·±è‰²æè¾¹ã€å¼ºé˜´å½±æˆ–å°é¢ç§¯åŠé€æ˜Žåº•,ä¸åªä¾èµ–红/绿文字本身表达伤害或治疗。 -15. å¹³å°äº®è‰² UI é…色以陶泥儿主视觉为准:暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›æ–°å¢žç•Œé¢ä¼˜å…ˆå¤ç”¨ `packages/shared/src/platform-theme.css` çš„ `--platform-*` 主题å˜é‡å’Œ `apps/admin-web/src/styles/admin.css` çš„åŒç³»è‰²å€¼ï¼Œä¸å†å¼•入粉红ã€è“绿等独立主色方案。 +15. å¹³å°äº®è‰² UI é…色以陶泥儿主视觉为准:暖白 / ç±³æåº•ã€é™¶åœŸæ©™ä¸»æŒ‰é’®ã€æ·±æ£•正文与浅æè¾¹æ¡†ï¼›æ–°å¢žç•Œé¢ä¼˜å…ˆå¤ç”¨ `packages/shared/src/theme.css` çš„ `--platform-*` 主题å˜é‡å’Œå¹³å°å­—体栈ã€`apps/admin-web/src/styles/admin.css` çš„åŒç³»è‰²å€¼ï¼Œä¸å†å¼•入粉红ã€è“绿等独立主色方案;具体全局字体选择器ä¿ç•™åœ¨æ¶ˆè´¹ç«¯åŽŸæœ‰å±‚å ä½ç½®ï¼Œé¿å…共享样å¼å¯¼å…¥æ”¹å˜æ—¢æœ‰è§†è§‰ã€‚ ## æ–‡æ¡ˆä¸Žç¼–ç  diff --git a/packages/shared/src/theme.css b/packages/shared/src/theme.css index 4ffe8d340..08fc0a5b5 100644 --- a/packages/shared/src/theme.css +++ b/packages/shared/src/theme.css @@ -1,10 +1,6 @@ -.platform-theme, -.platform-theme * { - font-family: 'Inter', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'PingFang SC', - 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif !important; -} - .platform-theme { + --platform-font-family: 'Inter', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei', + 'PingFang SC', 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif; color: var(--platform-text-strong); } diff --git a/packages/shared/src/theme.test.ts b/packages/shared/src/theme.test.ts index 35e63d76d..0fe6c9c9a 100644 --- a/packages/shared/src/theme.test.ts +++ b/packages/shared/src/theme.test.ts @@ -30,6 +30,14 @@ function getCssBlock(source: string, selector: string) { } describe('shared platform theme', () => { + it('exposes the shared platform font stack without fixing its cascade position', () => { + const css = fs.readFileSync(themePath, 'utf8'); + const platformTheme = getCssBlock(css, '.platform-theme'); + + expect(platformTheme).toContain("--platform-font-family: 'Inter'"); + expect(css).not.toContain('.platform-theme,\n.platform-theme *'); + }); + it('keeps the light and dark semantic color contracts', () => { const css = fs.readFileSync(themePath, 'utf8'); const light = getCssBlock(css, '.platform-theme--light'); diff --git a/src/index.css b/src/index.css index 1666d6a50..463ba71b3 100644 --- a/src/index.css +++ b/src/index.css @@ -958,6 +958,11 @@ html[data-mobile-keyboard-open='true'] #root { 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif !important; } +.platform-theme, +.platform-theme * { + font-family: var(--platform-font-family) !important; +} + .platform-brand-logo { display: inline-flex; diff --git a/src/index.test.ts b/src/index.test.ts index 124430a01..412a98df0 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -225,6 +225,20 @@ describe('index stylesheet unread dots', () => { } }); + it('keeps platform font overrides after branded title typography', () => { + const css = readIndexCss(); + const themeCss = readPlatformThemeCss(); + const titleRuleIndex = css.indexOf('.selection-hero-brand__title'); + const platformThemeRuleIndex = css.indexOf('.platform-theme,\n.platform-theme *'); + + expect(titleRuleIndex).toBeGreaterThanOrEqual(0); + expect(platformThemeRuleIndex).toBeGreaterThan(titleRuleIndex); + expect(getCssBlock(css, '.platform-theme,\n.platform-theme *')).toContain( + 'font-family: var(--platform-font-family) !important;', + ); + expect(themeCss).not.toContain('.platform-theme,\n.platform-theme *'); + }); + it('keeps the creation shelf share button on a visible surface', () => { const css = readIndexCss(); const block = getCssBlock(css, '.creation-work-card__quick-action-button'); -- 2.52.0