UI 与主题

01MVP 的视觉语言、token 体系、主题预设和组件约束——改样式前先读这个。

设计背景

这套模板的定位很明确:给需要快速上线 MVP 的开发者、技术创始人和小团队用的。核心目标就一个——在不牺牲专业感的前提下,尽快交付。

品牌个性:现代、极简、克制。方向接近 Vercel,但 01MVP 在边框、留白和少量阴影上保留了自己的层次感。

设计方向是硬核极简:单色系优先、边框分层、紧凑信息密度,移动端默认接近 App 级紧凑程度。

硬性约束(每次提交 UI 代码前请自查):

  • 永远优先语义化 token,而不是硬编码颜色(如 bg-background 而非 bg-white
  • 使用边框作为主要分层机制,阴影只做辅助
  • 交互反馈尽量依赖颜色、透明度和边框变化
  • 所有页面默认支持键盘导航与清晰焦点态

先理解三层组件边界

项目使用 shadcn/ui,组件按三层划分:

  • packages/ui:纯 shadcn/ui 原版组件,不能手动修改,只能通过 shadcn CLI 更新
  • packages/ui-shared:跨多个 app 复用的业务组件(如 UserAvatar、Logo)
  • apps/*/src/components:只属于单个 app 的页面组件

Typography

  • font-sans(Geist Sans + Source Han Sans CN):正文、界面、标题
  • font-mono(Geist Mono):代码、日志
Token尺寸用途
text-sm14px次级文本、标签
text-base16px正文默认值
text-lg ~ text-xl18-20px强调正文、小标题
text-2xl24px模块标题
text-4xl ~ text-6xl36-60px页面标题、Hero

大标题用 tracking-tight,长文正文用 leading-relaxed

Color System

以黑白灰单色系为基础,通过少量语义色(success、destructive)承载状态,不作为装饰。

实际页面分层规则:

页面分层速查:最外层用黑色强边框 -> 内部元素用灰色细边框 -> 文档正文用浅灰底 -> 状态色只给反馈用。每个语义 token 在亮色和暗色模式下有对应取值,组件引用 token 就能自动适配,不需要写 dark: 前缀。

Spacing 和 Layout

基于 4px 基准:4, 8, 12, 16, 24, 32, 48, 64, 96

常用建议:按钮 px-4 py-2、输入框 px-3 py-2、卡片 p-6、Modal p-8、Section py-16 md:py-24

Radius 和 Shadow

01MVP 默认方角(--radius: 0px)。头像、开关和 pill badge 可用 rounded-full

推荐语义类:

<div className="theme-frame p-6">外层强面板</div>
<div className="theme-surface p-4">普通信息块</div>
<input className="theme-control h-12 px-4" />

阴影策略:默认不依赖阴影,优先用边框和背景对比。只有浮层、悬浮卡片和下拉菜单才使用轻量阴影。

Motion

  • 动画必须有明确目的,时长控制在 150ms300ms
  • 尊重 prefers-reduced-motion
  • 推荐 easing:ease-outease-in-out

切换网站风格

内置了 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

新增页面、组件、弹窗、表单时,不要只按当前截图写死一套样式。默认要求是:切换 01mvpvercellinearclaude 后仍然能读、能点、层级清楚。

优先使用这些语义类:

<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。

响应式设计

断点:sm: 640pxmd: 768pxlg: 1024pxxl: 1280px2xl: 1536px。移动优先,用 md:lg: 前缀逐层增强。交互元素尽量达到 44x44px 触摸区域。

手机端要更紧凑

移动端是主要使用场景之一,默认按更接近微信小程序和 WhatsApp 的信息密度来做:

  • 手机端先保证可扫读,不要把桌面端的大留白直接搬过来
  • section 默认用更小的 py,到 md: / lg: 再放大
  • 卡片、列表、按钮组在手机端少用大圆角、大阴影和过厚 padding
  • 保留 44px 左右的点击区域,但减少图标框、文字上下间距和整屏空白
  • 底部导航、固定按钮和弹窗要避开安全区,不要让正文被迫留出过大底部空白

设置页和表单页要更像 App 设置,而不是桌面后台卡片:

  • 优先用分组列表、分割线、边框和背景层级,不要在手机端堆大阴影卡片
  • 分组间距通常控制在 8px12px,内容 padding 通常控制在 12px16px
  • 输入框高度保持 44px48px,既能点击,又不撑高页面
  • 未修改时不要常驻一个灰色不可用的保存按钮;用户修改后再出现满宽主按钮
  • 头像、账号信息这类内容优先用横向行:小头像 + 操作文字 / 按钮,不要用大头像占掉半屏

常见改造点

替换 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" 指令包裹错误展示
  • 空状态:在组件内判断数据是否为空,渲染友好的占位提示

相关资源