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

15 KiB
Raw Permalink Blame History

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. 推荐目录

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 层读取数据,再将可序列化数据传给页面组件。

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 时钟与问候

问候规则:

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。

优先级:

标题完全匹配
标题前缀或包含
关键词匹配
描述匹配
分类匹配

同分时按链接 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. 图标策略

运行时优先级:

自定义本地图标
明确映射的 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. 验证与视觉验收

完成后必须运行:

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. 完成后再执行提交操作。