主题
WellChina 前端技术审查报告
审查日期: 2026-03-27 审查范围: 全部前端代码 — 无障碍、性能、主题、响应式、反模式 关联文档:
docs/design-critique.md(UI 设计审查)、docs/design-overhaul-plan.md(整改计划)
Anti-Patterns 判定
未通过。 与 design-critique 一致:Inter 字体、蓝色渐变 + 白底、blur blob 装饰、均匀卡片网格、单一 fade-up 动画。从代码层面看,模板感的根源在于——所有页面复用相同的 StaggerContainer > StaggerItem > Card 结构,零布局变化。
Audit Health Score
| # | 维度 | 分数 | 关键发现 |
|---|---|---|---|
| 1 | Accessibility | 1 | 6 个 WCAG A 级违规:accent 色对比度 2.77:1、全站 h1 缺失、对比表非语义化、搜索下拉无键盘导航 |
| 2 | Performance | 2 | 无页面级缓存策略(所有请求直查数据库);Navbar scroll handler 每像素触发 re-render |
| 3 | Theming | 1 | 165 处硬编码 Tailwind 颜色;无 Dark Mode;Admin 与前台色彩体系完全脱节 |
| 4 | Responsive | 2 | 对比页 min-w-[640px] 在手机端强制横向滚动;CompareButton 触控目标仅 24px |
| 5 | Anti-Patterns | 1 | 5+ 个 AI 模板特征(详见 design-critique) |
| 合计 | 7/20 | Poor — 需要全面整改 |
Executive Summary
- Health Score: 7/20 (Poor)
- 问题统计: P0 × 6, P1 × 8, P2 × 9, P3 × 5 = 28 项
- Top 5 关键问题:
- Accent 色 (#0ea5e9) 对比度仅 2.77:1,全站链接和徽章不合规
- 165 处硬编码颜色 + 零 Dark Mode 支持,整改后才能实现主题切换
- 除首页外所有页面缺少
<h1>标签(屏幕阅读器无法定位页面主题) - 公共页面无缓存策略,每次请求直查数据库
- 搜索自动补全下拉框完全无键盘导航
详细发现
P0 — 阻断级
P0-1. Accent 色对比度不达标
- 位置:
src/app/globals.css:11→ 全站所有使用--color-accent的元素 - 类别: Accessibility
- 问题:
#0ea5e9在白底上对比度仅 2.77:1,远低于 WCAG AA 要求的 4.5:1 - 影响: 所有 accent 色链接、价格文字、Badge info 变体、HeroSearch 按钮对低视力用户不可读
- 其他不合规色:
--color-neutral-400(#94a3b8) 2.56:1;--color-success(#16a34a) 3.30:1 - 修复:
/colorize阶段统一处理,新 accent 色必须达到 4.5:1 - 建议命令:
/colorize
P0-2. 全站 h1 缺失(除首页外)
- 位置: hospitals/page.tsx, procedures/page.tsx, cities/page.tsx, pricing/page.tsx, compare/page.tsx, search/page.tsx, contact/page.tsx, about/page.tsx, guides/page.tsx
- 类别: Accessibility
- 问题:
SectionHeader组件渲染<h2>,所有列表页/详情页以 h2 开头,跳过 h1 - 标准: WCAG 1.3.1 Info and Relationships (Level A)
- 影响: 屏幕阅读器用户无法通过标题导航找到页面主题
- 修复:
SectionHeader增加asprop 支持渲染 h1,各页面首标题使用 h1 - 建议命令:
/harden
P0-3. 搜索下拉框无键盘导航
- 位置:
src/components/ui/HeroSearch.tsx:104-192 - 类别: Accessibility
- 问题: 下拉结果仅支持鼠标点击,无上下箭头选择、无
role="listbox"/role="option"、无aria-expanded、无aria-live通知新结果 - 标准: WCAG 2.1.1 Keyboard (Level A)
- 影响: 键盘用户和屏幕阅读器用户完全无法使用搜索自动补全
- 修复: 实现完整的 combobox 模式(WAI-ARIA Combobox pattern)
- 建议命令:
/harden
P0-4. 对比表非语义 HTML
- 位置:
src/app/compare/page.tsx:127-326 - 类别: Accessibility
- 问题: 对比数据使用
<div>网格模拟表格,无<table>/<th>/<td>语义 - 标准: WCAG 1.3.1 Info and Relationships (Level A)
- 影响: 屏幕阅读器无法按行/列导航对比数据
- 修复: 重写为语义
<table>或添加role="table"/role="row"/role="cell"ARIA - 建议命令:
/harden
P0-5. 公共页面无缓存策略
- 位置: 所有
src/app/*/page.tsx公共页面 - 类别: Performance
- 问题: 无
revalidate或cache配置,每次页面请求直查数据库 - 影响: 首页、医院列表、手术列表、城市列表等高流量页面无缓存,数据库负载随流量线性增长;Vercel 部署时每次冷启动都要建立数据库连接
- 修复: 公共页面添加 ISR:
export const revalidate = 3600(1 小时) - 建议命令:
/optimize
P0-6. 无 prefers-reduced-motion 支持
- 位置:
src/app/globals.css(缺失)+src/components/ui/AnimatedSection.tsx - 类别: Accessibility
- 问题: 全站无 reduced-motion 媒体查询,所有 CSS transition 和 Framer Motion 动画在用户请求减少动效时仍全速播放
- 标准: WCAG 2.3.3 Animation from Interactions (Level AAA),实践中 AA 也推荐
- 影响: 前庭障碍用户可能因滚动触发的连续动画感到不适
- 修复: globals.css 添加
@media (prefers-reduced-motion: reduce)规则;AnimatedSection 读取该设置并跳过动画 - 建议命令:
/animate
P1 — 重要
P1-1. 165 处硬编码 Tailwind 颜色
- 位置: 全站,Admin 页面 125 处,用户页面 40 处
- 类别: Theming
- 分布:
text-gray-*/bg-gray-*: 94 处(Admin 为主)text-green-*/bg-green-*: 18 处(Badge, 状态指示)text-yellow-*/bg-yellow-*: 15 处(Fudan 排名, 警告)text-red-*: 8 处(错误, 必填标记)- 硬编码 hex:
#e0f2fe,#bae6fd(Badge accent 变体)
- 影响: Dark Mode 实现时这些颜色不会响应主题变更,导致深色背景上出现不可读的文字
- 修复: 分两步——
/colorize定义完整变量体系,/normalize批量替换 Admin 硬编码 - 建议命令:
/colorize+/normalize
P1-2. Badge 组件色彩不一致
- 位置:
src/components/ui/Badge.tsx:5-11 - 类别: Theming
- 问题: success/warning/error/info 用 Tailwind 原生色,neutral 用 CSS 变量,accent 用硬编码 hex——一个组件三套色彩策略
- 影响: Dark Mode 时只有 neutral 变体正确,其余全部不可用
- 修复: 全部迁移到 CSS 变量,新增
--color-success-bg、--color-warning-bg等表面色变量 - 建议命令:
/colorize
P1-3. CompareButton/CompareBar 缺少 ARIA
- 位置:
src/components/compare/CompareButton.tsx:33-47,CompareBar.tsx:28-33 - 类别: Accessibility
- 问题:
- CompareButton 无
aria-pressed(切换按钮应有) - CompareButton 无描述性
aria-label(disabled 时无解释) - CompareBar 关闭按钮无
aria-label(仅 X 图标)
- CompareButton 无
- 修复: 添加
aria-pressed={selected}、aria-label描述 - 建议命令:
/harden
P1-4. 表单无障碍不足
- 位置:
src/app/contact/page.tsx - 类别: Accessibility
- 问题:
- 必填标记
*仅视觉展示,无aria-label="required"语义 - 无
aria-describedby连接错误提示 - 无客户端实时验证反馈
- 必填标记
- 修复: 补全 ARIA 属性
- 建议命令:
/harden
P1-5. Navbar scroll handler 性能
- 位置:
src/components/layout/Navbar.tsx:32-36 - 类别: Performance
- 问题:
setScrolled(window.scrollY > 10)在每次 scroll 事件时调用 setState,即使布尔值未变化也触发 re-render - 影响: 快速滚动时 Navbar 组件每像素触发一次 re-render
- 修复: 添加变化判断:
const next = window.scrollY > 10; if (next !== scrolled) setScrolled(next)或使用 ref 存储上次值 - 建议命令:
/optimize
P1-6. 搜索输入框无 label
- 位置:
src/components/ui/HeroSearch.tsx:86-94 - 类别: Accessibility
- 问题: 搜索输入仅有 placeholder,无
<label>或aria-label - 标准: WCAG 1.3.1
- 修复: 添加
aria-label="Search hospitals, procedures, cities" - 建议命令:
/harden
P1-7. 对比页手机端强制横向滚动
- 位置:
src/app/compare/page.tsx:127 - 类别: Responsive
- 问题:
min-w-[640px]在 < 640px 屏幕上产生强制横向滚动 - 影响: 手机用户需要左右滚动才能看完对比数据,体验很差
- 修复: 移动端切换为垂直卡片堆叠布局
- 建议命令:
/distill
P1-8. CompareButton 触控目标过小
- 位置:
src/components/compare/CompareButton.tsx:28 - 类别: Responsive
- 问题: sm 尺寸
px-2.5 py-1 text-xs实际高度仅约 24px,远低于 44px 推荐最小触控目标 - 标准: WCAG 2.5.5 Target Size (Level AAA),实践中 Google 推荐 48px
- 修复: 增加 sm 尺寸的 padding 或设置
min-h-[44px] - 建议命令:
/harden
P2 — 一般
P2-1. 图片优化遗漏
- 位置:
src/app/hospitals/[slug]/page.tsx第二张图片 - 类别: Performance
- 问题: 使用
fill但缺少sizesprop,浏览器无法优化 srcset - 修复: 添加
sizes="(max-width: 896px) 100vw, 896px" - 建议命令:
/optimize
P2-2. Admin 删除无确认
- 位置: Admin 编辑页面(hospitals/procedures/insurance)
- 类别: UX Safety
- 问题: DELETE 操作直接执行,无确认对话框
- 影响: 误点删除按钮将直接丢失数据
- 修复: 添加
confirm()或自定义确认 modal - 建议命令:
/harden
P2-3. 全局焦点指示器缺失
- 位置:
src/app/globals.css - 类别: Accessibility
- 问题: 无全局
:focus-visible默认样式。虽然 Button 和 FilterBar 有组件级 focus ring,但原生链接和其他交互元素无焦点指示 - 修复: globals.css 添加
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; } - 建议命令:
/harden
P2-4. Footer 语义不完整
- 位置:
src/components/layout/Footer.tsx - 类别: Accessibility
- 问题: 链接分组使用
<div>而非<nav>,缺少aria-label区分 - 修复: 链接列使用
<nav aria-label="Find Care">等 - 建议命令:
/harden
P2-5. FilterBar 使用 focus: 而非 focus-visible:
- 位置:
src/components/ui/FilterBar.tsx:54 - 类别: Accessibility
- 问题:
focus:ring-2在鼠标点击时也显示 focus ring,应为focus-visible:ring-2仅键盘触发时显示 - 修复: 替换为
focus-visible: - 建议命令:
/harden
P2-6. Pricing 表格移动端体验
- 位置:
src/app/pricing/page.tsx:122-183 - 类别: Responsive
- 问题: 表格在移动端依赖横向滚动(overflow-x-auto),虽然隐藏了部分列,但核心对比数据仍需滚动查看
- 修复: 移动端切换为卡片式布局(
/distill中已规划) - 建议命令:
/distill
P2-7. Navbar 无 aria-label 区分导航区域
- 位置:
src/components/layout/Navbar.tsx:51 - 类别: Accessibility
- 问题:
<nav>无aria-label,页面可能有多个 nav 元素(Header + Footer),屏幕阅读器无法区分 - 修复: 添加
aria-label="Main navigation" - 建议命令:
/harden
P2-8. 硬编码字符串未走 i18n
- 位置:
src/app/page.tsx(HERO_TRUST, PRICE_HIGHLIGHTS, TRUST_ITEMS, QUICK_TOOLS 等) - 类别: Maintainability
- 问题: 大量面向用户的文案硬编码在组件中,未使用
next-intl翻译函数 - 影响: 未来多语言化时需要逐个文件迁移
- 修复: 迁移到
messages/en.json - 建议命令:
/normalize
P2-9. Hero 中 hardcoded hex 色
- 位置:
src/app/page.tsx:37—to-[#1e6fa8] - 类别: Theming
- 问题: 渐变终点使用硬编码 hex,绕过了 CSS 变量体系
- 修复:
/colorize中 Hero 重设计时一并处理 - 建议命令:
/colorize
P3 — 打磨
P3-1. Prisma 连接池配置
- 位置:
src/lib/prisma.ts - 问题: 生产环境无显式连接池配置。Next.js 开发模式的全局单例已实现,但 Vercel serverless 部署时每个 function 实例独立创建连接
- 修复: 在生产环境使用 Prisma Accelerate 或在 DATABASE_URL 中配置
?pgbouncer=true&connection_limit=1
P3-2. Favicon 使用 Next.js 默认
- 位置:
public/ - 问题: 仅有 SVG 占位文件,无品牌 favicon
- 修复:
/polish阶段添加品牌 favicon
P3-3. 无自定义 404 页面
- 位置: 缺少
src/app/not-found.tsx - 修复:
/onboard阶段创建
P3-4. OG Image 缺失
- 位置:
src/app/layout.tsxmetadata - 问题: 有 OpenGraph 配置但无图片
- 修复:
/polish阶段添加
P3-5. z-index 潜在冲突
- 位置: Navbar z-50 + CompareBar z-50 + HeroSearch dropdown z-50
- 问题: 三个 z-50 在特定场景可能重叠
- 修复: 建立 z-index 层级系统
系统性问题
| 模式 | 出现次数 | 根因 | 修复阶段 |
|---|---|---|---|
| 硬编码 Tailwind 颜色 | 165 处 | Admin 开发时未使用 CSS 变量体系 | Phase 1 /colorize + Phase 3 /normalize |
| 缺少 ARIA 属性 | 12+ 处 | 交互组件开发时未考虑无障碍 | Phase 3 /harden |
| h1 缺失 | 9 个页面 | SectionHeader 固定渲染 h2 | Phase 3 /harden |
| 无缓存 | 全部公共页 | 开发阶段未配置 ISR | Phase 3 /optimize |
| 单一动画模式 | 全站 | AnimatedSection 只有 fade-up | Phase 2 /animate |
正面发现
- 客户端/服务端边界划分合理 — 11 个 'use client' 组件全部必要,页面层全部是 Server Components,数据获取无 N+1 查询
- Lucide 图标 tree-shaking 正确 — 每个文件仅导入所需图标,无全量导入
- Framer Motion 使用 GPU 加速属性 — 所有动画基于 opacity 和 transform,无 layout thrashing
- Pagination 无障碍良好 —
<nav aria-label="Pagination">、disabled 状态使用<span>、当前页有指示 - Navbar 移动菜单无障碍良好 —
aria-label、aria-expanded、aria-controls齐全 - 图片懒加载和 sizes — 大部分图片正确使用
loading="lazy"和sizesprop
整改风险评估
以下是 design-overhaul-plan.md 中各阶段可能引入的技术风险:
Phase 1 风险
| 风险 | 概率 | 影响 | 缓解措施 |
|---|---|---|---|
| 字体替换导致布局偏移 (CLS) | 高 | 中 | 使用 font-display: swap + size-adjust 属性;用 Next.js next/font 内置优化 |
| 新字体加载性能退化 | 中 | 中 | 限制字重数量(标题 400+700,正文 400+600);使用 next/font 自动子集化 |
| 色彩变量重命名导致遗漏 | 高 | 高 | 分两步:先全局搜索替换变量名,再逐文件验证;Admin 的 165 处硬编码需逐一替换 |
| Dark Mode 闪烁 (FOUC) | 高 | 中 | 必须在 <head> 中使用阻塞 <script> 同步读取 localStorage 并设置 theme 属性,不能用 useEffect |
Phase 2 风险
| 风险 | 概率 | 影响 | 缓解措施 |
|---|---|---|---|
| Hero 重设计影响首屏性能 | 中 | 中 | 如使用背景图片需 priority 加载;避免大尺寸未压缩图片 |
| 布局多样化增加代码复杂度 | 中 | 低 | 新布局组件应可复用,避免每页自定义一套 |
| 新动画增加 JS bundle | 低 | 低 | Framer Motion 已引入,新动画不额外增加依赖;CSS 动画零成本 |
Phase 3 风险
| 风险 | 概率 | 影响 | 缓解措施 |
|---|---|---|---|
| Admin 批量颜色替换引入视觉回归 | 高 | 中 | 替换后逐页截图对比;先建立 CSS 变量映射表再批量替换 |
| 骨架屏/Toast 增加 client 组件 | 低 | 低 | 控制在必要范围内,Skeleton 可以是纯 CSS |
| 信息重组(tabs/accordion)改变 SEO | 中 | 中 | Tab 内容使用 CSS 隐藏而非条件渲染,确保爬虫可见 |
SectionHeader as prop 改动影响全站 | 中 | 高 | 该组件在 10+ 页面使用,修改时需全量回归 |
总体建议
- Phase 1 完成后必须做全站截图回归测试(浅色 + 深色),确认无遗漏
- 硬编码颜色替换建议使用全局搜索 + 替换脚本,不要手动逐个改
- Dark Mode 的
<script>阻塞方案是非常规做法,需要确认与 Next.js App Router 的兼容性(推荐参考 next-themes 库的实现) - ISR 缓存开启后,Admin 修改数据后需要有 revalidation 机制(
revalidatePath或revalidateTag),否则用户看到的是旧数据
推荐行动
按优先级排序(与 design-overhaul-plan 对齐,补充本报告发现的技术项):
| 优先级 | 命令 | 任务 | 本报告新增发现 |
|---|---|---|---|
| P0 | /colorize | 色彩体系重建 + Dark Mode | 修复 accent 对比度 2.77:1;新增 success-bg/warning-bg 等表面色变量 |
| P0 | /harden | 无障碍加固 | h1 缺失(9 页)、搜索 combobox、对比表语义化、ARIA 补全、focus 指示器、reduced-motion |
| P0 | /optimize | 性能优化 | ISR 缓存策略、Navbar scroll 防抖、图片 sizes 补全 |
| P1 | /typeset | 字体系统重建 | 注意 CLS 风险,限制字重数量 |
| P1 | /bolder | Hero 重设计 | 注意首屏性能,避免大图未优化 |
| P1 | /animate | 动画系统 | 必须加入 prefers-reduced-motion 支持 |
| P1 | /arrange | 布局多样化 | — |
| P1 | /normalize | 组件一致性 | 165 处硬编码颜色批量替换;Admin 迁移到 CSS 变量 |
| P2 | /delight | 微反馈 | — |
| P2 | /distill | 信息密度 | 对比页移动端布局、Pricing 移动端卡片化 |
| P2 | /onboard | 空状态 | 自定义 404 页面 |
| P3 | /polish | 最终打磨 | Favicon、OG Image、z-index 层级、i18n 字符串迁移 |
可以逐个执行,也可以按 Phase 并行。完成后重新运行
/audit查看分数变化。