Skip to content

SEO: update /graphing-libraries/ meta title and description - #460

Open
chriddyp wants to merge 3 commits into
masterfrom
seo-meta-titles-descriptions
Open

SEO: update /graphing-libraries/ meta title and description#460
chriddyp wants to merge 3 commits into
masterfrom
seo-meta-titles-descriptions

Conversation

@chriddyp

@chriddyp chriddyp commented Aug 4, 2026

Copy link
Copy Markdown
Member

Authored by Claude Code:

Updates the meta title and description for https://plotly.com/graphing-libraries/, per the Plotly: Metadata Optimization (7/26) sheet.

Changes

Title before Plotly Open Source Graphing Libraries
Title after Open-Source Graphing Libraries for Python & JavaScript | Plotly
Description before Interactive charts and maps for Python, R, Julia, Javascript, ggplot2, F#, MATLAB®, and Dash.
Description after Access interactive charts and maps for Python, and Javascript, for use with Dash, ..., Notebooks and LLMs. Graphing libraries for data analytics.

The new description leads with the platforms people actually search for (Dash, Notebooks, LLMs) rather than enumerating every language binding.

Both edits are in _includes/layouts/seo.html. The page's permalink is /api/ and /graphing-libraries/ is a redirect_from pointing at it, so the two conditional branches that already existed for those paths are updated together — /api/ is the page that carries the metadata.

Validation

Ran a full local bundle exec jekyll build --config _config_dev.yml, then parsed the generated _site/api/index.html (HTML-unescaped, whitespace collapsed):

TITLE: 'Open-Source Graphing Libraries for Python & JavaScript | Plotly'
LEN: 63
DESC: 'Access interactive charts and maps for Python, and Javascript, for use with Dash, Streamlit, Hex, Notebooks and LLMs.  Graphing libraries for data analytics.'
LEN: 157

Both match the sheet's recommended values and stated lengths exactly (63 and 157).

Also confirmed:

  • _site/graphing-libraries/index.html is the expected redirect stub (<title>Redirecting…), so no metadata is stranded on it.
  • Sibling language pages are untouched — e.g. _site/python/index.html still renders Plotly Python Graphing Library.
  • & is written as &amp; in the template and unescapes back to a literal & in the rendered tag.

CI checks, run exactly as .github/workflows/build.yml does:

python front-matter-ci.py _posts          -> End CI Checks! (all passed)
python check-or-enforce-order.py _posts/python      -> *Check Passed!*
python check-or-enforce-order.py _posts/python-v3   -> *Check Passed!*
python check-or-enforce-order.py _posts/r/          -> *Check Passed!*
python check-or-enforce-order.py _posts/matlab      -> *Check Passed!*
python check-or-enforce-order.py _posts/plotly_js   -> *Check Passed!*

Note on the rendered title

The built <title> carries surrounding whitespace from the pre-existing {% capture title %} block:

<title>
     Open-Source Graphing Libraries for Python &amp; JavaScript | Plotly
</title>

That's how every title in this repo already renders — browsers and crawlers collapse it, so it isn't a regression from this change. Happy to tighten the capture block with {%-/-%} trim markers in a follow-up if you'd like, but I kept this PR to the metadata copy.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YM2aoEs6ZN6SNrw9udWEnm

chriddyp and others added 3 commits August 3, 2026 22:16
From the "Plotly: Metadata Optimization" sheet (7/26), the row for
https://plotly.com/graphing-libraries/:

- title:       "Plotly Open Source Graphing Libraries"
            -> "Open-Source Graphing Libraries for Python & JavaScript | Plotly"
- description: now leads with the platforms people actually search for
               (Dash, Streamlit, Hex, Notebooks, LLMs) instead of listing
               every language binding.

The page's permalink is /api/ and /graphing-libraries/ redirects to it,
so both branches of the conditional are updated together. Verified with a
local `jekyll build`: title renders at 63 chars, description at 157.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM2aoEs6ZN6SNrw9udWEnm
- title:       "Plotly javascript graphing library in JavaScript"
            -> "Open Source JavaScript Graphing & Chart Library | Plotly"
- description: "A" (!) -> "Build interactive JavaScript charts and graphs
               with Plotly's open-source JavaScript chart library. Explore
               over 40+ advanced JavaScript data visualizations."

Two pre-existing bugs made the old values what they were.

1. The title condition on line 5 compares page.language against
   permalink-style values ('/julia/', '/r/', '/javascript/', ...), which can
   never match, so only the page.permalink == '/python/' arm ever fired.
   /javascript/ therefore fell through to "{{page.name | capitalize}} in
   {{language}}" -- and Liquid's capitalize downcases the rest of the string,
   producing "Plotly javascript graphing library in JavaScript". Rather than
   rewrite that condition (and risk changing other languages' titles), this
   adds an explicit /javascript/ arm above it.

2. The content= attributes were unquoted, so every description was truncated
   at its first space. /javascript/ served literally content=A. 530 of the
   533 pages with a description tag were affected; the 3 that weren't are the
   branches that wrap their text in literal double quotes to compensate. This
   quotes all five title/description attributes and normalizes the captured
   values once (strip padding from the multi-line capture blocks, drop the
   now-redundant literal quotes).

Verified with a full `jekyll build --config _config.yml` (the production
config -- _config_dev.yml excludes _posts/plotly_js, so /javascript/ isn't
built locally by default):

  /javascript/  title 56 chars, description 156 chars, both exact
  /api/         unchanged (63 / 157)
  /python/      title unchanged, description now complete at 258 chars
  /javascript/line-charts/  "Line charts in JavaScript" / "Over 33 examples..."

Across all 1620 built HTML files: 0 unquoted description attributes remain
(was 530 truncated), 0 word-collapse from the whitespace normalization, and
404.html's empty description is unchanged from before.

front-matter-ci.py and all five check-or-enforce-order.py runs pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM2aoEs6ZN6SNrw9udWEnm
Applies the requested changes from the SEO doc plus Nick Hartsell's five
review comments on it.

Copy (from the doc):
- H1: "Plotly JavaScript Open Source Graphing Library"
   -> "Open Source JavaScript Graphing & Chart Library", matching the new
      meta title.
- "declarative charting library" -> "declarative JavaScript charting library"
- "high performance 2D and 3D charting" -> "... 3D JavaScript charting"
- "Most plotly graphs" -> "Most Plotly graphs"
- Feature headings to title case: Sophisticated Chart Types, Fully
  Customizable, High Performance.
- Oxford commas: "report issues, or contribute", "R, and MATLAB".
- The collapsible section's summary becomes the "plotly.js Features" H2 from
  the doc, replacing "Read more about plotly.js features".

Review comments:
1. "The link here is unnecessary i would remove it" -- dropped the
   /javascript/is-plotly-free link from "free and open source". (The sidebar
   nav entry for that page stays.)
2. "unclear ... that this is how you open a collapsible section" -- the global
   `summary::-webkit-details-marker { display:none !important }` hid the
   native triangle, and the replacement arrow was scoped to .--sidebar-fixed
   only, so this <details> had no affordance at all. Added a scoped arrow
   (rotates when open), pointer cursor, and underlined heading-sized text.
3. "not clear to users that these are clickable links" -- .langindexlink
   ("More Fundamentals >>" etc.) had text-decoration:none and a muted slate
   colour, underlining only on hover. Now underlined and blue by default.
4. "This links to a 410. Remove it" -- removed "view the interactive
   version". All four Chart Studio ~user/N.embed links on the page are in
   fact 410 (verified with curl), so the other three, which wrapped images,
   are unwrapped too: the images stay, the dead links go.
5. "all of these have self referencing on page links that serve no purpose"
   -- the 19 section headings in mainlang_documentation_eg.html were
   <a href="#slug" id="slug">Text</a>, linking to themselves. Now <span>s
   that keep their id/name so the anchors still resolve. Extended the two
   a-scoped CSS rules (darkmode colour, ::before reset) to cover the spans.

Note: scss/ is compiled to the tracked all_static/css/*.css by gulp, which
needs Node <=11 and won't run here. The three rule changes were hand-applied
to the compiled files in their existing nested style, then verified equal to
`sass` output after normalising comments/whitespace/brace style -- the
js-splash--features, langindexlink and section-header>span rules match the
compiler byte for byte. A full recompile was deliberately avoided: it would
rewrite all 1109 lines (2046 diff lines) and pick up unrelated pre-existing
drift between scss/ and the committed css.

Verified with a production-config build, then screenshotted collapsed and
expanded: H1, arrow, blue underlined links and title-case headings all
render, and the turbulence image still shows now that its dead link wrapper
is gone. front-matter-ci.py and all five check-or-enforce-order.py runs pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM2aoEs6ZN6SNrw9udWEnm
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.

1 participant