feat(a11y,ux): implement comprehensive accessibility and UX optimizations

Phase 1: Accessibility Optimizations
- Add proper label associations and ARIA attributes to form inputs
- Implement aria-required, aria-invalid, aria-describedby for better form accessibility
- Add role='alert' for error messages
- Enhance keyboard navigation with aria-expanded, aria-controls
- Add aria-label for mobile menu button
- Implement aria-current for active navigation items
- Add semantic HTML with aria-labelledby for sections

Phase 2: UX Optimizations
- Create loading skeleton components for better loading states
- Add FormSkeleton, SectionSkeleton, and LoadingSkeleton components
- Prepare for lazy loading implementation

Files modified:
- src/components/ui/input.tsx: Enhanced with ARIA attributes
- src/components/ui/textarea.tsx: Enhanced with ARIA attributes
- src/components/layout/header.tsx: Added navigation ARIA labels
- src/components/sections/hero-section.tsx: Added section labels
- src/components/sections/services-section.tsx: Added section labels
- src/components/ui/loading-skeleton.tsx: New loading state components

Impact:
- WCAG 2.1 AA compliance improvements
- Better screen reader support
- Enhanced keyboard navigation
- Improved user feedback during loading
This commit is contained in:
张翔
2026-02-24 00:40:19 +08:00
parent 44ba75e4d1
commit 016b7cfb91
22 changed files with 2479 additions and 7 deletions
+8 -1
View File
@@ -104,7 +104,7 @@ export function Header() {
/>
</a>
<nav className="hidden md:flex items-center gap-1">
<nav className="hidden md:flex items-center gap-1" role="navigation" aria-label="主导航">
{NAVIGATION.map((item) => (
<a
key={item.id}
@@ -118,6 +118,7 @@ export function Header() {
: 'text-[#3D3D3D] hover:text-[#1C1C1C]'
}
`}
aria-current={activeSection === item.id.replace('#', '') ? 'page' : undefined}
>
{item.label}
{activeSection === item.id.replace('#', '') && (
@@ -148,6 +149,9 @@ export function Header() {
<button
className="md:hidden p-2 -mr-2 text-[#3D3D3D] hover:text-[#1C1C1C] transition-colors"
onClick={() => setIsOpen(!isOpen)}
aria-expanded={isOpen}
aria-controls="mobile-menu"
aria-label={isOpen ? '关闭菜单' : '打开菜单'}
>
{isOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
@@ -173,6 +177,9 @@ export function Header() {
exit={{ y: -20, opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
className="absolute top-16 left-0 right-0 bg-white/95 backdrop-blur-xl border-b border-[#E2E8F0] shadow-lg"
id="mobile-menu"
role="navigation"
aria-label="移动端导航"
>
<nav className="container-wide py-4">
{NAVIGATION.map((item, index) => (