Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: 3b3665b40cc3a3d06dd828e9716a9ebe55d9ec2b
Timestamp: Thu, 27 Apr 2023 16:01:14 +0000 (1 year ago)

+197 -77 +/-8 browse
web: css changes for accessibility
1diff --git a/web/src/main.rs b/web/src/main.rs
2index 52b7a00..c2bc13f 100644
3--- a/web/src/main.rs
4+++ b/web/src/main.rs
5 @@ -162,7 +162,7 @@ async fn root(
6 name => &list.name,
7 posts => &posts,
8 months => &months,
9- body => &list.description.as_deref().unwrap_or_default(),
10+ description => &list.description.as_deref().unwrap_or_default(),
11 root_url_prefix => &state.root_url_prefix,
12 list => Value::from_object(MailingList::from(list.clone())),
13 })
14 diff --git a/web/src/templates/auth.html b/web/src/templates/auth.html
15index 7df3deb..570c38e 100644
16--- a/web/src/templates/auth.html
17+++ b/web/src/templates/auth.html
18 @@ -1,10 +1,10 @@
19 {% include "header.html" %}
20 <div class="body body-grid">
21- <p>Sign <mark><code>{{ ssh_challenge }}</code></mark> with your previously configured key within <time title="{{ timeout_left }}" datetime="{{ timeout_left }}">{{ timeout_left }} minutes</time>. Example:</p>
22- <pre class="command-line-example">printf '<ruby><mark>{{ ssh_challenge }}</mark><rp>(</rp><rt>signin challenge</rt><rp>)</rp></ruby>' | ssh-keygen -Y sign -f <ruby>~/.ssh/id_rsa <rp>(</rp><rt>your account's key</rt><rp>)</rp></ruby> -n <ruby>{{ namespace }}<rp>(</rp><rt>namespace</rt><rp>)</rp></ruby></pre>
23- <form method="post" class="login-form login-ssh">
24- <label for="id_address">Email address:</label>
25- <input type="text" name="address" required="" id="id_address">
26+ <p aria-label="instructions">Sign <mark class="ssh-challenge-token" title="challenge token">{{ ssh_challenge }}</mark> with your previously configured key within <time title="{{ timeout_left }} minutes left" datetime="{{ timeout_left }}">{{ timeout_left }} minutes</time>. Example:</p>
27+ <pre class="command-line-example" title="example terminal command for UNIX shells that signs the challenge token with a public SSH key" >printf <ruby>'<mark>{{ ssh_challenge }}</mark>'<rp>(</rp><rt>signin challenge</rt><rp>)</rp></ruby> | ssh-keygen -Y sign -f <ruby>~/.ssh/id_rsa <rp>(</rp><rt>your account's key</rt><rp>)</rp></ruby> -n <ruby>{{ namespace }}<rp>(</rp><rt>namespace</rt><rp>)</rp></ruby></pre>
28+ <form method="post" class="login-form login-ssh" aria-label="login form">
29+ <label for="id_address" id="id_address_label">Email address:</label>
30+ <input type="text" name="address" required="" id="id_address" aria-labelledby="id_address_label">
31 <label for="id_password">SSH signature:</label>
32 <textarea class="key-or-sig-input" name="password" cols="15" rows="5" placeholder="-----BEGIN SSH SIGNATURE-----&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;mechangemechangemechangemechangemechangemechangemechangemechangemechan&#10;gemechangemechangemechangemechangemechangemechangemechangemechangemech&#10;angemechangemechangemechangemechangemechangemechangemechangemechangeme&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;mechangemechangemechangemechangemechangemechangemechangemechangemechan&#10;gemechangemechangemechangemechangemechangemechangemechangemechangemech&#10;angemechangemechangemechangemechangemechangemechangemechangemechangeme&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;mechangemechangemechangemechangemechangemechangemechangemechangemechan&#10;gemechangemechangemechangemechangemechangemechangemechangemechangemech&#10;angemechangemechangemechangemechangemechangemechangemechangemechangeme&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;mechangemechangemechangemechangemechangemechangemechangemechangemechan&#10;gemechangemechangemechangemechangemechangemechangemechangemechangemech&#10;angemechangemechangemechangemechangemechangemechangemechangemechangeme&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;mechangemechangemechangemechangemechangemechangemechangemechangemechan&#10;gemechangemechangemechangemechangemechangemechangemechangemechangemech&#10;angemechangemechangemechangemechangemechangemechangemechangemechangeme&#10;changemechangemechangemechangemechangemechangemechangemechangemechange&#10;chang=&#10;-----END SSH SIGNATURE-----&#10;" required="" id="id_password"></textarea>
33 <input type="submit" value="login">
34 diff --git a/web/src/templates/css.html b/web/src/templates/css.html
35index 5c06388..534d1a1 100644
36--- a/web/src/templates/css.html
37+++ b/web/src/templates/css.html
38 @@ -50,7 +50,7 @@
39 -webkit-font-smoothing:antialiased;
40 -moz-osx-font-smoothing:grayscale;
41 font-family:-apple-system,BlinkMacSystemFont,Roboto,Roboto Slab,Droid Serif,Segoe UI,system-ui,Arial,sans-serif;
42- font-size:1.125em
43+ font-size:100%;
44 }
45
46 /* Remove unintuitive behaviour such as gaps around media elements. */
47 @@ -63,6 +63,10 @@
48 overflow-wrap: break-word;
49 }
50
51+ p {
52+ line-height: 1.4;
53+ }
54+
55 h1,
56 h2,
57 h3,
58 @@ -82,6 +86,9 @@
59
60 a.self-link::before {
61 content: "ยง";
62+ /* increase surface area for clicks */
63+ padding: 1rem;
64+ margin: -1rem;
65 }
66
67 a.self-link {
68 @@ -120,9 +127,14 @@
69 }
70
71 code {
72+ font-family: var(--monospace-system-stack);
73 overflow-wrap: anywhere;
74 }
75
76+ pre {
77+ font-family: var(--monospace-system-stack);
78+ }
79+
80 input {
81 border: none;
82 }
83 @@ -137,7 +149,14 @@
84 }
85
86 :root {
87+ --monospace-system-stack: /* apple */ ui-monospace, SFMono-Regular, Menlo, Monaco,
88+ /* windows */ "Cascadia Mono", "Segoe UI Mono", Consolas,
89+ /* free unixes */ "Liberation Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
90 --text-primary: CanvasText;
91+ --text-faded: GrayText;
92+ --horizontal-rule: #88929d;
93+ --code-foreground: #124;
94+ --code-background: #8fbcbb;
95 --a-visited-text: var(--a-normal-text);
96 }
97
98 @@ -303,7 +322,7 @@
99
100 body>main.layout {
101 width: 100%;
102- height: 99%;
103+ height: 100%;
104 overflow-wrap: anywhere;
105
106 display: grid;
107 @@ -328,6 +347,19 @@
108 grid-area: footer;
109 border-top: 2px inset;
110 margin-block-start: 1rem;
111+ border-color: var(--text-link);
112+ background-color: var(--text-primary-blue);
113+ color: var(--text-invert);
114+ }
115+
116+ main.layout>footer a[href] {
117+ box-shadow: 2px 2px 2px black;
118+ background: Canvas;
119+ border: .3rem solid Canvas;
120+ border-radius: 3px;
121+ font-weight: bold;
122+ font-family: var(--monospace-system-stack);
123+ font-size: small;
124 }
125
126 main.layout>footer>* {
127 @@ -349,6 +381,11 @@
128 margin-top: 1rem;
129 }
130
131+ main.layout>div.body *:is(h2,h3,h4,h5,h6) {
132+ padding-bottom: .3em;
133+ border-bottom: 1px solid var(--horizontal-rule);
134+ }
135+
136 nav.main-nav {
137 padding: 0rem 1rem;
138 border: 1px solid var(--border-secondary);
139 @@ -377,8 +414,8 @@
140 margin-left: auto;
141 }
142
143- main.layout>div.body h2 {
144- margin: 1rem;
145+ main.layout>div.header h2.page-title {
146+ margin: 1rem 0px;
147 }
148
149 nav.breadcrumbs {
150 @@ -388,11 +425,12 @@
151 nav.breadcrumbs ol {
152 list-style-type: none;
153 padding-left: 0;
154+ font-size: small;
155 }
156
157 /* If only the root crumb is visible, hide it to avoid unnecessary visual clutter */
158 li.crumb:only-child>span[aria-current="page"] {
159- --secs: 500ms;
160+ --secs: 150ms;
161 transition: all var(--secs) linear;
162 color: transparent;
163 }
164 @@ -403,23 +441,24 @@
165 }
166
167 .crumb, .crumb>a {
168- display: contents;
169+ display: inline;
170 }
171
172 .crumb a::after {
173 display: inline-block;
174 color: var(--text-primary);
175 content: '>';
176+ content: '>' / '';
177 font-size: 80%;
178 font-weight: bold;
179 padding: 0 3px;
180 }
181
182 .crumb span[aria-current="page"] {
183- color: GrayText;
184+ color: var(--text-faded);
185 padding: 0.4rem;
186 margin-left: -0.4rem;
187- display: contents;
188+ display: inline;
189 }
190
191 ul.messagelist {
192 @@ -434,12 +473,14 @@
193 }
194
195 ul.messagelist>li {
196- padding: 0.5rem 1rem;
197+ padding: 1rem 0.7rem;
198+ --message-background: var(--icon-secondary);
199 background: var(--message-background);
200- border: .1rem solid var(--border-secondary);
201- border-radius: 0.2rem;
202+ border: 1px outset var(--message-background);
203+ border-radius: 2px;
204 font-weight: 400;
205 margin-block-end: 1.0rem;
206+ color: #0d0b0b;
207 }
208
209 ul.messagelist>li>span.label {
210 @@ -448,37 +489,63 @@
211 }
212
213 ul.messagelist>li.error {
214- --message-background: var(--background-critical);
215+ --message-background: var(--icon-critical);
216 }
217
218 ul.messagelist>li.success {
219- --message-background: var(--background-success);
220+ --message-background: var(--icon-success);
221 }
222
223 ul.messagelist>li.warning {
224- --message-background: var(--background-warning);
225+ --message-background: var(--icon-warning);
226 }
227
228 ul.messagelist>li.info {
229- --message-background: var(--background-information);
230+ --message-background: var(--icon-information);
231 }
232
233- div.preamble {
234+ div.body>section {
235 display: flex;
236 flex-direction: column;
237 gap: 1rem;
238 }
239
240+ div.body>section+section{
241+ margin-top: 1rem;
242+ }
243+
244+ div.calendar rt {
245+ white-space: nowrap;
246+ font-size: 50%;
247+ -moz-min-font-size-ratio: 50%;
248+ line-height: 1;
249+ }
250+ @supports not (display: ruby-text) {
251+ /* Chrome seems to display it at regular size, so scale it down */
252+ div.calendar rt {
253+ scale: 50%;
254+ font-size: 100%;
255+ }
256+ }
257+
258+ div.calendar rt {
259+ display: ruby-text;
260+ }
261+
262 div.calendar th {
263 padding: 0.5rem;
264 opacity: 0.7;
265+ text-align: center;
266+ }
267+
268+ div.calendar tr {
269+ text-align: right;
270 }
271
272 div.calendar tr,
273 div.calendar th {
274- text-align: right;
275 font-variant-numeric: tabular-nums;
276- font-family: monospace;
277+ font-family: var(--monospace-system-stack);
278 }
279
280 div.calendar table {
281 @@ -488,10 +555,14 @@
282
283 div.calendar td {
284 padding: 0.1rem 0.4rem;
285+ font-size: 80%;
286+ width: 2.3rem;
287+ height: 2.3rem;
288+ text-align: center;
289 }
290
291 div.calendar td.empty {
292- color: GrayText;
293+ color: var(--text-faded);
294 }
295
296 div.calendar td:not(.empty) {
297 @@ -499,11 +570,11 @@
298 }
299
300 div.calendar td:not(:empty) {
301- border: 1px solid var(--text-primary);
302+ border: 1px solid var(--text-faded);
303 }
304
305 div.calendar td:empty {
306- background: GrayText;
307+ background: var(--text-faded);
308 opacity: 0.2;
309 }
310
311 @@ -540,6 +611,12 @@
312 border-top:none;
313 }
314
315+ div.entries>div.entry>span.subject>a {
316+ /* increase surface area for clicks */
317+ padding: 1rem;
318+ margin: -1rem;
319+ }
320+
321 div.entries>div.entry span.metadata.replies {
322 background: CanvasText;
323 border-radius: .6rem;
324 @@ -551,7 +628,7 @@
325
326 div.entries>div.entry>span.metadata {
327 font-size: small;
328- color: GrayText;
329+ color: var(--text-faded);
330 word-break: break-all;
331 }
332
333 @@ -565,7 +642,7 @@
334 }
335
336 div.entries>div.entry span.value.empty {
337- color: GrayText;
338+ color: var(--text-faded);
339 }
340
341 div.posts>div.entry>span.metadata>span.from {
342 @@ -574,7 +651,7 @@
343
344 table.headers tr>th {
345 text-align: right;
346- color: GrayText;
347+ color: var(--text-faded);
348 }
349
350 table.headers th[scope="row"] {
351 @@ -601,7 +678,7 @@
352
353 td.message-id,
354 span.message-id{
355- color: GrayText;
356+ color: var(--text-faded);
357 }
358 td.message-id:before,
359 span.message-id:before{
360 @@ -622,7 +699,7 @@
361 }
362 td.faded,
363 span.faded {
364- color: GrayText;
365+ color: var(--text-faded);
366 }
367 td.faded:is(:focus, :hover, :focus-visible, :focus-within),
368 span.faded:is(:focus, :hover, :focus-visible, :focus-within) {
369 @@ -638,11 +715,32 @@
370 }
371
372 ul.lists li + li {
373- margin-top: 1rem;
374+ margin-top: 0.2rem;
375+ }
376+
377+ dl.lists dt {
378+ font-weight: bold;
379+ }
380+
381+ dl.lists dl,
382+ dl.lists dd {
383+ font-size: small;
384+ }
385+
386+ dl.lists dd {
387+ /* fallback in case margin-block-* is not supported */
388+ margin-bottom: 1rem;
389+ margin-block-start: 0.3rem;
390+ margin-block-end: 1rem;
391+ }
392+
393+ dl.lists dd.no-description {
394+ color: var(--text-faded);
395 }
396
397 hr {
398 margin: 1rem 0rem;
399+ border-bottom: 1px solid #88929d;
400 }
401
402 .command-line-example {
403 @@ -650,25 +748,27 @@
404 display: inline-block;
405 ruby-align: center;
406 ruby-position: under;
407- padding: 0;
408
409- background: var(--background-information);
410- outline: 5px solid var(--background-information);
411- width: min-content;
412+ background: var(--code-background);
413+ outline: 1px inset var(--code-background);
414+ border-radius: 1px;
415+ color: var(--code-foreground);
416+ font-weight: 500;
417+ width: auto;
418 max-width: 90vw;
419- padding: 2px 7px;
420+ padding: 1.2rem 0.8rem 1rem 0.8rem;
421 overflow-wrap: break-word;
422 overflow: auto;
423 white-space: pre;
424 }
425
426 textarea.key-or-sig-input {
427- font-family: monospace;
428+ font-family: var(--monospace-system-stack);
429 font-size: 0.5rem;
430 font-weight: 400;
431 width: auto;
432- height: 29rem;
433- max-width: min(71ch, 75vw);
434+ height: 26rem;
435+ max-width: min(71ch, 100%);
436 overflow-wrap: break-word;
437 overflow: auto;
438 white-space: pre;
439 @@ -686,9 +786,16 @@
440 line-height: 1rem;
441 }
442
443+ mark.ssh-challenge-token {
444+ font-family: var(--monospace-system-stack);
445+ overflow-wrap: anywhere;
446+ }
447+
448 .body-grid {
449 display: grid;
450+ /* fallback */
451 grid-template-columns: 1fr;
452+ grid-template-columns: fit-content(100%);
453 grid-auto-rows: min-content;
454 row-gap: min(6vw, 1rem);
455 width: 100%;
456 @@ -726,7 +833,7 @@
457
458 form.settings-form>fieldset>legend {
459 padding: .5rem 1rem;
460- border: 1px ridge GrayText;
461+ border: 1px ridge var(--text-faded);
462 font-weight: bold;
463 font-size: small;
464 margin-left: 0.8rem;
465 @@ -808,6 +915,11 @@
466 cursor: text;
467 }
468
469+ input[type="text"], textarea {
470+ outline: 3px inset #6969694a;
471+ outline-offset: -5px;
472+ }
473+
474 button, ::file-selector-button, input:is([type="color"], [type="reset"], [type="button"], [type="submit"]) {
475 appearance: auto;
476 -moz-default-appearance: button;
477 @@ -825,7 +937,7 @@
478 }
479
480 button:disabled, input:is([type="color"], [type="reset"], [type="button"], [type="submit"]):disabled {
481- color: GrayText;
482+ color: var(--text-faded);
483 background: Field;
484 cursor: not-allowed;
485 }
486 @@ -834,4 +946,13 @@
487 list-style: decimal outside;
488 padding-inline-start: 4rem;
489 }
490+
491+ .screen-reader-only {
492+ position:absolute;
493+ left:-500vw;
494+ top:auto;
495+ width:1px;
496+ height:1px;
497+ overflow:hidden;
498+ }
499 </style>
500 diff --git a/web/src/templates/header.html b/web/src/templates/header.html
501index eef1a3b..d86dcf3 100644
502--- a/web/src/templates/header.html
503+++ b/web/src/templates/header.html
504 @@ -15,12 +15,12 @@
505 {% endif %}
506 {% include "menu.html" %}
507 <div class="page-header">
508+ {% if crumbs|length > 1 %}<nav aria-labelledby="breadcrumb-menu" class="breadcrumbs">
509+ <ol id="breadcrumb-menu" role="menu" aria-label="Breadcrumb menu">{% for crumb in crumbs %}<li class="crumb" aria-describedby="bread_{{ loop.index }}">{% if loop.last %}<span role="menuitem" id="bread_{{ loop.index }}" aria-current="page" title="current page">{{ crumb.label }}</span>{% else %}<a role="menuitem" id="bread_{{ loop.index }}" href="{{ root_url_prefix }}{{ crumb.url }}" tabindex="0">{{ crumb.label }}</a>{% endif %}</li>{% endfor %}</ol>
510+ </nav>{% endif %}
511 {% if page_title %}
512- <h2>{{ page_title }}</h2>
513+ <h2 class="page-title">{{ page_title }}</h2>
514 {% endif %}
515- <nav aria-label="Breadcrumb" class="breadcrumbs">
516- <ol>{% for crumb in crumbs %}{% if loop.last %}<li class="crumb"><span aria-current="page" title="current page">{{ crumb.label }}</span></li>{% else %}<li class="crumb"><a href="{{ root_url_prefix }}{{ crumb.url }}">{{ crumb.label }}</a></li>{% endif %}{% endfor %}</ol>
517- </nav>
518 {% if messages %}
519 <ul class="messagelist">
520 {% for message in messages %}
521 diff --git a/web/src/templates/lists.html b/web/src/templates/lists.html
522index 768884c..eb47baa 100644
523--- a/web/src/templates/lists.html
524+++ b/web/src/templates/lists.html
525 @@ -1,12 +1,13 @@
526 {% include "header.html" %}
527 <div class="body">
528- <p>{{ lists|length }} lists</p>
529+ <!-- {{ lists|length }} lists -->
530 <div class="entry">
531- <ul class="lists">
532+ <dl class="lists" aria-label="list of mailing lists">
533 {% for l in lists %}
534- <li><a href="{{ root_url_prefix }}{{ list_path(l.list.id) }}">{{ l.list.name }}</a></li>
535+ <dt aria-label="mailing list name"><a href="{{ root_url_prefix }}{{ list_path(l.list.id) }}">{{ l.list.name }}</a></dt>
536+ <dd aria-label="mailing list description"{% if not l.list.description %} class="no-description"{% endif %}>{{ l.list.description if l.list.description else "no description" }}</dd>
537 {% endfor %}
538- </ul>
539+ </dl>
540 </div>
541 </div>
542 {% include "footer.html" %}
543 diff --git a/web/src/templates/lists/list.html b/web/src/templates/lists/list.html
544index 00c246a..113b326 100644
545--- a/web/src/templates/lists/list.html
546+++ b/web/src/templates/lists/list.html
547 @@ -1,11 +1,11 @@
548 {% include "header.html" %}
549 <div class="body">
550 {% if list.description %}
551- <p>List description: {{ list.description }}</p>
552+ <p title="mailing list description">List description: {{ list.description }}</p>
553 {% else %}
554- <p>No list description.</p>
555+ <p title="mailing list description">No list description.</p>
556 {% endif %}
557- <br>
558+ <br aria-hidden="true">
559 {% if current_user and not post_policy.no_subscriptions and subscription_policy.open %}
560 {% if user_context %}
561 <form method="post" action="{{ root_url_prefix }}{{ settings_path() }}" class="settings-form">
562 @@ -22,8 +22,7 @@
563 {% endif %}
564 {% endif %}
565 {% if preamble %}
566- <hr>
567- <div id="preamble" class="preamble">
568+ <section id="preamble" class="preamble" aria-label="mailing list instructions">
569 {% if preamble.custom %}
570 {{ preamble.custom|safe }}
571 {% else %}
572 @@ -76,12 +75,9 @@
573 <p>List is not open for submissions.</p>
574 {% endif %}
575 {% endif %}
576- </div>
577- <hr>
578- {% else %}
579- <hr>
580+ </section>
581 {% endif %}
582- <div class="list">
583+ <section class="list" aria-hidden="true">
584 <h3 id="calendar">Calendar<a class="self-link" href="#calendar"></a></h3>
585 <div class="calendar">
586 {%- from "calendar.html" import cal %}
587 @@ -89,18 +85,19 @@
588 {{ cal(date, hists, root_url_prefix, list.pk) }}
589 {% endfor %}
590 </div>
591- <hr>
592+ </section>
593+ <section aria-label="mailing list posts">
594 <h3 id="posts">Posts<a class="self-link" href="#posts"></a></h3>
595- <div class="posts entries">
596- <p>{{ posts | length }} post(s)</p>
597+ <div class="posts entries" role="list" aria-label="list of mailing list posts">
598+ <p>{{ posts | length }} post{{ posts|length|pluralize }}</p>
599 {% for post in posts %}
600- <div class="entry">
601- <span class="subject"><a href="{{ root_url_prefix }}{{ list_post_path(list.id, post.message_id) }}">{{ post.subject }}</a></span>
602- <span class="metadata">๐Ÿ‘ค&nbsp;<span class="from">{{ post.address }}</span> ๐Ÿ“†&nbsp;<span class="date">{{ post.datetime }}</span></span>
603- <span class="metadata">๐Ÿชช &nbsp;<span class="message-id">{{ post.message_id }}</span> &olarr;&nbsp;<span class="replies">{{ post.replies }}</span>
604- </div>
605+ <div class="entry" role="listitem" aria-labelledby="post_link_{{ loop.index }}">
606+ <span class="subject"><a id="post_link_{{ loop.index }}" href="{{ root_url_prefix }}{{ list_post_path(list.id, post.message_id) }}">{{ post.subject }}</a>&nbsp;<span class="metadata replies" title="reply count">{{ post.replies }} repl{{ post.replies|pluralize("y","ies") }}</span></span>
607+ <span class="metadata"><span aria-hidden="true">๐Ÿ‘ค&nbsp;</span><span class="from" title="post author">{{ post.address }}</span><span aria-hidden="true"> ๐Ÿ“†&nbsp;</span><span class="date" title="post date">{{ post.datetime }}</span></span>
608+ <span class="metadata"><span aria-hidden="true">๐Ÿชช </span><span class="message-id" title="e-mail Message-ID">{{ post.message_id }}</span></span>
609+ </div>
610 {% endfor %}
611 </div>
612- </div>
613+ </section>
614 </div>
615 {% include "footer.html" %}
616 diff --git a/web/src/templates/lists/post.html b/web/src/templates/lists/post.html
617index 18611bc..27d0c68 100644
618--- a/web/src/templates/lists/post.html
619+++ b/web/src/templates/lists/post.html
620 @@ -1,6 +1,7 @@
621 {% include "header.html" %}
622 <div class="body">
623- <table class="headers">
624+ <table class="headers" title="E-mail headers">
625+ <caption class="screen-reader-only">E-mail headers</caption>
626 <tr>
627 <th scope="row">List:</th>
628 <td class="faded">{{ list.id }}</td>
629 @@ -35,7 +36,7 @@
630 {% endif %}
631 </table>
632 <div class="post-body">
633- <pre>{{ body }}</pre>
634+ <pre title="E-mail text content">{{ body }}</pre>
635 </div>
636 </div>
637 {% include "footer.html" %}
638 diff --git a/web/src/templates/menu.html b/web/src/templates/menu.html
639index ead407e..646615c 100644
640--- a/web/src/templates/menu.html
641+++ b/web/src/templates/menu.html
642 @@ -1,11 +1,11 @@
643- <nav class="main-nav">
644+ <nav class="main-nav" aria-label="main menu" role="menu">
645 <ul>
646- <li><a href="{{ root_url_prefix }}/">Index</a></li>
647- <li><a href="{{ root_url_prefix }}{{ help_path() }}">Help&nbsp;&amp; Documentation</a></li>
648+ <li><a role="menuitem" href="{{ root_url_prefix }}/">Index</a></li>
649+ <li><a role="menuitem" href="{{ root_url_prefix }}{{ help_path() }}">Help&nbsp;&amp; Documentation</a></li>
650 {% if current_user %}
651- <li class="push">Settings: <a href="{{ root_url_prefix }}{{ settings_path() }}">{{ current_user.address }}</a></li>
652+ <li class="push">Settings: <a role="menuitem" href="{{ root_url_prefix }}{{ settings_path() }}" title="User settings">{{ current_user.address }}</a></li>
653 {% else %}
654- <li class="push"><a href="{{ root_url_prefix }}{{ login_path() }}">Login with SSH OTP</a></li>
655+ <li class="push"><a role="menuitem" href="{{ root_url_prefix }}{{ login_path() }}" title="login with one time password using your SSH key">Login with SSH OTP</a></li>
656 {% endif %}
657 </ul>
658 </nav>