web: css changes for accessibility

axum-login-upgrade
Manos Pitsidianakis 2023-04-27 19:01:14 +03:00
parent 3a22ea6887
commit 3b3665b40c
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
8 changed files with 197 additions and 77 deletions

View File

@ -162,7 +162,7 @@ async fn root(
name => &list.name,
posts => &posts,
months => &months,
body => &list.description.as_deref().unwrap_or_default(),
description => &list.description.as_deref().unwrap_or_default(),
root_url_prefix => &state.root_url_prefix,
list => Value::from_object(MailingList::from(list.clone())),
})

View File

@ -1,10 +1,10 @@
{% include "header.html" %}
<div class="body body-grid">
<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>
<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>
<form method="post" class="login-form login-ssh">
<label for="id_address">Email address:</label>
<input type="text" name="address" required="" id="id_address">
<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>
<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>
<form method="post" class="login-form login-ssh" aria-label="login form">
<label for="id_address" id="id_address_label">Email address:</label>
<input type="text" name="address" required="" id="id_address" aria-labelledby="id_address_label">
<label for="id_password">SSH signature:</label>
<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>
<input type="submit" value="login">

View File

@ -50,7 +50,7 @@
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
font-family:-apple-system,BlinkMacSystemFont,Roboto,Roboto Slab,Droid Serif,Segoe UI,system-ui,Arial,sans-serif;
font-size:1.125em
font-size:100%;
}
/* Remove unintuitive behaviour such as gaps around media elements. */
@ -63,6 +63,10 @@
overflow-wrap: break-word;
}
p {
line-height: 1.4;
}
h1,
h2,
h3,
@ -82,6 +86,9 @@
a.self-link::before {
content: "§";
/* increase surface area for clicks */
padding: 1rem;
margin: -1rem;
}
a.self-link {
@ -120,9 +127,14 @@
}
code {
font-family: var(--monospace-system-stack);
overflow-wrap: anywhere;
}
pre {
font-family: var(--monospace-system-stack);
}
input {
border: none;
}
@ -137,7 +149,14 @@
}
:root {
--monospace-system-stack: /* apple */ ui-monospace, SFMono-Regular, Menlo, Monaco,
/* windows */ "Cascadia Mono", "Segoe UI Mono", Consolas,
/* free unixes */ "Liberation Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
--text-primary: CanvasText;
--text-faded: GrayText;
--horizontal-rule: #88929d;
--code-foreground: #124;
--code-background: #8fbcbb;
--a-visited-text: var(--a-normal-text);
}
@ -303,7 +322,7 @@
body>main.layout {
width: 100%;
height: 99%;
height: 100%;
overflow-wrap: anywhere;
display: grid;
@ -328,6 +347,19 @@
grid-area: footer;
border-top: 2px inset;
margin-block-start: 1rem;
border-color: var(--text-link);
background-color: var(--text-primary-blue);
color: var(--text-invert);
}
main.layout>footer a[href] {
box-shadow: 2px 2px 2px black;
background: Canvas;
border: .3rem solid Canvas;
border-radius: 3px;
font-weight: bold;
font-family: var(--monospace-system-stack);
font-size: small;
}
main.layout>footer>* {
@ -349,6 +381,11 @@
margin-top: 1rem;
}
main.layout>div.body *:is(h2,h3,h4,h5,h6) {
padding-bottom: .3em;
border-bottom: 1px solid var(--horizontal-rule);
}
nav.main-nav {
padding: 0rem 1rem;
border: 1px solid var(--border-secondary);
@ -377,8 +414,8 @@
margin-left: auto;
}
main.layout>div.body h2 {
margin: 1rem;
main.layout>div.header h2.page-title {
margin: 1rem 0px;
}
nav.breadcrumbs {
@ -388,11 +425,12 @@
nav.breadcrumbs ol {
list-style-type: none;
padding-left: 0;
font-size: small;
}
/* If only the root crumb is visible, hide it to avoid unnecessary visual clutter */
li.crumb:only-child>span[aria-current="page"] {
--secs: 500ms;
--secs: 150ms;
transition: all var(--secs) linear;
color: transparent;
}
@ -403,23 +441,24 @@
}
.crumb, .crumb>a {
display: contents;
display: inline;
}
.crumb a::after {
display: inline-block;
color: var(--text-primary);
content: '>';
content: '>' / '';
font-size: 80%;
font-weight: bold;
padding: 0 3px;
}
.crumb span[aria-current="page"] {
color: GrayText;
color: var(--text-faded);
padding: 0.4rem;
margin-left: -0.4rem;
display: contents;
display: inline;
}
ul.messagelist {
@ -434,12 +473,14 @@
}
ul.messagelist>li {
padding: 0.5rem 1rem;
padding: 1rem 0.7rem;
--message-background: var(--icon-secondary);
background: var(--message-background);
border: .1rem solid var(--border-secondary);
border-radius: 0.2rem;
border: 1px outset var(--message-background);
border-radius: 2px;
font-weight: 400;
margin-block-end: 1.0rem;
color: #0d0b0b;
}
ul.messagelist>li>span.label {
@ -448,37 +489,63 @@
}
ul.messagelist>li.error {
--message-background: var(--background-critical);
--message-background: var(--icon-critical);
}
ul.messagelist>li.success {
--message-background: var(--background-success);
--message-background: var(--icon-success);
}
ul.messagelist>li.warning {
--message-background: var(--background-warning);
--message-background: var(--icon-warning);
}
ul.messagelist>li.info {
--message-background: var(--background-information);
--message-background: var(--icon-information);
}
div.preamble {
div.body>section {
display: flex;
flex-direction: column;
gap: 1rem;
}
div.body>section+section{
margin-top: 1rem;
}
div.calendar rt {
white-space: nowrap;
font-size: 50%;
-moz-min-font-size-ratio: 50%;
line-height: 1;
}
@supports not (display: ruby-text) {
/* Chrome seems to display it at regular size, so scale it down */
div.calendar rt {
scale: 50%;
font-size: 100%;
}
}
div.calendar rt {
display: ruby-text;
}
div.calendar th {
padding: 0.5rem;
opacity: 0.7;
text-align: center;
}
div.calendar tr {
text-align: right;
}
div.calendar tr,
div.calendar th {
text-align: right;
font-variant-numeric: tabular-nums;
font-family: monospace;
font-family: var(--monospace-system-stack);
}
div.calendar table {
@ -488,10 +555,14 @@
div.calendar td {
padding: 0.1rem 0.4rem;
font-size: 80%;
width: 2.3rem;
height: 2.3rem;
text-align: center;
}
div.calendar td.empty {
color: GrayText;
color: var(--text-faded);
}
div.calendar td:not(.empty) {
@ -499,11 +570,11 @@
}
div.calendar td:not(:empty) {
border: 1px solid var(--text-primary);
border: 1px solid var(--text-faded);
}
div.calendar td:empty {
background: GrayText;
background: var(--text-faded);
opacity: 0.2;
}
@ -540,6 +611,12 @@
border-top:none;
}
div.entries>div.entry>span.subject>a {
/* increase surface area for clicks */
padding: 1rem;
margin: -1rem;
}
div.entries>div.entry span.metadata.replies {
background: CanvasText;
border-radius: .6rem;
@ -551,7 +628,7 @@
div.entries>div.entry>span.metadata {
font-size: small;
color: GrayText;
color: var(--text-faded);
word-break: break-all;
}
@ -565,7 +642,7 @@
}
div.entries>div.entry span.value.empty {
color: GrayText;
color: var(--text-faded);
}
div.posts>div.entry>span.metadata>span.from {
@ -574,7 +651,7 @@
table.headers tr>th {
text-align: right;
color: GrayText;
color: var(--text-faded);
}
table.headers th[scope="row"] {
@ -601,7 +678,7 @@
td.message-id,
span.message-id{
color: GrayText;
color: var(--text-faded);
}
td.message-id:before,
span.message-id:before{
@ -622,7 +699,7 @@
}
td.faded,
span.faded {
color: GrayText;
color: var(--text-faded);
}
td.faded:is(:focus, :hover, :focus-visible, :focus-within),
span.faded:is(:focus, :hover, :focus-visible, :focus-within) {
@ -638,11 +715,32 @@
}
ul.lists li + li {
margin-top: 1rem;
margin-top: 0.2rem;
}
dl.lists dt {
font-weight: bold;
}
dl.lists dl,
dl.lists dd {
font-size: small;
}
dl.lists dd {
/* fallback in case margin-block-* is not supported */
margin-bottom: 1rem;
margin-block-start: 0.3rem;
margin-block-end: 1rem;
}
dl.lists dd.no-description {
color: var(--text-faded);
}
hr {
margin: 1rem 0rem;
border-bottom: 1px solid #88929d;
}
.command-line-example {
@ -650,25 +748,27 @@
display: inline-block;
ruby-align: center;
ruby-position: under;
padding: 0;
background: var(--background-information);
outline: 5px solid var(--background-information);
width: min-content;
background: var(--code-background);
outline: 1px inset var(--code-background);
border-radius: 1px;
color: var(--code-foreground);
font-weight: 500;
width: auto;
max-width: 90vw;
padding: 2px 7px;
padding: 1.2rem 0.8rem 1rem 0.8rem;
overflow-wrap: break-word;
overflow: auto;
white-space: pre;
}
textarea.key-or-sig-input {
font-family: monospace;
font-family: var(--monospace-system-stack);
font-size: 0.5rem;
font-weight: 400;
width: auto;
height: 29rem;
max-width: min(71ch, 75vw);
height: 26rem;
max-width: min(71ch, 100%);
overflow-wrap: break-word;
overflow: auto;
white-space: pre;
@ -686,9 +786,16 @@
line-height: 1rem;
}
mark.ssh-challenge-token {
font-family: var(--monospace-system-stack);
overflow-wrap: anywhere;
}
.body-grid {
display: grid;
/* fallback */
grid-template-columns: 1fr;
grid-template-columns: fit-content(100%);
grid-auto-rows: min-content;
row-gap: min(6vw, 1rem);
width: 100%;
@ -726,7 +833,7 @@
form.settings-form>fieldset>legend {
padding: .5rem 1rem;
border: 1px ridge GrayText;
border: 1px ridge var(--text-faded);
font-weight: bold;
font-size: small;
margin-left: 0.8rem;
@ -808,6 +915,11 @@
cursor: text;
}
input[type="text"], textarea {
outline: 3px inset #6969694a;
outline-offset: -5px;
}
button, ::file-selector-button, input:is([type="color"], [type="reset"], [type="button"], [type="submit"]) {
appearance: auto;
-moz-default-appearance: button;
@ -825,7 +937,7 @@
}
button:disabled, input:is([type="color"], [type="reset"], [type="button"], [type="submit"]):disabled {
color: GrayText;
color: var(--text-faded);
background: Field;
cursor: not-allowed;
}
@ -834,4 +946,13 @@
list-style: decimal outside;
padding-inline-start: 4rem;
}
.screen-reader-only {
position:absolute;
left:-500vw;
top:auto;
width:1px;
height:1px;
overflow:hidden;
}
</style>

View File

@ -15,12 +15,12 @@
{% endif %}
{% include "menu.html" %}
<div class="page-header">
{% if crumbs|length > 1 %}<nav aria-labelledby="breadcrumb-menu" class="breadcrumbs">
<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>
</nav>{% endif %}
{% if page_title %}
<h2>{{ page_title }}</h2>
<h2 class="page-title">{{ page_title }}</h2>
{% endif %}
<nav aria-label="Breadcrumb" class="breadcrumbs">
<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>
</nav>
{% if messages %}
<ul class="messagelist">
{% for message in messages %}

View File

@ -1,12 +1,13 @@
{% include "header.html" %}
<div class="body">
<p>{{ lists|length }} lists</p>
<!-- {{ lists|length }} lists -->
<div class="entry">
<ul class="lists">
<dl class="lists" aria-label="list of mailing lists">
{% for l in lists %}
<li><a href="{{ root_url_prefix }}{{ list_path(l.list.id) }}">{{ l.list.name }}</a></li>
<dt aria-label="mailing list name"><a href="{{ root_url_prefix }}{{ list_path(l.list.id) }}">{{ l.list.name }}</a></dt>
<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>
{% endfor %}
</ul>
</dl>
</div>
</div>
{% include "footer.html" %}

View File

@ -1,11 +1,11 @@
{% include "header.html" %}
<div class="body">
{% if list.description %}
<p>List description: {{ list.description }}</p>
<p title="mailing list description">List description: {{ list.description }}</p>
{% else %}
<p>No list description.</p>
<p title="mailing list description">No list description.</p>
{% endif %}
<br>
<br aria-hidden="true">
{% if current_user and not post_policy.no_subscriptions and subscription_policy.open %}
{% if user_context %}
<form method="post" action="{{ root_url_prefix }}{{ settings_path() }}" class="settings-form">
@ -22,8 +22,7 @@
{% endif %}
{% endif %}
{% if preamble %}
<hr>
<div id="preamble" class="preamble">
<section id="preamble" class="preamble" aria-label="mailing list instructions">
{% if preamble.custom %}
{{ preamble.custom|safe }}
{% else %}
@ -76,12 +75,9 @@
<p>List is not open for submissions.</p>
{% endif %}
{% endif %}
</div>
<hr>
{% else %}
<hr>
</section>
{% endif %}
<div class="list">
<section class="list" aria-hidden="true">
<h3 id="calendar">Calendar<a class="self-link" href="#calendar"></a></h3>
<div class="calendar">
{%- from "calendar.html" import cal %}
@ -89,18 +85,19 @@
{{ cal(date, hists, root_url_prefix, list.pk) }}
{% endfor %}
</div>
<hr>
</section>
<section aria-label="mailing list posts">
<h3 id="posts">Posts<a class="self-link" href="#posts"></a></h3>
<div class="posts entries">
<p>{{ posts | length }} post(s)</p>
<div class="posts entries" role="list" aria-label="list of mailing list posts">
<p>{{ posts | length }} post{{ posts|length|pluralize }}</p>
{% for post in posts %}
<div class="entry">
<span class="subject"><a href="{{ root_url_prefix }}{{ list_post_path(list.id, post.message_id) }}">{{ post.subject }}</a></span>
<span class="metadata">👤&nbsp;<span class="from">{{ post.address }}</span> 📆&nbsp;<span class="date">{{ post.datetime }}</span></span>
<span class="metadata">🪪 &nbsp;<span class="message-id">{{ post.message_id }}</span> &olarr;&nbsp;<span class="replies">{{ post.replies }}</span>
</div>
<div class="entry" role="listitem" aria-labelledby="post_link_{{ loop.index }}">
<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>
<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>
<span class="metadata"><span aria-hidden="true">🪪 </span><span class="message-id" title="e-mail Message-ID">{{ post.message_id }}</span></span>
</div>
{% endfor %}
</div>
</div>
</section>
</div>
{% include "footer.html" %}

View File

@ -1,6 +1,7 @@
{% include "header.html" %}
<div class="body">
<table class="headers">
<table class="headers" title="E-mail headers">
<caption class="screen-reader-only">E-mail headers</caption>
<tr>
<th scope="row">List:</th>
<td class="faded">{{ list.id }}</td>
@ -35,7 +36,7 @@
{% endif %}
</table>
<div class="post-body">
<pre>{{ body }}</pre>
<pre title="E-mail text content">{{ body }}</pre>
</div>
</div>
{% include "footer.html" %}

View File

@ -1,11 +1,11 @@
<nav class="main-nav">
<nav class="main-nav" aria-label="main menu" role="menu">
<ul>
<li><a href="{{ root_url_prefix }}/">Index</a></li>
<li><a href="{{ root_url_prefix }}{{ help_path() }}">Help&nbsp;&amp; Documentation</a></li>
<li><a role="menuitem" href="{{ root_url_prefix }}/">Index</a></li>
<li><a role="menuitem" href="{{ root_url_prefix }}{{ help_path() }}">Help&nbsp;&amp; Documentation</a></li>
{% if current_user %}
<li class="push">Settings: <a href="{{ root_url_prefix }}{{ settings_path() }}">{{ current_user.address }}</a></li>
<li class="push">Settings: <a role="menuitem" href="{{ root_url_prefix }}{{ settings_path() }}" title="User settings">{{ current_user.address }}</a></li>
{% else %}
<li class="push"><a href="{{ root_url_prefix }}{{ login_path() }}">Login with SSH OTP</a></li>
<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>
{% endif %}
</ul>
</nav>