Add docs/ui-refactor-proposal.md to outline the UI theme and style refactoring plan, including design system tokens, typography, responsive layout strategies, semantic navigation menu refactoring, and accessibility testing guidelines as requested. Co-authored-by: cloud-neutral <4133689+cloud-neutral@users.noreply.github.com>
11 KiB
11 KiB
UI 主题与风格重构方案
作为资深 Web UI 设计师和前端工程师,针对我们 SaaS 控制台当前的 Next.js + Tailwind CSS 架构,结合 WCAG 可访问性标准和跨端响应式需求,特制定此 UI 重构方案。方案旨在提升整体视觉一致性、深色模式的可访问性,并优化桌面与移动端的用户体验(特别是 iOS/Android 浏览器的触控和渲染差异)。
1. 审查现有界面
在审查当前的界面代码(如 tailwind.config.js, src/app/globals.css, src/components/theme/ 及 Navbar.tsx)后,我发现了以下改进点:
- 色彩硬编码与对比度:在部分文件(如
designTokens.ts和Navbar.tsx的内联类名)中仍然存在类似#3467e9,bg-[#f6f7f9]的硬编码颜色,这破坏了主题切换的完整性。同时,深色模式下的次级文本(如text-muted#cbd5f5)在深色背景(#0f172a)上的对比度可能无法满足 WCAG AA 级 4.5:1 的标准。 - 语义化不足:
Navbar.tsx中的菜单项过度使用了<div>和普通的<a>标签,缺少<nav>,<ul>,<li>结构,并且缺乏管理下拉/折叠状态的aria-expanded属性。 - 触控目标尺寸:移动端的某些交互元素(如链接和图标按钮)没有保证至少 44px × 44px 的物理点击区域,这在 iOS/Android 设备上容易造成误触。
- 深色模式层级:深色模式主要依赖背景色区分层级(如
surface-muted),缺乏细微的边框(Border)或发光阴影(Glow/Shadow)来凸显浮动面板(如 Dropdown 菜单)。
2. 定义设计系统 Token
我们需要收敛硬编码颜色,改用语义化的 CSS 变量(Token),并对深/浅色模式设定严格的对比度要求。
颜色 Token 规划
- 背景色:
- 浅色:纯白
#ffffff(Surface) 或极浅灰#f8fafc(Background)。 - 深色:避免纯黑,使用
#0f172a(Background) 和微亮的暗灰#1e293b(Surface),减轻视觉疲劳。
- 浅色:纯白
- 文本色:
- 主要文本 (
--color-text):浅色模式#0f172a,深色模式#f8fafc。 - 次要文本 (
--color-text-muted):确保在深浅背景下对比度均大于 4.5:1。浅色推荐#475569,深色推荐#94a3b8。
- 主要文本 (
- 主色与交互色:
--color-primary:统一使用高对比度的主题蓝(如#2563eb),确保按钮上的白色文字(--color-primary-foreground)对比度达标。
Tailwind / CSS 变量伪代码
/* src/app/globals.css 补充与覆盖 */
:root {
/* 基础结构色 */
--bg-color: #f8fafc;
--surface-color: #ffffff;
/* 文本色 */
--text-color: #0f172a;
--secondary-color: #475569;
/* 主色调 */
--primary: #2563eb;
--primary-foreground: #ffffff;
/* 边框与分隔线 */
--border-color: #e2e8f0;
}
:root[data-theme="dark"],
.dark {
--bg-color: #0f172a;
--surface-color: #1e293b;
--text-color: #f8fafc;
--secondary-color: #94a3b8; /* 保证对比度 > 4.5:1 */
--primary: #3b82f6; /* 在深色背景下稍微提亮主色 */
--primary-foreground: #ffffff;
--border-color: #334155;
/* 深色模式下的特殊视觉补偿 */
--shadow-elevation: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -2px rgba(0, 0, 0, 0.5);
}
3. 设计排版体系
排版需兼顾多语言(中英文混合)和多端阅读体验。
- 字体选择:保留现有的
Geist,同时后备配置Inter或Noto Sans SC以优化中文显示:font-family: var(--font-geist-sans), 'Inter', 'Noto Sans SC', sans-serif; - 字号范围 (Fluid Typography / 断点响应):
- 移动端:基础字号 16px(防止 iOS Safari 输入框自动缩放),正文
16px - 18px,大标题约24px - 28px。 - 桌面端:正文
16px - 20px,大标题可达32px - 48px。
- 移动端:基础字号 16px(防止 iOS Safari 输入框自动缩放),正文
- 行高与间距:正文行高
1.5至1.6(150%-160%),段落间距使用margin-bottom: 1.5em。标题行高缩紧至1.2。
4. 构建全局样式与主题切换
当前系统已通过 Zustand (store.ts) 和 ThemeProvider.tsx 实现了基于 localStorage 和 data-theme 的切换。
- 优化防闪烁 (FOUC):由于在 Next.js 中客户端 Hydration 会有延迟,需要确保在
<head>中注入一个同步脚本读取localStorage和prefers-color-scheme,在 React 渲染前就应用dark类或data-theme。 - 深色模式下的层级分离:避免仅仅改变背景色。对于弹窗、Dropdown 等元素,在深色模式下增加一个极细的亮色边框:
.dark .surface-elevated { border: 1px solid rgba(255,255,255,0.1); box-shadow: var(--shadow-elevation); }
5. 响应式布局策略
- 断点定义:
- 移动端:
< 768px(Tailwind 默认md以下)。 - 平板/桌面端:
>= 768px(md及以上)。
- 移动端:
- 移动端 (iOS/Android) 策略:
- 隐藏复杂的侧边栏(Sidebar),顶部导航精简为 Logo 和汉堡菜单。
- 所有的按钮 (
button,a) 必须拥有至少min-h-[44px] min-w-[44px]的触控区域(可以使用 padding 撑开)。
- 桌面端策略:
- 最大化利用横向空间,采用 Sidebar + Main Content 或全宽 Header 的多列布局。
6. 重构导航菜单 (示例)
当前 Navbar.tsx 使用了平铺的链接。我们需要使用语义化标签,并通过 aria-expanded 增强可访问性,并分离移动端的汉堡菜单和桌面端菜单。
菜单组件伪代码
'use client'
import { useState } from 'react';
export default function SemanticNavbar() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
return (
<header className="sticky top-0 z-50 w-full bg-surface border-b border-border">
<nav className="max-w-7xl mx-auto px-4 min-h-[64px] flex items-center justify-between" aria-label="Main Navigation">
{/* Logo */}
<div className="flex-shrink-0">
<a href="/" className="font-bold text-text-color text-lg flex items-center min-h-[44px]">Logo</a>
</div>
{/* Desktop Menu */}
<ul className="hidden md:flex items-center gap-6">
<li>
<a href="/dashboard" className="text-secondary-color hover:text-text-color transition-colors py-2">
控制台
</a>
</li>
<li className="relative group">
{/* 父菜单如果是功能性展开按钮,使用 button */}
<button
aria-expanded={isDropdownOpen}
aria-controls="services-dropdown"
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
className="flex items-center gap-1 text-secondary-color hover:text-text-color py-2"
>
服务 <ChevronDownIcon />
</button>
{/* Dropdown 弹窗 */}
<ul
id="services-dropdown"
className={`absolute top-full left-0 mt-2 w-48 bg-surface-elevated border border-border rounded-md shadow-md ${isDropdownOpen ? 'block' : 'hidden'}`}
>
<li>
{/* 保证键盘 Tab 键能聚焦到这里 */}
<a href="/service/1" className="block px-4 py-3 text-text-color hover:bg-surface-hover">服务一</a>
</li>
</ul>
</li>
</ul>
{/* Mobile Hamburger Button */}
<div className="md:hidden">
<button
aria-controls="mobile-menu"
aria-expanded={isMobileMenuOpen}
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="p-2 min-w-[44px] min-h-[44px] text-text-color"
aria-label="Toggle navigation"
>
<HamburgerIcon />
</button>
</div>
</nav>
{/* Mobile Menu Panel */}
<div
id="mobile-menu"
className={`md:hidden overflow-hidden transition-[max-height] duration-300 ease-in-out ${isMobileMenuOpen ? 'max-h-screen' : 'max-h-0'}`}
>
<ul className="px-4 pb-4 space-y-2 bg-surface">
<li>
<a href="/dashboard" className="block py-3 text-text-color">控制台</a>
</li>
{/* 移动端子菜单可以直接平铺或使用手风琴折叠 */}
<li>
<span className="block py-3 text-secondary-color font-semibold">服务</span>
<ul className="pl-4 space-y-2 border-l-2 border-border ml-2">
<li><a href="/service/1" className="block py-3 text-text-color">服务一</a></li>
</ul>
</li>
</ul>
</div>
</header>
);
}
7. 针对移动端和桌面端的优化差异
- iOS/Android 浏览器差异处理:
- iOS Safari 底部安全区:在主内容区底部增加
padding-bottom: env(safe-area-inset-bottom);,避免按钮被 Home Indicator 遮挡。 - 字体抗锯齿 (Anti-aliasing):在
globals.css中的body标签已设置-webkit-font-smoothing: antialiased;,在深色模式下这能让细小的亮色文字边缘更加平滑,没有晕影。 - 长文本限制:在博客或文档页面,容器设置
max-w-prose(相当于max-width: 65ch),确保每行不超过 60 个字符,提升阅读体验。
- iOS Safari 底部安全区:在主内容区底部增加
- 桌面端强化:
- 可利用 Hover 状态提供丰富的反馈(如背景变色、细微的
transform: translateY(-1px))。 - 在宽屏上显示侧边栏和次要信息列(多列网格布局)。
- 可利用 Hover 状态提供丰富的反馈(如背景变色、细微的
8. 可访问性测试与迭代流程
为确保重构后的界面符合标准,开发团队应在提交代码前进行以下检查:
- 对比度扫描:使用 Chrome DevTools 的 Lighthouse 或 WebAIM 对比度检查器,确保所有的正文/背景对比度 >= 4.5:1,大号文本 >= 3.0:1。
- 重排与缩放测试:使用浏览器的缩放功能放大页面到
200%和400%。确保在400%下页面自动转为单列移动端布局,且文字不出现截断或相互重叠(开启 CSS 文本重排text-wrap: balance或避免固定高度)。 - 键盘导航测试:不使用鼠标,仅用
Tab和Enter遍历界面。确保所有交互元素有明显的:focus-visible外框线(Tailwindfocus-visible:ring-2 focus-visible:ring-primary)。二级菜单不能在 Tab 聚焦到父元素时自动弹出(除非是用按钮触发),以避免用户被迫穿过无数个子菜单才能到达下一个主栏目。 - 屏幕阅读器体验:开启 VoiceOver (Mac/iOS) 或 TalkBack (Android),验证
aria-expanded,aria-controls等状态是否能被正确播报。