Personal Budget Docs
定制

主题

五套主题(浅色 / 纸张 / 纸张深色 / 深色 / OLED)与配色。解析顺序:显式选择(cookie)> 系统偏好 > 浅色(默认),配色用 CSS 变量集中管理。

fork 后你要改哪: 换配色改 src/styles/app.css 的 CSS 变量;改兜底明暗改 src/server/preferences.ts + __root.tsx 里的 boot script。

工作原理

  • 偏好:主题存在 theme cookie 里,值是 light、paper、paper-dark、dark 或 oled。 getPreferences(src/server/preferences.ts)读取它,SSR 按 cookie 渲染, 无 cookie(或值非法)时兜底浅色。
  • 系统偏好:__root.tsx 的 <head> 里有一段首绘前的内联脚本——没有 cookie 且 prefers-color-scheme: dark 时把 <html> 翻成深色(无闪烁)。它刻意不写 cookie: 没做过选择的访客持续跟随系统,点过切换按钮才锁定。纸张、纸张深色与 OLED 只来自显式选择(cookie / 设置里选),永远不会跟随系统。
  • 应用:__root.tsx 把主题作为 class 加到 <html class={theme}>(带 suppressHydrationWarning,因为脚本可能在水合前改过 class)。
  • 切换:两处入口——ThemeToggle(src/features/theme/theme-toggle.tsx)在 浅色 → 纸张 → 纸张深色 → 深色 → OLED 间循环;账号设置页(app/account)的 「外观」区有带预览色卡的五选一。 施加都走 src/features/theme/theme.ts 的 applyTheme:写 cookie(一年)+ 改 <html> class + 同步 next-themes 的 localStorage。
  • 客户端读真实主题:loader 的值在「无 cookie + 系统浅色」时是错的,依赖主题渲染的 组件(切换按钮图标、Toaster)用 useResolvedTheme 在挂载后读 DOM。

改配色

配色集中在 src/styles/app.css 的 CSS 变量:

  • :root 定义浅色令牌(--background、--foreground、--primary 等);
  • :root.dark, .dark 覆盖为深色值;
  • :root.paper, .paper 覆盖为纸张(极简中性纸:纯中性灰纸底、发丝线边框、 墨色 primary,图表/语义色按色相骨架取铅笔色调)值;
  • :root.paper-dark, .paper-dark 覆盖为纸张深色(中性近黑面、浅墨 primary,类似电子墨水)值;
  • :root.oled, .oled 覆盖为 OLED 纯黑(#000000 底、ember 强调,Dark 配色抬到纯黑上)值;
  • dark: Tailwind 变体由 .dark、.paper-dark 或 .oled 祖先触发(两份样式表里的 @custom-variant dark (&:where(.dark *, .paper-dark *, .oled *)));
  • @theme inline 把这些变量映射成 Tailwind v4 的 --color-*,组件里就能用 bg-background、text-fg-2 这类类名。

色相骨架(所有主题共用)

每个变量在所有主题里绑定同一个色相——切主题只换明度/饱和度(调 tint), 绝不换"变量 → 色相"的对应关系。src/styles/app.css 里 "Theme hue skeleton" 注释是唯一事实源:

  • chart-1 与 primary = 品牌强调色(纸张系是墨色,其余是 ember 橙);
  • chart-2 与 transfer = 蓝;chart-3、income、success = 绿;
  • chart-4 = 紫;chart-5 与 goal = 琥珀;chart-6 = 粉;
  • expense 与 destructive = 红。

新增一套主题 = 新加一个 :root.xxx, .xxx 变量块,按骨架给每个变量取一套 tint;shadcn 标准令牌和 Tailwind 桥接会自动重新解析。

新增一套主题 checklist

  1. src/styles/app.css:新 :root.xxx, .xxx 变量块(按上表骨架铺 tint); 若是深色系主题,把 .xxx 加进两份样式表(app.css + docs.css)的 @custom-variant dark 选择器列表。
  2. src/features/theme/theme.ts:Theme 联合类型、THEMES、isTheme 登记; toSonnerTheme 里为二元 sonner 指定 light/dark。
  3. src/routes/docs/$.tsx:next-themes defaultTheme 映射(主题 → light 或 dark)+ cookie 正则登记;文档站二元切换器不会覆盖该主题的 cookie。
  4. src/routes/{-$locale}/app/account.tsx:THEME_PREVIEWS 加色卡 (手抄 CSS 里的十六进制值)、labels 加标签;必要时调 grid 列数。
  5. src/features/theme/theme-toggle.tsx:icons 里加循环图标。
  6. i18n:en.ts 与 zh.ts 各加一个 themeX 键。
  7. 主题只来自显式选择——boot script 只认 light/dark,新主题不跟随 OS。

灯箱类组件(sonner Toaster、fumadocs 文档站切换器)只认明暗二元——纸张在 Toaster 里按浅色渲染(toSonnerTheme),纸张深色与 OLED 按深色;文档站在 纸张 cookie 下固定用浅色,纸张深色/OLED cookie 下固定用深色,且不会把切换 结果写回纸张系 / OLED cookie。

On this page