Say what a page is, to anyone who did not open it - #122
Conversation
The <head> carried a title and nothing else. A link to openipc.org shared in Telegram, in a forum post, or shown in a search result therefore appeared as a bare URL with no summary and no image -- for a project whose reach is almost entirely word of mouth between people posting links to each other. Added: a description, an OG/Twitter card set, a canonical, and a theme colour. Three of those have a detail worth keeping: The canonical carries the path and not the query string. ?locale=ru is a rendering of the same page rather than a different one, and pointing all three translations at one canonical is what stops them competing with each other for the same content. og:image is absolute, because crawlers do not resolve a relative one, and it lives in public/ rather than the asset pipeline. OG images are fetched once and cached by URL by every consumer that has ever seen the link, so a fingerprint in the name would invalidate those caches for no gain. The two font faces every page renders body text in are preloaded. crossorigin is required even though they are same-origin -- fonts are fetched in CORS mode, and a preload without it is simply fetched a second time. The navbar and footer move into layouts/ partials. Nothing about them changes: same links, same order, same locale switcher, and the footer's icons still resolve through asset_path, which is not cosmetic -- production runs with config.assets.compile = false, so a literal /assets/... path is a 404, and 2ec9baa fixed exactly these lines. Extracting them now means the relaunch can rewrite the navigation in one file a reviewer can read, instead of inside a diff that also touches <head>. <main> gains a content_for(:fullwidth) switch for pages that lay out their own full-bleed sections. Flash messages keep the container in both branches, or they would run edge to edge. Pages that set nothing keep the wrapper they have always had, and a test holds that. The tests are the ones that would have caught each way a <head> goes wrong: a missing or duplicated description, a description that came back as "translation missing", a canonical that kept the query string, a relative og:image, an og-default.png that is not there, and a preload naming a font file the stylesheet does not generate. 238 runs, 903 assertions, 0 failures. i18n-tasks reports no missing keys and 102 unused, unchanged -- the new key being used is what keeps that number flat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hvzXBErEjEGMyRme2K3hi
PR Summary by QodoAdd localized page metadata and modular layout partials
AI Description
Diagram
High-Level Assessment
Files changed (7)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt |
Third of five PRs rebasing the June 11 relaunch onto master.
Stacked on #121 — base is
feat/design-foundation, because both rewritelayouts/application.html.erb. Merge #121 first and GitHub will retarget thisto
master.Still no new pages and no IA change. The navigation content is untouched;
only its location and the
<head>change.The problem
The
<head>carried a title and nothing else. A link to openipc.org shared inTelegram, in a forum post, or shown in a search result appeared as a bare URL —
no summary, no image. For a project whose reach is almost entirely people
posting links to each other, that is the wrong thing to be missing.
What's added
A description, an OG/Twitter card set, a canonical and a theme colour. Three
details worth review attention:
?locale=ruis arendering of the same page, not a different one. Pointing all three
translations at one canonical is what stops them competing for the same
content.
og:imageis absolute and lives inpublic/, not the asset pipeline.Crawlers don't resolve a relative
og:image, and OG images are fetched onceand cached by URL by every consumer that has seen the link — a fingerprint in
the name would invalidate those caches for no gain.
crossoriginon the font preloads is required even same-origin. Fonts arefetched in CORS mode; a preload without it is simply fetched a second time.
What moves
layouts/_navbar.html.erbandlayouts/_footer.html.erb, verbatim — samelinks, same order, same locale switcher. The footer's icons still resolve
through
asset_path, which is not cosmetic: production runs withconfig.assets.compile = false, so a literal/assets/...path is a 404, and2ec9baa fixed exactly these lines. Extracting now means the relaunch can rewrite
navigation in one file a reviewer can read, rather than inside a diff that also
touches
<head>.<main>gains acontent_for(:fullwidth)switch for pages that lay out theirown full-bleed sections. Flash messages keep the container in both branches or
they'd run edge to edge; pages that set nothing keep the wrapper they've always
had, and a test holds that.
Tests
test/controllers/page_metadata_test.rbasserts the ways a<head>actuallygoes wrong: a missing or duplicated description, a description that came back as
"translation missing", a canonical that kept its query string, a relative
og:image, anog-default.pngthat isn't there, and a preload naming a fontfile
_fonts.scssdoes not generate.bin/rails testi18n-tasks missingi18n-tasks unusedThe meta description is written in all three locales; the Chinese is
machine-quality and would benefit from a native-speaker pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_015hvzXBErEjEGMyRme2K3hi