Skip to content

Commit 5f70439

Browse files
authored
Merge pull request #1588 from yamadashy/a11y/tabs-and-file-upload
fix(website): Improve accessibility of tabs and file upload widgets
2 parents d480700 + ed7c2b8 commit 5f70439

4 files changed

Lines changed: 88 additions & 9 deletions

File tree

website/client/components/Home/TryIt.vue

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,51 @@
22
<div class="container">
33
<form class="try-it-container" @submit.prevent="handleSubmit($event)">
44
<div class="input-row">
5-
<div class="tab-container">
5+
<div class="tab-container" role="tablist" aria-label="Repository input source">
66
<button
7+
id="tab-url"
78
type="button"
9+
role="tab"
10+
aria-label="Remote repository URL"
11+
aria-controls="tabpanel-input"
12+
:aria-selected="mode === 'url'"
813
:class="{ active: mode === 'url' }"
914
@click="setMode('url')"
1015
>
1116
<Link2 size="20" class="icon" />
1217
</button>
1318
<button
19+
id="tab-folder"
1420
type="button"
21+
role="tab"
22+
aria-label="Upload local folder"
23+
aria-controls="tabpanel-input"
24+
:aria-selected="mode === 'folder'"
1525
:class="{ active: mode === 'folder' }"
1626
@click="setMode('folder')"
1727
>
1828
<FolderOpen size="20" class="icon" />
1929
</button>
2030
<button
31+
id="tab-file"
2132
type="button"
33+
role="tab"
34+
aria-label="Upload ZIP archive"
35+
aria-controls="tabpanel-input"
36+
:aria-selected="mode === 'file'"
2237
:class="{ active: mode === 'file' }"
2338
@click="setMode('file')"
2439
>
2540
<FolderArchive size="20" class="icon" />
2641
</button>
2742
</div>
2843

29-
<div class="input-field">
44+
<div
45+
id="tabpanel-input"
46+
class="input-field"
47+
role="tabpanel"
48+
:aria-labelledby="`tab-${mode}`"
49+
>
3050
<TryItFileUpload
3151
v-if="mode === 'file'"
3252
@upload="handleFileUpload"
@@ -345,6 +365,12 @@ onMounted(() => {
345365
color: white;
346366
}
347367
368+
.tab-container button:focus-visible {
369+
outline: 2px solid var(--vp-c-brand-1);
370+
outline-offset: -2px;
371+
z-index: 1;
372+
}
373+
348374
.tab-container button.active::before {
349375
display: none;
350376
}

website/client/components/Home/TryItFileUpload.vue

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,15 @@ function clearFile() {
6666
<div
6767
class="upload-container"
6868
:class="{ 'drag-active': dragActive, 'has-error': hasError }"
69+
role="button"
70+
tabindex="0"
71+
aria-label="Upload ZIP file"
6972
@dragover.prevent="handleDragOver"
7073
@dragleave="handleDragLeave"
7174
@drop.prevent="onDrop"
7275
@click="triggerFileInput"
76+
@keydown.enter.self.prevent="triggerFileInput"
77+
@keydown.space.self.prevent="triggerFileInput"
7378
>
7479
<input
7580
ref="fileInput"
@@ -88,7 +93,12 @@ function clearFile() {
8893
</p>
8994
<p v-else-if="selectedFile" class="selected-file">
9095
Selected: {{ selectedFile }}
91-
<button class="clear-button" @click.stop="clearFile">×</button>
96+
<button
97+
type="button"
98+
class="clear-button"
99+
aria-label="Clear selected file"
100+
@click.stop="clearFile"
101+
>×</button>
92102
</p>
93103
<template v-else>
94104
<p>Drop your ZIP file here or click to browse (max 10MB)</p>
@@ -129,6 +139,11 @@ function clearFile() {
129139
background-color: var(--vp-c-bg-soft);
130140
}
131141
142+
.upload-container:focus-visible {
143+
outline: 2px solid var(--vp-c-brand-1);
144+
outline-offset: 2px;
145+
}
146+
132147
.drag-active {
133148
border-color: var(--vp-c-brand-1);
134149
background-color: var(--vp-c-bg-soft);

website/client/components/Home/TryItFolderUpload.vue

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,15 @@ function clearFolder() {
7676
<div
7777
class="upload-container"
7878
:class="{ 'drag-active': dragActive, 'has-error': hasError }"
79+
role="button"
80+
tabindex="0"
81+
aria-label="Upload folder"
7982
@dragover.prevent="handleDragOver"
8083
@dragleave="handleDragLeave"
8184
@drop.prevent="onDrop"
8285
@click="triggerFileInput"
86+
@keydown.enter.self.prevent="triggerFileInput"
87+
@keydown.space.self.prevent="triggerFileInput"
8388
>
8489
<input
8590
ref="fileInput"
@@ -98,7 +103,12 @@ function clearFolder() {
98103
</p>
99104
<p v-else-if="selectedFolder" class="selected-file">
100105
Selected: {{ selectedFolder }}
101-
<button class="clear-button" @click.stop="clearFolder">×</button>
106+
<button
107+
type="button"
108+
class="clear-button"
109+
aria-label="Clear selected folder"
110+
@click.stop="clearFolder"
111+
>×</button>
102112
</p>
103113
<template v-else>
104114
<p>Drop your folder here or click to browse (max 10MB)</p>
@@ -142,6 +152,11 @@ function clearFolder() {
142152
background-color: var(--vp-c-bg-soft);
143153
}
144154
155+
.upload-container:focus-visible {
156+
outline: 2px solid var(--vp-c-brand-1);
157+
outline-offset: 2px;
158+
}
159+
145160
.drag-active {
146161
border-color: var(--vp-c-brand-1);
147162
background-color: var(--vp-c-bg-soft);

website/client/components/Home/TryItResult.vue

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,25 @@ const handleRepack = (selectedFiles: FileInfo[]) => {
7575
/>
7676
<div v-else-if="result" class="result-content">
7777
<!-- Tab Navigation -->
78-
<div v-if="hasFileSelection" class="tab-navigation">
79-
<button
78+
<div v-if="hasFileSelection" class="tab-navigation" role="tablist" aria-label="Pack result view">
79+
<button
80+
id="tab-result"
8081
type="button"
82+
role="tab"
83+
aria-controls="tabpanel-result"
84+
:aria-selected="activeTab === 'result'"
8185
class="tab-button"
8286
:class="{ active: activeTab === 'result' }"
8387
@click="handleTabClick('result')"
8488
>
8589
Result
8690
</button>
87-
<button
91+
<button
92+
id="tab-files"
8893
type="button"
94+
role="tab"
95+
aria-controls="tabpanel-files"
96+
:aria-selected="activeTab === 'files'"
8997
class="tab-button"
9098
:class="{ active: activeTab === 'files' }"
9199
@click="handleTabClick('files')"
@@ -95,10 +103,20 @@ const handleRepack = (selectedFiles: FileInfo[]) => {
95103
</div>
96104

97105
<!-- Tab Content -->
98-
<div v-show="activeTab === 'result' || !hasFileSelection">
106+
<div
107+
id="tabpanel-result"
108+
role="tabpanel"
109+
aria-labelledby="tab-result"
110+
v-show="activeTab === 'result' || !hasFileSelection"
111+
>
99112
<TryItResultContent :result="result" :pack-options="packOptions" />
100113
</div>
101-
<div v-show="activeTab === 'files' && hasFileSelection">
114+
<div
115+
id="tabpanel-files"
116+
role="tabpanel"
117+
aria-labelledby="tab-files"
118+
v-show="activeTab === 'files' && hasFileSelection"
119+
>
102120
<TryItFileSelection
103121
v-if="hasFileSelection"
104122
:all-files="result.metadata!.allFiles!"
@@ -147,6 +165,11 @@ const handleRepack = (selectedFiles: FileInfo[]) => {
147165
color: var(--vp-c-text-1);
148166
}
149167
168+
.tab-button:focus-visible {
169+
outline: 2px solid var(--vp-c-brand-1);
170+
outline-offset: -2px;
171+
}
172+
150173
.tab-button.active {
151174
color: var(--vp-c-brand-1);
152175
border-bottom-color: var(--vp-c-brand-1);

0 commit comments

Comments
 (0)