feat(a11y): apply ARIA roles, landmarks, focus management, touch targets and contrast fixes across components
This commit is contained in:
@@ -418,7 +418,7 @@ export function AdapterConfigPage() {
|
||||
</CardHeader>
|
||||
<CollapsibleContent>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 md:gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 md:gap-4" role="radiogroup" aria-label="部署模式选择">
|
||||
{/* 预设模式 */}
|
||||
<div
|
||||
className={`border-2 rounded-lg p-3 md:p-4 cursor-pointer transition-all ${
|
||||
@@ -426,7 +426,11 @@ export function AdapterConfigPage() {
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-muted hover:border-primary/50 active:border-primary/70'
|
||||
}`}
|
||||
role="radio"
|
||||
aria-checked={mode === 'preset'}
|
||||
tabIndex={0}
|
||||
onClick={() => handleModeChange('preset')}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleModeChange('preset') } }}
|
||||
>
|
||||
<div className="flex items-start gap-2 md:gap-3">
|
||||
<Package className="h-4 w-4 md:h-5 md:w-5 mt-0.5 flex-shrink-0" />
|
||||
@@ -446,7 +450,11 @@ export function AdapterConfigPage() {
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-muted hover:border-primary/50 active:border-primary/70'
|
||||
}`}
|
||||
role="radio"
|
||||
aria-checked={mode === 'upload'}
|
||||
tabIndex={0}
|
||||
onClick={() => handleModeChange('upload')}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleModeChange('upload') } }}
|
||||
>
|
||||
<div className="flex items-start gap-2 md:gap-3">
|
||||
<Upload className="h-4 w-4 md:h-5 md:w-5 mt-0.5 flex-shrink-0" />
|
||||
@@ -466,7 +474,11 @@ export function AdapterConfigPage() {
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-muted hover:border-primary/50 active:border-primary/70'
|
||||
}`}
|
||||
role="radio"
|
||||
aria-checked={mode === 'path'}
|
||||
tabIndex={0}
|
||||
onClick={() => handleModeChange('path')}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleModeChange('path') } }}
|
||||
>
|
||||
<div className="flex items-start gap-2 md:gap-3">
|
||||
<FolderOpen className="h-4 w-4 md:h-5 md:w-5 mt-0.5 flex-shrink-0" />
|
||||
@@ -496,10 +508,14 @@ export function AdapterConfigPage() {
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-muted hover:border-primary/50'
|
||||
}`}
|
||||
role="radio"
|
||||
aria-checked={isSelected}
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
setSelectedPreset(key as PresetKey)
|
||||
handleLoadFromPreset(key as PresetKey)
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setSelectedPreset(key as PresetKey); handleLoadFromPreset(key as PresetKey) } }}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<Icon className="h-5 w-5 mt-0.5 flex-shrink-0" />
|
||||
|
||||
@@ -54,7 +54,7 @@ export const ModelTable = React.memo(function ModelTable({
|
||||
return (
|
||||
<div className="hidden md:block rounded-lg border bg-card overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<Table aria-label="模型列表">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-12">
|
||||
|
||||
@@ -209,10 +209,12 @@ export function ProviderForm({
|
||||
}
|
||||
}}
|
||||
placeholder="例如: DeepSeek, SiliconFlow"
|
||||
aria-invalid={formErrors.name ? true : undefined}
|
||||
aria-describedby={formErrors.name ? 'name-error' : undefined}
|
||||
className={formErrors.name ? 'border-destructive focus-visible:ring-destructive' : ''}
|
||||
/>
|
||||
{formErrors.name && (
|
||||
<p className="text-xs text-destructive">{formErrors.name}</p>
|
||||
<p id="name-error" role="alert" className="text-xs text-destructive">{formErrors.name}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -249,10 +251,12 @@ export function ProviderForm({
|
||||
}}
|
||||
placeholder="https://api.example.com/v1"
|
||||
disabled={isUsingTemplate}
|
||||
aria-invalid={formErrors.base_url ? true : undefined}
|
||||
aria-describedby={formErrors.base_url ? 'base-url-error' : undefined}
|
||||
className={`${isUsingTemplate ? 'bg-muted cursor-not-allowed' : ''} ${formErrors.base_url ? 'border-destructive focus-visible:ring-destructive' : ''}`}
|
||||
/>
|
||||
{formErrors.base_url && (
|
||||
<p className="text-xs text-destructive">{formErrors.base_url}</p>
|
||||
<p id="base-url-error" role="alert" className="text-xs text-destructive">{formErrors.base_url}</p>
|
||||
)}
|
||||
{isUsingTemplate && !formErrors.base_url && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
@@ -295,6 +299,8 @@ export function ProviderForm({
|
||||
}
|
||||
}}
|
||||
placeholder="sk-..."
|
||||
aria-invalid={formErrors.api_key ? true : undefined}
|
||||
aria-describedby={formErrors.api_key ? 'api-key-error' : undefined}
|
||||
className={`flex-1 ${formErrors.api_key ? 'border-destructive focus-visible:ring-destructive' : ''}`}
|
||||
/>
|
||||
<Button
|
||||
@@ -321,7 +327,7 @@ export function ProviderForm({
|
||||
</Button>
|
||||
</div>
|
||||
{formErrors.api_key && (
|
||||
<p className="text-xs text-destructive">{formErrors.api_key}</p>
|
||||
<p id="api-key-error" role="alert" className="text-xs text-destructive">{formErrors.api_key}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ export function ProviderList({
|
||||
{/* 桌面端表格视图 */}
|
||||
<div className="hidden md:block rounded-lg border bg-card overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<Table aria-label="AI 模型提供商列表">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-12">
|
||||
|
||||
@@ -400,7 +400,7 @@ export default function PackDetailPage() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<Table aria-label="API 提供商配置列表">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>名称</TableHead>
|
||||
@@ -435,7 +435,7 @@ export default function PackDetailPage() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<Table aria-label="模型配置列表">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>模型名称</TableHead>
|
||||
|
||||
Reference in New Issue
Block a user