Rework post page (lead, pager), add skip link, unified focus ring, reduced motion and keyboard-accessible menu

This commit is contained in:
tleininger committed 2026-10-01 16:48:52 +02:00
1 parent ae362639e3
commit ba65b18d29
14 files changed
+544 -69

No files matched your search

+129 -18
View File
@@ -11,20 +11,22 @@
--color-brand-dark: #1f5477;
--color-brand-tint: #eaf3f9;
/* The header bar's background: the IGMH blue at full strength. Its own token
(not --color-brand) both because it only ever carries white text (so it needs
no darkening) and because it must stay a deep blue in dark mode, where
--color-brand is lightened for text/links. */
--color-header: #328fc6;
/* The header bar's background: the IGMH blue, eased one step deeper. The
original #328fc6 carries white menu text at only 3.6:1, below AA for normal
text; #2a77a6 is the same hue at 4.9:1. Its own token (not --color-brand)
because it only ever carries white text and must stay a deep blue in dark
mode, where --color-brand is lightened for text/links. */
--color-header: #2a77a6;
/* Text and menu states on the header. White text, and two translucent white
washes for the menu pills: they lighten whatever part of the gradient sits
behind a link, so one value works from the blue end to the green end. The
active pill is the stronger one, so "where am I" outranks "where is the
mouse". */
/* Text and menu states on the header. White text, and two translucent dark
washes for the menu pills: they deepen whatever part of the gradient sits
behind a link, so one value works from the blue end to the green end and the
white text gains contrast instead of losing it (a white wash dropped the
active pill to 2.7:1). The active pill is the stronger one, so "where am I"
outranks "where is the mouse". */
--color-on-header: #fff;
--color-nav-hover: rgba(255, 255, 255, 0.12);
--color-nav-active: rgba(255, 255, 255, 0.2);
--color-nav-hover: rgba(0, 0, 0, 0.12);
--color-nav-active: rgba(0, 0, 0, 0.2);
/* Footer: a dark brand blue, so the page visibly ends there. Light text in two
steps (headings and the name brighter, links softer); both stay >= 7:1 on the
@@ -90,6 +92,11 @@
one; on the gradient the hero switches it to white. */
--color-focus: #1f5477;
/* The focus ring on the dark brand surfaces (header, hero gradient, footer),
where the dark ring above would vanish: white in both schemes, >= 4.9:1 on
every one of them. */
--color-focus-inverse: #fff;
/* Headings. Its own token (not --color-brand) so headings and links can differ:
in dark mode headings become a soft white while links keep the brand blue. */
--color-heading: #2b6f9c;
@@ -147,6 +154,27 @@
because heavy letters at large sizes otherwise look spaced out. */
--font-weight-heading: 800;
--letter-spacing-heading: -0.015em;
/* WCAG AA check of the colour pairs, light / dark (computed, October 2026).
Text needs 4.5:1, large text and focus rings/graphics 3:1.
text on card / page ............... 15.9 / 6.2 14.6 / 7.2
soft text on card / page .......... 7.5 / 6.2 6.9 / 7.2
soft text on green / blue tint .... 6.8 / 5.6 6.6 / 5.7
link (brand) on card / page ....... 5.4 / 7.5 5.0 / 8.7
link on green / blue tint ......... 5.0 / 6.7 4.8 / 6.9
heading on card / page ............ 5.4 / 9.3 5.0 / 10.9
white on header / gradient green .. 4.9 / 5.5 5.2 / 6.4
white on menu pill hover / active . 6.0 / 6.6 6.9 / 7.6
footer heading / link on footer ... 10.4 / 10.5 7.4 / 6.9
white on button / button hover .... 5.4 / 5.5 8.1 / 8.1
light button text ................. 8.1 / 9.7 (hover 7.2 / 8.3)
white on calendar band ............ 5.2 / 6.4
avatar blue / green ............... 7.2 / 8.6 5.9 / 6.7
focus ring on page / card ......... 7.5 / 11.0 8.1 / 9.4
white ring on header / footer ..... 4.9 / 5.5 11.4 / 16.6
green markers on card (graphic) ... 3.8 / 7.5
All pass. Exempt as pure decoration: the light green --color-accent (card
stripes, heading bars, the active-menu stroke), which never carries text. */
}
/* Dark mode: follows the visitor's OS setting automatically (prefers-color-scheme).
@@ -164,7 +192,8 @@
--color-brand-dark: #a3d5f4; /* hover goes lighter, not darker, on dark */
--color-brand-tint: #223038;
--color-header: #266f9c; /* the IGMH blue eased down, to sit calmly on dark */
--color-header: #266f9c; /* the IGMH blue eased down, to sit calmly on
dark; white on it 5.5:1 */
--color-footer: #10202e; /* a deep night blue: darker than the page, so
the band still reads as the page's end */
@@ -310,6 +339,56 @@ h1:focus {
outline: none;
}
/* Keyboard focus, one look for the whole site: a thick ring in --color-focus with a
little air between ring and element, so it never melts into a card's border or a
button's fill. :focus-visible, not :focus -- the ring shows for keyboard users
and stays away after a mouse click. Covers every focusable thing a page has:
links (and with them every card, which is one big link), buttons, the <summary>
of each disclosure, form fields and anything made focusable with tabindex. Dark
brand surfaces (header, hero, footer) switch the colour to
--color-focus-inverse. */
:is(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 2px;
}
/* <main> takes focus from the skip link (tabindex="-1") only so the next Tab
continues inside the content; a ring around the whole page would just be noise. */
main:focus {
outline: none;
}
/* Skip link: the first thing on every page, so keyboard and screen-reader users
can jump past the header menu straight to the content. Off screen until it gets
focus, then it drops in at the top left over the sticky header -- a link that
is only hidden visually stays in the tab order, display:none would remove it. */
.skip-link {
position: absolute;
top: var(--space-2);
left: var(--space-2);
z-index: 100;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-sm);
background: var(--color-button-light);
box-shadow: var(--shadow-lg);
color: var(--color-on-button-light);
font-weight: 700;
text-decoration: none;
transform: translateY(-200%);
}
/* It lands on the header, so it takes the light button look and the white ring
of the dark brand surfaces. Shown instantly, no transition: it is a jump aid,
not an effect. */
.skip-link:focus {
transform: none;
outline-color: var(--color-focus-inverse);
}
.skip-link:hover {
color: var(--color-on-button-light);
}
/* Text for screen readers only: off screen visually, still read aloud. For places
where the visible form is decoration (the calendar sheet) and the spoken form
must carry the full information. Not display:none -- that hides it from screen
@@ -353,11 +432,6 @@ h1:focus {
color var(--transition), border-color var(--transition);
}
.btn:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 2px;
}
.btn-primary {
background: var(--color-button);
border-color: var(--color-button);
@@ -382,6 +456,21 @@ h1:focus {
color: var(--color-brand);
}
/* Quiet = no frame at all, just the brand-blue label; hover lays the blue tint
behind it. For a way back ("Alle Beiträge") that should be easy to find but not
compete with the page's content. The negative inline margin lines the label up
with the text below while the hover tint still gets its padding. */
.btn-quiet {
margin-inline: calc(-1 * var(--space-3));
padding-inline: var(--space-3);
color: var(--color-brand);
}
.btn-quiet:hover {
background: var(--color-brand-tint);
color: var(--color-brand);
}
/* Header navigation links. These live here (global), not in MainLayout.razor.css:
NavLink renders plain <a> elements without a scoped-CSS attribute, so a scoped
".main-nav a" rule would not match them -- the links would fall back to the
@@ -403,6 +492,12 @@ h1:focus {
background: var(--color-nav-hover);
}
/* The dark default ring would vanish on the blue header (2.3:1); white stands out
on the whole gradient. */
.main-nav a:focus-visible {
outline-color: var(--color-focus-inverse);
}
/* NavLink adds .active to the current route's link: a stronger pill plus a short
green stroke under the text mark where the visitor is. The stroke is drawn by
::after, not a border, so it follows the text width inside the rounded pill. */
@@ -1029,3 +1124,19 @@ h1:focus {
border: none;
border-top: 1px solid var(--color-border);
}
/* Reduced motion: visitors who asked their system for less movement get none.
The site has no animations, only short transitions (hover colours, the
hamburger turning into an X, the mobile menu folding open); these are cut to
an instant here. Near zero rather than none, so anything that waits for a
transition to end still gets its event. */
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
}