Skip to content

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

#维度分数关键发现
1Accessibility16 个 WCAG A 级违规:accent 色对比度 2.77:1、全站 h1 缺失、对比表非语义化、搜索下拉无键盘导航
2Performance2无页面级缓存策略(所有请求直查数据库);Navbar scroll handler 每像素触发 re-render
3Theming1165 处硬编码 Tailwind 颜色;无 Dark Mode;Admin 与前台色彩体系完全脱节
4Responsive2对比页 min-w-[640px] 在手机端强制横向滚动;CompareButton 触控目标仅 24px
5Anti-Patterns15+ 个 AI 模板特征(详见 design-critique)
合计7/20Poor — 需要全面整改

Executive Summary

  • Health Score: 7/20 (Poor)
  • 问题统计: P0 × 6, P1 × 8, P2 × 9, P3 × 5 = 28 项
  • Top 5 关键问题:
    1. Accent 色 (#0ea5e9) 对比度仅 2.77:1,全站链接和徽章不合规
    2. 165 处硬编码颜色 + 零 Dark Mode 支持,整改后才能实现主题切换
    3. 除首页外所有页面缺少 <h1> 标签(屏幕阅读器无法定位页面主题)
    4. 公共页面无缓存策略,每次请求直查数据库
    5. 搜索自动补全下拉框完全无键盘导航

详细发现

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 增加 as prop 支持渲染 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
  • 问题: 无 revalidatecache 配置,每次页面请求直查数据库
  • 影响: 首页、医院列表、手术列表、城市列表等高流量页面无缓存,数据库负载随流量线性增长;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 图标)
  • 修复: 添加 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 但缺少 sizes prop,浏览器无法优化 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
  • 位置: 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:37to-[#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.tsx metadata
  • 问题: 有 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 固定渲染 h2Phase 3 /harden
无缓存全部公共页开发阶段未配置 ISRPhase 3 /optimize
单一动画模式全站AnimatedSection 只有 fade-upPhase 2 /animate

正面发现

  1. 客户端/服务端边界划分合理 — 11 个 'use client' 组件全部必要,页面层全部是 Server Components,数据获取无 N+1 查询
  2. Lucide 图标 tree-shaking 正确 — 每个文件仅导入所需图标,无全量导入
  3. Framer Motion 使用 GPU 加速属性 — 所有动画基于 opacity 和 transform,无 layout thrashing
  4. Pagination 无障碍良好<nav aria-label="Pagination">、disabled 状态使用 <span>、当前页有指示
  5. Navbar 移动菜单无障碍良好aria-labelaria-expandedaria-controls 齐全
  6. 图片懒加载和 sizes — 大部分图片正确使用 loading="lazy"sizes prop

整改风险评估

以下是 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 bundleFramer Motion 已引入,新动画不额外增加依赖;CSS 动画零成本

Phase 3 风险

风险概率影响缓解措施
Admin 批量颜色替换引入视觉回归替换后逐页截图对比;先建立 CSS 变量映射表再批量替换
骨架屏/Toast 增加 client 组件控制在必要范围内,Skeleton 可以是纯 CSS
信息重组(tabs/accordion)改变 SEOTab 内容使用 CSS 隐藏而非条件渲染,确保爬虫可见
SectionHeader as prop 改动影响全站该组件在 10+ 页面使用,修改时需全量回归

总体建议

  • Phase 1 完成后必须做全站截图回归测试(浅色 + 深色),确认无遗漏
  • 硬编码颜色替换建议使用全局搜索 + 替换脚本,不要手动逐个改
  • Dark Mode 的 <script> 阻塞方案是非常规做法,需要确认与 Next.js App Router 的兼容性(推荐参考 next-themes 库的实现)
  • ISR 缓存开启后,Admin 修改数据后需要有 revalidation 机制(revalidatePathrevalidateTag),否则用户看到的是旧数据

推荐行动

按优先级排序(与 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/bolderHero 重设计注意首屏性能,避免大图未优化
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 查看分数变化。

WellChina 内部文档 · 基于 VitePress