UI 与主题
在 01MVP 设计系统内修改页面、组件和主题,而不是随意堆样式。
先理解三层组件边界
项目使用 shadcn/ui(一个基于 Radix 的组件库,提供按钮、输入框、对话框等预制零件,开箱即用且可自由定制),组件按三层划分:
packages/ui:纯 shadcn/ui 原版组件,不能手动修改,只能通过 shadcn CLI 更新packages/ui-shared:跨多个 app 复用的业务组件(如 UserAvatar、Logo)apps/*/src/components:只属于单个 app 的页面组件
切换网站风格
内置了 4 组视觉风格 preset:
01mvp
默认风格,硬核极简,外层强边框
vercel
黑白灰、细线,适合产品官网
linear
柔和的产品 UI,适合仪表盘和协作工具
claude
温和阅读感,适合知识库和教程
通过环境变量切换:
NEXT_PUBLIC_THEME_PRESET=vercel或修改 apps/01mvp-web/src/lib/config/index.ts 里的 ui.defaultThemePreset。
切换 preset 后需要重启开发服务器(pnpm dev),环境变量的变更不会热更新。
这些 preset 不是旧的独立配色包,而是基于 Tailwind CSS v4 @theme 和 CSS 变量实现。实际 token 在 apps/01mvp-web/src/styles/theme.css,可选项在 apps/01mvp-web/src/lib/theme-presets.ts。
新增 UI 必须能切换 preset
新增页面、组件、弹窗、表单时,不要只按当前截图写死一套样式。默认要求是:切换 01mvp、vercel、linear、claude 后仍然能读、能点、层级清楚。
优先使用这些语义类:
<section className="theme-frame bg-background p-4 text-foreground">
<div className="theme-surface p-4">
<input className="theme-control h-11 px-3" />
<button className="theme-action-primary h-11 px-4">保存</button>
</div>
</section>不要在业务组件里直接写:
bg-white/bg-black/bg-slate-*这类固定背景色text-gray-*/border-zinc-*这类固定文字和边框色shadow-xl/ 大圆角 / 任意 hex 或 OKLCH 颜色- 只适配当前 preset 的局部 CSS
如果确实需要新的视觉角色,比如“强调面板”“代码预览框”“付费提示块”,先在 apps/01mvp-web/src/styles/theme.css 里补变量和类,并给每个 preset 和 dark mode 都定义好,再到组件里使用这个语义类。
新增顶层页面也要确认用户能切换风格:公共页面用现有 header 控制,文档页面用文档顶部的 ThemePresetSwitcher,应用内页面用 dashboard 的显示偏好或现有设置入口。不要做一个完全游离在 theme preset 系统之外的新 shell。
手机端要更紧凑
移动端是主要使用场景之一,默认按更接近微信小程序和 WhatsApp 的信息密度来做:
- 手机端先保证可扫读,不要把桌面端的大留白直接搬过来
- section 默认用更小的
py,到md:/lg:再放大 - 卡片、列表、按钮组在手机端少用大圆角、大阴影和过厚 padding
- 保留 44px 左右的点击区域,但减少图标框、文字上下间距和整屏空白
- 底部导航、固定按钮和弹窗要避开安全区,不要让正文被迫留出过大底部空白
设置页和表单页要更像 App 设置,而不是桌面后台卡片:
- 优先用分组列表、分割线、边框和背景层级,不要在手机端堆大阴影卡片
- 分组间距通常控制在
8px到12px,内容 padding 通常控制在12px到16px - 输入框高度保持
44px到48px,既能点击,又不撑高页面 - 未修改时不要常驻一个灰色不可用的保存按钮;用户修改后再出现满宽主按钮
- 头像、账号信息这类内容优先用横向行:小头像 + 操作文字 / 按钮,不要用大头像占掉半屏
常见改造点
换 Logo
替换 packages/ui-shared/src/components/Logo.tsx 中的 SVG/图片文件,或用 apps/01mvp-web/src/components/ 下的页面级组件覆盖。如果你有多个 Logo 变体(如亮色/暗色),可在 Logo 组件中根据当前主题自动切换。
改配色
在 apps/01mvp-web/src/styles/theme.css 中修改 CSS 变量。核心变量包括:
| 变量名 | 用途 | 示例值 |
|---|---|---|
--primary | 主色,按钮、链接等强调色 | hsl(0 0% 3.9%) |
--background | 页面背景色 | hsl(0 0% 100%) |
--foreground | 正文文字颜色 | hsl(0 0% 3.9%) |
--muted | 次级背景色,用于卡片、标签等 | hsl(0 0% 96.1%) |
--muted-foreground | 次级文字颜色 | hsl(0 0% 45.1%) |
--border | 边框颜色 | hsl(0 0% 89.8%) |
--ring | 聚焦环颜色 | hsl(0 0% 3.9%) |
--destructive | 危险操作颜色(删除等) | #dc2626 |
不要只改 :root。当前主题系统按 .theme-preset-01mvp、.theme-preset-vercel、.theme-preset-linear、.theme-preset-claude 和对应 dark mode 覆盖变量。新增变量时,每个 preset 都要有定义,否则切换风格时会退回到错误的视觉状态。
改字体
字体在 apps/01mvp-web/src/app/layout.tsx 中通过 next/font 引入(默认使用 Geist Sans 和 Geist Mono)。改字体只需替换 next/font/google 的导入和配置即可。
加新页面
在 apps/01mvp-web/src/app/ 下创建文件夹和 page.tsx。例如创建 /about 页面:新建 apps/01mvp-web/src/app/about/page.tsx,导出默认组件即可。Next.js 会基于文件夹结构自动生成路由。
给页面补加载态和空状态
- 加载态:创建
loading.tsx放在页面同级目录,Next.js 自动在页面数据加载时显示 - 错误态:创建
error.tsx,用"use client"指令包裹错误展示 - 空状态:在组件内判断数据是否为空,渲染友好的占位提示