# Fonrey UI System 设计规范
**版本**:v1.0
**最后更新**:2026-04-25
**维护者**:UI/UX 架构组
**适用技术栈**:Tailwind CSS + HTMX + Alpine.js + Django 模板
**目标分辨率**:桌面 Web,≥ 1280px(v1 不做移动端适配)
> **For developers**: 本文是开发还原 UI 的唯一权威。所有组件以 HTML + Tailwind class 描述,不输出 JSX。新增页面须先到此处查找可复用组件与模板;如需新模式,先补本文再落地。
> **关联文档**:
> - `Project/fonrey/TECH_STACK/TECH_STACK.md`(技术总纲)
> - `Project/fonrey/UI&UX/组件清单.md`(组件可行性分析)
> - `Project/fonrey/PRD/*`(业务需求)
---
## 0. 设计语言定调(Design Language Overview)
参考 Linear 的克制、Notion 的信息密度、Salesforce Lightning 的企业严谨,结合竞品(链家/贝壳式房产工具)的操作效率要求,Fonrey 的设计语言定位为:
- **专业、克制、高密度**:表格为王,单屏尽可能多展示数据;色彩只为信息服务,不用于装饰。
- **主色靛青(Teal)**:低饱和、冷静,与状态色(绿/黄/红)形成强区分;与房产行业"稳健/可靠"意象吻合,同时避免与 success 绿产生语义歧义。
- **中等圆角(8px)**:既不像消费端(12px+)过于柔软,也不像传统企业端(0-2px)过于呆板。
- **紧凑密度**:表格行高 40px、表单字段间距 12px,信息密度优先于呼吸感。
---
## 1. 设计原则(Design Principles)
1. **效率优先(Efficiency First)**
减少视觉噪音,让用户聚焦在数据和操作上。表格为核心场景,一屏可见行数 ≥ 15。
2. **状态可见(Visible State)**
任何异步请求必须有反馈(骨架屏 / Spinner / Toast);任何数据变更必须给出成功或失败的即时提示。HTMX 的"静默成功"是 Bug。
3. **复用先于新建(Reuse Over Reinvent)**
每个组件在本文中有唯一标准实现;新需求须先尝试组合既有组件,再提案新组件。
4. **键盘友好(Keyboard First)**
高频操作(搜索、翻页、新增、保存)须支持键盘;表格、表单、弹窗均支持 Tab / Enter / ESC。
5. **一致性高于美观(Consistency Over Cleverness)**
相同的动作在全产品使用相同的图标、色彩、位置。经纪人的肌肉记忆比单页的视觉惊喜更重要。
---
## 2. 设计 Token(Design Tokens)
所有 Token 均映射到 `tailwind.config.js` 的 `theme.extend`,禁止在模板中使用任意十六进制色值。
### 2.1 颜色系统
#### 2.1.1 品牌色(Primary — Teal)
| Token | Hex | Tailwind 类 | 使用场景 |
|---|---|---|---|
| `primary-50` | `#F0FDFA` | `bg-primary-50` | 页面强调区微底色、Tag 极淡底 |
| `primary-100` | `#CCFBF1` | `bg-primary-100` | 选中背景、标签底色 |
| `primary-200` | `#99F6E4` | `bg-primary-200` | Hover 标签底色 |
| `primary-500` | `#14B8A6` | `bg-primary-500` | 辅助主色(图标、强调文字) |
| `primary-600` | `#0F766E` | `bg-primary-600` | **主按钮、激活态、Tab 下划线(基准主色)** |
| `primary-700` | `#115E59` | `bg-primary-700` | 主按钮 Hover |
| `primary-800` | `#134E4A` | `bg-primary-800` | 主按钮 Active / 深色文字 |
#### 2.1.2 中性色(Neutral — Slate 系,偏冷灰)
| Token | Hex | 使用场景 |
|---|---|---|
| `neutral-50` | `#F8FAFC` | 页面背景 |
| `neutral-100` | `#F1F5F9` | Hover 底色、表头底色、禁用输入框底色 |
| `neutral-200` | `#E2E8F0` | 分隔线、默认边框 |
| `neutral-300` | `#CBD5E1` | 输入框边框、次级按钮边框 |
| `neutral-400` | `#94A3B8` | 占位符、禁用文字、辅助图标 |
| `neutral-500` | `#64748B` | 辅助文字、副标题 |
| `neutral-600` | `#475569` | 次级正文 |
| `neutral-700` | `#334155` | 正文 |
| `neutral-800` | `#1E293B` | 标题 |
| `neutral-900` | `#0F172A` | 强调标题 |
#### 2.1.3 语义色(Semantic)
| Token | Hex | 使用场景 |
|---|---|---|
| `success-600` | `#16A34A` | 操作成功 Toast、在售/激活状态 |
| `success-50` | `#F0FDF4` | Success Tag 底色 |
| `warning-600` | `#D97706` | 待确认/临期提醒 |
| `warning-50` | `#FFFBEB` | Warning Tag 底色 |
| `danger-600` | `#DC2626` | 删除/错误/逾期 |
| `danger-50` | `#FEF2F2` | Danger Tag 底色 |
| `info-600` | `#2563EB` | 信息提示、Link、已成交状态 |
| `info-50` | `#EFF6FF` | Info Tag 底色 |
> **语义色与主色分离**:主色是 Teal,success 是独立绿,避免"主操作按钮看起来像成功提示"。
#### 2.1.4 背景层级
| 层级 | Tailwind 类 | 使用场景 |
|---|---|---|
| L0 页面背景 | `bg-neutral-50` | 整体页面底色 |
| L1 卡片/面板 | `bg-white` | 内容区块 |
| L2 表头/次级区块 | `bg-neutral-100` | 表头、工具栏底色、代码块 |
| L3 悬浮层 | `bg-white shadow-lg border border-neutral-200` | 弹窗、下拉、抽屉 |
| L4 遮罩 | `bg-neutral-900/40` | Modal / Drawer 遮罩 |
### 2.2 字体系统
基础字体栈(Tailwind 默认即可):
```
font-sans: "Inter", "PingFang SC", "Microsoft YaHei", -apple-system, sans-serif;
font-mono: "JetBrains Mono", "SFMono-Regular", Menlo, monospace;
```
| 层级 | 字号 | 字重 | 行高 | Tailwind 类 | 使用场景 |
|---|---|---|---|---|---|
| H1 页面标题 | 20px | 600 | 28px | `text-xl font-semibold text-neutral-800` | 页面 H1(紧凑型,避免占太多垂直空间) |
| H2 区块标题 | 16px | 600 | 24px | `text-base font-semibold text-neutral-800` | 卡片/面板标题 |
| H3 次级标题 | 14px | 600 | 20px | `text-sm font-semibold text-neutral-700` | 表单分组、Section 内标题 |
| Body 正文 | 14px | 400 | 20px | `text-sm text-neutral-700` | 表单标签、描述、表格数据(默认) |
| Data 数据强调 | 14px | 500 | 20px | `text-sm font-medium text-neutral-900` | 表格关键列(房源标题、价格) |
| Number 数值 | 20px | 600 | 28px | `text-xl font-semibold tabular-nums` | Stat Card 数值、价格展示 |
| Caption 辅助 | 12px | 400 | 16px | `text-xs text-neutral-500` | 提示、占位符、时间戳 |
| Mono 代码/ID | 12px | 400 | 16px | `text-xs font-mono text-neutral-600` | 房源编号、系统 ID |
> **关键约定**:表格与表单中**所有数字列**必须加 `tabular-nums`(等宽数字),保证纵向对齐。
### 2.3 间距系统
4px 基础栅格,映射到 Tailwind 默认间距 Token。
| 场景 | Token | 值 |
|---|---|---|
| 原子间距(图标与文字) | `gap-1` / `gap-1.5` | 4 / 6 px |
| 组件内边距(密集) | `p-2` | 8 px(如 Tag 内) |
| 组件内边距(标准) | `px-3 py-2` | 12/8 px(输入框、按钮 md) |
| 卡片内边距 | `p-4` 或 `p-6` | 16 / 24 px |
| 表单字段纵向间距 | `space-y-3` | 12 px |
| 区块纵向间距 | `space-y-6` | 24 px |
| 页面两侧边距 | `px-6` | 24 px |
| 列表页内容区边距 | `px-6 py-4` | — |
### 2.4 阴影与圆角
| Token | 值 | 使用场景 |
|---|---|---|
| `rounded` | 4px | 紧凑 Tag、小 Badge |
| `rounded-md` | 6px | 按钮、输入框、下拉选项 |
| `rounded-lg` | 8px | **卡片、面板、表格容器(基准)** |
| `rounded-xl` | 12px | Modal / Drawer |
| `rounded-full` | 圆 | 头像、开关滑块、圆点 |
| 阴影 | 使用场景 |
|---|---|
| `shadow-xs`(自定义 `0 1px 2px rgba(15,23,42,0.04)`) | 卡片静态 |
| `shadow-sm` | 卡片 Hover、次级浮层(如 Tooltip) |
| `shadow-md` | Dropdown、Popover |
| `shadow-lg` | Modal、Drawer、Toast |
### 2.5 圆角圆点与边框
- 默认边框:`border border-neutral-200`
- 强调边框(Focus/激活):`ring-2 ring-primary-600/30 border-primary-600`
- 错误边框:`border-danger-600 ring-2 ring-danger-600/20`
### 2.6 Z-index 层级
| 层级 | 值 | 场景 |
|---|---|---|
| `z-20` | 20 | 侧边栏、顶部导航 |
| `z-30` | 30 | 页面内 Sticky 工具栏 |
| `z-40` | 40 | Dropdown / Popover |
| `z-50` | 50 | Modal / Drawer 遮罩 |
| `z-60` | 60 | Modal / Drawer 面板 |
| `z-70` | 70 | Toast 容器(始终最顶层) |
### 2.7 Tailwind 配置示例(节选)
```js
// tailwind.config.js
module.exports = {
content: ['./apps/**/templates/**/*.html', './templates/**/*.html'],
theme: {
extend: {
colors: {
primary: {
50: '#F0FDFA', 100: '#CCFBF1', 200: '#99F6E4',
500: '#14B8A6', 600: '#0F766E', 700: '#115E59', 800: '#134E4A',
},
success: { 50: '#F0FDF4', 600: '#16A34A' },
warning: { 50: '#FFFBEB', 600: '#D97706' },
danger: { 50: '#FEF2F2', 600: '#DC2626' },
info: { 50: '#EFF6FF', 600: '#2563EB' },
},
boxShadow: {
xs: '0 1px 2px rgba(15,23,42,0.04)',
},
fontFamily: {
sans: ['Inter', 'PingFang SC', 'Microsoft YaHei', 'sans-serif'],
mono: ['JetBrains Mono', 'SFMono-Regular', 'Menlo', 'monospace'],
},
},
},
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
}
```
---
## 3. 基础组件规范(Base Components)
### 3.1 按钮(Button)
#### 3.1.1 变体
| 变体 | 用途 | Tailwind 类 |
|---|---|---|
| Primary | 主操作(每个区域唯一) | `bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800` |
| Secondary | 次级操作 | `bg-white border border-neutral-300 text-neutral-700 hover:bg-neutral-50 hover:border-neutral-400` |
| Danger | 删除、不可逆操作 | `bg-danger-600 text-white hover:bg-danger-600/90` |
| Ghost | 工具栏、表格行操作 | `text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900` |
| Link | 内联跳转 | `text-primary-600 hover:text-primary-700 hover:underline underline-offset-2` |
| Icon | 仅图标的工具按钮 | `text-neutral-500 hover:bg-neutral-100 hover:text-neutral-700 rounded-md p-1.5` |
#### 3.1.2 尺寸
| 尺寸 | 场景 | Tailwind 类 |
|---|---|---|
| sm | 表格操作、Tag 内 | `px-2.5 py-1 text-xs rounded` |
| md(默认) | 表单提交、工具栏 | `px-3 py-1.5 text-sm rounded-md` |
| lg | 页面主操作(新增按钮) | `px-4 py-2 text-sm rounded-md` |
#### 3.1.3 状态
- **Focus**:`focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40`
- **Loading**:禁用 + 内嵌 Spinner + 文案改为进行时("保存中…")
- **Disabled**:`disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-current`(即 hover 无效果)
#### 3.1.4 标准 HTML 片段
```html
```
#### 3.1.5 禁忌
- ❌ 同一视觉区域不得出现两个 Primary 按钮
- ❌ Danger 按钮必须二次确认(Modal),不得直接触发删除
- ❌ 不得用颜色以外的方式(如加粗)表达"危险"
- ❌ Ghost 按钮不允许填充背景色做变体(容易与 Secondary 混淆)
---
### 3.2 输入框(Input)
#### 3.2.1 状态
| 状态 | Tailwind 类 |
|---|---|
| 默认 | `border-neutral-300` |
| Hover | `hover:border-neutral-400` |
| Focus | `focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20` |
| 错误 | `border-danger-600 focus:border-danger-600 focus:ring-danger-600/20` |
| 禁用 | `bg-neutral-100 text-neutral-400 cursor-not-allowed` |
| 只读 | `bg-neutral-50 text-neutral-700` |
#### 3.2.2 标准结构
```html
```
#### 3.2.3 变体
- **Textarea**:同上,`rows="3"` 起步,右下角 `resize-y`;需字数统计时右下角显示 `x-text="val.length + '/200'"`
- **带前缀/后缀**:用 `relative` 包裹,前缀/后缀绝对定位 `absolute left-3 / right-3`
- **带单位**:右侧灰色文字(如"万元"),用 `pr-14` 留白
- **数字输入**:`type="number"` + `tabular-nums` + 取消浏览器 spinner(用 `appearance-none`)
- **密码输入**:默认 `type="password"`,右侧眼睛图标按钮(Alpine.js 切换 type)
---
### 3.3 下拉选择(Select / Dropdown)
#### 3.3.1 三种实现路径
| 场景 | 实现 |
|---|---|
| 单选、选项 ≤ 10、不需搜索 | **原生 `