Skip to content

fix(website): unescape JSON-LD strings, answer "is this Screenly OSE?" - #3222

Merged
vpetersson merged 1 commit into
Screenly:masterfrom
vpetersson-bot:seo-screenly-ose-rename
Jul 28, 2026
Merged

fix(website): unescape JSON-LD strings, answer "is this Screenly OSE?"#3222
vpetersson merged 1 commit into
Screenly:masterfrom
vpetersson-bot:seo-screenly-ose-rename

Conversation

@vpetersson-bot

Copy link
Copy Markdown
Contributor

Two changes, both found while investigating why the old product name converts so badly in Search Console.

1. Every JSON-LD string value carries literal quote characters

Go's html/template applies contextual auto-escaping inside <script>, so {{ .Title | jsonify }} is escaped a second time as a JS string. What ships is:

"headline": "\"Installation Options\""

instead of:

"headline": "Installation Options"

The JSON stays valid, which is why this went unnoticed — but the string value has stray quotes, so Google renders "Installation Options" (with quotes) in rich results.

19 affected values:

Where What
all 9 /docs/* pages TechArticle headline + description
home site description on SoftwareApplication
/faq/ all 29 Question names and Answer text

Fixed by appending | safeJS at all five jsonify-inside-<script> call sites in baseof.html. jsonify already emits correctly escaped JSON — the second escaping pass was pure corruption, and safeJS just stops it. Values come from data/faq.yaml and page frontmatter, not user input.

Verified: all 11 ld+json blocks across the built site parse as valid JSON with zero stray-quote values (was 19).

2. The site never mentioned "Screenly OSE" — its own former name

Search Console, last 90 days: 23 old-name queries, 1,962 impressions, 43 clicks — 2.19% CTR.

Query Impressions Clicks Position
screenly ose 1,253 17 6.8
screenly ose pi3 227 0 6.5
screenly ose pi 3 202 0 7.3
screenly ose demo 90 0 12.8

Now compare the queries where both names appear:

Query Impressions Clicks CTR
anthias (screenly ose) 31 16 51.6%
anthias screenly ose 20 9 45.0%

Same searchers, ~30× the CTR. People looking for the old name genuinely want this project — the site just never confirms they've landed in the right place. Every grep for "Screenly OSE" across content/, layouts/ and data/ came back empty.

Adds:

  • an FAQ entry — "Is Anthias the same as Screenly OSE?" — placed first in the About group. It inherits the existing FAQPage schema, so it's eligible as a rich result for exactly these queries. Cites the December 2022 announcement already linked from the repo README.
  • one muted line under the homepage hero: "Formerly Screenly OSE — same project, renamed in 2022."

Checks

  • bun run build with hugo 0.157.0 (the version pinned in deploy-website.yaml) — clean, 18 pages
  • FAQ schema re-verified: 29 questions, valid JSON, no stray quotes
  • website/assets/styles/style.css deliberately not included. My local Tailwind resolve produced 59 lines of churn unrelated to this change, and css:build regenerates it during deploy anyway.

Not changed

Worth recording, since it looks alarming in Search Console but isn't a defect:

  • /docs/, /features/ and /api/ show large zero-click impression counts. Those impressions come from brand queries (anthias, anthias digital signage) where the homepage ranks Manual for downloading the git repository doesn't work like expected #1 and these pages appear as sitelinks on the same SERP — users click the homepage. /api/ earns all 25 of its clicks from anthias api at position 1.5, the one query matching its intent. Working as intended.
  • digital signage raspberry pi has 4,509 impressions at position 4.0 with 0.2% CTR, while raspberry pi digital signage has 1,009 at position 4.5 with 5.0%. Same page, same position, 25× CTR gap — that difference lives in the SERP layout, not in this repo.

Opened from a fork: vpetersson-bot has read-only access here.

🤖 Generated with Claude Code

https://claude.ai/code/session_017Na8LvnqCPqMHRC5LwEbhB

Two things, both found while looking at why the old product name
converts so badly in Search Console.

1. Every JSON-LD string value is wrapped in literal quote characters.

   Go's html/template applies contextual auto-escaping inside <script>,
   so `{{ .Title | jsonify }}` gets escaped a second time as a JS
   string. The emitted value is "\"Installation Options\"" rather than
   "Installation Options". The JSON stays valid, which is why this went
   unnoticed, but every headline, description and FAQ question carries
   stray quotes into Google's rich results.

   19 affected values: TechArticle headline + description on all 9 docs
   pages, the site description, and all 29 FAQ questions and answers.
   Fixed by adding `| safeJS` after `jsonify` at all five call sites —
   jsonify already produces correctly escaped JSON, so the second pass
   was pure corruption. All 11 ld+json blocks now parse clean with zero
   stray-quote values.

2. The site never mentioned "Screenly OSE" — its own former name.

   Search Console, last 90 days: 23 old-name queries, 1,962 impressions,
   43 clicks (2.19% CTR). "screenly ose" alone is 1,253 impressions at
   position 6.8 for 17 clicks; "screenly ose pi3" and "screenly ose
   pi 3" are 429 impressions for zero clicks.

   Where the two names appear together, the same searchers convert an
   order of magnitude better: "anthias (screenly ose)" is 16 clicks from
   31 impressions (51.6%) and "anthias screenly ose" 9 from 20 (45%).
   People looking for the old name do want this project — the site just
   never confirms they are in the right place.

   Adds an FAQ entry (inherits the existing FAQPage schema, so it is
   eligible as a rich result) citing the December 2022 announcement
   linked from the repo README, plus one line under the homepage hero.

hugo 0.157.0 (the version pinned in deploy-website.yaml) builds clean.
@vpetersson-bot
vpetersson-bot requested a review from a team as a code owner July 28, 2026 08:43
@sonarqubecloud

Copy link
Copy Markdown

@vpetersson
vpetersson merged commit 248af07 into Screenly:master Jul 28, 2026
5 checks passed
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.

2 participants