-
Notifications
You must be signed in to change notification settings - Fork 34
/
index.html
322 lines (275 loc) · 13.4 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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<title>
Styled Radio Button Star Rating Component
</title>
<meta name="viewport"
content="width=device-width, initial-scale=1">
<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/radio--rating.css">
</head>
<body>
<!--
Single SVG Reference
-->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;">
<symbol id="star"><g transform="scale(0.03125 0.03125)"><path d="M896 384l-313.5-40.781-134.5-279.219-134.531 279.219-313.469 40.781 230.469 208.875-59.469 303.063 277-148.812 277.062 148.812-59.562-303.063 230.5-208.875z" /></g></symbol>
<symbol viewBox="0 0 18 18" id="no_rating"><g transform="scale(0.017578125 0.017578125)"><path d="M512 56.889c-251.362 0-455.111 203.749-455.111 455.111s203.749 455.111 455.111 455.111 455.111-203.749 455.111-455.111-203.749-455.111-455.111-455.111zM170.667 512c0-73.639 23.666-141.694 63.472-197.527l475.388 475.388c-55.833 39.806-123.888 63.472-197.527 63.472-188.222 0-341.333-153.111-341.333-341.333zM789.861 709.527l-475.388-475.388c55.833-39.806 123.888-63.472 197.527-63.472 188.222 0 341.333 153.111 341.333 341.333 0 73.639-23.666 141.694-63.472 197.527z" /></g></symbol>
</svg>
<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 Radio Button Star Rating Component</h1>
<p>Published: <time>July 26, 2018</time></p>
<p>Last updated: <time>March 15, 2021</time></p>
<p>
Pattern to restyle a radio button group into a star rating component.
</p>
</header>
<h2>
Pattern Demo
</h2>
<form action="#" method="post">
<fieldset class="r-radios">
<legend>
Rate my rating system:
</legend>
<input checked type="radio" name="r_input" id="r_input_0">
<label for="r_input_0" class="r-radio r-radio--none">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#no_rating"/>
</svg>
<span role="presentation">No rating</span>
</label>
<input type="radio" name="r_input" id="r_input_1">
<label for="r_input_1" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">1 Star</span>
</label>
<input type="radio" name="r_input" id="r_input_2">
<label for="r_input_2" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">2 Stars</span>
</label>
<input type="radio" name="r_input" id="r_input_3">
<label for="r_input_3" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">3 Stars</span>
</label>
<input type="radio" name="r_input" id="r_input_4">
<label for="r_input_4" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">4 Stars</span>
</label>
<input type="radio" name="r_input" id="r_input_5">
<label for="r_input_5" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">5 Stars</span>
</label>
</fieldset>
<fieldset class="r-radios" disabled>
<legend>
Disabled (via fieldset) my rating system:
</legend>
<input checked type="radio" name="r_dinput" id="r_input_d0">
<label for="r_input_d0" class="r-radio r-radio--none">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#no_rating"/>
</svg>
<span role="presentation">No rating</span>
</label>
<input type="radio" name="r_dinput" id="r_input_d1">
<label for="r_input_d1" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">1 Star</span>
</label>
<input type="radio" name="r_dinput" id="r_input_d2">
<label for="r_input_d2" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">2 Stars</span>
</label>
<input type="radio" name="r_dinput" id="r_input_d3">
<label for="r_input_d3" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">3 Stars</span>
</label>
<input type="radio" name="r_dinput" id="r_input_d4">
<label for="r_input_d4" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">4 Stars</span>
</label>
<input type="radio" name="r_dinput" id="r_input_d5">
<label for="r_input_d5" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">5 Stars</span>
</label>
</fieldset>
<fieldset class="r-radios">
<legend>
Rate my rating system (some inputs disabled):
</legend>
<input checked type="radio" name="r_input_dd" id="r_input_dd_0">
<label for="r_input_dd_0" class="r-radio r-radio--none">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#no_rating"/>
</svg>
<span role="presentation">No rating</span>
</label>
<input type="radio" name="r_input_dd" id="r_input_dd_1">
<label for="r_input_dd_1" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">1 Star</span>
</label>
<input disabled type="radio" name="r_input_dd" id="r_input_dd_2">
<label for="r_input_dd_2" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">2 Stars</span>
</label>
<input type="radio" name="r_input_dd" id="r_input_dd_3">
<label for="r_input_dd_3" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">3 Stars</span>
</label>
<input type="radio" name="r_input_dd" id="r_input_dd_4">
<label for="r_input_dd_4" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">4 Stars</span>
</label>
<input disabled type="radio" name="r_input_dd" id="r_input_dd_5">
<label for="r_input_dd_5" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span role="presentation">5 Stars</span>
</label>
</fieldset>
<script src="../assets/js/radio-ratings.js"></script>
</form>
<section class="demo-details">
<h3>
Pattern Details
</h3>
<details open>
<summary>Pattern Markup</summary>
<pre><code class="language-html"><fieldset class="r-radios">
<legend>
Rate my rating system:
</legend>
<input type="radio" name="r_input" id="r_input_0" checked>
<label for="r_input_0" class="r-radio r-radio--none">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#no_rating"/>
</svg>
<span>No rating</span>
</label>
<input type="radio" name="r_input" id="r_input_1">
<label for="r_input_1" class="r-radio">
<svg aria-hidden="true" focusable="false">
<use xlink:href="#star"/>
</svg>
<span>1 Star</span>
</label>
<!-- add more stars here! -->
</fieldset></code></pre>
</details>
<p>
As the rating system is built by a series of radio buttons, these form controls should live within a <code>fieldset</code> with a <code>legend</code> to appropriately caption the radio button group's purpose. If it makes sense for these groupings of radio buttons to be called out in the document outline, then the <code>legend</code> can have an appropriately ranked heading element as its child element, containing the text of the <code>legend</code>.
</p>
<p>
The ratings begin with a "No rating" radio button that has is set to the <code>checked</code> attribute, by default. Radio buttons do not have a native way to revert to a completely unchecked state, without usage of a form reset button or a user performing a hard refresh with their browser. If a user erroneously "rates" an item, there should be a way for them to negate that rating without having to reset a form or refresh the page, hence the "No rating" option.
</p>
<p>
The actual radio button <code>input</code> elements are visually hidden, but maintain the ability to be focused and found by keyboard and screen reader users. The <code>label</code> for each radio button contains an inline SVG of a star, so the SVG can be manipulated by CSS. A text string is also contained within the label and is visible on <code>:hover</code> and <code>:focus</code> of the SVG and <code>input</code>, respectively.
</p>
<p>
The <code>SVG</code> elements have the following attributes:
</p>
<ul>
<li><code>aria-hidden="true"</code> to hide the SVG from screen readers.</li>
<li><code>focusable="false"</code> to ensure that Internet Explorer does not allow keyboard focus to attempt to traverse the SVG.</li>
</ul>
<p>
<strong>Note:</strong> to meet WCAG Success Criterion 1.4.13 Content on Hover or Focus, a small script has been written to listen for the <kbd>Esc</kbd> key. If pressed, a <code>hide-label</code> class is added to the <code>input</code> element. On blur, the class is removed. A design change to ensure the label doesn't float atop other content, or to place it in a static location and update based on the currently selected "star" could be other ways around needing to accommodate this SC.
</p>
<h4>Affects on Screen Reader Announcements?</h4>
<p>
Since the native radio buttons remain in the DOM, and are used as the focusable elements that control the state of their custom visualization, there are no alterations to how a screen reader announces keyboard focused styled radio buttons.
</p>
<p>
Using standard visually hidden CSS to hide the native radio button off screen, or as a single pixel in the document, will mean that certain screen reader users might be unable to locate the radio button. For instance, those searching by touch, or using a mouse while a screen reader announces what is being hovered. To mitigate this, the radio buttons have been positioned on top of their styled <code>label</code>s, yet still styled to be visually hidden. Note that NVDA does not announce the radio button role when hovered by mouse, regardless of it being styled or not, unless default NVDA settings are changed.
</p>
<p>
When a radio button is receives keyboard focus, screen readers like JAWS and NVDA may automatically go into forms mode, or require a user to hit <kbd>Enter</kbd> or <kbd>Space</kbd> to focus into the radio button, depending on the user's settings. Then arrow keys can be used to select the different radio buttons, as expected in forms mode.
</p>
<p>
VoiceOver on macOS may begin to focus the radio button labels when using arrow keys to change the selected radio button. This issue does not exist in VoiceOver + Chrome and is not necessarily unique to this particular styling of radio buttons.
</p>
</section> <!-- /.demo-details -->
<h3>Continue Reading</h3>
<p>
WCAG's Understanding Success Criterion 1.4.11: Non-text Contrast document specifically looks at rating stars with examples of passing and failing contrast examples. <a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html#figure-two-star-ratings">1.4.11: Non-text Contrast - star rating examples</a>.
</p>
<p>
For another take on star rating radio groups, with a bunch of style variants, check out the <a href="https://lunarlogic.github.io/starability/">Starability project by Lunar Logic</a>.
</p>
</article> <!-- /.demo -->
</main>
</div> <!-- /.demo-wrap -->
<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>