Files
lab/ts/web/nextjs/polaris/docs/design/2026-08-26-Polaris-首页实现规格.md
T
labmem 86f6eb39b2 docs(polaris): 同步设计与部署文档现状
对齐深色玻璃样式、响应式布局、数据规模与配置生效范围。
明确仅由 polaris-v* 标签触发发布,并保留历史规格说明。
2026-10-14 16:38:07 +08:00

465 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Polaris 首页实现规格
> 日期:2026-08-26
>
> 状态:用户已批准,可进入实施
>
> 实现分支:`feat/polaris-homepage`
>
> 历史状态(2026-10-14 补记):本规格是首页阶段的实施依据,正文(含范围、视觉与验收要求)保留 2026-08-26 时点的决策原貌,不作逐行改写;后续开发已推进多项内容,部分条目不再代表现状,见下方「当前实现索引」。
## 当前实现索引(2026-10-14 补记,非原规格内容)
- **视觉**:深色玻璃主题为当前默认外观(无主题切换),设计 tokens 见 `src/app/globals.css`;第 8.1 节的「CSS 低饱和青蓝渐变背景」已由可替换的背景素材层 + 深色遮罩取代,样式由现有 CSS 管理,素材类型/路径/画面不作设计规范。
- **移动端**:第 2.2 节「本次不实现」中的「移动端与平板布局」已完成——≤767px 时侧边栏变为顶部横栏,其余断点适配见两份设计文档的第 0 章;「深色模式」以深色玻璃默认主题的形式落地(无切换开关)。
- **数据**:第 7.1 节的「7 个分类 / 20 条链接 / 8 条 pinned」为当时规模;当前为 8 个分类、51 条链接、6 条 pinned。
- **分类页**:第 2.2 节「正式分类页」与第 3 节「分类导航临时禁用」已被后续工作取代——分类页已在 `/categories/[categoryId]` 实现并启用导航。
- **设置**:`settings.theme`、`settings.background` 仍仅有数据模型与校验,不驱动样式;`settings.search` 与 `settings.sidebar` 已接入。
- 其余章节(目录结构、交互、测试与验收)保留原貌,作为首页阶段的历史验收依据。
## 1. 背景与目标
Polaris 是一个轻量、安静、高效的个人浏览器起始页,不是后台管理系统或信息密集型 Dashboard。
本次工作只建立推荐的项目结构并实现完整可交互首页。用户验收首页视觉和交互后,才会明确决定是否继续实现分类页及第一阶段其余目标。
## 2. 范围
### 2.1 本次实现
- 初始化 Next.js App Router 项目;
- 建立 Feature-first 混合目录;
- 整理现有设计文档和资产位置;
- 实现低饱和青蓝 CSS 渐变背景与浅色玻璃视觉;
- 实现可折叠且持久化的桌面侧栏;
- 实现实时时钟、日期和分时问候语;
- 实现 Google、Bing、GitHub、Local 四种严格分离的搜索模式;
- 实现 Local 搜索结果、键盘导航和空状态;
- 实现 8 个常用链接;
- 实现统一图标 fallback;
- 建立本地 JSON、service、类型与数据校验边界;
- 完成单元测试、组件交互测试、静态构建和真实页面视觉检查。
### 2.2 本次不实现
- 正式分类页;
- Go API 与 PostgreSQL;
- 管理页或隐藏管理入口;
- 深色模式;
- 移动端与平板布局;
- 自定义背景上传;
- 自定义搜索引擎、URL 直达、AI 搜索;
- 加载骨架和后端错误重试。
## 3. 已确认决策
| 决策项 | 结论 |
| ---------- | ----------------------------------------------------------------- |
| 首页范围 | 完整可交互首页 |
| 目录方案 | Feature-first + 共享基础层 |
| 分支粒度 | 项目骨架与首页位于同一个 `feat/polaris-homepage` 分支 |
| 文档整理 | 三份文档迁入 `docs/design/`,设计图迁入 `docs/assets/` |
| Logo | 现有占位 Logo 迁入 `public/brand/`,不追加品牌设计 |
| 运行时背景 | CSS 低饱和青蓝渐变;设计图只作构图参考 |
| 外部搜索 | Google、Bing、GitHub 在当前标签页跳转 |
| 本地链接 | Local 结果与常用链接在新标签页打开 |
| 排序模型 | `sort` 管理分类内顺序;可选 `pinnedSort` 独立管理首页常用链接顺序 |
| 分类导航 | 首页验收阶段展示但禁用,避免进入未实现路由;分类页完成后启用 |
| 注释 | 关键规则、非显然决策、边界条件和阶段性限制使用简洁中文注释 |
| 提交时机 | 分支全部目标完成并验证后再提交 |
## 4. 技术基线
- Next.js App Router;
- React;
- TypeScript strict;
- Tailwind CSS v4,使用 CSS-first 配置;
- shadcn/ui,只引入实际使用的基础原语;
- lucide-react;
- simple-icons;
- pnpm;
- Vitest;
- Testing Library;
- ESLint;
- Prettier。
`next.config.ts` 使用 `output: "export"`,构建产物必须能作为纯静态文件部署。
不引入 Redux、Zustand、React Query、Zod、Storybook、Repository interface 或依赖注入容器。当前规模下这些抽象没有必要。
## 5. 推荐目录
```text
polaris/
├─ docs/
│ ├─ design/
│ │ ├─ Polaris 项目说明文档.md
│ │ ├─ Polaris 首页设计文档.md
│ │ ├─ Polaris 分类页设计文档.md
│ │ └─ 2026-08-26-Polaris-首页实现规格.md
│ └─ assets/
│ └─ Polaris页面设计图.png
├─ public/
│ ├─ brand/
│ │ └─ logo.svg
│ └─ icons/
├─ src/
│ ├─ app/
│ │ ├─ globals.css
│ │ ├─ layout.tsx
│ │ └─ page.tsx
│ ├─ components/
│ │ ├─ ui/
│ │ └─ shared/
│ ├─ features/
│ │ ├─ home/
│ │ │ ├─ components/
│ │ │ ├─ hooks/
│ │ │ └─ lib/
│ │ └─ navigation/
│ │ └─ components/
│ ├─ data/
│ │ ├─ categories.json
│ │ ├─ links.json
│ │ └─ settings.json
│ ├─ services/
│ │ ├─ categories.ts
│ │ ├─ links.ts
│ │ └─ settings.ts
│ ├─ types/
│ │ ├─ navigation.ts
│ │ ├─ search.ts
│ │ └─ settings.ts
│ ├─ lib/
│ │ └─ utils.ts
│ └─ test/
│ └─ setup.ts
├─ components.json
├─ eslint.config.mjs
├─ next.config.ts
├─ package.json
├─ pnpm-lock.yaml
├─ postcss.config.mjs
├─ prettier.config.mjs
├─ tsconfig.json
└─ vitest.config.ts
```
### 5.1 目录职责
- `app/`:路由、metadata、根布局、全局样式和页面组合;
- `features/`:按功能聚合组件、hooks 和专属纯函数;
- `components/ui/`:shadcn/ui 基础原语;
- `components/shared/`:跨功能复用组件,例如 `GlassPanel`、`SiteIcon`;
- `data/`:第一版本地 JSON;
- `services/`:页面与数据源之间的唯一访问边界;
- `types/`:跨功能共享的数据契约;
- `lib/`:真正通用且不依赖业务功能的工具;
- `test/`:全局测试环境与共享测试辅助代码。
测试文件与被测试实现就近放置,`src/test/` 不作为所有测试的集中目录。
## 6. 渲染与组件边界
`src/app/page.tsx` 保持为 Server Component,在构建时调用 service 层读取数据,再将可序列化数据传给页面组件。
```text
app/page.tsx
└─ AppShell
├─ Sidebar
│ ├─ SidebarHeader
│ └─ SidebarNav
└─ HomeContent
├─ ClockGreeting
├─ SearchBox
│ ├─ SearchProviderMenu
│ └─ LocalSearchResults
└─ QuickLinks
└─ QuickLinkCard
└─ SiteIcon
```
Client Component 边界仅用于浏览器状态:
- `AppShell`:侧栏折叠与持久化;
- `ClockGreeting`:当前时间;
- `SearchBox`:Provider、query、结果选中项和快捷键;
- `SiteIcon`:图标加载失败后的 fallback。
把 Server Component 作为 `AppShell` 的 `children` 传入,不因为外层存在 Client Component 就将全部展示代码改成客户端代码。
不为每个小标签或图标创建组件。只有存在独立状态、复用价值或清晰职责时才拆分。
## 7. 数据模型与数据流
### 7.1 数据规模
- 7 个分类;
- 开发工具 15 条;
- ChatGPT、Gmail、Notion、YouTube、Bilibili 5 条跨分类链接;
- 共 20 条链接;
- 8 条 `pinned` 常用链接;
- `sort` 只表示分类内顺序,可选 `pinnedSort` 只表示首页常用链接顺序。
首页常用链接固定按以下 `pinnedSort` 顺序展示:GitHub、ChatGPT、Gmail、Notion、Figma、YouTube、Vercel、Bilibili。该字段解决开发工具分类中 Vercel 位于 Figma 之前、但首页中 Figma 位于 Vercel 之前的双重排序需求。
设计资源、学习资料、生活服务保持无链接;AI 工具、效率工具和娱乐媒体因常用链接而有少量数据。
### 7.2 访问路径
1. `page.tsx` 调用 `getCategories()`、`getLinks()` 和 `getSettings()`;
2. service 层复制、排序和筛选数据;
3. 页面不直接 import JSON;
4. 首页只把所需的分类、全部搜索候选、8 条常用链接和设置传给客户端;
5. 未来接 Go API 时保持 service 的对外职责不变,只替换其内部数据来源。
### 7.3 数据校验
Vitest 必须校验:
- 必填字段类型;
- 分类和链接 ID 唯一;
- URL 合法;
- `categoryId` 必须指向现有分类;
- `sort` 与可选 `pinnedSort` 合法;
- Provider 配置有效;
- `pinned` 数量为 8,且每条 pinned 链接都有唯一 `pinnedSort`。
## 8. 视觉系统
### 8.1 背景
使用多层 CSS 渐变和柔和光斑模拟晨雾、天空和水面氛围,不把 `Polaris页面设计图.png` 作为运行时背景。
增加浅色遮罩和适度模糊,保证未来替换背景时仍具有可读性。
### 8.2 布局
- 侧栏外边距:`20px`;
- 展开宽度:`240px`;
- 折叠宽度:`72px`;
- 主内容视觉重心位于中上方;
- 使用 `minmax()` 与 `clamp()` 适配 1024px 以上桌面窗口;
- 搜索框最大宽度约 `760px`、高度约 `68px`;
- 常用链接最大四列、共两行,缩窄窗口时允许减少列数;
- 第一版不实现移动端抽屉侧栏。
### 8.3 Design tokens
在 `globals.css` 中定义 Polaris 语义变量,并通过 Tailwind v4 `@theme inline` 映射:
- 主文本、次级文本、弱文本;
- 主色、hover 色、柔和主色;
- 侧栏、搜索框、卡片表面;
- 玻璃边框;
- 软阴影、中等阴影;
- 圆角与模糊强度。
第一版只实现浅色值,但变量命名允许以后通过主题选择器覆盖。
### 8.4 动效与无障碍
- hover、focus 和折叠过渡约为 180ms;
- 不使用夸张缩放或弹跳;
- 尊重 `prefers-reduced-motion`;
- 所有图标按钮有可访问名称;
- 键盘焦点清晰;
- 搜索输入与结果使用合理的 combobox/listbox 语义;
- 禁用分类项使用 `aria-disabled` 并提供“分类页将在下一阶段提供”的轻量提示。
## 9. 首页交互
### 9.1 时钟与问候
问候规则:
```text
05:00 - 11:59 早上好
12:00 - 13:59 中午好
14:00 - 17:59 下午好
18:00 - 23:59 晚上好
00:00 - 04:59 夜深了
```
客户端按分钟更新;跨午夜后日期、星期和问候语同步变化。
### 9.2 Provider 状态
- 初始值来自 `settings.search.defaultProvider`;
- 用户切换后保存到 `localStorage`;
- 无效值或存储不可用时回退到设置默认值;
- 外部 Provider 与 Local 模式严格分离。
### 9.3 外部搜索
Google、Bing、GitHub:
- 输入先 `trim()`;
- 空内容不提交;
- 使用 `URLSearchParams` 编码;
- 在当前标签页跳转;
- 不显示 Local 结果下拉。
### 9.4 Local 搜索
匹配字段:
- 链接标题;
- 关键词;
- 描述;
- 分类名称;
- 分类 ID。
优先级:
```text
标题完全匹配
标题前缀或包含
关键词匹配
描述匹配
分类匹配
```
同分时按链接 `sort` 和标题稳定排序,最多返回 8 条。
交互:
- 默认选中第一项;
- `↑` / `↓` 移动;
- `Enter` 打开选中项;
- 鼠标点击直接打开;
- 结果在新标签页打开;
- `Esc` 清空输入并关闭面板;
- 无结果显示“没有匹配的网站”。
### 9.5 快捷键
- 首页加载后自动聚焦搜索框;
- Windows/Linux:`Ctrl+K`;
- macOS:`⌘K`;
- 快捷键阻止浏览器默认行为后聚焦;
- 监听器只存在于首页生命周期内。
### 9.6 侧栏
- 折叠状态保存到 `localStorage`;
- 存储不可用或值损坏时回退到设置默认值;
- hydration 后同步持久化值;
- 同步完成前禁用布局过渡,减少闪动;
- 首页项为当前选中态;
- 分类项本次显示但禁用,不进入 404。
### 9.7 常用链接
- 对 `pinned` 链接按 `pinnedSort` 排序取前 8 条;
- `pinned: true` 时必须提供唯一的 `pinnedSort`;
- 使用真实 `<a>`;
- `target="_blank"`;
- `rel="noopener noreferrer"`;
- 卡片仅显示图标与名称。
## 10. 图标策略
运行时优先级:
```text
自定义本地图标
明确映射的 Simple Icons
favicon
标题首字符文字占位
```
当前 20 条数据尽量命中显式 Simple Icons 映射,避免正常页面依赖远程 favicon。远程图标失败时不得显示破图。
侧栏分类图标使用 lucide-react,不参与站点图标 fallback。
## 11. 状态与故障处理
- 静态 JSON 无 Loading UI;
- 数据结构错误由测试和构建阶段阻止;
- `localStorage` 读写使用容错封装;
- favicon 失败显示文字占位;
- 空搜索不跳转;
- Local 无结果显示轻量空状态;
- 非法 Provider 回退到配置默认值;
- Server 初始值与 Client 首次渲染保持一致,避免 hydration error。
## 12. 注释规范
必须为以下非显然逻辑添加简洁中文注释,解释“为什么”:
- Local 搜索权重及稳定排序;
- Server/Client 边界选择;
- 图标 fallback 顺序;
- hydration 后同步 `localStorage`;
- 分类导航临时禁用;
- 数据校验中的跨分类引用规则。
普通 JSX、显然的变量赋值和框架惯用代码不添加复述式注释。
## 13. 测试设计
### 13.1 纯函数
- 问候语全部时间边界;
- 日期格式;
- 搜索 URL 与特殊字符编码;
- Local 搜索字段、权重、排序和数量上限;
- 图标引用解析;
- 数据结构及交叉引用。
### 13.2 组件交互
- 自动聚焦;
- Provider 严格分离;
- Provider 持久化与非法值回退;
- Local 键盘导航、Enter、Esc、点击和无结果;
- `Ctrl+K` / `⌘K`;
- 侧栏折叠、恢复和异常存储回退;
- 新标签页链接安全属性;
- 分类项禁用状态。
## 14. 验证与视觉验收
完成后必须运行:
```text
pnpm lint
pnpm typecheck
pnpm test
pnpm build
```
随后实际启动页面并检查:
- `1440 × 900`;
- `1920 × 1080`;
- `1024 × 768`。
检查项:
- 无横向溢出;
- 背景渐变层次和文本对比度;
- 玻璃透明度、边框与阴影一致;
- 侧栏展开/折叠比例;
- 搜索聚焦与 Provider 交互;
- Local 结果定位和键盘交互;
- 卡片排列、hover 和 focus;
- `prefers-reduced-motion`;
- 控制台无 hydration error 或运行时错误。
设计图用于构图和氛围参照,不要求像素级复制图片背景。
## 15. 完成定义
只有以下条件全部满足,`feat/polaris-homepage` 才完成:
1. 推荐目录和工程配置建立;
2. 文档及资产迁移完成,内部链接有效;
3. 完整可交互首页实现;
4. 自动化检查全部通过;
5. 真实页面完成三个尺寸的视觉检查;
6. 实际结果、视觉观察和已知偏差如实报告;
7. 未提前实现正式分类页;
8. 完成后再执行提交操作。