Skip to content

fix(seo): lowercase language in plot page title#7206

Merged
MarkusNeusinger merged 12 commits into
mainfrom
claude/fix-plot-title-caps-YzsWy
May 18, 2026
Merged

fix(seo): lowercase language in plot page title#7206
MarkusNeusinger merged 12 commits into
mainfrom
claude/fix-plot-title-caps-YzsWy

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

  • The browser tab title for plot detail pages rendered as {spec-id} · Python · {library} | anyplot.ai — language was Title-Cased while spec-id and library stayed lowercase, which looked inconsistent.
  • Use the raw URL language value in SpecPage.tsx so the title now reads {spec-id} · python · {library} | anyplot.ai, matching the lowercase library tokens (e.g. matplotlib, ggplot2).
  • Breadcrumbs still use LANG_DISPLAY (Title Case) — that's the correct convention for breadcrumb labels and was left untouched.

Test plan

  • yarn type-check passes
  • yarn lint passes (only pre-existing warnings)
  • Verify on deploy: <title> on a detail page like /bar-grouped/python/matplotlib reads Grouped Bar Chart · python · matplotlib | anyplot.ai
  • Verify breadcrumb still shows Python (unchanged)

https://claude.ai/code/session_01EovzGTufzre1isXFPwA8k2


Generated by Claude Code

The browser tab title for plot detail pages was rendering
`{spec-id} · Python · {library}` with Python/R capitalized while spec-id
and library remained lowercase, which looked inconsistent. Use the raw
URL language value so the title now reads `{spec-id} · python · {library}`,
matching the lowercase library tokens. Breadcrumb display (which still
uses LANG_DISPLAY) keeps the capitalized form where Title Case is
appropriate.
Copilot AI review requested due to automatic review settings May 18, 2026 05:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the plot detail page <title> formatting to use the lowercase language token from the URL (e.g. python) instead of the display-mapped value (e.g. Python), improving visual consistency with lowercase library IDs.

Changes:

  • Use urlLanguage / languageFilter directly (no LANG_DISPLAY mapping) when building the page title suffix.
  • Update the inline comment to reflect the lowercase language convention for the browser tab title.
Comments suppressed due to low confidence (1)

app/src/pages/SpecPage.tsx:366

  • This change updates the document title language token to be lowercase (e.g. python), but existing frontend unit tests still assert Python in the title (see app/src/pages/SpecPage.test.tsx expectations). Update those tests (and any other title assertions) to match the new lowercase behavior so yarn test passes.
  // Page title surfaces language alongside library so the browser tab matches
  // the rendered image-title format `{spec-id} · {language} · {library}`. Hub
  // mode under a `?language=` filter also surfaces the language so users see
  // what's been narrowed down. Language is kept lowercase to match the
  // lowercase library name (e.g. `matplotlib`, `ggplot2`).
  const detailLanguage = mode === 'detail' && urlLanguage ? urlLanguage : null;
  const hubFilterLanguage = languageFilter ? languageFilter : null;
  const titleSuffix =
    mode === 'detail' && detailLanguage && selectedLibrary
      ? ` · ${detailLanguage} · ${selectedLibrary}`
      : hubFilterLanguage

Match the lowercased `python` token introduced in the previous commit
so frontend tests reflect the new title format.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Mirror the frontend change: rendered image titles now use
`{spec-id} · {language} · {library} · anyplot.ai` with `python`/`r`
lowercase to match the lowercase `{spec-id}` and `{library}` tokens.

Updates the canonical title definition, all examples in plot-generator.md,
the R example in library/ggplot2.md, and the SC-04 rubric line in
quality-criteria.md / quality-evaluator.md / ai-quality-review.md so
generation and review stay in sync.

Existing plot files generated since PR #7141 still carry the capitalized
form; they'll be normalised the next time daily-regen.yml picks them up.
Copilot AI review requested due to automatic review settings May 18, 2026 06:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment thread app/src/pages/SpecPage.tsx Outdated
Comment on lines +356 to +362
// Page title surfaces language alongside library so the browser tab matches
// the rendered image-title format `{spec-id} · {Language} · {library}`. Hub
// the rendered image-title format `{spec-id} · {language} · {library}`. Hub
// mode under a `?language=` filter also surfaces the language so users see
// what's been narrowed down.
const detailLanguage = mode === 'detail' && urlLanguage ? (LANG_DISPLAY[urlLanguage] || urlLanguage) : null;
const hubFilterLanguage = languageFilter ? (LANG_DISPLAY[languageFilter] || languageFilter) : null;
// what's been narrowed down. Language is kept lowercase to match the
// lowercase library name (e.g. `matplotlib`, `ggplot2`).
const detailLanguage = mode === 'detail' && urlLanguage ? urlLanguage : null;
const hubFilterLanguage = languageFilter ? languageFilter : null;
claude and others added 2 commits May 18, 2026 06:22
Per Copilot review: the previous fix relied on URL/query params already
being lowercase, but a mixed-case route like /scatter-basic/Python/matplotlib
or ?language=Python would still surface "Python" in the title. Force the
token to lowercase when building titleSuffix so the canonical form
(python/r) is always emitted regardless of URL casing.

Scoped to the title only; broader URL normalization (filtering, canonical
redirects) is out of scope for this PR.
Copilot AI review requested due to automatic review settings May 18, 2026 06:25
@MarkusNeusinger MarkusNeusinger enabled auto-merge (squash) May 18, 2026 06:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

app/src/pages/SpecPage.test.tsx:209

  • This block is labeled "language in title + breadcrumb", but it only asserts the document title. Since this PR explicitly aims to keep breadcrumbs Title-Cased, adding an assertion for the breadcrumb language label (or renaming the describe block to only mention title) would prevent regressions and align the test intent with what’s being verified.
  describe('language in title + breadcrumb', () => {
    it('includes ` · python · matplotlib` in the document title in detail mode', async () => {
      mockParams = { specId: 'scatter-basic', language: 'python', library: 'matplotlib' };
      mockSearchParams.delete('language');
      mockFetchSuccess();
      render(<SpecPage />);

      await waitFor(() => {
        expect(document.title).toContain('Basic Scatter Plot · python · matplotlib');
      });
    });

    it('includes ` · python` in the document title when ?language= is set in hub mode', async () => {
      mockParams = { specId: 'scatter-basic' };
      mockSearchParams.set('language', 'python');
      mockFetchSuccess();
      render(<SpecPage />);

      await waitFor(() => {
        expect(document.title).toContain('Basic Scatter Plot · python');
      });
      mockSearchParams.delete('language');
    });
  });

Comment on lines 356 to 365
// Page title surfaces language alongside library so the browser tab matches
// the rendered image-title format `{spec-id} · {Language} · {library}`. Hub
// the rendered image-title format `{spec-id} · {language} · {library}`. Hub
// mode under a `?language=` filter also surfaces the language so users see
// what's been narrowed down.
const detailLanguage = mode === 'detail' && urlLanguage ? (LANG_DISPLAY[urlLanguage] || urlLanguage) : null;
const hubFilterLanguage = languageFilter ? (LANG_DISPLAY[languageFilter] || languageFilter) : null;
// what's been narrowed down. Force-lowercase the language token so a
// mixed-case URL (`/Python/...` or `?language=Python`) still renders the
// canonical lowercase form (`python`/`r`) that matches `{spec-id}` and
// `{library}`.
const detailLanguage = mode === 'detail' && urlLanguage ? urlLanguage.toLowerCase() : null;
const hubFilterLanguage = languageFilter ? languageFilter.toLowerCase() : null;
const titleSuffix =
@MarkusNeusinger MarkusNeusinger disabled auto-merge May 18, 2026 06:29
claude and others added 2 commits May 18, 2026 06:30
Per Copilot's broader review: only lowercasing in the title left the rest
of the page (impl matching, gridImpls filter, canonical URL, analytics
pageview, navigate() back-writes) using the unnormalised value. A
mixed-case route like /Python/matplotlib or ?language=Python would
desync — title shows `python` while filtering finds no matching impls
and the canonical URL keeps the wrong case.

Force-lowercase `urlLanguage` and the `?language` query param once where
they're read, so every downstream comparison, URL, and tracker uses the
canonical form. The earlier .toLowerCase() calls in the title block are
now redundant and removed.

Breadcrumb keeps Title Case via LANG_DISPLAY['python'] -> 'Python'.

Also rename the test describe block to 'language in document title' —
the previous name promised a breadcrumb assertion that doesn't exist
(breadcrumbs render only as JSON-LD inside Helmet, which is mocked).
Copilot AI review requested due to automatic review settings May 18, 2026 06:55
@MarkusNeusinger MarkusNeusinger enabled auto-merge (squash) May 18, 2026 06:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread app/src/pages/SpecPage.tsx Outdated
Comment on lines +46 to +49
// Language tokens are canonical lowercase (`python`/`r`) everywhere they're
// compared against impl data, written back to URLs, or rendered. Force-lowercase
// at the read site so a mixed-case route like /Python/matplotlib or
// ?language=Python doesn't desync title, filtering, canonical URL, and analytics.
expect(document.title).toContain('Basic Scatter Plot · python');
});
mockSearchParams.delete('language');
});
Copilot AI review requested due to automatic review settings May 18, 2026 07:01
Per Copilot review:
- Reword the read-site comment so it no longer implies every UI surface
  must be lowercase — breadcrumbs intentionally render Title Case via
  LANG_DISPLAY.
- Add two SpecPage tests that pass mixed-case input (`/Python/matplotlib`
  path and `?language=Python` query) and assert the document title is
  emitted in canonical lowercase, locking in the new read-site
  normalisation.
@MarkusNeusinger MarkusNeusinger merged commit f993a8b into main May 18, 2026
7 checks passed
@MarkusNeusinger MarkusNeusinger deleted the claude/fix-plot-title-caps-YzsWy branch May 18, 2026 07:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment on lines 64 to 71
const mode: Mode = urlLibrary ? 'detail' : 'hub';
const selectedLibrary = urlLibrary || null;
// Carousel scope. The `?language=` query param expresses user intent —
// "I'm browsing R impls" — independent of which impl is currently rendered.
// It's set in both hub and detail mode. Without it, the carousel walks
// through ALL impls (cross-language). With it, the carousel stays scoped to
// that language. This is also what lets a future python.anyplot.ai-style
// subdomain pin the scope without users having to manually re-filter.
Comment on lines +48 to +52
// Force-lowercase at the read site so a mixed-case route like
// /Python/matplotlib or ?language=Python doesn't desync title, filtering,
// canonical URL, and analytics. Title-cased display labels for UI surfaces
// (e.g. breadcrumbs) still go through LANG_DISPLAY.
const urlLanguage = urlLanguageRaw?.toLowerCase();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants