├── README.md ├── aria-current └── index.html ├── aria-tables └── index.html ├── autocomplete ├── css │ ├── autocomplete.css │ └── glyphicon.css ├── index.html └── js │ ├── autocomplete.js │ └── jquery.js ├── css └── basic.css ├── disclosure1 ├── css │ └── disclosure.css ├── index.html └── js │ └── disclosure.js ├── disclosure2 ├── css │ └── disclosure.css ├── index.html └── js │ └── disclosure.js ├── disclosure3 ├── css │ └── disclosure.css ├── index.html └── js │ └── disclosure.js ├── index.html ├── link1 ├── css │ └── link.css ├── index.html └── js │ └── link.js ├── live1 ├── index.html └── js │ ├── liveregion.js │ └── liveregion2.js ├── live2 ├── index.html └── js │ ├── liveregion.js │ └── liveregion2.js ├── svg-flowchart └── index.html ├── svg-line-graph ├── index.html └── original.html ├── tabpanels1 ├── css │ └── tabpanels.css ├── index.html └── js │ └── jQuery.js ├── tabpanels2 ├── css │ └── tabpanels.css ├── index.html └── js │ └── jQuery.js ├── toggle1 ├── css │ └── button.css ├── index.html └── js │ └── button.js ├── toggle2 ├── css │ └── button.css ├── index.html └── js │ └── button.js ├── toggle3 ├── css │ └── button.css ├── index.html └── js │ └── button.js ├── toggletip1 ├── css │ └── toggletip.css ├── index.html └── js │ ├── toggletip.js │ └── toggletip.js~ └── toggletip2 ├── css └── toggletip.css ├── index.html └── js └── toggletip.js /README.md: -------------------------------------------------------------------------------- 1 | == Accessible design patterns == 2 | HTML, CSS, JavaScript and ARIA design patterns 3 | http://ljwatson.github.io/design-patterns/ 4 | -------------------------------------------------------------------------------- /aria-current/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | aria-current design patterns 9 | 10 | 11 | 34 | 35 | 36 | 37 |

aria-current design patterns

38 | 39 |

The aria-current attribute indicates the element that represents the current item within a container or set of related elements. It is an enumerated type attribute that takes one of a number of predefined tokens ("page", "step", "location", "date" or "time"), or which may be set to "true".

40 | 41 |

aria-current="date"

42 | 43 |

Screen readers should announce "Current date" to indicate that Saturday 16th is the current date in the month.

44 | 45 |

Code

46 |

 47 | <table>
 48 | <caption>July 2016</caption>
 49 | <tr>
 50 | <th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th><th>Sun</th>
 51 | </tr>
 52 | <tr>
 53 | <td></td><td></td><td></td><td></td><td>1</td><td>2</td><td>3</td>
 54 | </tr>
 55 | <tr>
 56 | <td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>
 57 | </tr>
 58 | <tr>
 59 | <td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td aria-current="date">16</td><td>17</td>
 60 | </tr>
 61 | <tr>
 62 | <td>18</td><td>19</td><td>20</td><td>21</td><td>21</td><td>22</td><td>23</td>
 63 | </tr>
 64 | <tr>
 65 | <td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td>
 66 | </tr>
 67 | <tr>
 68 | <td>31</td><td></td><td></td><td></td><td></td><td></td><td></td>
 69 | </tr>
 70 | </table>
 71 | 
72 | 73 |

Example

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 |
July 2016
MonTueWedThuFriSatSun
123
45678910
11121314151617
18192021212223
24252627282930
31
99 |
100 | 101 |

aria-current="location"

102 | 103 |

Screen readers should announce "Current location" to indicate the last link is the current location in the breadcrumb navigation.

104 | 105 |

Code

106 |

107 | <nav aria-label="Breadcrumb">
108 | <ul>
109 | <li><a href="/">Home</a></li>
110 | <li><a href="/"> Contact us</a></li>
111 | <li><a href="/" aria-current="location"> Phone numbers</a></li>
112 | </ul>
113 | </nav>
114 | 
115 | 116 | 117 |

Example

118 |
119 | 126 |
127 | 128 |

aria-current="page"

129 | 130 |

Screen readers should announce "Current page" to indicate the first link is the current page in the navigation.

131 | 132 |

Code

133 |

134 | <nav>
135 | <ul>
136 | <li><a href="/" aria-current="page">Home</a></li>
137 | <li><a href="/">About</a></li>
138 | <li><a href="/">Contact</a></li>
139 | </ul>
140 | </nav>
141 | 
142 | 143 |

Example

144 |
145 | 152 |
153 | 154 |

aria-current="step"

155 | 156 |

Screen readers should announce "Current step" to indicate the first link is the current step in the process.

157 | 158 |

Code

159 |

160 | <ol>
161 | <li><a href="/" aria-current="step">Do this</a></li>
162 | <li><a href="/">Do that</a></li>
163 | <li><a href="/">Do the other</a></li>
164 | </ol>
165 | 
166 | 167 |

Example

168 |
169 |
    170 |
  1. Do this
  2. 171 |
  3. Do that
  4. 172 |
  5. Do the other
  6. 173 |
174 |
175 | 176 |

aria-current="time"

177 | 178 |

Screen readers should announce "Current time" to indicate the keynote is happening at the current time in the schedule.

179 | 180 |

Code

181 |

182 | <ul>
183 | <li>9am to 10am: Welcome</li>
184 | <li aria-current="time">10am to 11am: Keynote</li>
185 | <li>11am to 11.30pm: Break</li>
186 | <li>11.30am to 1pm: Workshop</li>
187 | <li>1pm to 2pm: Lunch</li>
188 | <li>2pm to 3pm: Lecture</li>
189 | <li>3pm to 3.30pm: Break</li>
190 | <li>3.30pm to 5pm: Workshop</li>
191 | </ul>
192 | 
193 | 194 |

Example

195 |
196 | 206 |
207 | 208 |

aria-current="true"

209 | 210 |

Screen readers should announce "Current" to indicate the first link is the current link in the set.

211 | 212 |

Code

213 |

214 | <ul>
215 | <li><a href="/" aria-current="true">Apples</a></li>
216 | <li><a href="/">Bananas</a></li>
217 | <li><a href="/">Cherries</a></li>
218 | </ul>
219 | 
220 | 221 |

Example

222 |
223 | 228 |
229 | 230 | 231 |

Screen reader support

232 |

Unless otherwise stated, tests were carried out on the latest OS, browser, and screen reader version. Last updated on 16 August 2019.

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 |
Screen reader support for aria-current
=date"="location"="page"="step"="time"="true"Notes
Jaws/ChromeYesYesYesYesYesYes
Jaws/Edge 44NoNoNoNoNoNoIssue #48
Jaws/Edge 77YesYesYesYesYesYes
Jaws/FirefoxYesYesYesYesYesYes
Jaws/IEYesYesYesYesYesYes
Narrator/Edge 44NoNoNoNoNoNo
NVDA/ChromeYesYesYesYesYesYes
NVDA/Edge 44YesYesYesYesYesYes
NVDA/Edge 77YesYesYesYesYesYes
NVDA/FirefoxYesYesYesYesYesYes
NVDA/IEYesYesYesYesYesYes
TalkBack/Chrome??????
TalkBack/FirefoxNoNoNoNoNoNo
VoiceOver (iOS)/SafariNoYesYesYesYesYes
VoiceOver (MacOS/SafariYesYesYesYesYesYes
300 | 301 |

Screen reader demos

302 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /aria-tables/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ARIA tables test case 9 | 10 | 11 | 12 | 13 | 14 |

ARIA tables test case

15 | 16 |

The ARIA table, columnheader, rowheader, row, and cell roles, can be used to polyfill native HTML table semantics.

17 | 18 |

A screen reader should recognise the structure as a table and indicate how many rows and columns it contains. It should be possible to navigate through the table using the standard commands for each screen reader, and for the appropriate row or column headers to be announced as focus moves between table cells.

19 | 20 |
21 | > 22 | Expenses 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Sales 31 | Expenses 32 | Net 33 | 34 | 35 | 36 | Q1 37 | $ 223 38 | $ 195 39 | $ 28 40 | 41 | 42 | 43 | Q2 44 | $ 183 45 | $ 70 46 | $ 113 47 | 48 | 49 | 50 | Q3 51 | $ 277 52 | $ 88 53 | $ 189 54 | 55 | 56 | 57 | Q4 58 | $ 402 59 | $ 133 60 | $ 269 61 | 62 | 63 | 64 |
65 | 66 |

Screen reader support

67 |

Unless otherwise stated, tests were carried out on the latest OS, browser, and screen reader version. Last updated on 3rd February 2018.

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 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
Screen reader support for ARIA tables
tablecolumnheaderrowheaderrow/columnNotes
Jaws/ChromeYesYesYesYes
Jaws 2018/EdgeNoNoNoNoIssue #49
Jaws/FirefoxYesYesYesYes
Jaws/IEYesYesYesYes
Narrator/EdgeYesYesYesYesNarrator needs to be in scan mode.
Narrator/IENoNoNoNo
NVDA/EdgeNoNoNoNo
NVDA/ChromeYesNoNoNoTable is recognised but reported as "0 rows, 0 columns".
NVDA/FirefoxYesNoYesYesTable, row headers and column headers are recognised, but rows/columns are not.
NVDA/IENoNoNoNo
Talkback/ChromePartYesN/APartTable is recognised, but reported as "4 columns, 0 rows". 118 |
119 | Talkback doesn't support row navigation.
TalkBack/FirefoxYesYesN/APartTalkback doesn't support row navigation
Voiceover/Safari (iOS)YesYesYesYes
VoiceOver/Safari (MacOS)YesYesYesYes
134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /autocomplete/css/autocomplete.css: -------------------------------------------------------------------------------- 1 | .icon-expand { 2 | position: relative; 3 | left: -7px; 4 | font-size: 1.7em; 5 | } 6 | 7 | label { 8 | vertical-align: top; 9 | } 10 | input#ac { 11 | vertical-align: top; 12 | } 13 | 14 | ul[role=listbox] { 15 | color: #000; 16 | background: #fff; 17 | display: none; 18 | position: absolute; 19 | list-style: none; 20 | margin-left: 0; 21 | padding: 0; 22 | outline: 1px solid #999; 23 | padding: 0.5em; 24 | max-height: 12em; 25 | overflow: auto; 26 | } 27 | 28 | ul[role=listbox] li { 29 | margin: 0 -0.5em 0 -0.5em; 30 | padding: 0.5em; 31 | } 32 | 33 | ul[role=listbox] li:hover, ul[role=listbox] li.focused:hover { 34 | color: #333; 35 | background: #ccc; 36 | outline: 1px solid #333; 37 | } 38 | 39 | ul[role=listbox] li.focused { 40 | color: #000; 41 | background: #fc0; 42 | outline: 1px solid #000; 43 | } 44 | -------------------------------------------------------------------------------- /autocomplete/css/glyphicon.css: -------------------------------------------------------------------------------- 1 | 2 | html, 3 | html .halflings { 4 | -webkit-font-smoothing: antialiased !important; 5 | } 6 | @font-face { 7 | font-family: 'Glyphicons'; 8 | src: url('fonts/glyphicons-regular.eot'); 9 | src: url('fonts/glyphicons-regular.eot?#iefix') format('embedded-opentype'), url('fonts/glyphicons-regular.woff') format('woff'), url('fonts/glyphicons-regular.ttf') format('truetype'), url('fonts/glyphicons-regular.svg#glyphicons_halflingsregular') format('svg'); 10 | font-weight: normal; 11 | font-style: normal; 12 | } 13 | [class^="icon-"], [class*=" icon-"] { 14 | display: inline-block; 15 | font-family: 'Glyphicons'; 16 | speak: none; 17 | position: relative; 18 | text-decoration: none; 19 | *display: inline; 20 | *zoom: 1; 21 | } 22 | 23 | .icon-ie-fix [class^="icon-"], .icon-ie-fix [class*=" icon-"] { 24 | font-family: inherit; 25 | } 26 | 27 | .icon-ie-fix [class^="icon-"]:before, .icon-ie-fix [class*=" icon-"]:before { 28 | content: ""; 29 | } 30 | 31 | .glyphicons.white:before { 32 | color: #fff; 33 | } 34 | .icon-glass:before { 35 | content: "\e001"; 36 | } 37 | .icon-leaf:before { 38 | content: "\e002"; 39 | } 40 | .icon-dog:before { 41 | content: "\e003"; 42 | } 43 | .icon-user:before { 44 | content: "\e004"; 45 | } 46 | .icon-girl:before { 47 | content: "\e005"; 48 | } 49 | .icon-car:before { 50 | content: "\e006"; 51 | } 52 | .icon-user_add:before { 53 | content: "\e007"; 54 | } 55 | .icon-user_remove:before { 56 | content: "\e008"; 57 | } 58 | .icon-film:before { 59 | content: "\e009"; 60 | } 61 | .icon-magic:before { 62 | content: "\e010"; 63 | } 64 | .icon-envelope:before { 65 | content: "\2709"; 66 | } 67 | .icon-camera:before { 68 | content: "\e012"; 69 | } 70 | .icon-heart:before { 71 | content: "\e013"; 72 | } 73 | .icon-beach_umbrella:before { 74 | content: "\e014"; 75 | } 76 | .icon-train:before { 77 | content: "\e015"; 78 | } 79 | .icon-print:before { 80 | content: "\e016"; 81 | } 82 | .icon-bin:before { 83 | content: "\e017"; 84 | } 85 | .icon-music:before { 86 | content: "\e018"; 87 | } 88 | .icon-note:before { 89 | content: "\e019"; 90 | } 91 | .icon-heart_empty:before { 92 | content: "\e020"; 93 | } 94 | .icon-home:before { 95 | content: "\e021"; 96 | } 97 | .icon-snowflake:before { 98 | content: "\2744"; 99 | } 100 | .icon-fire:before { 101 | content: "\e023"; 102 | } 103 | .icon-magnet:before { 104 | content: "\e024"; 105 | } 106 | .icon-parents:before { 107 | content: "\e025"; 108 | } 109 | .icon-binoculars:before { 110 | content: "\e026"; 111 | } 112 | .icon-road:before { 113 | content: "\e027"; 114 | } 115 | .icon-search:before { 116 | content: "\e028"; 117 | } 118 | .icon-cars:before { 119 | content: "\e029"; 120 | } 121 | .icon-notes_2:before { 122 | content: "\e030"; 123 | } 124 | .icon-pencil:before { 125 | content: "\270F"; 126 | } 127 | .icon-bus:before { 128 | content: "\e032"; 129 | } 130 | .icon-wifi_alt:before { 131 | content: "\e033"; 132 | } 133 | .icon-luggage:before { 134 | content: "\e034"; 135 | } 136 | .icon-old_man:before { 137 | content: "\e035"; 138 | } 139 | .icon-woman:before { 140 | content: "\e036"; 141 | } 142 | .icon-file:before { 143 | content: "\e037"; 144 | } 145 | .icon-coins:before { 146 | content: "\e038"; 147 | } 148 | .icon-airplane:before { 149 | content: "\2708"; 150 | } 151 | .icon-notes:before { 152 | content: "\e040"; 153 | } 154 | .icon-stats:before { 155 | content: "\e041"; 156 | } 157 | .icon-charts:before { 158 | content: "\e042"; 159 | } 160 | .icon-pie_chart:before { 161 | content: "\e043"; 162 | } 163 | .icon-group:before { 164 | content: "\e044"; 165 | } 166 | .icon-keys:before { 167 | content: "\e045"; 168 | } 169 | .icon-calendar:before { 170 | content: "\1F4C5"; 171 | } 172 | .icon-router:before { 173 | content: "\e047"; 174 | } 175 | .icon-camera_small:before { 176 | content: "\e048"; 177 | } 178 | .icon-dislikes:before { 179 | content: "\e049"; 180 | } 181 | .icon-star:before { 182 | content: "\e050"; 183 | } 184 | .icon-link:before { 185 | content: "\e051"; 186 | } 187 | .icon-eye_open:before { 188 | content: "\e052"; 189 | } 190 | .icon-eye_close:before { 191 | content: "\e053"; 192 | } 193 | .icon-alarm:before { 194 | content: "\e054"; 195 | } 196 | .icon-clock:before { 197 | content: "\e055"; 198 | } 199 | .icon-stopwatch:before { 200 | content: "\e056"; 201 | } 202 | .icon-projector:before { 203 | content: "\e057"; 204 | } 205 | .icon-history:before { 206 | content: "\e058"; 207 | } 208 | .icon-truck:before { 209 | content: "\e059"; 210 | } 211 | .icon-cargo:before { 212 | content: "\e060"; 213 | } 214 | .icon-compass:before { 215 | content: "\e061"; 216 | } 217 | .icon-keynote:before { 218 | content: "\e062"; 219 | } 220 | .icon-paperclip:before { 221 | content: "\e063"; 222 | } 223 | .icon-power:before { 224 | content: "\e064"; 225 | } 226 | .icon-lightbulb:before { 227 | content: "\e065"; 228 | } 229 | .icon-tag:before { 230 | content: "\e066"; 231 | } 232 | .icon-tags:before { 233 | content: "\e067"; 234 | } 235 | .icon-cleaning:before { 236 | content: "\e068"; 237 | } 238 | .icon-ruller:before { 239 | content: "\e069"; 240 | } 241 | .icon-gift:before { 242 | content: "\e070"; 243 | } 244 | .icon-umbrella:before { 245 | content: "\2602"; 246 | } 247 | .icon-book:before { 248 | content: "\e072"; 249 | } 250 | .icon-bookmark:before { 251 | content: "\1F516"; 252 | } 253 | .icon-wifi:before { 254 | content: "\e074"; 255 | } 256 | .icon-cup:before { 257 | content: "\e075"; 258 | } 259 | .icon-stroller:before { 260 | content: "\e076"; 261 | } 262 | .icon-headphones:before { 263 | content: "\e077"; 264 | } 265 | .icon-headset:before { 266 | content: "\e078"; 267 | } 268 | .icon-warning_sign:before { 269 | content: "\e079"; 270 | } 271 | .icon-signal:before { 272 | content: "\e080"; 273 | } 274 | .icon-retweet:before { 275 | content: "\e081"; 276 | } 277 | .icon-refresh:before { 278 | content: "\e082"; 279 | } 280 | .icon-roundabout:before { 281 | content: "\e083"; 282 | } 283 | .icon-random:before { 284 | content: "\e084"; 285 | } 286 | .icon-heat:before { 287 | content: "\e085"; 288 | } 289 | .icon-repeat:before { 290 | content: "\e086"; 291 | } 292 | .icon-display:before { 293 | content: "\e087"; 294 | } 295 | .icon-log_book:before { 296 | content: "\e088"; 297 | } 298 | .icon-adress_book:before { 299 | content: "\e089"; 300 | } 301 | .icon-building:before { 302 | content: "\e090"; 303 | } 304 | .icon-eyedropper:before { 305 | content: "\e091"; 306 | } 307 | .icon-adjust:before { 308 | content: "\e092"; 309 | } 310 | .icon-tint:before { 311 | content: "\e093"; 312 | } 313 | .icon-crop:before { 314 | content: "\e094"; 315 | } 316 | .icon-vector_path_square:before { 317 | content: "\e095"; 318 | } 319 | .icon-vector_path_circle:before { 320 | content: "\e096"; 321 | } 322 | .icon-vector_path_polygon:before { 323 | content: "\e097"; 324 | } 325 | .icon-vector_path_line:before { 326 | content: "\e098"; 327 | } 328 | .icon-vector_path_curve:before { 329 | content: "\e099"; 330 | } 331 | .icon-vector_path_all:before { 332 | content: "\e100"; 333 | } 334 | .icon-font:before { 335 | content: "\e101"; 336 | } 337 | .icon-italic:before { 338 | content: "\e102"; 339 | } 340 | .icon-bold:before { 341 | content: "\e103"; 342 | } 343 | .icon-text_underline:before { 344 | content: "\e104"; 345 | } 346 | .icon-text_strike:before { 347 | content: "\e105"; 348 | } 349 | .icon-text_height:before { 350 | content: "\e106"; 351 | } 352 | .icon-text_width:before { 353 | content: "\e107"; 354 | } 355 | .icon-text_resize:before { 356 | content: "\e108"; 357 | } 358 | .icon-left_indent:before { 359 | content: "\e109"; 360 | } 361 | .icon-right_indent:before { 362 | content: "\e110"; 363 | } 364 | .icon-align_left:before { 365 | content: "\e111"; 366 | } 367 | .icon-align_center:before { 368 | content: "\e112"; 369 | } 370 | .icon-align_right:before { 371 | content: "\e113"; 372 | } 373 | .icon-justify:before { 374 | content: "\e114"; 375 | } 376 | .icon-list:before { 377 | content: "\e115"; 378 | } 379 | .icon-text_smaller:before { 380 | content: "\e116"; 381 | } 382 | .icon-text_bigger:before { 383 | content: "\e117"; 384 | } 385 | .icon-embed:before { 386 | content: "\e118"; 387 | } 388 | .icon-embed_close:before { 389 | content: "\e119"; 390 | } 391 | .icon-table:before { 392 | content: "\e120"; 393 | } 394 | .icon-message_full:before { 395 | content: "\e121"; 396 | } 397 | .icon-message_empty:before { 398 | content: "\e122"; 399 | } 400 | .icon-message_in:before { 401 | content: "\e123"; 402 | } 403 | .icon-message_out:before { 404 | content: "\e124"; 405 | } 406 | .icon-message_plus:before { 407 | content: "\e125"; 408 | } 409 | .icon-message_minus:before { 410 | content: "\e126"; 411 | } 412 | .icon-message_ban:before { 413 | content: "\e127"; 414 | } 415 | .icon-message_flag:before { 416 | content: "\e128"; 417 | } 418 | .icon-message_lock:before { 419 | content: "\e129"; 420 | } 421 | .icon-message_new:before { 422 | content: "\e130"; 423 | } 424 | .icon-inbox:before { 425 | content: "\e131"; 426 | } 427 | .icon-inbox_plus:before { 428 | content: "\e132"; 429 | } 430 | .icon-inbox_minus:before { 431 | content: "\e133"; 432 | } 433 | .icon-inbox_lock:before { 434 | content: "\e134"; 435 | } 436 | .icon-inbox_in:before { 437 | content: "\e135"; 438 | } 439 | .icon-inbox_out:before { 440 | content: "\e136"; 441 | } 442 | .icon-cogwheel:before { 443 | content: "\e137"; 444 | } 445 | .icon-cogwheels:before { 446 | content: "\e138"; 447 | } 448 | .icon-picture:before { 449 | content: "\e139"; 450 | } 451 | .icon-adjust_alt:before { 452 | content: "\e140"; 453 | } 454 | .icon-database_lock:before { 455 | content: "\e141"; 456 | } 457 | .icon-database_plus:before { 458 | content: "\e142"; 459 | } 460 | .icon-database_minus:before { 461 | content: "\e143"; 462 | } 463 | .icon-database_ban:before { 464 | content: "\e144"; 465 | } 466 | .icon-folder_open:before { 467 | content: "\e145"; 468 | } 469 | .icon-folder_plus:before { 470 | content: "\e146"; 471 | } 472 | .icon-folder_minus:before { 473 | content: "\e147"; 474 | } 475 | .icon-folder_lock:before { 476 | content: "\e148"; 477 | } 478 | .icon-folder_flag:before { 479 | content: "\e149"; 480 | } 481 | .icon-folder_new:before { 482 | content: "\e150"; 483 | } 484 | .icon-edit:before { 485 | content: "\e151"; 486 | } 487 | .icon-new_window:before { 488 | content: "\e152"; 489 | } 490 | .icon-check:before { 491 | content: "\e153"; 492 | } 493 | .icon-unchecked:before { 494 | content: "\e154"; 495 | } 496 | .icon-more_windows:before { 497 | content: "\e155"; 498 | } 499 | .icon-show_big_thumbnails:before { 500 | content: "\e156"; 501 | } 502 | .icon-show_thumbnails:before { 503 | content: "\e157"; 504 | } 505 | .icon-show_thumbnails_with_lines:before { 506 | content: "\e158"; 507 | } 508 | .icon-show_lines:before { 509 | content: "\e159"; 510 | } 511 | .icon-playlist:before { 512 | content: "\e160"; 513 | } 514 | .icon-imac:before { 515 | content: "\e161"; 516 | } 517 | .icon-macbook:before { 518 | content: "\e162"; 519 | } 520 | .icon-ipad:before { 521 | content: "\e163"; 522 | } 523 | .icon-iphone:before { 524 | content: "\e164"; 525 | } 526 | .icon-iphone_transfer:before { 527 | content: "\e165"; 528 | } 529 | .icon-iphone_exchange:before { 530 | content: "\e166"; 531 | } 532 | .icon-ipod:before { 533 | content: "\e167"; 534 | } 535 | .icon-ipod_shuffle:before { 536 | content: "\e168"; 537 | } 538 | .icon-ear_plugs:before { 539 | content: "\e169"; 540 | } 541 | .icon-phone:before { 542 | content: "\e170"; 543 | } 544 | .icon-step_backward:before { 545 | content: "\e171"; 546 | } 547 | .icon-fast_backward:before { 548 | content: "\e172"; 549 | } 550 | .icon-rewind:before { 551 | content: "\e173"; 552 | } 553 | .icon-play:before { 554 | content: "\e174"; 555 | } 556 | .icon-pause:before { 557 | content: "\e175"; 558 | } 559 | .icon-stop:before { 560 | content: "\e176"; 561 | } 562 | .icon-forward:before { 563 | content: "\e177"; 564 | } 565 | .icon-fast_forward:before { 566 | content: "\e178"; 567 | } 568 | .icon-step_forward:before { 569 | content: "\e179"; 570 | } 571 | .icon-eject:before { 572 | content: "\e180"; 573 | } 574 | .icon-facetime_video:before { 575 | content: "\e181"; 576 | } 577 | .icon-download_alt:before { 578 | content: "\e182"; 579 | } 580 | .icon-mute:before { 581 | content: "\e183"; 582 | } 583 | .icon-volume_down:before { 584 | content: "\e184"; 585 | } 586 | .icon-volume_up:before { 587 | content: "\e185"; 588 | } 589 | .icon-screenshot:before { 590 | content: "\e186"; 591 | } 592 | .icon-move:before { 593 | content: "\e187"; 594 | } 595 | .icon-more:before { 596 | content: "\e188"; 597 | } 598 | .icon-brightness_reduce:before { 599 | content: "\e189"; 600 | } 601 | .icon-brightness_increase:before { 602 | content: "\e190"; 603 | } 604 | .icon-circle_plus:before { 605 | content: "\e191"; 606 | } 607 | .icon-circle_minus:before { 608 | content: "\e192"; 609 | } 610 | .icon-circle_remove:before { 611 | content: "\e193"; 612 | } 613 | .icon-circle_ok:before { 614 | content: "\e194"; 615 | } 616 | .icon-circle_question_mark:before { 617 | content: "\e195"; 618 | } 619 | .icon-circle_info:before { 620 | content: "\e196"; 621 | } 622 | .icon-circle_exclamation_mark:before { 623 | content: "\e197"; 624 | } 625 | .icon-remove:before { 626 | content: "\e198"; 627 | } 628 | .icon-ok:before { 629 | content: "\e199"; 630 | } 631 | .icon-ban:before { 632 | content: "\e200"; 633 | } 634 | .icon-download:before { 635 | content: "\e201"; 636 | } 637 | .icon-upload:before { 638 | content: "\e202"; 639 | } 640 | .icon-shopping_cart:before { 641 | content: "\e203"; 642 | } 643 | .icon-lock:before { 644 | content: "\1F512"; 645 | } 646 | .icon-unlock:before { 647 | content: "\e205"; 648 | } 649 | .icon-electricity:before { 650 | content: "\e206"; 651 | } 652 | .icon-ok_2:before { 653 | content: "\e207"; 654 | } 655 | .icon-remove_2:before { 656 | content: "\e208"; 657 | } 658 | .icon-cart_out:before { 659 | content: "\e209"; 660 | } 661 | .icon-cart_in:before { 662 | content: "\e210"; 663 | } 664 | .icon-left_arrow:before { 665 | content: "\e211"; 666 | } 667 | .icon-right_arrow:before { 668 | content: "\e212"; 669 | } 670 | .icon-down_arrow:before { 671 | content: "\e213"; 672 | } 673 | .icon-up_arrow:before { 674 | content: "\e214"; 675 | } 676 | .icon-resize_small:before { 677 | content: "\e215"; 678 | } 679 | .icon-resize_full:before { 680 | content: "\e216"; 681 | } 682 | .icon-circle_arrow_left:before { 683 | content: "\e217"; 684 | } 685 | .icon-circle_arrow_right:before { 686 | content: "\e218"; 687 | } 688 | .icon-circle_arrow_top:before { 689 | content: "\e219"; 690 | } 691 | .icon-circle_arrow_down:before { 692 | content: "\e220"; 693 | } 694 | .icon-play_button:before { 695 | content: "\e221"; 696 | } 697 | .icon-unshare:before { 698 | content: "\e222"; 699 | } 700 | .icon-share:before { 701 | content: "\e223"; 702 | } 703 | .icon-chevron-right:before { 704 | content: "\e224"; 705 | } 706 | .icon-chevron-left:before { 707 | content: "\e225"; 708 | } 709 | .icon-bluetooth:before { 710 | content: "\e226"; 711 | } 712 | .icon-euro:before { 713 | content: "\20AC"; 714 | } 715 | .icon-usd:before { 716 | content: "\e228"; 717 | } 718 | .icon-gbp:before { 719 | content: "\e229"; 720 | } 721 | .icon-retweet_2:before { 722 | content: "\e230"; 723 | } 724 | .icon-moon:before { 725 | content: "\e231"; 726 | } 727 | .icon-sun:before { 728 | content: "\2609"; 729 | } 730 | .icon-cloud:before { 731 | content: "\2601"; 732 | } 733 | .icon-direction:before { 734 | content: "\e234"; 735 | } 736 | .icon-brush:before { 737 | content: "\e235"; 738 | } 739 | .icon-pen:before { 740 | content: "\e236"; 741 | } 742 | .icon-zoom_in:before { 743 | content: "\e237"; 744 | } 745 | .icon-zoom_out:before { 746 | content: "\e238"; 747 | } 748 | .icon-pin:before { 749 | content: "\e239"; 750 | } 751 | .icon-albums:before { 752 | content: "\e240"; 753 | } 754 | .icon-rotation_lock:before { 755 | content: "\e241"; 756 | } 757 | .icon-flash:before { 758 | content: "\e242"; 759 | } 760 | .icon-google_maps:before { 761 | content: "\e243"; 762 | } 763 | .icon-anchor:before { 764 | content: "\2693"; 765 | } 766 | .icon-conversation:before { 767 | content: "\e245"; 768 | } 769 | .icon-chat:before { 770 | content: "\e246"; 771 | } 772 | .icon-male:before { 773 | content: "\e247"; 774 | } 775 | .icon-female:before { 776 | content: "\e248"; 777 | } 778 | .icon-asterisk:before { 779 | content: "\002A"; 780 | } 781 | .icon-divide:before { 782 | content: "\00F7"; 783 | } 784 | .icon-snorkel_diving:before { 785 | content: "\e251"; 786 | } 787 | .icon-scuba_diving:before { 788 | content: "\e252"; 789 | } 790 | .icon-oxygen_bottle:before { 791 | content: "\e253"; 792 | } 793 | .icon-fins:before { 794 | content: "\e254"; 795 | } 796 | .icon-fishes:before { 797 | content: "\e255"; 798 | } 799 | .icon-boat:before { 800 | content: "\e256"; 801 | } 802 | .icon-delete:before { 803 | content: "\e257"; 804 | } 805 | .icon-sheriffs_star:before { 806 | content: "\e258"; 807 | } 808 | .icon-qrcode:before { 809 | content: "\e259"; 810 | } 811 | .icon-barcode:before { 812 | content: "\e260"; 813 | } 814 | .icon-pool:before { 815 | content: "\e261"; 816 | } 817 | .icon-buoy:before { 818 | content: "\e262"; 819 | } 820 | .icon-spade:before { 821 | content: "\e263"; 822 | } 823 | .icon-bank:before { 824 | content: "\e264"; 825 | } 826 | .icon-vcard:before { 827 | content: "\e265"; 828 | } 829 | .icon-electrical_plug:before { 830 | content: "\e266"; 831 | } 832 | .icon-flag:before { 833 | content: "\e267"; 834 | } 835 | .icon-credit_card:before { 836 | content: "\e268"; 837 | } 838 | .icon-keyboard-wireless:before { 839 | content: "\e269"; 840 | } 841 | .icon-keyboard-wired:before { 842 | content: "\e270"; 843 | } 844 | .icon-shield:before { 845 | content: "\e271"; 846 | } 847 | .icon-ring:before { 848 | content: "\02DA"; 849 | } 850 | .icon-cake:before { 851 | content: "\e273"; 852 | } 853 | .icon-drink:before { 854 | content: "\e274"; 855 | } 856 | .icon-beer:before { 857 | content: "\e275"; 858 | } 859 | .icon-fast_food:before { 860 | content: "\e276"; 861 | } 862 | .icon-cutlery:before { 863 | content: "\e277"; 864 | } 865 | .icon-pizza:before { 866 | content: "\e278"; 867 | } 868 | .icon-birthday_cake:before { 869 | content: "\e279"; 870 | } 871 | .icon-tablet:before { 872 | content: "\e280"; 873 | } 874 | .icon-settings:before { 875 | content: "\e281"; 876 | } 877 | .icon-bullets:before { 878 | content: "\e282"; 879 | } 880 | .icon-cardio:before { 881 | content: "\e283"; 882 | } 883 | .icon-t-shirt:before { 884 | content: "\e284"; 885 | } 886 | .icon-pants:before { 887 | content: "\e285"; 888 | } 889 | .icon-sweater:before { 890 | content: "\e286"; 891 | } 892 | .icon-fabric:before { 893 | content: "\e287"; 894 | } 895 | .icon-leather:before { 896 | content: "\e288"; 897 | } 898 | .icon-scissors:before { 899 | content: "\e289"; 900 | } 901 | .icon-bomb:before { 902 | content: "\e290"; 903 | } 904 | .icon-skull:before { 905 | content: "\e291"; 906 | } 907 | .icon-celebration:before { 908 | content: "\e292"; 909 | } 910 | .icon-tea_kettle:before { 911 | content: "\e293"; 912 | } 913 | .icon-french_press:before { 914 | content: "\e294"; 915 | } 916 | .icon-coffe_cup:before { 917 | content: "\e295"; 918 | } 919 | .icon-pot:before { 920 | content: "\e296"; 921 | } 922 | .icon-grater:before { 923 | content: "\e297"; 924 | } 925 | .icon-kettle:before { 926 | content: "\e298"; 927 | } 928 | .icon-hospital:before { 929 | content: "\e299"; 930 | } 931 | .icon-hospital_h:before { 932 | content: "\e300"; 933 | } 934 | .icon-microphone:before { 935 | content: "\e301"; 936 | } 937 | .icon-webcam:before { 938 | content: "\e302"; 939 | } 940 | .icon-temple_christianity_church:before { 941 | content: "\e303"; 942 | } 943 | .icon-temple_islam:before { 944 | content: "\e304"; 945 | } 946 | .icon-temple_hindu:before { 947 | content: "\e305"; 948 | } 949 | .icon-temple_buddhist:before { 950 | content: "\e306"; 951 | } 952 | .icon-bicycle:before { 953 | content: "\e307"; 954 | } 955 | .icon-life_preserver:before { 956 | content: "\e308"; 957 | } 958 | .icon-share_alt:before { 959 | content: "\e309"; 960 | } 961 | .icon-comments:before { 962 | content: "\e310"; 963 | } 964 | .icon-flower:before { 965 | content: "\2698"; 966 | } 967 | .icon-baseball:before { 968 | content: "\e312"; 969 | } 970 | .icon-rugby:before { 971 | content: "\e313"; 972 | } 973 | .icon-ax:before { 974 | content: "\e314"; 975 | } 976 | .icon-table_tennis:before { 977 | content: "\e315"; 978 | } 979 | .icon-bowling:before { 980 | content: "\e316"; 981 | } 982 | .icon-tree_conifer:before { 983 | content: "\e317"; 984 | } 985 | .icon-tree_deciduous:before { 986 | content: "\e318"; 987 | } 988 | .icon-more_items:before { 989 | content: "\e319"; 990 | } 991 | .icon-sort:before { 992 | content: "\e320"; 993 | } 994 | .icon-filter:before { 995 | content: "\e321"; 996 | } 997 | .icon-gamepad:before { 998 | content: "\e322"; 999 | } 1000 | .icon-playing_dices:before { 1001 | content: "\e323"; 1002 | } 1003 | .icon-calculator:before { 1004 | content: "\e324"; 1005 | } 1006 | .icon-tie:before { 1007 | content: "\e325"; 1008 | } 1009 | .icon-wallet:before { 1010 | content: "\e326"; 1011 | } 1012 | .icon-piano:before { 1013 | content: "\e327"; 1014 | } 1015 | .icon-sampler:before { 1016 | content: "\e328"; 1017 | } 1018 | .icon-podium:before { 1019 | content: "\e329"; 1020 | } 1021 | .icon-soccer_ball:before { 1022 | content: "\e330"; 1023 | } 1024 | .icon-blog:before { 1025 | content: "\e331"; 1026 | } 1027 | .icon-dashboard:before { 1028 | content: "\e332"; 1029 | } 1030 | .icon-certificate:before { 1031 | content: "\e333"; 1032 | } 1033 | .icon-bell:before { 1034 | content: "\e334"; 1035 | } 1036 | .icon-candle:before { 1037 | content: "\e335"; 1038 | } 1039 | .icon-pushpin:before { 1040 | content: "\e336"; 1041 | } 1042 | .icon-iphone_shake:before { 1043 | content: "\e337"; 1044 | } 1045 | .icon-pin_flag:before { 1046 | content: "\e338"; 1047 | } 1048 | .icon-turtle:before { 1049 | content: "\e339"; 1050 | } 1051 | .icon-rabbit:before { 1052 | content: "\e340"; 1053 | } 1054 | .icon-globe:before { 1055 | content: "\e341"; 1056 | } 1057 | .icon-briefcase:before { 1058 | content: "\1F4BC"; 1059 | } 1060 | .icon-hdd:before { 1061 | content: "\e343"; 1062 | } 1063 | .icon-thumbs_up:before { 1064 | content: "\e344"; 1065 | } 1066 | .icon-thumbs_down:before { 1067 | content: "\e345"; 1068 | } 1069 | .icon-hand_right:before { 1070 | content: "\e346"; 1071 | } 1072 | .icon-hand_left:before { 1073 | content: "\e347"; 1074 | } 1075 | .icon-hand_up:before { 1076 | content: "\e348"; 1077 | } 1078 | .icon-hand_down:before { 1079 | content: "\e349"; 1080 | } 1081 | .icon-fullscreen:before { 1082 | content: "\e350"; 1083 | } 1084 | .icon-shopping_bag:before { 1085 | content: "\e351"; 1086 | } 1087 | .icon-book_open:before { 1088 | content: "\e352"; 1089 | } 1090 | .icon-nameplate:before { 1091 | content: "\e353"; 1092 | } 1093 | .icon-nameplate_alt:before { 1094 | content: "\e354"; 1095 | } 1096 | .icon-vases:before { 1097 | content: "\e355"; 1098 | } 1099 | .icon-bullhorn:before { 1100 | content: "\e356"; 1101 | } 1102 | .icon-dumbbell:before { 1103 | content: "\e357"; 1104 | } 1105 | .icon-suitcase:before { 1106 | content: "\e358"; 1107 | } 1108 | .icon-file_import:before { 1109 | content: "\e359"; 1110 | } 1111 | .icon-file_export:before { 1112 | content: "\e360"; 1113 | } 1114 | .icon-bug:before { 1115 | content: "\e361"; 1116 | } 1117 | .icon-crown:before { 1118 | content: "\e362"; 1119 | } 1120 | .icon-smoking:before { 1121 | content: "\e363"; 1122 | } 1123 | .icon-cloud-upload:before { 1124 | content: "\e364"; 1125 | } 1126 | .icon-cloud-download:before { 1127 | content: "\e365"; 1128 | } 1129 | .icon-restart:before { 1130 | content: "\e366"; 1131 | } 1132 | .icon-security_camera:before { 1133 | content: "\e367"; 1134 | } 1135 | .icon-expand:before { 1136 | content: "\e368"; 1137 | } 1138 | .icon-collapse:before { 1139 | content: "\e369"; 1140 | } 1141 | .icon-collapse_top:before { 1142 | content: "\e370"; 1143 | } 1144 | .icon-globe_af:before { 1145 | content: "\e371"; 1146 | } 1147 | .icon-global:before { 1148 | content: "\e372"; 1149 | } 1150 | .icon-spray:before { 1151 | content: "\e373"; 1152 | } 1153 | .icon-nails:before { 1154 | content: "\e374"; 1155 | } 1156 | .icon-claw_hammer:before { 1157 | content: "\e375"; 1158 | } 1159 | .icon-classic_hammer:before { 1160 | content: "\e376"; 1161 | } 1162 | .icon-hand_saw:before { 1163 | content: "\e377"; 1164 | } 1165 | .icon-riflescope:before { 1166 | content: "\e378"; 1167 | } 1168 | .icon-electrical_socket_eu:before { 1169 | content: "\e379"; 1170 | } 1171 | .icon-electrical_socket_us:before { 1172 | content: "\e380"; 1173 | } 1174 | .icon-pinterest:before { 1175 | content: "\e381"; 1176 | } 1177 | .icon-dropbox:before { 1178 | content: "\e382"; 1179 | } 1180 | .icon-google_plus:before { 1181 | content: "\e383"; 1182 | } 1183 | .icon-jolicloud:before { 1184 | content: "\e384"; 1185 | } 1186 | .icon-yahoo:before { 1187 | content: "\e385"; 1188 | } 1189 | .icon-blogger:before { 1190 | content: "\e386"; 1191 | } 1192 | .icon-picasa:before { 1193 | content: "\e387"; 1194 | } 1195 | .icon-amazon:before { 1196 | content: "\e388"; 1197 | } 1198 | .icon-tumblr:before { 1199 | content: "\e389"; 1200 | } 1201 | .icon-wordpress:before { 1202 | content: "\e390"; 1203 | } 1204 | .icon-instapaper:before { 1205 | content: "\e391"; 1206 | } 1207 | .icon-evernote:before { 1208 | content: "\e392"; 1209 | } 1210 | .icon-xing:before { 1211 | content: "\e393"; 1212 | } 1213 | .icon-zootool:before { 1214 | content: "\e394"; 1215 | } 1216 | .icon-dribbble:before { 1217 | content: "\e395"; 1218 | } 1219 | .icon-deviantart:before { 1220 | content: "\e396"; 1221 | } 1222 | .icon-read_it_later:before { 1223 | content: "\e397"; 1224 | } 1225 | .icon-linked_in:before { 1226 | content: "\e398"; 1227 | } 1228 | .icon-forrst:before { 1229 | content: "\e399"; 1230 | } 1231 | .icon-pinboard:before { 1232 | content: "\e400"; 1233 | } 1234 | .icon-behance:before { 1235 | content: "\e401"; 1236 | } 1237 | .icon-github:before { 1238 | content: "\e402"; 1239 | } 1240 | .icon-youtube:before { 1241 | content: "\e403"; 1242 | } 1243 | .icon-skitch:before { 1244 | content: "\e404"; 1245 | } 1246 | .icon-foursquare:before { 1247 | content: "\e405"; 1248 | } 1249 | .icon-quora:before { 1250 | content: "\e406"; 1251 | } 1252 | .icon-badoo:before { 1253 | content: "\e407"; 1254 | } 1255 | .icon-spotify:before { 1256 | content: "\e408"; 1257 | } 1258 | .icon-stumbleupon:before { 1259 | content: "\e409"; 1260 | } 1261 | .icon-readability:before { 1262 | content: "\e410"; 1263 | } 1264 | .icon-facebook:before { 1265 | content: "\e411"; 1266 | } 1267 | .icon-twitter:before { 1268 | content: "\e412"; 1269 | } 1270 | .icon-instagram:before { 1271 | content: "\e413"; 1272 | } 1273 | .icon-posterous_spaces:before { 1274 | content: "\e414"; 1275 | } 1276 | .icon-vimeo:before { 1277 | content: "\e415"; 1278 | } 1279 | .icon-flickr:before { 1280 | content: "\e416"; 1281 | } 1282 | .icon-last_fm:before { 1283 | content: "\e417"; 1284 | } 1285 | .icon-rss:before { 1286 | content: "\e418"; 1287 | } 1288 | .icon-skype:before { 1289 | content: "\e419"; 1290 | } 1291 | .icon-e-mail:before { 1292 | content: "\e420"; 1293 | } 1294 | .icon-phone_alt:before { 1295 | content: "\e442"; 1296 | } -------------------------------------------------------------------------------- /autocomplete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Autocomplete design pattern 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

Autocomplete design pattern

20 | 21 |

Type into the edit box and a list of autocomplete suggestions appears.

22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /autocomplete/js/autocomplete.js: -------------------------------------------------------------------------------- 1 | function selectItem(objAutocomplete, objCurrent, objNext) { 2 | if (objNext) { 3 | $(objCurrent).removeClass('focused'); 4 | $(objNext).addClass('focused'); 5 | $(objAutocomplete).attr('aria-activedescendant', $(objNext).attr('id')); 6 | $(objAutocomplete).val($(objNext).text()); 7 | $(objNext).focus(); 8 | } 9 | } 10 | 11 | function displayMenu(objAutocomplete, bEmpty) { 12 | var strSearch = $(objAutocomplete).attr('data-typed').replace(/^\s*|\s*$/g, '').toLowerCase(); 13 | var iVisible = 0; 14 | 15 | if (bEmpty || strSearch.length > 0) { 16 | $('#suggestions').find('li').each(function() { 17 | if ($(this).text().toLowerCase().indexOf(strSearch) < 0) { 18 | $(this).hide(); 19 | } 20 | else { 21 | $(this).show(); 22 | iVisible++; 23 | } 24 | }); 25 | } 26 | 27 | return iVisible; 28 | } 29 | 30 | function showMenu(objAutocomplete, objEvent) { 31 | var iVisible; 32 | 33 | $(objAutocomplete).attr('data-typed', ''); 34 | iVisible = displayMenu(objAutocomplete, true); 35 | 36 | if ($(objEvent.target).attr('class') === 'icon-expand') { 37 | if (iVisible && $('#suggestions').css('display') === 'none') { 38 | $('#suggestions').show(); 39 | $(objAutocomplete).attr('aria-expanded', 'true'); 40 | } 41 | else { 42 | $('#suggestions').hide(); 43 | $(objAutocomplete).attr('aria-expanded', 'false'); 44 | } 45 | } 46 | else { 47 | $('#suggestions').hide(); 48 | $(objAutocomplete).attr('aria-expanded', 'false'); 49 | } 50 | } 51 | 52 | function select(objItem, objEvent) { 53 | var objTarget = objEvent.target; 54 | 55 | $('#ac').val($(objTarget).text()); 56 | $('#ac').attr('aria-expanded', 'false').attr('data-selected', $('#ac').val()); 57 | $('#suggestions').find('li').removeClass('focused'); 58 | $(objTarget).addClass('focused'); 59 | $('#ac').attr('aria-activedescendant', $(objTarget).attr('id')); 60 | $('#suggestions').hide(); 61 | } 62 | 63 | function suggest(objAutocomplete, objEvent) { 64 | var objCurrent, objNext, iVisible, iIndex, bDisplay = true; 65 | if ($(objAutocomplete).attr('aria-activedescendant')) { 66 | objCurrent = $('#suggestions').find('li#' + $(objAutocomplete).attr('aria-activedescendant')); 67 | } 68 | 69 | switch (objEvent.keyCode) { 70 | case 9: // TAB 71 | if ($(objAutocomplete).attr('data-selected').length > 0) { 72 | $(objAutocomplete).val($(objAutocomplete).attr('data-selected')); 73 | } 74 | else { 75 | $(objAutocomplete).val($(objAutocomplete).attr('data-typed')); 76 | } 77 | $('#suggestions').hide(); 78 | $(objAutocomplete).select(); 79 | bDisplay = false; 80 | break; 81 | case 16: // SHIFT 82 | return false; 83 | case 13: // ENTER 84 | case 32: // SPACE 85 | $(objAutocomplete).attr('data-selected', $(objAutocomplete).val()); 86 | $('#suggestions').hide(); 87 | bDisplay = false; 88 | if ($(objEvent.target).prop('tagName') === 'LI') { 89 | $(objAutocomplete).focus(); 90 | } 91 | objEvent.preventDefault(); 92 | break; 93 | case 27: // ESCAPE 94 | if ($(objAutocomplete).attr('data-selected').length > 0) { 95 | $(objAutocomplete).val($(objAutocomplete).attr('data-selected')); 96 | } 97 | else { 98 | $(objAutocomplete).val($(objAutocomplete).attr('data-typed')); 99 | } 100 | $(objAutocomplete).attr('aria-activedescendant', ''); 101 | $('#suggestions').find('li').removeClass('focused'); 102 | $('#suggestions').hide(); 103 | bDisplay = false; 104 | if ($(objEvent.target).prop('tagName') === 'LI') { 105 | $(objAutocomplete).focus(); 106 | } 107 | objEvent.preventDefault(); 108 | break; 109 | case 38: //UP 110 | if ($('#suggestions').css('display') === 'block') { 111 | if ($(objAutocomplete).attr('data-typed').length > 0) { 112 | iVisible = displayMenu(objAutocomplete, false); 113 | } 114 | else { 115 | iVisible = displayMenu(objAutocomplete, true); 116 | } 117 | if (iVisible > 0 ) { 118 | if (!$(objAutocomplete).attr('aria-activedescendant')) { 119 | objNext = $('#suggestions').find('li:visible:last'); 120 | } 121 | else { 122 | iIndex = parseInt($('#suggestions').find('li:visible').index(objCurrent), 10) - 1; 123 | if (iIndex >= 0) { 124 | objNext = $('#suggestions').find('li:visible').get(iIndex); 125 | } 126 | else { 127 | objNext = $('#suggestions').find('li:visible:last'); 128 | } 129 | } 130 | selectItem(objAutocomplete, objCurrent, objNext); 131 | } 132 | } 133 | 134 | objEvent.preventDefault(); 135 | break; 136 | case 40: //DOWN 137 | if ($(objAutocomplete).attr('data-typed').length > 0) { 138 | iVisible = displayMenu(objAutocomplete, false); 139 | } 140 | else { 141 | iVisible = displayMenu(objAutocomplete, true); 142 | } 143 | if (iVisible > 0 ) { 144 | if (!$(objAutocomplete).attr('aria-activedescendant')) { 145 | objNext = $('#suggestions').find('li:visible')[0]; 146 | } 147 | else { 148 | iIndex = parseInt($('#suggestions').find('li:visible').index(objCurrent), 10) + 1; 149 | if (iIndex < $('#suggestions').find('li:visible').length) { 150 | objNext = $('#suggestions').find('li:visible').get(iIndex); 151 | } 152 | else { 153 | objNext = $('#suggestions').find('li:visible')[0]; 154 | } 155 | } 156 | selectItem(objAutocomplete, objCurrent, objNext); 157 | } 158 | 159 | objEvent.preventDefault(); 160 | break; 161 | default: 162 | if ($(objEvent.target).prop('tagName') === 'LI') { 163 | $(objAutocomplete).focus(); 164 | } 165 | $(objAutocomplete).attr('data-typed', $(objAutocomplete).val()); 166 | iVisible = displayMenu(objAutocomplete, false); 167 | if (iVisible > 0) { 168 | $('#feedback').html(iVisible + ' results are available'); 169 | } 170 | else { 171 | $('#feedback').html('No search results'); 172 | } 173 | setTimeout(function(){$('#feedback').html('');}, 1000); 174 | } 175 | 176 | if (bDisplay && iVisible) { 177 | $('#suggestions').show(); 178 | $(objAutocomplete).attr('aria-expanded', 'true'); 179 | } 180 | else { 181 | $('#suggestions').hide(); 182 | $(objAutocomplete).attr('aria-expanded', 'false'); 183 | } 184 | 185 | return false; 186 | } 187 | 188 | function loadMenu(arLanguages) { 189 | var objList = $(''); 190 | var iPos = $('#ac').offset(); 191 | var iWidth = $('#ac').width(); 192 | 193 | $.each(arLanguages, function(iKey, strValue) { 194 | objList.append('
  • ' + strValue + '
  • '); 195 | }); 196 | 197 | $(objList).insertAfter($('#ac').next()); 198 | $(objList).click(function(event){select(this, event);}); 199 | $(objList).css({'left': iPos.left + 2, 'top': iPos.top + 6, 'width': iWidth}); 200 | $(objList).keydown(function(event){suggest($('#ac'), event);}); 201 | } 202 | 203 | $(document).ready(function() { 204 | var objAutocomplete = $('#ac'); 205 | var arLanguages = [ 206 | 'ActionScript', 207 | 'AppleScript', 208 | 'Asp', 209 | 'BASIC', 210 | 'C', 211 | 'C++', 212 | 'Clojure', 213 | 'COBOL', 214 | 'ColdFusion', 215 | 'Erlang', 216 | 'Fortran', 217 | 'Groovy', 218 | 'Haskell', 219 | 'Java', 220 | 'JavaScript', 221 | 'Lisp', 222 | 'Perl', 223 | 'PHP', 224 | 'Python', 225 | 'Ruby', 226 | 'Scala', 227 | 'Scheme']; 228 | 229 | objAutocomplete.attr('role', 'combobox') 230 | .attr('aria-autocomplete', 'list') 231 | .attr('aria-expanded', 'false') 232 | .attr('aria-owns', 'suggestions') 233 | .attr('aria-labelledby', $(objAutocomplete).prev().attr('id')) 234 | .attr('data-typed', '') 235 | .attr('data-selected', '') 236 | .parent().append(''); 237 | $('body').click(function(event){showMenu($('#ac'), event);}); 238 | $('body').append('
    '); 239 | objAutocomplete.keyup(function(event){suggest(this, event);}); 240 | loadMenu(arLanguages); 241 | }); -------------------------------------------------------------------------------- /css/basic.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font: normal 95% Helvetica, Verdana, Arial, sans-serif; 3 | color: #000; 4 | padding: 0; 5 | margin: 0; 6 | } 7 | 8 | body { 9 | padding: 0.5em 0.7em; 10 | margin: 0 4em 0 1em; 11 | max-width: 55em; 12 | background: #fff; 13 | } 14 | 15 | h1, h2, h3 { 16 | margin-bottom: 0; 17 | background: transparent; 18 | } 19 | 20 | h1 { 21 | padding: 0.2em; 22 | } 23 | 24 | a { 25 | color: #009; 26 | background: transparent; 27 | } 28 | 29 | a:visited { 30 | color:#360; 31 | background:transparent; 32 | } 33 | 34 | a:hover, a:focus, a:active { 35 | color:#000; 36 | background-color: #fc0; 37 | } 38 | 39 | img { 40 | border: none; 41 | } 42 | 43 | code { 44 | font: bold "Courier new", Courier, Monospace; 45 | } 46 | 47 | mark { 48 | color: #fff; 49 | background-color: #820bbb; 50 | } 51 | 52 | .example { 53 | margin-top: 1em; 54 | margin-bottom: 2em; 55 | } 56 | -------------------------------------------------------------------------------- /disclosure1/css/disclosure.css: -------------------------------------------------------------------------------- 1 | /* button */ 2 | 3 | button { 4 | display: block; 5 | width: 8em; 6 | font-size: 1.1em; 7 | font-weight: bold; 8 | text-align: left; 9 | padding: 0.5em 1em; 10 | color: #ccc; 11 | background-color: #555; 12 | border: 0 none; 13 | border-radius: 3px; 14 | text-shadow: 0 -1px 0 #000; 15 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.6); 16 | } 17 | 18 | button:hover, button:focus { 19 | background-color: #820bbb; 20 | color: #fff; 21 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 22 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 23 | cursor: pointer; 24 | } 25 | 26 | button[aria-expanded="false"] #icon:after { 27 | font-size: 0.75em; 28 | padding-left: 0.5em; 29 | content: "\25BC"; 30 | } 31 | 32 | button[aria-expanded="true"] #icon:after { 33 | font-size: 0.75em; 34 | padding-left: 0.5em; 35 | content: "\25B2"; 36 | } 37 | 38 | /* content panel */ 39 | 40 | div[hidden="true"] { 41 | display: none; 42 | } 43 | 44 | div[id="content"] { 45 | border: 1px #000 solid; 46 | padding: 1em; 47 | background: #555; 48 | color: #FFF; 49 | } 50 | -------------------------------------------------------------------------------- /disclosure1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Disclosure button design pattern: <button> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Disclosure button design pattern: <button> element

    16 | 17 |

    This disclosure button is created using the <button> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    Example

    20 |
    21 | 22 |
    23 |

    Makes me happy!

    24 |
    25 |
    26 | 27 |

    Notes

    28 | 29 |

    Using the <button> element means the browser handles keyboard interaction automatically. The button can be focused on with the keyboard, and it can be activated using either the space or enter keys.

    30 | 31 |

    The aria-expanded attribute is used to indicate the state of the disclosure widget. It is applied to the <button> so that assistive technologies can determine the state of the widget whether it is expanded or collapsed. The aria-expanded attribute is set to false (collapsed) initially, then scripted to toggle between true and false whenever the button is activated.

    32 | 33 |

    The aria-controls attribute is used to create a relationship between the <button> and the <div> that contains the content. It takes the idref of the <div> as its value. This enables assistive technologies to provide specific commands for moving focus from the <button> to the <div>.

    34 | 35 |

    The hidden attribute indicates the state of the <div> containing the content. It is used to hide the content both visually and from assistive technologies. The hidden attribute is added initially, then scripted to be added or removed whenever the button is activated.

    36 | 37 |

    The visible style of the <button> when pressed is hooked to the aria-expanded attribute. Similarly the visibility of the <div> is hooked to the hidden attribute.

    38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /disclosure1/js/disclosure.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | var icon = document.getElementById('icon'); 6 | var content = document.getElementById('content'); 7 | 8 | button.setAttribute('aria-expanded', 'false'); 9 | icon.setAttribute('aria-hidden', 'true'); 10 | content.setAttribute('hidden', 'true'); 11 | 12 | function disclose(event) { 13 | if (content.hasAttribute('hidden')) { 14 | button.setAttribute('aria-expanded', 'true'); 15 | button.setAttribute('aria-controls', 'content'); 16 | content.removeAttribute('hidden'); 17 | } 18 | else { 19 | button.setAttribute('aria-expanded', 'false'); 20 | content.setAttribute('hidden', 'true'); 21 | button.removeAttribute('aria-controls'); 22 | } 23 | } 24 | 25 | button.addEventListener('click', disclose, false); 26 | 27 | })(); 28 | -------------------------------------------------------------------------------- /disclosure2/css/disclosure.css: -------------------------------------------------------------------------------- 1 | /* button */ 2 | 3 | [role="button"],[role="button"]:visited { 4 | display: block; 5 | height: 15px; 6 | width: 80px; 7 | font-size: 1.1em; 8 | font-weight: bold; 9 | padding: 10px 15px; 10 | color: #ccc; 11 | background-color: #555; 12 | border: 0 none; 13 | border-radius: 3px; 14 | text-decoration: none; 15 | text-shadow: 0 -1px 0 #000; 16 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 17 | } 18 | 19 | [role="button"]:hover, [role="button"]:focus { 20 | background-color: #820bbb; 21 | color: #fff; 22 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 23 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 24 | cursor: pointer; 25 | } 26 | 27 | [role="button"][aria-expanded="false"] #icon:after { 28 | font-size: 0.75em; 29 | padding-left: 0.5em; 30 | content: "\25BC"; 31 | } 32 | 33 | [role="button"][aria-expanded="true"] #icon:after { 34 | font-size: 0.75em; 35 | padding-left: 0.5em;content: "\25B2"; 36 | } 37 | 38 | /* content panel */ 39 | 40 | div[id="content"] { 41 | // display: block; 42 | border: 1px #000 solid; 43 | padding: 1em; 44 | background: #555; 45 | color: #FFF; 46 | } 47 | -------------------------------------------------------------------------------- /disclosure2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Disclosure button design pattern: <a> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Disclosure button design pattern: <a> element

    16 | 17 |

    This disclosure button is created using the <a> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    It is better to use the <button> element to create a disclosure button because the browser will automatically provide keyboard support and most semantic information.

    20 | 21 |

    Example

    22 | 23 |
    24 | Tequila 25 |
    26 |

    Makes me happy!

    27 |
    28 |
    29 | 30 |

    Notes

    31 | 32 |

    Uses the HTML tabindex attribute with a value of 0. This places the <span> into the tab order based on its location in the source code. The visual appearance of the button changes when focus moves to it.

    33 | 34 |

    Uses the ARIA role attribute with a value of button. This instructs the browser to expose the <span> element as a button through its accessibility API. This information is used by assistive technologies to inform users what they're dealing with.

    35 | 36 |

    Keyboard interaction for the button control is handled through scripting. Both enter and space key events are captured, and used to trigger the button's expected behaviour.

    37 | 38 |

    The aria-expanded attribute is used to indicate the state of the disclosure widget. It is applied to the <span> so that assistive technologies can determine the state of the widget whether it is expanded or collapsed. The aria-expanded attribute is set to false (collapsed) initially, then scripted to toggle between true and false whenever the button is activated.

    39 | 40 |

    The aria-controls attribute is used to create a relationship between the <span> and the <div> that contains the content. It takes the idref of the <div> as its value. This enables assistive technologies to provide specific commands for moving focus from the <span> to the <div>.

    41 | 42 |

    The hidden attribute indicates the state of the <div> containing the content. It is used to hide the content both visually and from assistive technologies. The hidden attribute is added initially, then scripted to be added or removed whenever the button is activated.

    43 | 44 |

    The visible style of the <button> when pressed is hooked to the aria-expanded attribute. Similarly the visibility of the <div> is hooked to the aria-hidden attribute using CSS selectors. This has no direct impact on accessibility, but it creates a cleaner separation between structure and design.

    45 | 46 |

    Note: There is an issue with Firefox, where content with aria-hidden is exposed to assistive technologies when inside a <button> or an element with role="button" applied. Refer to bug 1147359 for more information.

    47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /disclosure2/js/disclosure.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | var icon = document.getElementById('icon'); 6 | var content = document.getElementById('content'); 7 | 8 | button.setAttribute('role', 'button'); 9 | button.setAttribute('aria-expanded', 'false'); 10 | icon.setAttribute('aria-hidden', 'true'); 11 | content.setAttribute('hidden', 'true'); 12 | 13 | function disclose(event) { 14 | if (content.hasAttribute('hidden')) { 15 | button.setAttribute('aria-expanded', 'true'); 16 | button.setAttribute('aria-controls', 'content'); 17 | content.removeAttribute('hidden'); 18 | } 19 | else { 20 | button.setAttribute('aria-expanded', 'false'); 21 | content.setAttribute('hidden', 'true'); 22 | button.removeAttribute('aria-controls'); 23 | } 24 | } 25 | 26 | button.addEventListener('click', disclose, false); 27 | 28 | button.addEventListener('keydown', function(event) { 29 | if (event.keyCode ==32) { 30 | disclose(); 31 | } 32 | }); 33 | 34 | })(); 35 | -------------------------------------------------------------------------------- /disclosure3/css/disclosure.css: -------------------------------------------------------------------------------- 1 | /* button */ 2 | 3 | [role="button"] { 4 | display: block; 5 | height: 15px; 6 | width: 80px; 7 | font-size: 1.1em; 8 | font-weight: bold; 9 | padding: 10px 15px; 10 | color: #ccc; 11 | background-color: #555; 12 | border: 0 none; 13 | border-radius: 3px; 14 | text-shadow: 0 -1px 0 #000; 15 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 16 | } 17 | 18 | [role="button"]:hover, [role="button"]:focus { 19 | background-color: #820bbb; 20 | color: #fff; 21 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 22 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 23 | cursor: pointer; 24 | } 25 | 26 | [role="button"][aria-expanded="false"] #icon:after { 27 | font-size: 0.75em; 28 | padding-left: 0.5em; 29 | content: "\25BC"; 30 | } 31 | 32 | [role="button"][aria-expanded="true"] #icon:after { 33 | font-size: 0.75em; 34 | padding-left: 0.5em;content: "\25B2"; 35 | } 36 | 37 | /* content panel */ 38 | 39 | div[id="content"] { 40 | border: 1px #000 solid; 41 | padding: 1em; 42 | background: #555; 43 | color: #FFF; 44 | } 45 | -------------------------------------------------------------------------------- /disclosure3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Disclosure button design pattern: <span> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Disclosure button design pattern: <span> element

    16 | 17 |

    This disclosure button is created using the <span> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    It is better to use the <button> element to create a disclosure button because the browser will automatically provide keyboard support and most semantic information.

    20 | 21 |

    Example

    22 | 23 |
    24 | Tequila 25 |
    26 |

    Makes me happy!

    27 |
    28 |
    29 | 30 |

    Notes

    31 | 32 |

    Uses the HTML tabindex attribute with a value of 0. This places the <span> into the tab order based on its location in the source code. The visual appearance of the button changes when focus moves to it.

    33 | 34 |

    Uses the ARIA role attribute with a value of button. This instructs the browser to expose the <span> element as a button through its accessibility API. This information is used by assistive technologies to inform users what they're dealing with.

    35 | 36 |

    Keyboard interaction for the button control is handled through scripting. Both enter and space key events are captured, and used to trigger the button's expected behaviour.

    37 | 38 |

    The aria-expanded attribute is used to indicate the state of the disclosure widget. It is applied to the <span> so that assistive technologies can determine the state of the widget whether it is expanded or collapsed. The aria-expanded attribute is set to false (collapsed) initially, then scripted to toggle between true and false whenever the button is activated.

    39 | 40 |

    The aria-controls attribute is used to create a relationship between the <span> and the <div> that contains the content. It takes the idref of the <div> as its value. This enables assistive technologies to provide specific commands for moving focus from the <span> to the <div>.

    41 | 42 |

    The hidden attribute indicates the state of the <div> containing the content. It is used to hide the content both visually and from assistive technologies. The hidden attribute is added initially, then scripted to be added or removed whenever the button is activated.

    43 | 44 |

    The visible style of the <button> when pressed is hooked to the aria-expanded attribute. Similarly the visibility of the <div> is hooked to the aria-hidden attribute using CSS selectors. This has no direct impact on accessibility, but it creates a cleaner separation between structure and design.

    45 | 46 |

    Note: There is an issue with Firefox, where content with aria-hidden is exposed to assistive technologies when inside a <button> or an element with role="button" applied. Refer to bug 1147359 for more information.

    47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /disclosure3/js/disclosure.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | var icon = document.getElementById('icon'); 6 | var content = document.getElementById('content'); 7 | 8 | button.setAttribute('role', 'button'); 9 | button.setAttribute('tabindex', 0); 10 | button.setAttribute('aria-expanded', 'false'); 11 | icon.setAttribute('aria-hidden', 'true'); 12 | content.setAttribute('hidden', 'true'); 13 | 14 | function disclose(event) { 15 | if (content.hasAttribute('hidden')) { 16 | button.setAttribute('aria-expanded', 'true'); 17 | button.setAttribute('aria-controls', 'content'); 18 | content.removeAttribute('hidden'); 19 | } 20 | else { 21 | button.setAttribute('aria-expanded', 'false'); 22 | content.setAttribute('hidden', 'true'); 23 | button.removeAttribute('aria-controls'); 24 | } 25 | } 26 | 27 | button.addEventListener('click', disclose, false); 28 | 29 | button.addEventListener('keydown', function(event) { 30 | if (event.keyCode == 13 || event.keyCode ==32) { 31 | disclose(); 32 | } 33 | }); 34 | 35 | })(); 36 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Accessible design patterns 9 | 10 | 11 | 12 | 13 | 14 |

    Accessible design patterns

    15 | 16 |

    Examples of common custom control and widget design patterns.

    17 | 18 |

    Toggle button design patterns

    19 | 24 | 25 |

    Disclosure button design patterns

    26 | 31 | 32 |

    ToggleTip button design patterns

    33 | 37 | 38 |

    Link design patterns

    39 | 42 | 43 |

    Tabpanel design patterns

    44 | 48 | 49 |

    Live region design patterns

    50 | 53 | 54 |

    Autocomplete design patterns

    55 | 58 | 59 |

    ARIA design patterns

    60 | 66 | *** 67 | 68 | -------------------------------------------------------------------------------- /link1/css/link.css: -------------------------------------------------------------------------------- 1 | span { 2 | color: #009; 3 | background: transparent; 4 | text-decoration: underline; 5 | } 6 | 7 | span:hover, span:focus, span:active { 8 | color:#000; 9 | background-color: #fc0; 10 | cursor: pointer; 11 | } 12 | -------------------------------------------------------------------------------- /link1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Link design pattern 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Link design pattern

    16 | 17 |

    This link is created using the <span> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    It is better to use the <a> element because the browser will automatically provide complete functionality (including context menu, URL to status bar, and visited state information), as well as keyboard support and full semantics.

    20 | 21 |

    Example

    22 | 23 |
    24 | Tink UK 25 |
    26 | 27 |

    Notes

    28 | 29 |

    The tabindex attribute places the span element into the tab order, based on the element's location within the DOM.

    30 | 31 |

    The link changes appearance with hover and focus.

    32 | 33 |

    The link role exposes the span as a link in the accessibility tree.

    34 | 35 |

    The click and keydown event listeners capture the expected mouse and keyboard events for activating the link.

    36 | 37 | 38 | -------------------------------------------------------------------------------- /link1/js/link.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function makeLinks () { 5 | 6 | if (typeof NodeList.prototype.forEach === 'undefined') { 7 | NodeList.prototype.forEach = Array.prototype.forEach; 8 | } 9 | 10 | var links = document.querySelectorAll('[data-link]'); 11 | 12 | var fetchResource = function (e) { 13 | 14 | if (e.type === "keydown" && e.keyCode !== 13) { 15 | return false; 16 | } 17 | 18 | e.preventDefault(); 19 | 20 | window.location.href = e.target.dataset.link; 21 | }; 22 | 23 | links.forEach(function (link) { 24 | link.tabIndex = '0'; 25 | link.setAttribute('role', 'link'); 26 | link.addEventListener("click", fetchResource); 27 | link.addEventListener("keydown", fetchResource); 28 | }); 29 | } 30 | 31 | document.addEventListener('DOMContentLoaded', function () { 32 | makeLinks(); 33 | }); 34 | })(); 35 | -------------------------------------------------------------------------------- /live1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Live region design pattern 9 | 10 | 11 | 12 | 13 | 14 |

    Live region design pattern

    15 | 16 |

    This live region is created using the <button> and <div> elements, with scripting to provide the expected behaviour. Additional semantic information is provided with ARIA.

    17 | 18 |

    Example

    19 | 20 |
    21 | 22 |

    Basket summary

    23 | 24 |
    25 |

    Your basket contains 0 items.

    26 |
    27 | 28 |
    29 | 30 |
    31 | 32 |
    33 | 34 |

    Notes

    35 | 36 |

    The aria-live attribute has been used to convert the <div> into a live region. The assertive value means that screen readers will interupt their current activity to notify the user that the content of the live region has changed.

    37 | 38 |

    The aria-atomic attribute has been used to define how much of the live region content should be announced. The value of true means that all the content in the live region will be announced, even when only part of the content has been updated.

    39 | 40 |

    Notes on screen reader support for live regions are available.

    41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /live1/js/liveregion.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | document.getElementById("button").addEventListener("click", updateItems); 5 | 6 | var items = 0; 7 | 8 | function updateItems (e) { 9 | items = items + 1; 10 | document.getElementById("quantity").innerHTML=items; 11 | } 12 | })(); -------------------------------------------------------------------------------- /live1/js/liveregion2.js: -------------------------------------------------------------------------------- 1 | var items = 0; 2 | 3 | function init() 4 | { 5 | document.getElementById("button").addEventListener("click", updateItems); 6 | } 7 | 8 | function updateItems(event) 9 | { 10 | items = items + 1; 11 | document.getElementById("quantity").innerHTML=items; 12 | } 13 | 14 | window.onload = init; -------------------------------------------------------------------------------- /live2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Live region design pattern 9 | 10 | 11 | 12 | 13 | 14 |

    Live region design pattern

    15 | 16 |

    This live region is created using the <button> and <div> elements, with scripting to provide the expected behaviour. Additional semantic information is provided with ARIA.

    17 | 18 |

    Example

    19 | 20 |
    21 | 22 |

    Live update

    23 | 24 |
    25 | 26 |
    27 | 28 |
    29 | 30 |
    31 | 32 |
    33 | 34 |

    Notes

    35 | 36 |

    The aria-live attribute has been used to convert the <div> into a live region. The assertive value means that screen readers will interupt their current activity to notify the user that the content of the live region has changed.

    37 | 38 |

    The aria-atomic attribute has been used to define how much of the live region content should be announced. The value of true means that all the content in the live region will be announced, even when only part of the content has been updated.

    39 | 40 |

    Notes on screen reader support for live regions are available.

    41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /live2/js/liveregion.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | document.getElementById("button").addEventListener("click", updateLiveRegion); 5 | 6 | function updateLiveRegion (e) { 7 | var update = document.getElementById("update"); 8 | update.innerHTML = ""; 9 | setTimeout(function(){ update.innerHTML = "Successfully updated!";}, 100); 10 | } 11 | } )(); -------------------------------------------------------------------------------- /live2/js/liveregion2.js: -------------------------------------------------------------------------------- 1 | var items = 0; 2 | 3 | function init() 4 | { 5 | document.getElementById("button").addEventListener("click", updateItems); 6 | } 7 | 8 | function updateItems(event) 9 | { 10 | items = items + 1; 11 | document.getElementById("quantity").innerHTML=items; 12 | } 13 | 14 | window.onload = init; -------------------------------------------------------------------------------- /svg-flowchart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SVG flowchart design pattern 5 | 6 | 7 | 8 |

    SVG flowchart design pattern

    9 | 10 |

    The flowchart from the W3C Process 2018, as described in Accessible SVG flowcharts.

    11 | 12 |
    13 | 14 | Basic W3C Recommendation Track 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | First Public Working Draft (FPWD) - Exclusion opportunity 25 | 26 | 27 | 28 | 29 | WG decision 30 | Director's approval 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Working Draft (WD) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Publish a new Working Draft 57 | 58 | 59 | WG Decision: review needed, or 60 | No change for 6 months 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Advance to Candidate Recommendation 74 | 75 | 76 | Director's approval 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Candidate Recommendation (CR) - Patent Policy exclusion opportunity 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Publish revised Candidate Recommendation 102 | 103 | 104 | Working Group Decision, 105 | Directors approval 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Advance to Proposed Recommendation 119 | 120 | 121 | Director's approval 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | Return to Working Draft 132 | 133 | 134 | WG or Director decision 135 | e.g. for further review 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Proposed Recommendation (PR) - Advisory Committee review 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | Advance to Recommendation 163 | 164 | 165 | Advisory Committee Review 166 | Director's Decision 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | Return to Candidate Recommendation 179 | 180 | 181 | AC Review, 182 | Director Decision 183 | e.g. for minor changes 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | Return to Working Draft 196 | 197 | 198 | 199 | Advisory Committee review and Director's Decision, e.g. for further work and review 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | Recommendation (Rec) 213 | 214 | 215 | 216 | 217 | 218 | 219 |
    220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /svg-line-graph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SVG line graph design pattern 5 | 6 | 24 | 25 | 26 | 27 |

    SVG line graph design pattern

    28 | 29 |

    Demo from this accessible SVG line graph post.

    30 | 31 | 32 | WebAIM screen reader survey: results 2009 to 2015 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 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 99 | 100 | 101 | 102 | 103 | Time 104 | Jan 2009 105 | Dec 2010 106 | May 2012 107 | Jan 2014 108 | Jul 2015 109 | 110 | 111 | 112 | 113 | 114 | 115 | Jaws 116 | 117 | 118 | 119 | 120 | 121 | 74% 122 | 123 | 124 | 125 | 126 | 127 | 69.6% 128 | 129 | 130 | 131 | 132 | 133 | 63.7% 134 | 135 | 136 | 137 | 138 | 139 | 63.9% 140 | 141 | 142 | 143 | 144 | 145 | 43.7% 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | NVDA 154 | 155 | 156 | 157 | 158 | 159 | 8% 160 | 161 | 162 | 163 | 164 | 165 | 34.8% 166 | 167 | 168 | 169 | 170 | 171 | 43% 172 | 173 | 174 | 175 | 176 | 177 | 51.2% 178 | 179 | 180 | 181 | 182 | 183 | 41.4% 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | VoiceOver 192 | 193 | 194 | 195 | 196 | 197 | 6% 198 | 199 | 200 | 201 | 202 | 203 | 20.2% 204 | 205 | 206 | 207 | 208 | 209 | 30.7% 210 | 211 | 212 | 213 | 214 | 215 | 36.8% 216 | 217 | 218 | 219 | 220 | 221 | 30.9% 222 | 223 | 224 | 225 | 226 | 227 | Desktop screen reader use: 2009 to 2015 228 | 229 | 252 | 253 | 254 | 255 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /svg-line-graph/original.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SVG line graph 5 | 6 | 24 | 25 | 26 | 27 |

    SVG graph

    28 | 29 |

    An accessible version is available.

    30 | 31 | 32 | WebAIM screen reader survey: results 2009 to 2015 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 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Percentage usage 98 | 99 | 100 | 101 | Time 102 | Jan 2009 103 | Dec 2010 104 | May 2012 105 | Jan 2014 106 | Jul 2015 107 | 108 | 109 | 110 | Jaws 111 | 112 | 113 | 114 | 74% 115 | 116 | 117 | 118 | 69.6% 119 | 120 | 121 | 122 | 63.7% 123 | 124 | 125 | 126 | 63.9% 127 | 128 | 129 | 130 | 43.7% 131 | 132 | 133 | 134 | 135 | NVDA 136 | 137 | 138 | 139 | 8% 140 | 141 | 142 | 143 | 34.8% 144 | 145 | 146 | 147 | 43% 148 | 149 | 150 | 151 | 51.2% 152 | 153 | 154 | 155 | 41.4% 156 | 157 | 158 | 159 | 160 | VoiceOver 161 | 162 | 163 | 164 | 6% 165 | 166 | 167 | 168 | 20.2% 169 | 170 | 171 | 172 | 30.7% 173 | 174 | 175 | 176 | 36.8% 177 | 178 | 179 | 180 | 30.9% 181 | 182 | 183 | 184 | 185 | 186 | Desktop screen reader use: 2009 to 2015 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | Jaws 196 | 197 | 198 | 199 | 200 | 201 | NVDA 202 | 203 | 204 | 205 | 206 | 207 | VoiceOver 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /tabpanels1/css/tabpanels.css: -------------------------------------------------------------------------------- 1 | .tab { 2 | // placeholder 3 | } 4 | 5 | .selected { 6 | // placeholder 7 | } 8 | 9 | ul#tabs li { 10 | display: inline; 11 | } 12 | /* 13 | ul#tabs li:hover { 14 | background-color: red; 15 | } 16 | */ 17 | 18 | ul#tabs li a.selected { 19 | background-color: #ddd; 20 | } 21 | 22 | ul#tabs li a { 23 | text-decoration: none; 24 | color: #000; 25 | border-top: solid black thin; 26 | border-left: solid black thin; 27 | border-right: solid black thin; 28 | padding: 1em 1em 0 1em; 29 | font-weight: 700; 30 | } 31 | 32 | ul#tabs li a:hover, ul#tabs li a:focus { 33 | background-color: blue; 34 | color: #fff; 35 | outline: 0; 36 | } 37 | 38 | ul#tabs { 39 | margin: 0; 40 | padding: 0; 41 | } 42 | 43 | .tabcontent { 44 | display: block; 45 | border: solid black thin; 46 | padding: 1em; 47 | } 48 | 49 | .hidden { 50 | display: none; 51 | } 52 | -------------------------------------------------------------------------------- /tabpanels1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Tabpanels with ARIA design pattern 9 | 10 | 11 | 12 | 13 | 66 | 67 | 68 | 69 |

    Tabpanels with ARIA design pattern

    70 | 71 |

    These tabpanels are created using the <ul>, <li>, and <div> elements, with scripting to provide the expected behaviour. Additional semantic information is provided with ARIA.

    72 | 73 |

    Example

    74 |
    75 | 76 | 81 | 82 |
    83 |

    Blanco tequila is...

    84 |
    85 | 86 | 89 | 90 | 93 | 94 |
    95 | 96 |

    Notes

    97 | 98 |

    The tablist, tab, and tabpanel roles expose the <ul>, <a>, and <div> elements as a collection of tabs and corresponding tabpanels in the accessibility tree.

    99 | 100 |

    The presentation role prevents the <li> elements from being mapped in the accessibility tree.

    101 | 102 |

    The aria-selected attribute indicates which tab is currently selected.

    103 | 104 |

    The tabindex attribute ensures that only the currently selected tab is included in the tab order.

    105 | 106 |

    The hidden attribute hides all but the currently displayed tabpanel from view.

    107 | 108 |

    The aria-controls attribute establishes a relationship between each tab and its corresponding tabpanel.

    109 | 110 |

    The aria-labelledby attribute reuses the name of the tab as the accessible name of the corresponding tabpanel.

    111 | 112 |

    The click and keydown event listeners capture the expected mouse and keyboard events for activating the tabs.

    113 | 114 | 115 | -------------------------------------------------------------------------------- /tabpanels2/css/tabpanels.css: -------------------------------------------------------------------------------- 1 | .tab { 2 | // placeholder 3 | } 4 | 5 | .selected { 6 | // placeholder 7 | } 8 | 9 | ul#tabs li { 10 | display: inline; 11 | } 12 | /* 13 | ul#tabs li:hover { 14 | background-color: red; 15 | } 16 | */ 17 | 18 | ul#tabs li a.selected { 19 | background-color: #ddd; 20 | } 21 | 22 | ul#tabs li a { 23 | text-decoration: none; 24 | color: #000; 25 | border-top: solid black thin; 26 | border-left: solid black thin; 27 | border-right: solid black thin; 28 | padding: 1em 1em 0 1em; 29 | font-weight: 700; 30 | } 31 | 32 | ul#tabs li a:hover, ul#tabs li a:focus { 33 | background-color: blue; 34 | color: #fff; 35 | outline: 0; 36 | } 37 | 38 | ul#tabs { 39 | margin: 0; 40 | padding: 0; 41 | } 42 | 43 | .tabcontent { 44 | display: block; 45 | border: solid black thin; 46 | padding: 1em; 47 | } 48 | 49 | .hidden { 50 | display: none; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tabpanels2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Tabpanels without ARIA design pattern 9 | 10 | 11 | 12 | 13 | 64 | 65 | 66 | 67 |

    Tabpanels without ARIA design pattern

    68 | 69 |

    These tabpanels are created using the <ul>, <li>, and <div> elements, with scripting to provide the expected behaviour. No additional semantic information has been provided using ARIA.

    70 | 71 |

    Example

    72 | 73 |
    74 | 75 | 80 | 81 |
    82 |

    Blanco tequila is...

    83 |
    84 | 85 | 88 | 89 | 92 | 93 |
    94 | 95 |

    Notes

    96 | 97 |

    Despite looking like a set of tabs, screen readers treat the underlying structure as a list of "same page" anchor links, followed by a chunk of unassociated content.

    98 | 99 | 100 | -------------------------------------------------------------------------------- /toggle1/css/button.css: -------------------------------------------------------------------------------- 1 | button { 2 | display: block; 3 | height: 35px; 4 | width: 100px; 5 | font-size: 1.1em; 6 | font-weight: bold; 7 | padding: 10px 15px; 8 | margin: 20px auto; 9 | color: #ccc; 10 | background-color: #555; 11 | border: 0 none; 12 | border-radius: 3px; 13 | text-shadow: 0 -1px 0 #000; 14 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 15 | } 16 | 17 | button:hover, button:focus { 18 | background-color: #820bbb; 19 | } 20 | 21 | [aria-pressed="true"] { 22 | color: #fff; 23 | padding: 11px 12px 9px 16px; 24 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 25 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 26 | } 27 | -------------------------------------------------------------------------------- /toggle1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Toggle button design pattern: <button> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Toggle button design pattern: <button> element

    16 | 17 |

    This toggle button is created using the <button> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    Example

    20 | 21 |
    22 | 23 |
    24 | 25 |

    Notes

    26 | 27 |

    Keyboard focus and interaction is handled automatically by the browser, and the button can be activated using either the enter or space key.

    28 | 29 |

    The aria-pressed attribute is used to indicate the state of the button. It is set to false initially, then scripted to toggle between true/false whenever the button is activated.

    30 | 31 |

    The visual appearance of the button when pressed (or not) is hooked onto the aria-pressed attribute using an intelligent CSS selector, instead of a class name. Although this does not affect accessibility, it creates a cleaner separation between structure and design.

    32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /toggle1/js/button.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | button.setAttribute('aria-pressed', 'false'); 6 | 7 | function toggle(event) { 8 | if (button.getAttribute('aria-pressed') == 'false') { 9 | button.setAttribute('aria-pressed', 'true'); 10 | } 11 | else { 12 | button.setAttribute('aria-pressed', 'false'); 13 | } 14 | } 15 | 16 | button.addEventListener('click', toggle, false); 17 | 18 | })(); 19 | 20 | -------------------------------------------------------------------------------- /toggle2/css/button.css: -------------------------------------------------------------------------------- 1 | a[id="button"],a[id="button"]:visited { 2 | display: block; 3 | height: 15px; 4 | width: 65px; 5 | font-size: 1.1em; 6 | font-weight: bold; 7 | padding: 10px 15px; 8 | margin: 20px auto; 9 | color: #ccc; 10 | background-color: #555; 11 | border: 0 none; 12 | border-radius: 3px; 13 | text-decoration: none; 14 | text-shadow: 0 -1px 0 #000; 15 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 16 | } 17 | 18 | a[id="button"]:hover, a[id="button"]:focus { 19 | background-color: #820bbb; 20 | } 21 | 22 | a[aria-pressed="true"],a[aria-pressed="true"]:visited { 23 | color: #fff; 24 | padding: 11px 12px 9px 16px; 25 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 26 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 27 | } 28 | -------------------------------------------------------------------------------- /toggle2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Toggle button design pattern: <a> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Toggle button design pattern: <a> element

    16 | 17 |

    This toggle button is created using the <a> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    It is better to use the <button> element to create a toggle button because the browser automatically provides keyboard support and most semantic information.

    20 | 21 |

    Example

    22 | 23 |
    24 | Tequila! 25 |
    26 | 27 |

    Notes

    28 | 29 |

    Uses the ARIA role attribute with a value of button. This informs the browser that it should expose the <a> element as a button through its accessibility API. This information is utilised by assistive technologies to inform users what they're dealing with.

    30 | 31 |

    Keyboard focus is handled by the browser because <a> is an interactive element.

    32 | 33 |

    Keyboard interaction is handled by the browser and through scripting. The <a> element can be activated with the Enter key and the button inherits this behaviour. The ability to activate the button using the space key is provided through scripting.

    34 | 35 |

    The aria-pressed attribute is used to indicate the state of the button. It is set to false initially, then scripted to toggle between true/false whenever the button is activated.

    36 | 37 |

    The visual appearance of the button when pressed (or not) is hooked onto the aria-pressed attribute using an intelligent CSS selector, instead of a class name. Although this does not affect accessibility, it creates a cleaner separation between structure and design.

    38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /toggle2/js/button.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | button.setAttribute('role', 'button'); 6 | button.setAttribute('tabindex', 0); 7 | button.setAttribute('aria-pressed', 'false'); 8 | 9 | function toggle(event) { 10 | if (button.getAttribute('aria-pressed') == 'false') { 11 | button.setAttribute('aria-pressed', 'true'); 12 | } 13 | else { 14 | button.setAttribute('aria-pressed', 'false'); 15 | } 16 | } 17 | 18 | button.addEventListener('click', toggle, false); 19 | 20 | button.addEventListener('keydown', function(event) { 21 | if (event.keyCode ==13) { 22 | toggle(); 23 | } 24 | }); 25 | 26 | })(); 27 | 28 | -------------------------------------------------------------------------------- /toggle3/css/button.css: -------------------------------------------------------------------------------- 1 | span[id="button"] { 2 | display: block; 3 | height: 15px; 4 | width: 65px; 5 | font-size: 1.1em; 6 | font-weight: bold; 7 | padding: 10px 15px; 8 | margin: 20px auto; 9 | color: #ccc; 10 | background-color: #555; 11 | border: 0 none; 12 | border-radius: 3px; 13 | text-shadow: 0 -1px 0 #000; 14 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 15 | } 16 | 17 | span[id="button"]:hover, span[id="button"]:focus { 18 | background-color: #820bbb; 19 | } 20 | 21 | span[aria-pressed="true"] { 22 | color: #fff; 23 | padding: 11px 12px 9px 16px; 24 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 25 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 26 | } 27 | -------------------------------------------------------------------------------- /toggle3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Toggle button design pattern: <span> element 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Toggle button design pattern: <span> element

    16 | 17 |

    This toggle button is created using the <span> element, with scripting to provide the expected behaviour. Additional semantic information is provided using ARIA.

    18 | 19 |

    It is better to use the <button> element to create a toggle button because the browser automatically provides keyboard support and most semantic information.

    20 | 21 |

    Example

    22 | 23 |
    24 | Tequila! 25 |
    26 | 27 |

    Notes

    28 | 29 |

    Uses the ARIA role attribute with a value of button. This informs the browser that it should expose the <span> element as a button through its accessibility API. This information is utilised by assistive technologies to inform users what they're dealing with.

    30 | 31 |

    Uses the HTML tabindex attribute with a value of 0. This places the button into the tab order based on its location in the source order. The visual appearance of the button changes when focus moves to it.

    32 | 33 |

    Keyboard interaction for the button is handled through scripting. Both enter and space key events are captured, and used to trigger the button's expected behaviour.

    34 | 35 |

    The aria-pressed attribute is used to indicate the state of the button. It is set to false initially, then scripted to toggle between true/false whenever the button is activated.

    36 | 37 |

    The visual appearance of the button when pressed (or not) is hooked onto the aria-pressed attribute using an intelligent CSS selector, instead of a class name. Although this does not affect accessibility, it creates a cleaner separation between structure and design.

    38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /toggle3/js/button.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var button = document.getElementById('button'); 5 | button.setAttribute('role', 'button'); 6 | button.setAttribute('tabindex', 0); 7 | button.setAttribute('aria-pressed', 'false'); 8 | 9 | function toggle(event) { 10 | if (button.getAttribute('aria-pressed') == 'false') { 11 | button.setAttribute('aria-pressed', 'true'); 12 | } 13 | else { 14 | button.setAttribute('aria-pressed', 'false'); 15 | } 16 | } 17 | 18 | button.addEventListener('click', toggle, false); 19 | 20 | button.addEventListener('keydown', function(event) { 21 | if (event.keyCode == 13 || event.keyCode ==32) { 22 | toggle(); 23 | } 24 | }); 25 | 26 | })(); 27 | 28 | -------------------------------------------------------------------------------- /toggletip1/css/toggletip.css: -------------------------------------------------------------------------------- 1 | button { 2 | display: block; 3 | height: 35px; 4 | width: 100px; 5 | font-size: 1.1em; 6 | font-weight: bold; 7 | padding: 10px 15px; 8 | color: #ccc; 9 | background-color: #555; 10 | border: 0 none; 11 | border-radius: 3px; 12 | box-shadow: 0 1px 0 #666, 0 5px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 13 | } 14 | 15 | button:hover, button:focus { 16 | background-color: #820bbb; 17 | color: #fff; 18 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 19 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 20 | cursor: pointer; 21 | } 22 | 23 | [aria-expanded"true"] { 24 | color: #fff; 25 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 26 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 27 | } 28 | 29 | aside span{ 30 | z-index:99; 31 | display: block; 32 | background: #eee; 33 | border: 1px solid #ccc; 34 | padding: 10px; 35 | border-radius: 8px; 36 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 37 | position: absolute; 38 | width: 14m; 39 | } 40 | -------------------------------------------------------------------------------- /toggletip1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Toggletip design pattern 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Toggletip design pattern

    16 | 17 | 20 | 21 |
    22 | 23 | 24 | 25 | 28 | 29 |
    30 | 31 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /toggletip1/js/toggletip.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var button = document.getElementById('button'); 4 | var tip = document.getElementById('tip'); 5 | var content = document.getElementById('content'); 6 | 7 | button.setAttribute('aria-expanded', 'false'); 8 | button.setAttribute('aria-describedby', 'tip'); 9 | 10 | tip.setAttribute('hidden', true); 11 | 12 | content.setAttribute('role', 'tooltip'); 13 | content.setAttribute('aria-live', 'polite'); 14 | 15 | function toggleTip(e) { 16 | 17 | if (tip.hasAttribute('hidden')) { 18 | button.setAttribute('aria-expanded', 'true'); 19 | content.innerHTML = "Tequila (makes me happy)!"; 20 | tip.removeAttribute('hidden'); 21 | } else { 22 | button.setAttribute('aria-expanded', 'false'); 23 | content.innerHTML = ''; 24 | tip.setAttribute('hidden', true); 25 | } 26 | } 27 | 28 | button.addEventListener('click', toggleTip, false); 29 | 30 | document.addEventListener('keydown', function(e) { 31 | 32 | if (e.keyCode == 27) { 33 | toggleTip(); 34 | } 35 | }); 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /toggletip1/js/toggletip.js~: -------------------------------------------------------------------------------- 1 | var tip = document.getElementById('tip'); 2 | var button = document.getElementById('button'); 3 | 4 | button.setAttribute('aria-expanded', 'false'); 5 | button.setAttribute('aria-describedby', 'tip'); 6 | 7 | tip.setAttribute('hidden', true); 8 | 9 | function showTip() { 10 | button.setAttribute('aria-expanded', 'true'); 11 | 12 | tip.innerHTML = " Tequila (makes me happy)!"; 13 | tip.removeAttribute('hidden'); 14 | } 15 | 16 | function hideTip() { 17 | button.setAttribute('aria-expanded', 'false'); 18 | 19 | tip.innerHTML = ''; 20 | tip.setAttribute('hidden', true); 21 | } 22 | 23 | function toggleTip(e) { 24 | 25 | if (tip.hasAttribute('hidden')) { 26 | showTip(); 27 | } else { 28 | hideTip(); 29 | } 30 | } 31 | 32 | button.addEventListener('click', toggleTip, false); 33 | 34 | document.addEventListener('keyup', function(e) { 35 | 36 | if (e.keyCode == 27) { 37 | toggleTip(); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /toggletip2/css/toggletip.css: -------------------------------------------------------------------------------- 1 | span[id="button"] { 2 | display: block; 3 | height: 15px; 4 | width: 60px; 5 | font-size: 1.1em; 6 | font-weight: bold; 7 | padding: 10px 15px; 8 | color: #ccc; 9 | background-color: #555; 10 | border: 0 none; 11 | border-radius: 3px; 12 | box-shadow: 0 1px 0 #666, 0 5px 0 #444, 0 6px 6px rgba(0,0,0,0.6); 13 | } 14 | 15 | span[id="button"]:hover, span[id="button"]:focus { 16 | background-color: #820bbb; 17 | color: #fff; 18 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 19 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 20 | cursor: pointer; 21 | } 22 | 23 | [aria-expanded"true"] { 24 | color: #fff; 25 | text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; 26 | box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); 27 | } 28 | 29 | span[role="tooltip"] { 30 | z-index:99; 31 | display: block; 32 | background: #eee; 33 | border: 1px solid #ccc; 34 | padding: 10px; 35 | border-radius: 8px; 36 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 37 | position: absolute; 38 | width: 14m; 39 | } 40 | -------------------------------------------------------------------------------- /toggletip2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Toggletip design pattern (div/span) 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Toggletip design pattern (div/span)

    16 | 17 |

    This toggletip is created using the <span> and <div> elements, with scripting to provide the expected behaviour. Additional semantic information is provided with ARIA.

    18 | 19 |

    It is better to use the <button> and <aside> elements to create toggletips because the browser provides more keyboard support and semantic information.

    20 | 21 | 22 |

    Example

    23 | 24 |
    25 | 26 | Tequila 27 | 28 |
    29 | 30 |
    31 | 32 |
    33 | 34 |

    Notes

    35 | 36 |

    This toggletip is based on the design pattern first proposed by Steve Faulkner.

    37 | 38 |

    The button role is used to expose the <span> element as a button in the accessibility tree.

    39 | 40 |

    The tabindex attribute is used to add the <span> element to the tab order, based on its location within the DOM.

    41 | 42 |

    The complementary role is used to expose the <div> element as complementary information in the accessibility tree.

    43 | 44 |

    The aria-expanded attribute indicates the current state of the toggletip.

    45 | 46 |

    The aria-describedby attribute associates the tooltip content with the <button>.

    47 | 48 |

    The hidden attribute hides the <aside>> element until the toggletip is activated.

    49 | 50 |

    The tooltip role exposes the <span> element as a tooltip in the accessibility tree.

    51 | 52 |

    The aria-live attribute turns the <span> element into a live region that is announced automatically by screen readers.

    53 | 54 |

    The click and keydown event listeners capture the expected mouse and keyboard events for opening and closing the toggletip.

    55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /toggletip2/js/toggletip.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var tip = document.getElementById('tip'); 4 | var button = document.getElementById('button'); 5 | var content = document.getElementById('content'); 6 | 7 | button.setAttribute('role', 'button'); 8 | button.setAttribute('tabindex', '0'); 9 | button.setAttribute('aria-expanded', 'false'); 10 | button.setAttribute('aria-describedby', 'tip'); 11 | 12 | tip.setAttribute('role', 'complementary'); 13 | tip.setAttribute('hidden', true); 14 | 15 | content.setAttribute('role', 'tooltip'); 16 | content.setAttribute('aria-live', 'polite'); 17 | 18 | function toggleTip(e) { 19 | 20 | if (tip.hasAttribute('hidden')) { 21 | button.setAttribute('aria-expanded', 'true'); 22 | content.innerHTML = "Makes me happy!"; 23 | tip.removeAttribute('hidden'); 24 | } else { 25 | button.setAttribute('aria-expanded', 'false'); 26 | content.innerHTML = ''; 27 | tip.setAttribute('hidden', true); 28 | } 29 | } 30 | 31 | button.addEventListener('click', toggleTip, false); 32 | 33 | button.addEventListener('keydown', function(e) { 34 | 35 | if (e.keyCode == 13 || e.keycode == 32) { 36 | toggleTip(); 37 | } 38 | }); 39 | 40 | document.addEventListener('keydown', function(e) { 41 | 42 | if (e.keyCode == 27) { 43 | toggleTip(); 44 | } 45 | }); 46 | 47 | })(); 48 | --------------------------------------------------------------------------------