Give every fastext destination a distinct mark; add skip-link, fix aria-label
All checks were successful
Deploy / deploy (push) Successful in 23s
All checks were successful
Deploy / deploy (push) Successful in 23s
Colours: "if the intent is to have them all different colors than lets have them all different colors." The locked eight only give five safe as full text (blue fails contrast, black's invisible on black), so the other three get their own treatments instead of sharing plain ink: Watch gets blue as a swatch-only accent (the button cap's colour, not the label), Activity gets an ordered-dither swatch (the other legal way to mark something without a ninth colour), and Settings alone keeps plain ink deliberately, as the one page with no live status of its own. Accessibility (items 1 and 3 from the design review, item 2 skipped per request — the header's download figure stays live and unmuted): - Added a skip-link, off-screen until focused, jumping past the 8-item top nav straight to each page's #main-content. - Renamed the nav's aria-label from "Fastext" (meaningless without the visual teletext context) to "Page navigation".
This commit is contained in:
parent
15936027de
commit
5dd49c505c
4 changed files with 79 additions and 28 deletions
28
CLAUDE.md
28
CLAUDE.md
|
|
@ -105,18 +105,22 @@ the user's Unraid home server. Server-side project directory is still named
|
||||||
under the header on every page but the index — those pages carry no
|
under the header on every page but the index — those pages carry no
|
||||||
other nav, so it needs no scroll to reach. The index overrides it
|
other nav, so it needs no scroll to reach. The index overrides it
|
||||||
with `HOME_FASTEXT`, the original four-entry row, as its own closing
|
with `HOME_FASTEXT`, the original four-entry row, as its own closing
|
||||||
line, since its body already lists every destination once. The
|
line, since its body already lists every destination once. Every
|
||||||
original four (Index/Library/Discover/System) keep their `r`/`g`/
|
destination gets its own distinct mark — the locked eight colours
|
||||||
`y`/`c` keyboard shortcut and colour; Requests also gets a colour
|
only give five that read as legible text on black, so getting to
|
||||||
(magenta, `--pending`) without a shortcut — a deliberate fit, not
|
eight took three treatments: five flat colours as text+swatch
|
||||||
an arbitrary fifth pick, since Requests already is "waiting on a
|
(Index/Library/Discover/System keep their `r`/`g`/`y`/`c` shortcut;
|
||||||
human" everywhere else magenta appears. That's the ceiling: the
|
Requests gets magenta/`--pending` with no shortcut — a deliberate
|
||||||
locked eight colours leave exactly five that read as legible text
|
fit, since Requests already is "waiting on a human" everywhere else
|
||||||
on black (blue fails contrast there, black is invisible), so Watch/
|
magenta appears); Watch gets blue as a swatch-only accent (`.k-blue`
|
||||||
Activity/Settings share plain ink rather than a sixth colour that
|
— blue fails contrast as body text, so only the small cell carries
|
||||||
doesn't exist in this palette. Whichever entry matches the current
|
it, not the row's own label); Activity gets an ordered-dither swatch
|
||||||
page renders as a `.fastext-here` span instead of a link — a page
|
(`.k-dither`, same mechanism as `--dither-mid`) rather than a flat
|
||||||
linking to itself did nothing, so it isn't offered as a click.
|
colour. Settings alone keeps plain ink with no swatch treatment —
|
||||||
|
deliberately, as the one page with no live status of its own.
|
||||||
|
Whichever entry matches the current page renders as a
|
||||||
|
`.fastext-here` span instead of a link — a page linking to itself
|
||||||
|
did nothing, so it isn't offered as a click.
|
||||||
- **Nothing is clipped, hidden, or sticky.** All seven page numbers and
|
- **Nothing is clipped, hidden, or sticky.** All seven page numbers and
|
||||||
every fastext entry stay visible at every width, reflowing to another
|
every fastext entry stay visible at every width, reflowing to another
|
||||||
row rather than scrolling, truncating, or folding into a menu. No
|
row rather than scrolling, truncating, or folding into a menu. No
|
||||||
|
|
|
||||||
34
app.py
34
app.py
|
|
@ -1303,24 +1303,34 @@ def service_urls():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Every destination, for the top of every page but the index. Order
|
# Every destination, for the top of every page but the index, each
|
||||||
# matches nav_items. The locked eight teletext colours leave exactly
|
# with its own distinct treatment. Order matches nav_items. The
|
||||||
# five that read as text on black without inventing a ninth tone or
|
# locked eight teletext colours only give five that read as full-
|
||||||
# borrowing a colour that already means something else here: the four
|
# saturation text on black (blue fails contrast there), so getting all
|
||||||
# with a real keyboard shortcut (r/g/y/c), plus magenta for Requests —
|
# eight distinct without inventing a ninth colour takes three
|
||||||
# not arbitrary, it's the same --pending magenta a request already
|
# treatments, not one:
|
||||||
# shows once approved elsewhere, and Requests IS "waiting on a human".
|
# - Five flat colours, text and swatch both: the four with a real
|
||||||
# Blue fails contrast as text and black is invisible on black, so the
|
# keyboard shortcut (r/g/y/c), plus magenta for Requests — not
|
||||||
# three destinations left over (Watch/Activity/Settings) share plain
|
# arbitrary, it's the same --pending magenta a request already
|
||||||
# ink rather than a sixth colour that doesn't exist in this palette.
|
# shows once approved elsewhere, and Requests IS "waiting on a
|
||||||
|
# human".
|
||||||
|
# - Blue as a swatch-only accent (Watch): legible as a small solid
|
||||||
|
# cell — "the physical coloured button on the remote" — but not as
|
||||||
|
# the row's own text colour, where it fails contrast.
|
||||||
|
# - An ordered dither swatch (Activity): the one other legal way to
|
||||||
|
# get a distinct mark out of this palette per base.css's own first
|
||||||
|
# discipline, without smuggling in a ninth hue.
|
||||||
|
# Settings keeps plain ink deliberately — the one purely administrative
|
||||||
|
# destination with no live status of its own, so it's the one page
|
||||||
|
# that gets no colour at all rather than an arbitrary last one.
|
||||||
FULL_FASTEXT = [
|
FULL_FASTEXT = [
|
||||||
("k-red", "r", "100", "Index", "/"),
|
("k-red", "r", "100", "Index", "/"),
|
||||||
("", "", "101", "Watch", "/watch"),
|
("k-blue", "", "101", "Watch", "/watch"),
|
||||||
("k-green", "g", "102", "Library", "/library"),
|
("k-green", "g", "102", "Library", "/library"),
|
||||||
("k-yellow", "y", "103", "Discover", "/discover"),
|
("k-yellow", "y", "103", "Discover", "/discover"),
|
||||||
("k-magenta", "", "104", "Requests", "/requests"),
|
("k-magenta", "", "104", "Requests", "/requests"),
|
||||||
("k-cyan", "c", "105", "System", "/system"),
|
("k-cyan", "c", "105", "System", "/system"),
|
||||||
("", "", "106", "Activity", "/activity"),
|
("k-dither", "", "106", "Activity", "/activity"),
|
||||||
("", "", "900", "Settings", "/settings"),
|
("", "", "900", "Settings", "/settings"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,26 @@
|
||||||
RULER (every bar is --bar-cells wide, no exceptions).
|
RULER (every bar is --bar-cells wide, no exceptions).
|
||||||
═══════════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ── Skip-link ──────────────────────────────────────────
|
||||||
|
Off-screen until it receives focus, then it's a plain field row
|
||||||
|
like any other — a solid colour ground, no border — sitting above
|
||||||
|
everything else so tabbing past it reaches the header next. */
|
||||||
|
.skip-link {
|
||||||
|
position: absolute;
|
||||||
|
top: -999px;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0.5em 1ch;
|
||||||
|
background: var(--field);
|
||||||
|
color: var(--ink);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
}
|
||||||
|
.skip-link:focus {
|
||||||
|
top: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Header row ─────────────────────────────────────────
|
/* ── Header row ─────────────────────────────────────────
|
||||||
Teletext's top line, unchanged for forty years: the page
|
Teletext's top line, unchanged for forty years: the page
|
||||||
number you are on, who is transmitting, and the clock. It
|
number you are on, who is transmitting, and the clock. It
|
||||||
|
|
@ -512,8 +532,10 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
/* The key's colour is carried by a solid cell at its head — the
|
/* The key's identity is carried by a cell at its head — the physical
|
||||||
physical coloured button on the remote. */
|
coloured button on the remote. Solid for most; k-blue and k-dither
|
||||||
|
override its background below, since a flat swatch can't carry
|
||||||
|
every one of the eight destinations without repeating a colour. */
|
||||||
.fastext a > span, .fastext-here > span {
|
.fastext a > span, .fastext-here > span {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -538,6 +560,15 @@ a.tt-row:focus-visible .tt-row-sub { color: var(--tt-black); }
|
||||||
.fastext .k-yellow { color: var(--voice); }
|
.fastext .k-yellow { color: var(--voice); }
|
||||||
.fastext .k-cyan { color: var(--live); }
|
.fastext .k-cyan { color: var(--live); }
|
||||||
.fastext .k-magenta { color: var(--pending); }
|
.fastext .k-magenta { color: var(--pending); }
|
||||||
|
/* Blue fails contrast as body text on black, so it marks the swatch
|
||||||
|
only — the button cap's colour, not the printed label — while the
|
||||||
|
row's own text stays legible ink. */
|
||||||
|
.fastext .k-blue::before { background: var(--field); }
|
||||||
|
.fastext .k-blue:hover, .fastext .k-blue:focus-visible { background: var(--field); }
|
||||||
|
/* The one other legal way to get a distinct mark out of eight colours
|
||||||
|
without a ninth: an ordered dither, same mechanism as --dither-mid,
|
||||||
|
in place of a flat swatch. */
|
||||||
|
.fastext .k-dither::before { background: var(--dither-mid); }
|
||||||
.fastext a:hover, .fastext a:focus-visible {
|
.fastext a:hover, .fastext a:focus-visible {
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@
|
||||||
</head>
|
</head>
|
||||||
<body data-timezone="{{ timezone }}" data-url-base="{{ request.script_root }}"
|
<body data-timezone="{{ timezone }}" data-url-base="{{ request.script_root }}"
|
||||||
data-pages='{{ nav_items | tojson }}'>
|
data-pages='{{ nav_items | tojson }}'>
|
||||||
|
{#- Skip-link: every page but the index now opens with a full nav
|
||||||
|
block before any real content — a keyboard or screen-reader visitor
|
||||||
|
would otherwise tab through all eight destinations on every single
|
||||||
|
visit. Off-screen until focused, then it's the first thing tabbing
|
||||||
|
into the page reaches. -#}
|
||||||
|
<a class="skip-link" href="#main-content">Skip to content</a>
|
||||||
<!--
|
<!--
|
||||||
THESIS: Media Centre is a broadcast information service, not a dashboard. It
|
THESIS: Media Centre is a broadcast information service, not a dashboard. It
|
||||||
refuses the bordered-card grid every self-hosted dashboard ships, this one
|
refuses the bordered-card grid every self-hosted dashboard ships, this one
|
||||||
|
|
@ -56,7 +62,7 @@ provenance.
|
||||||
index's own body already is the full nav. Everywhere else it leads,
|
index's own body already is the full nav. Everywhere else it leads,
|
||||||
right under the header, since it's the only nav those pages carry. -#}
|
right under the header, since it's the only nav those pages carry. -#}
|
||||||
{% set fastext_nav %}
|
{% set fastext_nav %}
|
||||||
<nav class="fastext{{ '' if page == 'home' else ' fastext-top' }}" aria-label="Fastext">
|
<nav class="fastext{{ '' if page == 'home' else ' fastext-top' }}" aria-label="Page navigation">
|
||||||
{% for cls, key, num, label, href in fastext %}
|
{% for cls, key, num, label, href in fastext %}
|
||||||
{% if num == here[1] %}
|
{% if num == here[1] %}
|
||||||
<span class="fastext-here" aria-current="page"><span>{{ num }} {{ label }}</span></span>
|
<span class="fastext-here" aria-current="page"><span>{{ num }} {{ label }}</span></span>
|
||||||
|
|
@ -71,7 +77,7 @@ provenance.
|
||||||
|
|
||||||
{% if page != 'home' %}{{ fastext_nav }}{% endif %}
|
{% if page != 'home' %}{{ fastext_nav }}{% endif %}
|
||||||
|
|
||||||
<main class="tt-page">
|
<main class="tt-page" id="main-content">
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue