-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.html
890 lines (824 loc) · 39.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
<!DOCTYPE html>
<html lang="en">
<head>
<title>Responsible Web Applications</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="HTML and CSS Tips and Tricks for creating applications that are both responsive and accessible out of the box">
<meta name="author" content="Joy Heron">
<meta name="twitter:card" value="summary_large_image">
<meta name="twitter:site" content="@iamjoyheron">
<meta name="twitter:title" content="Responsible Web Applications">
<meta name="twitter:description" content="HTML and CSS Tips and Tricks for creating applications that are both responsive and accessible out of the box">
<meta name="twitter:creator" content="@iamjoyheron">
<meta name="twitter:image" content="https://responsibleweb.app/assets/responsible-web-cover.jpg">
<meta property="og:title" content="Responsible Web Applications">
<meta property="og:type" content="article">
<meta property="og:url" content="https://responsibleweb.app">
<meta property="og:image" content="https://responsibleweb.app/assets/responsible-web-cover.jpg">
<meta property="og:description" content="HTML and CSS Tips and Tricks for creating applications that are both responsive and accessible out of the box">
<meta property="og:site_name" content="Responsible Web Applications">
<link rel="stylesheet" href="fonts.css">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="responsive-examples.css">
<link rel="stylesheet" href="prism-theme.css">
<script defer data-domain="responsibleweb.app" src="https://plausible.io/js/plausible.js"></script>
</head>
<body>
<header>
<div class="title">
<h1 id="Responsible-Web-Applications">
Responsible Web Applications
</h1>
<div class="author">
<img src="https://joyheron.com/img/profile-medium.jpg" alt="Picture of Joy Heron" width="80">
<p>By <a href="https://www.innoq.com/en/staff/joy-heron/">Joy Heron</a></p>
</div>
</div>
<nav>
<h2>
<toggle-button data-target="#toc">
<button hidden aria-label="Expand Table of Contents">
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="angle-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512" class="svg-inline--fa fa-angle-right" width="0.5em"><path fill="currentColor" d="M187.8 264.5L41 412.5c-4.7 4.7-12.3 4.7-17 0L4.2 392.7c-4.7-4.7-4.7-12.3 0-17L122.7 256 4.2 136.3c-4.7-4.7-4.7-12.3 0-17L24 99.5c4.7-4.7 12.3-4.7 17 0l146.8 148c4.7 4.7 4.7 12.3 0 17z" class=""></path></svg>
</button>
Table of Contents
</toggle-button>
</h2>
<ul id="toc">
<li><a href="#Responsive-Web-Design">Responsive Web Design</a></li>
<li>
<a href="#Responsive-Layout-Containers">Responsive Layout Containers</a>
<ul aria-label="Responsive Layout Containers Examples">
<li><a href="#CSS-Grid-with-Breakpoints">Explicit CSS Grid Layout with Breakpoints</a></li>
</ul>
</li>
<li>
<a href="#Squishy-Components">Squishy Components</a>
<ul aria-label="Squishy Component Examples">
<li><a href="#Flexbox--Flex-Wrap">Flexbox + Flex Wrap</a></li>
<li><a href="#Nested-Flexboxes">Nested Flexboxes</a></li>
<li><a href="#Intrinsic-Grid">Intrinsic Grid</a></li>
<li><a href="#Horizontal-Scrolling">Horizontal Scrolling</a></li>
<li><a href="#Squishy-Text">Squishy Text</a></li>
</ul>
</li>
<li>
<a href="#Accessible-Web-Design">Accessible Web Design</a>
<ul aria-label="Tips for ensuring Accessibility">
<li><a href="#Headings">Headings</a></li>
<li><a href="#Landmarks">Landmarks</a></li>
<li><a href="#nav-element">nav Element</a></li>
<li><a href="#list-elements">List Elements</a></li>
<li><a href="#accordions">Accordions</a></li>
<li><a href="#Hiding-content-visually">Hiding Content Visually</a></li>
<li><a href="#Hiding-content-from-assistive-technologies">Hiding Content from Assistive Technologies</a></li>
<li><a href="#Image-Alt">Descriptive alt text for images</a></li>
<li><a href="#Styling-Focus">Adding clear focus styles</a></li>
<li><a href="#Setting-focus-correctly">Setting focus correctly</a></li>
</ul>
</li>
</ul>
</nav>
</header>
<main>
<section>
<p>
The title for this page came from a slip of my tongue.
I actually had wanted to say "responsive and accessible" web applications,
but somehow "responsible" slipped out.
</p>
<p>
Regardless of how I came up with the term, I do consider it to be fitting
because I feel that we as developers have a responsibility to ensure that
our application is responsive and accessible.
If we don't, who will?
</p>
<p>
It is extremely difficult and expensive to add responsiveness and accessibility after the fact.
For this reason, we need to take them into account
from the very beginning.
</p>
<p>
Luckily, with modern HTML and CSS, we can create responsive and
accessible web apps with relative ease. In my years of doing software development,
I have learned some HTML and CSS tips and tricks, and I want to present these in
this post. This list is not exhaustive, but these are tried and true patterns that
I frequently use in different projects.
</p>
</section>
<section class="responsive-web-design">
<h2 id="Responsive-Web-Design">Responsive Web Design<a href="#Responsive-Web-Design" aria-label="Link to Responsive Web Design"></a></h2>
<p>Do we really need responsive web design for our web application? We will only use our web application on desktop computers!</p>
<p>
I've heard this argument many times, but in the long term, it has never turned out to be true.
For this reason, I've created Joy's two laws of web development:
</p>
<h3 id="Joy’s-First-Law-of-Web-Development">Joy’s First Law of Web Development<a href="#Joy’s-First-Law-of-Web-Development" aria-label="Link to Joy's First Law of Web Development"></a></h3>
<blockquote>
<blockquote-quote aria-hidden="true"></blockquote-quote>
<p>There is no such thing as a non-responsive web application</p>
</blockquote>
<p><em>Your web application WILL be opened in a mobile phone or tablet at some time in the future and your users WILL expect it to work correctly.</em></p>
<h3 id="Joy’s-Second-Law-of-Web-Development">Joy’s Second Law of Web Development<a href="#Joy’s-Second-Law-of-Web-Development" aria-label="Link to Joy's Second Law of Web Development"></a></h3>
<blockquote>
<blockquote-quote aria-hidden="true"></blockquote-quote>
Any work you do now to ensure that your web application behaves responsively WILL be appreciated in the future.
</blockquote>
<p>When it comes to the technical implementation of responsive design, there are two main categories of components that we need to develop:</p>
<ul class="em-list negative-padding squishy-text">
<li><a href="#Responsive-Layout-Containers">Responsive Layout Containers</a></li>
<li aria-hidden="true" data-and>&</li>
<li><a href="#Squishy-Components">Squishy Components</a></li>
</ul>
<p>I want to cover these two aspects in the next sections</p>
<h3 id="Responsive-Layout-Containers">Responsive Layout Containers<a href="#Responsive-Layout-Containers" aria-label="Link to Responsive Layout Containers"></a></h3>
<p>
We firstly need to make sure that we put a lot of thought into designing layout containers which adjust themselves based on the size of our viewport.
</p>
<p>
In the following demo, we can see how we can define a layout conceptually using different grid areas.
</p>
<figure>
<div class="horizontal-scroll responsive-example" aria-hidden="true">
<span class="caption">Mobile Devices</span>
<article class="layout-example" data-observe-resizes>
<header>
Header
</header>
<aside>
Sidebar
</aside>
<div class="main">
Main Content
</div>
<aside>
Second Sidebar
</aside>
<footer>
Footer
</footer>
</article>
<span class="caption">Tablet and up</span>
<article class="layout-example tabletUp no-grid-message">
<header>
Header
</header>
<aside class="squishy-text">
Sidebar
</aside>
<div class="main">
Main Content
</div>
<aside class="squishy-text">
Second Sidebar
</aside>
<footer>
Footer
</footer>
</article>
<footer class="caption tabletUp">Use the lower right corner to resize the layout and see it respond to change</footer>
</div>
<figcaption>
A UI example showing how content will be stacked vertically on smaller devices,
but on larger viewports we can then use the increased horizontal space to display
certain content areas next to each other.
<p class="visually-hidden">
The demo shows five content areas: Header, Sidebar, Main Content, Second Sidebar, and Footer.
On a mobile device, these areas are shown vertically stacked.
On larger viewports, the example layout now has three rows and three columns.
The Header area spans the full width of the top of the viewport in the first row.
In the middle row, the Sidebar, Main Content, and Second Sidebar areas are positioned
next to each other in a three column layout, with the Main Content area expanding
to take up as much space as it can. The Footer area then spans the full width of
the viewport in the bottom row of the layout.
</p>
</figcaption>
</figure>
<p>How can we implement this? To do this, I like to use the following technique.</p>
<h4 id="CSS-Grid-with-Breakpoints">Explicit CSS Grid Layout with Breakpoints<a href="#CSS-Grid-with-Breakpoints" aria-label="Link to Explicit CSS Grid Layout with Breakpoints"></a></h4>
<p>
Using CSS Grid, I like to use the following technique to declaratively define a default CSS grid for our content.
This is then the CSS which is used for smaller devices.
</p>
<h5>CSS for Mobile Devices</h5>
<pre class="language-css" aria-label="CSS Code used to program the mobile layout Responsive Layout example"><code>.layout {
display: grid;
grid-template-areas:
"header"
"sidebar"
"main"
"sidebar-right"
"footer";
grid-template-rows: auto auto 1fr auto auto;
}
.layout > header {
grid-area: header;
}
.layout > aside:nth-of-type(1) {
grid-area: sidebar;
}
.layout > main {
grid-area: main;
}
.layout > aside:nth-of-type(2) {
grid-area: sidebar-right;
}
.layout > footer {
grid-area: footer;
}
</code></pre>
<p>
Note that this markup explicitly references the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main">
main</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header">
header</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer">
footer</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside">
aside</a> semantic HTML elements in the CSS code.
This was intentional here, because it forces us to then use the semantic HTML elements and add important landmarks to our web application which improves its accessibility.
With the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator">child combinator</a> operator (<code>></code>) we ensure that this element targeted is the direct child of the parent with my layout class (which we would probably add directly to our HTML body).
</p>
<h5 id="On-Tablets-or-larger-devices">CSS for tablets or larger devices</h5>
<p>
Since we have already defined the <code>grid-areas</code> for our HTML elements, we can now declaratively change the layout using a relatively short CSS snippet within a media query:
</p>
<pre class="language-css" aria-label="CSS Code used to program the Responsive Layout example for larger devices"><code>@media (min-width: 40rem) { /* Breakpoint Tablet portrait up */
.layout {
grid-template-areas:
"header header header"
"sidebar main sidebar-right"
"footer footer footer";
grid-template-rows: auto 1fr auto;
grid-template-columns: 20% 1fr 20%;
}
}
</code></pre>
<p>
Note that when you are defining your breakpoints for your application,
you should consider the <a href="https://www.freecodecamp.org/news/the-100-correct-way-to-do-css-breakpoints-88d6a5ba1862/">correct way to do CSS breakpoints</a>.
</p>
<h3 id="Squishy-Components">Squishy Components<a href="#Squishy-Components" aria-label="Link to Squishy Components"></a></h3>
<p>
After we have a responsive layout, the next step is to make sure that all of our components are "squishy".
This means that when we place a component into a designated area of a layout,
it should never push itself outside of its designated area.
Instead it should "squish" down to fit inside of the available space.
</p>
<p>
This is especially important because our layout container assumes that all of its
content is going to fit, and if this assumption is not true, this could cause the
whole layout to be wider than the viewport and make it necessary for the user to scroll
horizontally.
</p>
<h4 id="Flexbox--Flex-Wrap">Flexbox + Flex Wrap<a href="#Flexbox--Flex-Wrap" aria-label="Link to Flexbox + Flex Wrap"></a></h4>
<pre class="language-css" aria-label="CSS Code used to create wrapping within a flexbox container"><code>.container {
display: flex;
flex-wrap: wrap;
}
</code></pre>
<p>
We can use the <code>display: flex;</code> rule to make the container
a <a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox">flexbox</a> and
then use the <code>flex-wrap: wrap;</code> rule to wrap content within the flexbox.
</p>
<p>
When there is not enough space for the items to be placed horizontally,
they will begin to wrap and be shown stacked vertically instead.
</p>
<figure>
<div class="horizontal-scroll responsive-example" aria-hidden="true">
<span class="caption">Mobile Devices</span>
<article class="flexbox-example highlight-flex">
<div>
<h5>Placeholder Title</h5>
<p>A description of an item</p>
</div>
<div class="inner-flex">
<span>
Price
</span>
<span>
Time
</span>
<span>
Amount
</span>
</div>
</article>
<span class="caption">Tablet and up</span>
<article class="flexbox-example highlight-flex">
<div>
<h5>Placeholder Title</h5>
<p>A description of an item</p>
</div>
<div class="inner-flex">
<span>
Price
</span>
<span>
Time
</span>
<span>
Amount
</span>
</div>
</article>
<footer class="caption tabletUp">Use the lower right corner to resize the flexbox and see it respond to change</footer>
</div>
<figcaption>
An example showing how we can use <code>flex-wrap</code> to wrap content in a flexbox.
<p class="visually-hidden">
The demo shows a flexbox containing two blocks: a block containing a title and a description,
and a block containing a price, time, and amount.
On a larger devices, these block can be positioned next to each other horizontally.
On smaller devices with flex-wrap activated, the second block containing the price, time and amount
will wrap onto the next line and be positioned underneath the first block.
</p>
</figcaption>
</figure>
<h4 id="Nested-Flexboxes">Nested Flexboxes<a href="#Nested-Flexboxes" aria-label="Link to Nested Flexboxes"></a></h4>
<p>
It is also possible to nest flexboxes inside each other to create more advanced responsive behavior.
This next example shows the same flex container from our last example, but highlights the
inner flexbox instead of the outer flexbox.
</p>
<figure>
<div class="horizontal-scroll responsive-example" aria-hidden="true">
<article class="flexbox-example">
<div>
<h5>Placeholder Title</h5>
<p>A description of an item</p>
</div>
<div class="inner-flex highlight-flex">
<span>
Price
</span>
<span>
Time
</span>
<span>
Amount
</span>
</div>
</article>
<footer class="caption tabletUp">Use the lower right corner to resize the flexbox and see it respond to change</footer>
</div>
<figcaption>
An example highlighting the items in the inner flexbox that can also wrap.
<p class="visually-hidden">
The demo shows the same demo as the one in the previous example, but in this instance,
the price, time, and amount items are highlighted because they are also contained within
an inner flexbox. This adds an extra layer to the responsiveness: when the viewport gets
so small that there is no longer space for these three items to be positioned next to each
other horizontally, they will begin to wrap as well.
</p>
</figcaption>
</figure>
<h4 id="Intrinsic-Grid">Intrinsic Grid<a href="#Intrinsic-Grid" aria-label="Link to Intrinsic Grid"></a></h4>
<pre class="language-css" aria-label="CSS Code for creating an intrinsic CSS Grid"><code>.container {
display: grid;
grid-template-columns:
repeat(auto-fill, minmax(var(—col-width), auto));
}
</code></pre>
<p>
We can use the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/repeat">repeat</a> function
with <code>grid-template-columns</code> in order to create an intrinsic grid which will generate as
many columns as fit in the given space. An example using this type of grid is available in
<a href="https://github.com/innoq/livecoding-css-layout">my demo for live-coding css layout</a>.
</p>
<figure>
<div class="horizontal-scroll responsive-example" aria-hidden="true">
<span class="caption">Mobile Devices</span>
<article class="intrinsic-grid-example">
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>
<div>Element 6</div>
</article>
<span class="caption">Tablet and up</span>
<article class="intrinsic-grid-example no-grid-message">
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>
<div>Element 6</div>
</article>
<footer class="caption tabletUp">Use the lower right corner to resize the grid and see it respond to change</footer>
</div>
<figcaption>
An example of an intrinsic grid which fits as many columns as possible within the viewport.
<p class="visually-hidden">
The demo shows a grid with 6 different content blocks that are positioned within an intrinsic CSS grid.
On mobile devices, there is only enough space for a single column, so the blocks are all positioned within
this column. When there is enough space for two columns, the layout will shift and the elements will be
positioned within the grid in two columns and end up filling three rows.
When there is enough space for three columns,
the layout will shift again and the elements will be positioned within the grid in three columns and will
end up filling two rows. This layout pattern will continue, and a grid will always be generated with
as many columns as fit within the viewport size.
</p>
</figcaption>
</figure>
<h4 id="Horizontal-Scrolling">Horizontal Scrolling<a href="#Horizontal-Scrolling" aria-label="Link to Horizontal Scrolling"></a></h4>
<pre class="language-css" aria-label="CSS Code for creating a container for larger elements that can be scrolled horizontally"><code>.horizontal-scroll {
overflow-x: auto;
}
</code></pre>
<p>
When we talk about creating an application which is responsive, this doesn't mean that we need to optimize
all of our layouts for small device screens. In certain contexts, we will have larger data representations
(e.g. tables or code examples), which we also want to be available for smaller devices even if they are not
optimized for that layout.
</p>
<p>
For this, I like to use a wrapper around tables and code examples which make them able to be scrolled horizontally
when there is not enough space for them in the current layout.
</p>
<figure>
<div class="horizontal-scroll responsive-example" aria-hidden="true">
<article class="example-table">
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th>H</th>
<th>I</th>
<th>J</th>
<th>K</th>
<th>L</th>
<th>M</th>
<th>N</th>
<th>O</th>
<th>P</th>
<th>Q</th>
<th>R</th>
<th>S</th>
<th>T</th>
</tr>
</thead>
<tbody>
<tr>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
</article>
<footer class="caption">Scroll horizontally to see the whole table</footer>
<footer class="caption tabletUp">Use the lower right corner to resize the table and see it respond to change</footer>
</div>
<figcaption>
An example showing how to use a container with horizontal scrolling to make larger
content available on smaller viewports.
<p class="visually-hidden">
In the example, a large table with twenty columns is shown. On smaller devices, there is
not enough space for the whole table to be shown. In this case, the large table is shown
within a container that fits within the viewport, and the user can scroll within that
container in order to view all of the contents.
</p>
</figcaption>
</figure>
<h4 id="Squishy-Text">Squishy Text<a href="#Squishy-Text" aria-label="Link to Squishy Text"></a></h4>
<pre class="language-css" aria-label="CSS Code for activating word wrapping in different browsers"><code>.squishy-text {
word-break: break-word; /* Samsung browser */
word-wrap: break-word; /* IE 11 */
overflow-wrap: anywhere;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
}
</code></pre>
<p>
The following example is a CSS meme now.
By default, long words in an HTML document will not be hyphenated by default,
so they will break out of their containing box instead of squishing to fit inside of it.
This is especially important when we are dealing with a language which has a lot of long words
(<em>*cough* German *cough*</em>).
</p>
<p>
The previous CSS snippet is one I have successfully used to make my text squishy in different contexts.
</p>
<div class="negative-padding">
<div class="flex-wrapper">
<div class="box-example" aria-label="Example showing the word 'Awesome' poking out of its containing box">
CSS is awesome
</div>
<div class="box-example squishy-text" aria-label="Example showing the word 'Awesome' wrapping to fit inside of its containing box">
CSS is awesome
</div>
</div>
</div>
</section>
<section class="accessible-web-design">
<h2 id="Accessible-Web-Design">Accessible Web Design<a href="#Accessible-Web-Design" aria-label="Link to Accessible Web Design"></a></h2>
<p>
We now come to accessibility.
Here I feel that I can only scratch the surface of the different things that we should consider.
I also am learning new things all the time, so I'm sure that this list is not exhaustive.
But I do think it is a good starting off point.
</p>
<h3 id="Headings">Headings<a href="#Headings" aria-label="Link to Headings"></a></h3>
<p>Don’t skip heading levels.</p>
<p>
In your HTML Document, you need to ensure that your document hierarchy is complete and doesn't skip levels.
Otherwise, users who depend on assistive technologies will get confused because it will seem like content is
missing.
</p>
<p>
This is a mistake that many developers make,
because we pay attention to how the heading appears visually without making sure that an <code>h2</code> is always
directly followed by a <code>h3</code>.
</p>
<h3 id="Landmarks">Landmarks<a href="#Landmarks" aria-label="Link to Landmarks"></a></h3>
<p>
We should make sure that we use elements like
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main">main</a> and
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header">header</a>
because then we get HTML <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#Landmark_roles">landmarks</a>
out of the box.
This makes the page much easier to navigate.
</p>
<p>
Note that the <code>main</code> element is not well supported for IE11, so if you have to
support older browsers, you should also consider using <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Main_role#Skip_navigation">skip links</a>.
</p>
<h3 id="nav-element">nav Element<a href="#nav-element" aria-label="Link to nav Element"></a></h3>
<p>
When you are providing links for a user to navigate within your page (e.g. a navbar or a table of contents),
you should wrap them in a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav">nav</a>.
</p>
<h3 id="label-element">label Element<a href="#label-element" aria-label="Link to label Element"></a></h3>
<p>
<em>Always</em> add a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label">label</a> to
let assistive technologies know which data is supposed to be entered in an input field.
</p>
<pre class="language-html" aria-label="HTML Code showing how to use a label element to wrap around an input field."><code><label>
First Name
<input type="text" value="name" placeholder="Jane" />
</label>
</code></pre>
<p>
Here we either wrap the input field directly in the label,
or we can use the <code>for</code> attribute and link it to a specific input field.
</p>
<p>
Here it is important to not use the <code>placeholder</code> attribute to label the input field.
The placeholder attribute should be used to show an example of how the data we expect should appear.
This is particularly important for users who have congnitive disabilities, because when the instructions
in the placeholder disappear, they may not remember what they were supposed to enter into the field.
Please also read <a href="https://adamsilver.io/articles/placeholders-are-problematic/">this article about why placeholder are problematic.</a>
</p>
<h3 id="list-elements">List Elements<a href="#list-elements" aria-label="Link to List Elements"></a></h3>
<p>
Using lists (an <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul">unordered list</a>,
an <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol">ordered list</a>, or a
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">description list</a>) for things in a UI will also add extra context for assistive technologies.
For instance, it will tell assistive technologies how many elements are contained in a list.
</p>
<p><del datetime="2021-01-22T08:41:57Z">
In practice, description lists can be difficult to style because we are not allowed to add an extra <code>div</code> as a wrapper around the <code>dt</code> and <code>dd</code> elements.
For this reason, I've also done <a href="https://joyheron.com/a11y-playground/represent-labelled-information.html" target="_blank">some experiments about how to best group information in the UI</a>.
Here there isn't a single correct solution.
You will have to find out what works best for your UI.
</del></p>
<p class="insert negative-padding"><ins datetime="2021-01-22T08:41:57Z">
<em>Update:</em> Originally, I discussed having difficulties styling description lists.
This statement is no longer correct for modern browsers which implement the
<a href="https://www.w3.org/TR/html52/grouping-content.html#the-dl-element">HTML 5.2 Recommendation</a>
because they now allow a <code>div</code> as a wrapper around the <code>dt</code> and <code>dd</code> elements.
</ins></p>
<h3 id="accordions">Accordions<a href="#accordions" aria-label="Link to Accordions"></a></h3>
<p>
If we need an accordion, or an element which shows/hides a content area,
we can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details">HTML details element</a>.
</p>
<pre class="language-html" aria-label="HTML Code example using a details element"><code><details>
<summary>Toggle Button</summary>
Content which will be expanded/collapsed when clicking the
summary element
</details>
</code></pre>
<p>
We can also implement this in JavaScript using a <code>button</code> and the
<code>aria-expanded</code> attribute.
The <code>aria-expanded</code> attribute adds context information to the <em>button</em> which will tell the screenreader
if the area that the button is toggling is currently collapsed or expanded.
</p>
<p>
I have seen incorrect implementations of this where the developer thought that the attribute was intended to be added to
the content block which is expanded or collapsed, but this is not the case and that implementation would be confusing to any user of an assistive technology.
If we do spend the effort to set aria roles in our application (which we should),
we need to test our application and make sure they are used correctly!
No aria usage is better than incorrect aria usage.
</p>
<p>
I have implemented this behavior many times, and when I do, I prefer to use a custom element and activate
the toggle with <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a>.
This means that my toggle button is hidden by default before JavaScript is activated, and the content area that is to be collapsed/expanded
will only be hidden once JavaScript is activated.
</p>
<p>
The contract for the <code>toggle-button</code> component that I usually end up writing therefore usually looks something like this:
</p>
<pre class="language-html" aria-label="HTML code showing the contract for the toggle-button component"><code><button is="toggle-button" data-target="#section2"
aria-expanded="false" hidden>
Toggle Section 2
</button>
</code></pre>
<p><em>aria-expanded</em> is an attribute of the <strong>BUTTON</strong> which tells assistive technologies if the content area that is being expanded/collapsed is currently visible or not.</p>
<p>
And my <code>toggle-button</code> implementation usually looks a lot like the following
(I really need to standardize this and publish a custom element one of these days...):
</p>
<pre class="language-js" aria-label="JavaScript Code showing an implementation of a toggle button custom element"><code>class ToggleButton extends HTMLButtonElement {
connectedCallback () {
this.removeAttribute("hidden");
if (this.getAttribute("aria-expanded") !== "true") {
this.setAttribute("aria-expanded", "false");
this.target.classList.add("hide");
}
this.addEventListener("click", this.toggle.bind(this));
}
toggle () {
let classList = this.target.classList;
if (classList.contains("hide")) {
classList.remove("hide");
this.setAttribute("aria-expanded", "true");
} else {
classList.add("hide");
this.setAttribute("aria-expanded", "false");
}
}
get target () {
return document.querySelector(this.getAttribute("data-target"));
}
}
customElements.define("toggle-button", ToggleButton, { extends: "button" });
</code></pre>
<p><a href="https://joyheron.com/a11y-playground/collapser.html" target="_blank">Here is a code demo of this component.</a></p>
<h3 id="aria-label-and-aria-labelledby">aria-label and aria-labelledby<a href="#aria-label-and-aria-labelledby" aria-label="Link to aria-label and aria-labelledby"></a></h3>
<p>
Sometimes we have visual elements which add information to the UI,
but are missing in the UI. For these cases, we can use
an <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute">aria-label</a> attribute
with a textual description of what the visual element is showing. If there is already a UI
element available, we can also use <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute">aria-labelledby</a> and reference the HTML id of
the existing element.
The HTML <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title">title</a>
attribute is not well supported by assistive technologies, so it should not be used to do this.
</p>
<p>
Here it really helps to test your UI in a screenreader so that you can figure out where
extra labels would be helpful <span aria-label="winking emoji">😉</span>.
</p>
<h3 id="Hiding-content-visually">Hiding content visually (but not from screenreaders)<a href="#Hiding-content-visually" aria-label="Link to hiding content visually"></a></h3>
<pre class="language-css" aria-label="CSS Code snippet to hide HTML visually"><code>.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
</code></pre>
<p>
Sometimes we want to hide elements from our UI.
If we use a CSS rule like <code>display: none;</code>
this not only hides the element visually,
but also hides it from assistive technologies.
We can use the previous CSS snippet (or one like it),
to hide the content visually without removing it from
the accessibility tree.
</p>
<h3 id="Hiding-content-from-assistive-technologies">Hiding content from assistive technologies<a href="#Hiding-content-from-assistive-technologies" aria-label="Link to Hiding Content from Assistive Technologies"></a></h3>
<p>
In the same vein, sometimes we add visual elements to our UI which are not necessary
for assistive technologies and could potentially cause confusion or unnecessary noise.
This could be the case, for instance, when we add icons to our visual design in addition
to existing textual labels. We may then want to hide these from assistive technologies
using the <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute">aria-hidden</a> attribute.
</p>
<h3 id="Image-Alt">Descriptive alt text for images<a href="#Image-Alt" aria-label="Link to Descriptive alt text for images"></a></h3>
<p>
When using images in a user interface, we need to set
the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt">alt</a> attribute for the image.
This alt-text should provide a description of the same information that we are trying to
convey visually with the image that we have chosen.
<a href="https://axesslab.com/alt-texts/">This guide provides more information about alt-texts.</a>
</p>
<h3 id="Styling-Focus">Adding clear focus styles<a href="#Styling-Focus" aria-label="Link to Adding clear focus styles"></a></h3>
<p>
By default, the browser will add focus styles to the UI to indicate to users which element on the
screen is currently active. This is especially important for keyboard users, who need the focus styles
in order to correctly navigate throughout your page.
</p>
<p>
If you find the default focus styles irritating in your design, you can customize them with the
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus">:focus</a> CSS pseudo-selector.
However, here it is very important that <strong>you still provide a focus style which has a high
contrast and is easily identifiable</strong> within the UI.
</p>
<h3 id="Setting-focus-correctly">Setting focus correctly<a href="#Setting-focus-correctly" aria-label="Link to setting focus correctly"></a></h3>
<p>
As long as you are writing only HTML and CSS,
there isn't any good reason why you would want to mess with the <code>focus</code> within your webpage,
because the semantics and structure of HTML is very well designed.
However, when we begin to modify the HTML of our application with client-side JavaScript,
we need to think about the focus and if we need to update it as well.
</p>
<p>
For instance, if we are adding content to our DOM with JavaScript,
as I did with this
<a href="https://joyheron.com/a11y-playground/show-more-pagination.html" target="_blank">“Show More” Pagination Example</a>,
we should consider whether we should move the focus to the new content after it has loaded.
</p>
<p>
In this case, we need to <strong>make absolutely sure</strong> that you
<strong>test the application with a screenreader and keyboard</strong>
to ensure that the focus is set correctly.
There is little that can break your UI more than incorrectly setting the focus.
</p>
</section>
<section>
<p>
I hope you enjoyed this little collection of tips and tricks for creating responsive and accessible web applications.
I also hope you were able to learn something that you can use in your next project.
Let's make a responsible web together.
</p>
<p>
If you have suggestions for other useful tricks or tips, please open an issue
on the <a href="https://github.com/joyheron/responsible-web-apps">GitHub Repo for this page</a>
and I will do my best to add it to this list.
</p>
</section>
</main>
<footer>
<p>
Supported by <a href="https://www.innoq.com/"><img src="/assets/innoq-logo--apricotpetrol.svg" alt="INNOQ" /></a>
</p>
<p>
<a href="https://www.innoq.com/de/datenschutz">Privacy Policy</a>
</p>
</footer>
<script src="https://unpkg.com/[email protected]/prism.js"></script>
<script src="resize-observer.js"></script>
<script>
class ToggleButton extends HTMLElement {
connectedCallback () {
this.button.hidden = false;
if (this.button.getAttribute("aria-expanded") !== "true") {
this.button.setAttribute("aria-expanded", "false");
this.target.classList.add("hide");
}
this.button.addEventListener("click", this.toggle.bind(this));
}
toggle (ev) {
let classList = this.target.classList;
if (classList.contains("hide")) {
classList.remove("hide");
this.button.setAttribute("aria-expanded", "true");
} else {
classList.add("hide");
this.button.setAttribute("aria-expanded", "false");
}
}
get button () {
return this.querySelector("button");
}
get target () {
return document.querySelector(this.getAttribute("data-target"));
}
}
customElements.define("toggle-button", ToggleButton);
</script>
<script>
document.body.classList.add('js');
</script>
</body>
</html>