Rework post page (lead, pager), add skip link, unified focus ring, reduced motion and keyboard-accessible menu
This commit is contained in:
1 parent
ae362639e3
commit
ba65b18d29
14 files changed
+544
-69
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user