Phase 1 — internal linking:
- Real [related] index (tags/keywords/category) in hugo.toml; rewrite
related-posts.html to use .Related instead of "first 4 posts", which
had left 12/20 posts with zero inbound links.
- Add tags to 6 previously untagged posts.
Phase 2 — de-cannibalization of the transactional cluster:
- Soft-301 mechanism (redirectTo -> meta refresh + noindex,follow) in baseof.
- xslt-online-editor-guide redirects to the app (pure duplicate of home).
- xslt-validator-online retargeted informational; drop the "common errors"
section that cannibalized the errors hub, link to it instead.
- Migrate why-xslt-playground-online-editor into /about/ with an alias.
- Remove the duplicate <h1> on the app landing page.
Phase 3 — consolidate authority on the money domain:
- baseURL -> https://xsltplayground.com/blog/ (served via NPM reverse
proxy to GitHub Pages; subdomain will 301 to the subpath).
- Rewrite internal blog links to /blog/... and cross-domain app links
to xsltplayground.com/blog/...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KZntQUfw463NW4ftgSjWw5
GSC shows the dedicated function pages already rank well — xpath-contains
at pos 3.3, xpath-substring at pos 5.8 — but the string-functions roundup
post competed with them for the same terms and often outranked the better
page at a worse position (e.g. it took "substring" impressions at pos 17
while xpath-substring sat at pos 5.8). Classic self-cannibalization.
- string-functions: link every function in the at-a-glance table to its
dedicated /xslt/functions/ reference page (18 of 19 exist). The roundup
now consolidates authority into the pages that already win and keeps the
head term "xslt string functions" for itself, instead of splitting
signals across both.
- template-matching (ranks pos 5.4 for "template match", no cannibalism):
link the "mode" and "apply-templates" mentions to their reference pages —
it previously linked to nothing but the homepage.
Hugo build verified locally (345 pages, exit 0); links resolve.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDk5guu51FjFxQMjgrrekW
Devs search literal error messages ("XPST0017", "Content is not allowed in
prolog") with almost no competition. New /xslt/errors/ section mirrors the
function-reference pattern that already ranks:
- 17 error pages: XPST0017/0081/0008, XPTY0004, XPDY0002, XTSE0630/0010,
XTDE1490, XTMM9000, XTRE0540, FORX0002, FODC0002, FORG0001, SXXP0003 and
the three big XML parser messages — each with meaning, causes and
before/after fixes
- Pillar post "XSLT error messages explained" (triage table by error-code
prefix, links all 17)
- Post "XSLT vs DataWeave" targeting the enterprise/MuleSoft audience
- Cross-links: functions hub ↔ errors hub; FirstUpper for category headings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgybX3QbAWVzb9ZnroCA79
Function pages ranked at pos 7-9 with 1.2% CTR (6.2k impressions/90d) and
the version pages sat at pos 22-24 with almost no internal links.
- Function/element pages: SERP title now "name — XPath function: syntax &
examples" (one layout change, applies to all 230 pages)
- Reference hub: own title, intro copy, category sections with anchor nav
(was a flat 230-item list ranking at pos 29.7)
- Cross-linking: version badge on every reference page links to its
/xslt-2-0/ / /xslt-3-0/ page (~160 new internal links); version pages
link back to the hub + 6 popular entries each; homepage links the hub
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgybX3QbAWVzb9ZnroCA79
- blog: "Best free online XSLT 3.0 testers compared (2026)" — answer-first
comparison post to improve LLM/AI-engine and search visibility
- README: keyword-rich header with live tool link, Saxon HE 3.0
differentiator, feature list and suggested GitHub topics
- frontend: raise EthicalAds auto-refresh throttle from 45s to 60s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgybX3QbAWVzb9ZnroCA79
Phase 1 (home): add explicit "viewer", "validate xslt online" and
"xslt transformation online" coverage in copy, meta keywords, a new
section and a FAQ entry (+ JSON-LD) to lift queries stuck at position 8-10.
Phase 2 (landing pages): add unique runnable examples with input/output
(for-each-group on /xslt-2-0/, maps + fold-left on /xslt-3-0/) plus
internal links to related blog posts and pre/code styling.
Phase 3 (blog): xsl-online-tester gets links to both version landing pages,
a FAQ block and related guides; xslt-string-functions gets a snippet-bait
summary table and related links.
Adds scripts/seo_report.py to pull GSC + GA4 metrics for tracking progress.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
adsbygoogle.js (28KB) + consent framework (126KB + 32KB) were loading
on every blog page despite inArticleSlot being empty — no ads shown,
186KB of JS downloaded for nothing. Mobile LCP: 11.1s → expected ~2-3s.
Keep the google-adsense-account meta tag for account verification.
Script now conditional on both client AND slot being configured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. Title: "XSLT Tester & Validator Online — Free, XSLT 1.0/2.0/3.0"
Description: leads with "Run, test and validate" action verbs.
Targets low-CTR queries: xslt tester, xsl validator (pos 6-8, CTR <1%).
2. Sitemap: add lastmod 2026-05-26 to all entries to signal freshness
and accelerate Googlebot crawl of /xslt-2-0/ and /xslt-3-0/
(both 0 impressions — never crawled despite being live for 3 weeks).
3. Blog internal links: update 3 posts to link directly to
/xslt-2-0/ and /xslt-3-0/ landing pages instead of root,
giving Googlebot anchor text and link equity for those URLs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A) Blog: add prominent playground-cta card at the end of each post.
Styled with brand gradient border, eyebrow + headline + CTA button.
Remove the buried related-cta line from related-posts.html.
B) App: load a meaningful XSLT welcome example for first-time visitors
(no localStorage) and auto-run it immediately (autoRunReady=true),
so new organic users see live output instead of a blank editor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds google-adsense-account meta for publisher ca-pub-1549720748100858
to both xsltplayground.com and blog.xsltplayground.com.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Article pages (single.html): two-column grid layout — article + 160px
sidebar with sticky EA ad (id="xsltplayground-blog")
- Non-article pages (home, list): keep existing sticky header bar
(id="xsltplayground-blog-header")
- Move EA script to <head> in baseof.html so it loads on all pages
regardless of placement; remove script tag from ethicalads partial
- has-ethicalads body class now only applied on non-article pages
(no 64px header-offset shift on article pages)
- Sidebar hidden on viewports <= 860px (mobile/tablet)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Colors data embedded in the XML input document, avoiding the local
file reference that fails in the sandbox.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 6 xsl:merge-related articles had tryUrls that failed at runtime because
doc() tried to load external XML files that don't exist in the backend sandbox.
Replaced with self-contained XSLTs using inline variable data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a second ad unit (text type) just before the "Open live example"
CTA on all /xslt/functions/ pages. Increases ad visibility on long
reference pages where the sticky header unit may scroll out of view.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- App: parse ?xslt=, ?xml=, ?version=, ?title= query params on load
(base64url-encoded) to pre-populate the editor from external links
- App: Share button in XSLT toolbar generates a preload URL for the
current tab and copies it to clipboard (icon flips to ✓ for 2s)
- Icon: add share and check icons
- Blog: 225 reference pages now include a tryUrl frontmatter field
with the first example pre-encoded; layout renders "Open live
example in XSLT Playground →" CTA pointing directly to that URL
- Script: scripts/add_try_urls.py to regenerate tryUrl fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full coverage of XSLT 1.0, 2.0 and 3.0 elements and XPath functions:
- 59 XSLT elements (xsl:stylesheet → xsl:use-accumulators)
- 170 XPath functions (1.0 node/string/numeric/boolean, 2.0 sequence/
date/QName/string, 3.0 HOF/map/array/JSON/streaming)
Each page: description, parameters table, return value, 2 runnable
Saxon examples with input XML + stylesheet + output, notes, cross-links.
xsltCompletions.js: all 229 entries now have blogSlug for hover links.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update index.html title, description, keywords to include xslt 3.0, validator, saxon
- Update JSON-LD schema with full feature list and XSLT 3.0
- Change footer link from "News" to "Blog & Tutorials", update URL to blog.xsltplayground.com
- New articles: online editor guide, validator guide, string functions, xsl:for-each-group
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move site/ Hugo source from gh-pages to main so the workflow can trigger correctly
- Fix publish-hugo.yml: now in main, builds and deploys to gh-pages:docs/ via peaceiris/actions-gh-pages
- Add 3 new SEO articles: XSLT 3.0 features, beginners guide, template matching
- Add llms.txt to blog and frontend so ChatGPT/Gemini can recommend the tool better
- Remove stale /site mkdocs ignore rule, add site/public to gitignore instead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>