diff --git a/components/calendar-heatmap/calendar-heatmap.wxss b/components/calendar-heatmap/calendar-heatmap.wxss index 8177f48..3721d42 100644 --- a/components/calendar-heatmap/calendar-heatmap.wxss +++ b/components/calendar-heatmap/calendar-heatmap.wxss @@ -15,7 +15,7 @@ width: 56rpx; text-align: center; font-size: 22rpx; - color: #999; + color: var(--text-secondary); } .calendar-grid { @@ -44,7 +44,7 @@ .day-num { font-size: 22rpx; - color: #333; + color: var(--text); font-weight: 500; } @@ -58,7 +58,7 @@ .legend-label { font-size: 20rpx; - color: #999; + color: var(--text-secondary); } .legend-block { diff --git a/components/progress-ring/progress-ring.js b/components/progress-ring/progress-ring.js index c4f710a..b37c5db 100644 --- a/components/progress-ring/progress-ring.js +++ b/components/progress-ring/progress-ring.js @@ -8,7 +8,10 @@ Component({ ringWidth: { type: Number, value: 14 }, status: { type: String, value: 'idle' }, primaryColor: { type: String, value: '#FF6B35' }, - primaryLightColor: { type: String, value: '#FF8C5A' } + primaryLightColor: { type: String, value: '#FF8C5A' }, + // Track (background ring) color. Caller passes a darker shade in dark mode + // so the ring isn't an eye-searing light circle on a dark background. + trackColor: { type: String, value: '#EEEEEE' } }, data: { @@ -25,7 +28,7 @@ Component({ }) }, - 'remaining, duration, size, ringWidth, primaryColor'() { + 'remaining, duration, size, ringWidth, primaryColor, trackColor'() { this._draw() } }, @@ -54,7 +57,7 @@ Component({ const ctx = this._ctx if (!ctx) return - const { size, ringWidth, duration, remaining, primaryColor } = this.data + const { size, ringWidth, duration, remaining, primaryColor, trackColor } = this.data const dpr = this._dpr const w = size * dpr @@ -69,7 +72,7 @@ Component({ // track ring ctx.beginPath() ctx.arc(cx, cy, radius, 0, Math.PI * 2) - ctx.strokeStyle = '#EEEEEE' + ctx.strokeStyle = trackColor ctx.lineWidth = lw ctx.lineCap = 'round' ctx.stroke() diff --git a/components/ui-btn/ui-btn.wxss b/components/ui-btn/ui-btn.wxss index df087af..00a4782 100644 --- a/components/ui-btn/ui-btn.wxss +++ b/components/ui-btn/ui-btn.wxss @@ -55,14 +55,14 @@ } .ui-btn--danger { - background: #FFFFFF; - color: #E53935; - border: 2rpx solid rgba(229, 57, 53, 0.3); + background: var(--card-bg); + color: var(--danger); + border: 2rpx solid rgba(var(--danger-rgb), 0.3); } .ui-btn--danger:active:not(.is-disabled) { - background: rgba(229, 57, 53, 0.05); - border-color: #E53935; + background: rgba(var(--danger-rgb), 0.05); + border-color: var(--danger); } .ui-btn--ghost { diff --git a/components/ui-modal/ui-modal.wxss b/components/ui-modal/ui-modal.wxss index e33fe89..6655f52 100644 --- a/components/ui-modal/ui-modal.wxss +++ b/components/ui-modal/ui-modal.wxss @@ -16,7 +16,7 @@ .ui-modal__body { position: relative; - background: #FFFFFF; + background: var(--card-bg); box-sizing: border-box; } @@ -24,7 +24,9 @@ width: 100%; border-radius: 32rpx 32rpx 0 0; padding: 28rpx 32rpx 40rpx; - padding-bottom: calc(40rpx + env(safe-area-inset-bottom)); + /* bottom sheet 需要避开 Dock TabBar(高度 ~110rpx + 40rpx 间隙), + 否则最底下的按钮会被 toolbar 遮挡。150rpx = dock 高度 + margin。 */ + padding-bottom: calc(150rpx + env(safe-area-inset-bottom)); animation: uiModalSlideUp 0.3s cubic-bezier(0.32, 0.72, 0, 1); } @@ -39,7 +41,7 @@ .ui-modal__handle { width: 64rpx; height: 8rpx; - background: #DDD; + background: var(--bg-soft); border-radius: 4rpx; margin: 0 auto 24rpx; } diff --git a/custom-tab-bar/index.js b/custom-tab-bar/index.js index 2201c48..13ef900 100644 --- a/custom-tab-bar/index.js +++ b/custom-tab-bar/index.js @@ -5,21 +5,23 @@ const themeMod = require('../utils/theme') const _initialTheme = themeMod.getCurrentTheme() const _initialThemeStyle = themeMod.getThemeStyle(_initialTheme) -// SVG icons as data URIs. We use two variants per icon: -// - *Fill*: solid filled (active state, theme color baked in at module load) -// - *Line*: outlined (inactive state, neutral gray) -// Themes are 5 fixed colors so we can bake the active color in at module init. -// If the user changes theme, the component re-renders via pageLifetimes.show. +// Inactive icon color follows the dark mode: matches --text-secondary in dark +// mode and a softer gray in light mode. Kept in sync with utils/theme.js so +// icons don't sit awkwardly on the dock background in either theme. +const LIGHT_INACTIVE = '#999999' +const DARK_INACTIVE = '#98989F' -// Solid (active) color from current theme -const _activeColor = _initialTheme.primary -// Inactive color -const _inactiveColor = '#999999' +const _getInactiveColor = () => { + const darkMod = require('../utils/darkMode') + return darkMod.getInitialDarkMode() ? DARK_INACTIVE : LIGHT_INACTIVE +} + +const _initialInactiveColor = _getInactiveColor() const _svg = (path, fill) => 'data:image/svg+xml,' + encodeURIComponent( '' + - path + '' + path + '' ) // 24x24 Material-style paths @@ -30,18 +32,18 @@ const PATH_CALENDAR_LINE = '' const PATH_RANK_LINE = '' const PATH_SETTINGS = '' -const PATH_SETTINGS_LINE = '' +const PATH_SETTINGS_LINE = '' -const _buildIcons = (activeHex) => { +const _buildIcons = (activeHex, inactiveHex) => { const a = activeHex return { - home: _svg(PATH_HOME_LINE, _inactiveColor), + home: _svg(PATH_HOME_LINE, inactiveHex), homeFill: _svg(PATH_HOME, a), - calendar: _svg(PATH_CALENDAR_LINE, _inactiveColor), + calendar: _svg(PATH_CALENDAR_LINE, inactiveHex), calendarFill: _svg(PATH_CALENDAR, a), - rank: _svg(PATH_RANK_LINE, _inactiveColor), + rank: _svg(PATH_RANK_LINE, inactiveHex), rankFill: _svg(PATH_RANK, a), - settings: _svg(PATH_SETTINGS_LINE, _inactiveColor), + settings: _svg(PATH_SETTINGS_LINE, inactiveHex), settingsFill: _svg(PATH_SETTINGS, a) } } @@ -51,12 +53,15 @@ Component({ selected: 0, theme: _initialTheme, themeStyle: _initialThemeStyle, - svgIcons: _buildIcons(_initialTheme.primary) + svgIcons: _buildIcons(_initialTheme.primary, _initialInactiveColor) }, lifetimes: { attached() { themeMod.applyThemeToPage(this) + // Rebuild icons in case the user changed theme/dark mode since module load + const theme = themeMod.getCurrentTheme() + this.setData({ svgIcons: _buildIcons(theme.primary, _getInactiveColor()) }) } }, @@ -64,7 +69,7 @@ Component({ show() { themeMod.applyThemeToPage(this) const theme = themeMod.getCurrentTheme() - this.setData({ svgIcons: _buildIcons(theme.primary) }) + this.setData({ svgIcons: _buildIcons(theme.primary, _getInactiveColor()) }) } }, diff --git a/pages/index/index.wxss b/pages/index/index.wxss index 0f9d751..872eca0 100644 --- a/pages/index/index.wxss +++ b/pages/index/index.wxss @@ -202,7 +202,7 @@ .done-badge { display: inline-flex; align-items: center; - background: rgba(76, 175, 80, 0.08); + background: rgba(var(--success-rgb), 0.08); padding: 10rpx 24rpx; border-radius: 24rpx; } @@ -227,7 +227,7 @@ .pending-text { font-size: 26rpx; - color: #CCC; + color: var(--text-secondary); line-height: 1; } @@ -281,7 +281,7 @@ .preset-item { flex: 0 0 calc(33.33% - 12rpx); - background: #F5F5F5; + background: var(--bg-soft); border-radius: 16rpx; padding: 20rpx 0; text-align: center; @@ -326,14 +326,14 @@ .custom-label { font-size: 28rpx; - color: #666; + color: var(--text-secondary); line-height: 1; } .custom-input { width: 160rpx; height: 64rpx; - background: #F5F5F5; + background: var(--bg-soft); border-radius: 12rpx; text-align: center; font-size: 32rpx; diff --git a/pages/leaderboard/leaderboard.json b/pages/leaderboard/leaderboard.json index cc24158..982e8c7 100644 --- a/pages/leaderboard/leaderboard.json +++ b/pages/leaderboard/leaderboard.json @@ -1,6 +1,5 @@ { "navigationBarTitleText": "排行榜", "enablePullDownRefresh": true, - "backgroundColor": "#F5F5F5", "usingComponents": {} } diff --git a/pages/leaderboard/leaderboard.wxss b/pages/leaderboard/leaderboard.wxss index 31e2ca0..40a3d76 100644 --- a/pages/leaderboard/leaderboard.wxss +++ b/pages/leaderboard/leaderboard.wxss @@ -112,9 +112,9 @@ margin-right: 16rpx; } -.rank-num.gold { background: #FFF7E6; color: #FA8C16; font-size: 36rpx; } +.rank-num.gold { background: rgba(250, 140, 22, 0.15); color: #FA8C16; font-size: 36rpx; } .rank-num.silver { background: var(--bg-soft); color: #8C8C8C; font-size: 36rpx; } -.rank-num.bronze { background: #FFF1E6; color: #D46B08; font-size: 36rpx; } +.rank-num.bronze { background: rgba(212, 107, 8, 0.15); color: #D46B08; font-size: 36rpx; } .rank-avatar { width: 64rpx; diff --git a/pages/records/records.wxss b/pages/records/records.wxss index a6b46e7..677ad0f 100644 --- a/pages/records/records.wxss +++ b/pages/records/records.wxss @@ -108,7 +108,7 @@ .history-hint { font-size: 22rpx; - color: #CCC; + color: var(--text-secondary); } .empty-state { @@ -126,14 +126,14 @@ } .empty-text { - color: #CCC; + color: var(--text-secondary); font-size: 28rpx; margin-bottom: 8rpx; } .empty-sub { font-size: 24rpx; - color: #E0E0E0; + color: var(--text-secondary); } .history-list { @@ -195,8 +195,8 @@ gap: 6rpx; padding: 0 18rpx; height: 56rpx; - background: rgba(229, 57, 53, 0.1); - border: 2rpx solid rgba(229, 57, 53, 0.2); + background: rgba(var(--danger-rgb), 0.1); + border: 2rpx solid rgba(var(--danger-rgb), 0.2); border-radius: 28rpx; flex-shrink: 0; transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease; @@ -204,8 +204,8 @@ .history-delete-btn:active { transform: scale(0.92); - background: rgba(229, 57, 53, 0.2); - border-color: rgba(229, 57, 53, 0.4); + background: rgba(var(--danger-rgb), 0.2); + border-color: rgba(var(--danger-rgb), 0.4); } .history-delete-icon { @@ -216,7 +216,7 @@ .history-delete-text { font-size: 24rpx; - color: #E53935; + color: var(--danger); font-weight: 600; line-height: 1; } diff --git a/pages/settings/settings.wxss b/pages/settings/settings.wxss index e36268b..ec1ed31 100644 --- a/pages/settings/settings.wxss +++ b/pages/settings/settings.wxss @@ -90,7 +90,7 @@ .plan-radio { width: 40rpx; height: 40rpx; - border: 3rpx solid #DDD; + border: 3rpx solid var(--border); border-radius: 50%; display: flex; align-items: center; @@ -240,7 +240,7 @@ height: 120rpx; padding: 0; margin: 0; - background: #F5F5F5; + background: var(--bg-soft); border-radius: 50%; border: none; line-height: 1; @@ -257,7 +257,7 @@ height: 100%; border-radius: 50%; /* peopleFill svg is mostly transparent; give it a soft tint */ - background: #EEE; + background: var(--bg-soft); padding: 24rpx; box-sizing: border-box; } @@ -319,7 +319,7 @@ .about-copy { font-size: 22rpx; - color: #CCCCCC; + color: var(--text-secondary); } /* ---- plan editor modal ---- */ @@ -347,7 +347,7 @@ max-height: 88vh; overflow-y: auto; box-sizing: border-box; - background: #FFFFFF; + background: var(--card-bg); border-radius: 32rpx 32rpx 0 0; padding: 12rpx 32rpx 40rpx; /* Sheet is z-index 1000 above the tab bar (z-index 999), but the tab bar @@ -361,7 +361,7 @@ .plan-editor-handle { width: 64rpx; height: 8rpx; - background: #DDD; + background: var(--bg-soft); border-radius: 4rpx; margin: 0 auto 16rpx; } @@ -409,7 +409,7 @@ .editor-input { height: 68rpx; - background: #F5F5F5; + background: var(--bg-soft); border-radius: 10rpx; padding: 0 16rpx; font-size: 26rpx; @@ -420,7 +420,7 @@ .editor-preview { margin-top: 6rpx; - background: #FAFAFA; + background: var(--bg); border-radius: 14rpx; padding: 12rpx 16rpx; } @@ -453,7 +453,7 @@ justify-content: space-between; align-items: center; padding: 8rpx 0; - border-bottom: 1rpx solid #EEE; + border-bottom: 1rpx solid var(--border); font-size: 24rpx; } @@ -498,13 +498,13 @@ } .editor-action-reset { - background: #FFFFFF; + background: var(--card-bg); color: var(--text-secondary); border: 2rpx solid var(--border); } .editor-action-reset:active { - background: #F5F5F5; + background: var(--bg-soft); } .editor-action-save { @@ -575,6 +575,6 @@ } .confirm-btn--danger { - background: #E53935; + background: var(--danger); color: #FFFFFF; } diff --git a/utils/theme.js b/utils/theme.js index 13cc739..ad77bae 100644 --- a/utils/theme.js +++ b/utils/theme.js @@ -41,7 +41,7 @@ const THEMES = [ } ] -const BASE_VARS = '--text:#333;--text-secondary:#999;--bg:#F5F5F5;--card-bg:#FFFFFF;--border:#EEE;--success:#4CAF50;--primary:#FF6B35;--primary-light:#FF8C5A;--primary-bg:#FFF3ED;--primary-rgb:255,107,53;' +const BASE_VARS = '--text:#333;--text-secondary:#999;--bg:#F5F5F5;--card-bg:#FFFFFF;--border:#EEE;--success:#4CAF50;--success-rgb:76,175,80;--danger:#E53935;--danger-rgb:229,57,53;--primary:#FF6B35;--primary-light:#FF8C5A;--primary-bg:#FFF3ED;--primary-rgb:255,107,53;' const DEFAULT_THEME_ID = 'orange' const THEME_KEY = 'app_theme' let _navColorTimer = null @@ -97,8 +97,8 @@ const setTheme = (id) => { const getThemeStyle = (theme, isDark = false) => { const t = theme || getCurrentTheme() const baseVars = isDark - ? '--text:#E5E5E7;--text-secondary:#98989F;--bg:#0F0F12;--bg-gradient-start:#0F0F12;--bg-gradient-end:#14141A;--card-bg:#1C1C1E;--bg-soft:#2C2C2E;--border:#2C2C2E;--success:#30D158;' - : '--text:#333333;--text-secondary:#999999;--bg:#F5F5F5;--bg-gradient-start:#F5F5F5;--bg-gradient-end:#FAFAFA;--card-bg:#FFFFFF;--bg-soft:#F0F0F0;--border:#EEEEEE;--success:#4CAF50;' + ? '--text:#E5E5E7;--text-secondary:#98989F;--bg:#0F0F12;--bg-gradient-start:#0F0F12;--bg-gradient-end:#14141A;--card-bg:#1C1C1E;--bg-soft:#2C2C2E;--border:#2C2C2E;--success:#30D158;--success-rgb:48,209,88;--danger:#FF6B6B;--danger-rgb:255,107,107;' + : '--text:#333333;--text-secondary:#999999;--bg:#F5F5F5;--bg-gradient-start:#F5F5F5;--bg-gradient-end:#FAFAFA;--card-bg:#FFFFFF;--bg-soft:#F0F0F0;--border:#EEEEEE;--success:#4CAF50;--success-rgb:76,175,80;--danger:#E53935;--danger-rgb:229,57,53;' return `${baseVars}--primary:${t.primary};--primary-light:${t.primaryLight};--primary-bg:${t.primaryBg};--primary-rgb:${t.primaryRgb};` }