-
Notifications
You must be signed in to change notification settings - Fork 34
/
index.html
287 lines (251 loc) · 12 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<title>
Styled File Uploads
</title>
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script>
// remove no-js and add 'js' to the HTML
document.documentElement.className = document.documentElement.className.replace('no-js', 'js');
</script>
<link rel="stylesheet" href="../assets/css/--demo-only--.css">
<link rel="stylesheet" href="../assets/css/--shared--.css">
<link rel="stylesheet" href="../assets/css/file-upload.css">
</head>
<body>
<div class="demo-wrap">
<header class="demo-wrap__header">
<p class="demo-wrap__header__title">
Accessible Styled Form Controls
</p>
<nav>
<a href="https://github.com/scottaohara/a11y_styled_form_controls">See source on GitHub</a>,
<a href="/a11y_styled_form_controls">Index of styled form controls</a>
</nav>
</header>
<main aria-label="content">
<article class="demo">
<header>
<h1>Styled File Uploads</h1>
<p>Published: <time>July 26, 2018</time></p>
<p>Last updated: <time>August 27, 2018</time></p>
<p>
Cross-browser styling for the native HTML file input type.
</p>
</header>
<h2>
Pattern Demo
</h2>
<div class="file-up" data-file-input>
<label for="file_upload">
<span class="file-up__label__text">
Upload an Image
</span>
<input type="file" id="file_upload" accept=".jpg">
</label>
</div>
<div class="file-up" data-file-input="compact">
<label for="file_upload_d">
<span class="file-up__label__text">
Disabled Upload
</span>
<input type="file" id="file_upload_d" disabled>
</label>
</div>
<div class="file-up" data-file-input="compact">
<label for="file_upload_m">
<span class="file-up__label__text">
Multi-Upload
</span>
<input type="file" id="file_upload_m" multiple>
</label>
</div>
<details style="margin-top: 1em;">
<summary>Visual design note</summary>
<div>
<p>
If a file name is too long, the element that contains the file name or number of files will wrap to another line. This is different than a native file upload, but is done to provide better responsive behavior.
</p>
</div>
</details>
<section class="demo-details">
<h3>
Pattern Details
</h3>
<details open>
<summary>Pattern Markup</summary>
<pre><code class="language-html"><div class="file-up" data-file-input>
<label for="file_upload">
<span class="file-up__label__text">
Upload
</span>
<input type="file" id="file_upload">
</label>
<-- generated by JavaScript -->
<span class="file-up__output" aria-hidden="true">
...
</span>
</div></code></pre>
</details>
<p>
The baseline markup for this component is a <cod>div</cod> wrapper. The <code>div</code> serves as an outer wrapper to the form control, and uses flexbox to position the <code>label</code>, which is a parent to the visually hidden input, and selected file output <code>span</code>.
</p>
<p>
Note: if using the <code>accept</code> attribute, or <code>multiple</code> attribute, it'd be good to provide context to the user that only certain file types, or that multiple files can be uploaded, respectively.
</p>
<h4>The JavaScript</h4>
<p>
The containing <code>div[data-file-input]</code> provides the necessary selector hook for the JavaScript. An <code>input[type="file"]</code> can't be styled directly, due to what makes up this component living in the Shadow DOM. Access to the Shadow DOM is still not widely supported at this time. Instead the JavaScript creates a <code>span</code>, injects some classes, and listens for a change event to the file input.
</p>
<p>
If using the <code>multiple</code> attribute, instead of the uploaded file name, the number of attached files will instead be visually printed on screen.
</p>
<p>
If the form control should be set to <code>disabled</code>, the JavaScript will add a class to the wrapping element, to add a visual style to promote the disabled state. (The actual <code>input</code> is hidden, so it can't be relied on to visually convey state, but it will announce it's state to screen reader users that happen upon it, when navigating with the virtual cursor, for example.)
</p>
<p>
If the <code>data-file-input</code> attribute has a value of "compact", then the class <code>.file-up--compact</code> will be added to the wrapper. This class will collapse the "no file selected" area until a user has selected a file or files to upload.
</p>
<h4>Affects on Screen Reader Announcements?</h4>
<dl>
<dt>JAWS 2018 + Internet Explorer 11</dt>
<dd>
<p>
Native file uploads consist of two focusable elements in Internet Explorer 11, so for sighted users interacting with this pattern, there will be two focus stops.
</p>
<p>
Focusing the input (with <kbd>tab</kbd> key) announces:<br>
<q>[Accessible Name] file upload edit. Enter a name of a file to upload.</q>
</p>
<p>
Focusing the browse button (with <kbd>tab</kbd> key) announces:<br>
<q>[Accessible Name] file upload edit. Browse...</q>.
</p>
<p>
If using the virtual cursor to navigate, JAWS will announce "Upload an image" when focus is on the label, and File upload edit" and "Browse... button" when virtual cursor focus is on each element, respectively.
</p>
<p>
When an image, or images have been selected for upload, JAWS will append the file name or names to the announcements.
</p>
</dd>
<dt>JAWS 2018 + Firefox 63 (nightly)</dt>
<dd>
<p>
Native file uploads have a single focus stop in tested, non-Microsoft browsers. Styled or unstyled, JAWS will announce the following:
</p>
<p>
<q>[Accessible Name], browse. No file selected (dot) frame. Browse button</q>.
</p>
<p>
When a file has been uploaded, JAWS will announce: <q>[Accessible Name], browse. [file name] frame. Browse button</q>.
</p>
<p>
If multiple files have been uploaded JAWS will announce: <q>[Accessible Name], browse. [# files selected] (dot) frame. Browse button</q>.
</p>
</dd>
<dt>JAWS 2018 or NVDA 2018.2.1 + Chrome (latest)</dt>
<dd>
<p>
While Chrome has a visually similar native file upload style to Firefox, the announcement of the element differs. Styled or unstyled, JAWS and NVDA will announce the following:
</p>
<p>
<q>[Accessible Name] button</q>.
</p>
<p>
After uploading a file or files, JAWS and NVDA + Chrome will not announce the name or number of any selected files. To provide an experience that is similar to other browser announcements, this script will add an <code>aria-describedby</code> to the file upload <code>input</code> and unique <code>ID</code> to the <code>span</code> the file name or number of files is printed in. Resulting in:
</p>
<p>
<q>[Accessible Name] button. [text contents of the output area]</q>.
</p>
</dd>
<dt>NVDA 2018.2.1 + Firefox 63 (nightly)</dt>
<dd>
<p>
File upload form controls have some serious issues with NVDA. The accessible name is not announced at all, nor is the default "no file selected" message. Firefox + NVDA announce the following when a styled or unstyled file upload is focued:
</p>
<p>
<q>Clickable browse button</q>
</p>
<p>
After uploading a file or files, tabbing to the control sill only produces the previous announcement. If using the virtual cursor, NVDA will announce the label and uploaded file name or number of files, if those text nodes are highlighted.
</p>
</dd>
<dt>VoiceOver + Safari 11.1.1 on macOS High Sierra</dt>
<dd>
<p>
The ordering in which VoiceOver announces a file upload control differs from how JAWS or NVDA announce the control. Instead of starting with the accessible name, VoiceOver announces a styled or unstyled file upload control in the following order:
</p>
<p>
<q>[No file announcement (or) file name/number] [Accessible Name], file upload button</q>.
</p>
<p>
For the styled upload controls, if navigating with <kbd>VO</kbd> and <kbd>left</kbd> or <kbd>right</kbd> arrow keys, it will seem as if there are two focus stops on the control. However this is merely VoiceOver focusing the label and then the file upload control itself, and is only odd due to the fact that the styled control is visually conveyed as a single element.
</p>
</dd>
<dt>VoiceOver + Safari on iOS 11.3 & 11.4</dt>
<dd>
<p>
It should be noted that, though they're named the same, VoiceOver for iOS should be treated as a different screen reader than it's desktop counterpart. Both styled and unstyled file upload controls have the following default state announcement:
</p>
<p>
<q>[Accessible name], no file selected, button</q>
</p>
<p>
Testing with photos and videos, regardless of whether it's a single or multi file upload control, after a user selects a file or files, VoiceOver will replace the "no file selected" announcement with "# Photo/video" or "# Photo and # Video" if both file types are chosen. The file name is not announced.
</p>
</dd>
<dt>TalkBack (Android Accessibility Suite 6.2) + Android Chrome</dt>
<dd>
<p>
Like Chrome on desktop, regardless of if files are selected or not, native file upload controls are only announced as the following:
</p>
<p>
<q>[Accessible Name] button. Double-tap to activate.</q>
</p>
<p>
Due to sniffing for Chrome, the <code>aria-describedby</code> gets added to the file upload control, so the styled control adds the text from the output <code>span</code> as an accessible description. This results in either "no file selected", or a file name being announced prior to the "Double-tap to activate" announcement.
</p>
<p>
Interestingly, multi file uploading doesn't appear to work with Chrome on Android. Or at least when testing, I could determine how to do it as selecting the first file automatically selected and closed the choose file UI.
</p>
</dd>
</dl>
<h4>Usage note:</h4>
<p>
If using NVDA with mouse settings updated to "report role when mouse enters object" this component will announce itself as a "text frame" since the input is visually hidden on top of the visual 'button'.
</p>
<p>
Due to browsers exposing native file uploads in different ways (both visually and with what's announced), a single design for a file upload may be confusing for some users if the visual style doesn't match expected announcements. <b>A more robust, custom, pattern may need to be considered</b>.
</p>
</section>
</article>
</main>
</div> <!-- /.demo-wrap -->
<script src="../assets/js/file-upload.js"></script>
<script src="../assets/js/global--focus-within.js"></script>
<script>
// some other file.js
var selector = '[data-file-input]';
var elList = document.querySelectorAll(selector);
var i;
for ( i = 0; i < elList.length; i++ ) {
var a11yFUP = new A11yFileUpload();
a11yFUP.init( elList[i] );
};
</script>
<script>
var highlighterCSS = function () {
var head = document.head;
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '../assets/vendor/prism.css';
head.appendChild(link);
}
highlighterCSS();
</script>
<script src="../assets/vendor/prism.js"></script>
</body>
</html>