Fix the two Lighthouse findings: muted contrast and meta description
Accessibility (95 -> 100). Light `--muted` #7a7568 was 4.14:1 on `--paper` and 3.72:1 on `--paper-2`, under the 4.5:1 AA floor for normal text — and muted is the ears line, the nav, bylines, the footer, table headers and placeholders, so it was most of the page's small type. An audit of every text token against both surfaces turned up one more: light `--warn` #8a6d1f at 4.42 / 3.97. Both are darkened just enough to clear 4.5 on the darker of the two surfaces, at constant hue and saturation, so the warm grey and the ochre read the same: --muted #7a7568 -> #6b665a 4.14/3.72 -> 5.16/4.63 --warn #8a6d1f -> #7d631c 4.42/3.97 -> 5.16/4.63 Everything else already passed on both surfaces (light ink 15.54/13.96, ink-2 7.83/7.03, accent 6.73/6.04, loved 5.44/4.89, good 5.29/4.76, down 5.96/5.36), as did the whole dark set against #151513 / #1e1d1a (muted 5.31/4.90 is its floor), so the dark blocks are untouched and stay identical to each other. `.badge` tints sit on near-paper, so the new token values carry it. SEO (91 -> 100). The layout had no `<meta name="description">`. `Page` now carries one — `DEFAULT_DESCRIPTION` for the site, so all ~26 `Page::new` callers keep their signature — with `with_description` for the pages worth writing one for: the issue page (issue number, date and the counts the masthead already prints), the archive, the empty-latest landing page and sign-in. Dashboard pages keep the default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
This commit is contained in:
1 parent
239125a8e5
commit
04bf14f710
6 files changed
+105
-9
No files matched your search
+35
-4
@@ -11,6 +11,19 @@ use crate::web::issue::{self, Download};
|
||||
use crate::web::session::{AuthSession, Viewer};
|
||||
use crate::web::{Html, Page, WebError};
|
||||
|
||||
/// The `<meta name="description">` for the landing page before the first issue
|
||||
/// of the day exists.
|
||||
const NO_ISSUE_DESCRIPTION: &str = concat!(
|
||||
"The latest issue of The Daily EPUB is not out yet; ",
|
||||
"the next one lands tomorrow morning."
|
||||
);
|
||||
|
||||
/// The `<meta name="description">` for the archive index.
|
||||
const ARCHIVE_DESCRIPTION: &str = concat!(
|
||||
"Every issue of The Daily EPUB, newest first: browse the archive by month ",
|
||||
"and read or download any past morning's paper."
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PublicIssue {
|
||||
pub date: Date,
|
||||
@@ -140,6 +153,20 @@ impl From<&Issue> for PublicIssue {
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicIssue {
|
||||
/// The `<meta name="description">` for an issue page: the same counts the
|
||||
/// masthead prints, in a sentence a search result can show.
|
||||
fn description(&self) -> String {
|
||||
format!(
|
||||
"Issue No. {} for {}: {} articles across {} sections.",
|
||||
self.issue_number,
|
||||
self.display_date,
|
||||
self.article_count,
|
||||
self.sections.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn domain(raw: &str) -> String {
|
||||
url::Url::parse(raw)
|
||||
.ok()
|
||||
@@ -193,7 +220,8 @@ pub async fn latest(
|
||||
let Some(date) = state.db.latest_issue_date().await? else {
|
||||
let viewer = auth.user().await.map(Viewer::from);
|
||||
let response = Html(IssuePublicTemplate {
|
||||
page: Page::new("Latest issue", viewer, "latest"),
|
||||
page: Page::new("Latest issue", viewer, "latest")
|
||||
.with_description(NO_ISSUE_DESCRIPTION),
|
||||
issue: empty_issue(),
|
||||
downloads: Vec::new(),
|
||||
empty: true,
|
||||
@@ -220,9 +248,11 @@ pub async fn show_issue(
|
||||
return Ok(public_cache(response, &headers));
|
||||
}
|
||||
let active_nav = if view.is_latest { "latest" } else { "archive" };
|
||||
let issue = PublicIssue::from(&view.issue);
|
||||
let response = Html(IssuePublicTemplate {
|
||||
page: Page::new(format!("Issue {date}"), None, active_nav),
|
||||
issue: PublicIssue::from(&view.issue),
|
||||
page: Page::new(format!("Issue {date}"), None, active_nav)
|
||||
.with_description(issue.description()),
|
||||
issue,
|
||||
downloads: Vec::new(),
|
||||
empty: false,
|
||||
})
|
||||
@@ -259,7 +289,8 @@ pub async fn archive(
|
||||
"Issue archive",
|
||||
auth.user().await.map(Viewer::from),
|
||||
"archive",
|
||||
),
|
||||
)
|
||||
.with_description(ARCHIVE_DESCRIPTION),
|
||||
months,
|
||||
})
|
||||
.into_response();
|
||||
|
||||
Reference in new issue
Block a user