← back to Thesetdecorator
TSD a11y/SEO: add sr-only h1 to 11 zero-h1 content pages (DTD verdict B)
91e0945aa1e782273b956134b4f8acf7d90649f8 · 2026-06-03 10:44:22 -0700 · Steve
11 pages (art-prints, best-sellers, catalog, films, print, roster, sdsa,
trades, videos, vintage, wall-treatments) had NO h1 anywhere — their visible
title was a bare h2. Added <h1 class=sr-only> with the page name (from the
first h2, entity-decoded) immediately inside <main>. Guarantees exactly one
h1 per page with ZERO rendered change (visible h2 untouched) — the
autonomous-safe choice (no visual QA available). Verified: 0 zero-h1 pages,
one h1 each, heading order clean, idempotent. Visible-h1 promotion (DTD opt A)
queued for Steve. Idempotent scripts/add-sr-only-h1.py.
Files touched
M public/art-prints.htmlM public/best-sellers.htmlM public/catalog.htmlM public/films.htmlM public/print.htmlM public/roster.htmlM public/sdsa.htmlM public/trades.htmlM public/videos.htmlM public/vintage.htmlM public/wall-treatments.htmlA scripts/add-sr-only-h1.py
Diff
commit 91e0945aa1e782273b956134b4f8acf7d90649f8
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 3 10:44:22 2026 -0700
TSD a11y/SEO: add sr-only h1 to 11 zero-h1 content pages (DTD verdict B)
11 pages (art-prints, best-sellers, catalog, films, print, roster, sdsa,
trades, videos, vintage, wall-treatments) had NO h1 anywhere — their visible
title was a bare h2. Added <h1 class=sr-only> with the page name (from the
first h2, entity-decoded) immediately inside <main>. Guarantees exactly one
h1 per page with ZERO rendered change (visible h2 untouched) — the
autonomous-safe choice (no visual QA available). Verified: 0 zero-h1 pages,
one h1 each, heading order clean, idempotent. Visible-h1 promotion (DTD opt A)
queued for Steve. Idempotent scripts/add-sr-only-h1.py.
---
public/art-prints.html | 1 +
public/best-sellers.html | 1 +
public/catalog.html | 1 +
public/films.html | 1 +
public/print.html | 1 +
public/roster.html | 1 +
public/sdsa.html | 1 +
public/trades.html | 1 +
public/videos.html | 1 +
public/vintage.html | 1 +
public/wall-treatments.html | 1 +
scripts/add-sr-only-h1.py | 51 +++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 62 insertions(+)
diff --git a/public/art-prints.html b/public/art-prints.html
index 7c06d62..5afafa7 100644
--- a/public/art-prints.html
+++ b/public/art-prints.html
@@ -66,6 +66,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Art Prints for Sale</h1>
<section class="section">
<header class="rail-header">
<h2>Art Prints for Sale</h2>
diff --git a/public/best-sellers.html b/public/best-sellers.html
index ebce612..1a21e4e 100644
--- a/public/best-sellers.html
+++ b/public/best-sellers.html
@@ -70,6 +70,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Best Sellers — Production-Vetted Wallcoverings</h1>
<section class="section">
<header class="rail-header">
<h2>Best Sellers — Production-Vetted Wallcoverings</h2>
diff --git a/public/catalog.html b/public/catalog.html
index 1bac8af..00cc682 100644
--- a/public/catalog.html
+++ b/public/catalog.html
@@ -49,6 +49,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Cleared Artwork & Designs</h1>
<section class="section">
<header class="rail-header">
<h2>Cleared Artwork & Designs</h2>
diff --git a/public/films.html b/public/films.html
index 2133f9f..eab9a90 100644
--- a/public/films.html
+++ b/public/films.html
@@ -87,6 +87,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Film Sources — Documented Pieces & Where They Came From</h1>
<section class="section">
<header class="rail-header">
<h2>Film Sources — Documented Pieces & Where They Came From</h2>
diff --git a/public/print.html b/public/print.html
index 8c1fe6b..adede6f 100644
--- a/public/print.html
+++ b/public/print.html
@@ -49,6 +49,7 @@
</header>
<main id="main" class="flow">
+ <h1 class="sr-only">Fast Print + Delivery</h1>
<header class="rail-header"><h2>Fast Print + Delivery</h2></header>
<section class="step" aria-labelledby="step-art">
diff --git a/public/roster.html b/public/roster.html
index 09372e6..6356eb0 100644
--- a/public/roster.html
+++ b/public/roster.html
@@ -52,6 +52,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Set Decorator Directory</h1>
<section class="section">
<header class="rail-header">
<h2>Set Decorator Directory</h2>
diff --git a/public/sdsa.html b/public/sdsa.html
index 75f4a52..8f0cbca 100644
--- a/public/sdsa.html
+++ b/public/sdsa.html
@@ -52,6 +52,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">SDSA Member Directory</h1>
<section class="section">
<header class="rail-header">
<h2>SDSA Member Directory</h2>
diff --git a/public/trades.html b/public/trades.html
index 4a8fd9e..fde943a 100644
--- a/public/trades.html
+++ b/public/trades.html
@@ -46,6 +46,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Industry News</h1>
<section class="section">
<header class="rail-header">
<h2>Industry News</h2>
diff --git a/public/videos.html b/public/videos.html
index 9ee4742..c2a14b2 100644
--- a/public/videos.html
+++ b/public/videos.html
@@ -49,6 +49,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Interviews & Talks</h1>
<section class="section">
<header class="rail-header" style="display:block">
<h2>Interviews & Talks</h2>
diff --git a/public/vintage.html b/public/vintage.html
index 7ce397a..0f544f1 100644
--- a/public/vintage.html
+++ b/public/vintage.html
@@ -61,6 +61,7 @@
}
</style>
<main id="main">
+ <h1 class="sr-only">Vintage Prop Dig</h1>
<section class="section">
<header class="rail-header">
<h2>Vintage Prop Dig</h2>
diff --git a/public/wall-treatments.html b/public/wall-treatments.html
index 7979470..63176de 100644
--- a/public/wall-treatments.html
+++ b/public/wall-treatments.html
@@ -46,6 +46,7 @@
</header>
<main id="main">
+ <h1 class="sr-only">Wall Treatments — Ombres</h1>
<section class="section">
<header class="rail-header">
<h2>Wall Treatments — Ombres</h2>
diff --git a/scripts/add-sr-only-h1.py b/scripts/add-sr-only-h1.py
new file mode 100644
index 0000000..e7033e7
--- /dev/null
+++ b/scripts/add-sr-only-h1.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+"""Idempotent: add a visually-hidden <h1> to TSD pages that have ZERO h1 (DTD verdict B).
+
+11 content pages used a bare <h2> as their visible main heading and had no <h1>
+anywhere. A page needs exactly one <h1> for SEO (strongest on-page heading
+signal) and a11y (screen-reader main-heading expectation). Chosen approach (DTD
+verdict B, autonomous context with no visual QA): insert <h1 class="sr-only">
+with the page name immediately inside <main id="main"> — guarantees one DOM h1
+with ZERO rendered-pixel change, leaving the visible h2 untouched. A future
+visible-h1 promotion (DTD option A) is queued for when Steve can QA typography.
+
+Page name = the page's first <h2> text (entity-decoded). Idempotent: skips any
+page that already has an <h1>. Reversible: single line inserted.
+ python3 scripts/add-sr-only-h1.py
+"""
+import os, re, glob, html as htmllib
+
+PUB = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "public")
+
+HAS_H1_RE = re.compile(r'<h1[ >]', re.IGNORECASE)
+MAIN_OPEN_RE = re.compile(r'<main\b[^>]*>[ \t]*\n', re.IGNORECASE)
+FIRST_H2_RE = re.compile(r'<h2[^>]*>(.*?)</h2>', re.IGNORECASE | re.DOTALL)
+
+changed, skipped, noanchor, noh2 = [], [], [], []
+for path in sorted(glob.glob(os.path.join(PUB, "*.html"))):
+ name = os.path.basename(path)
+ src = open(path, encoding="utf-8").read()
+ if HAS_H1_RE.search(src):
+ skipped.append(name); continue
+ m2 = FIRST_H2_RE.search(src)
+ if not m2:
+ noh2.append(name); continue # no visible title h2 — skip, don't guess
+ # page name from first h2 text: strip any inner tags, decode entities, collapse ws
+ title = re.sub(r'<[^>]+>', '', m2.group(1))
+ title = htmllib.unescape(title).strip()
+ title = re.sub(r'\s+', ' ', title)
+ if not title:
+ noh2.append(name); continue
+ mm = MAIN_OPEN_RE.search(src)
+ if not mm:
+ noanchor.append(name); continue
+ line = f' <h1 class="sr-only">{htmllib.escape(title)}</h1>\n'
+ out = src[:mm.end()] + line + src[mm.end():]
+ open(path, "w", encoding="utf-8").write(out)
+ changed.append(f"{name} (\"{title}\")")
+
+print(f"changed ({len(changed)}):")
+for c in changed: print(f" + {c}")
+print(f"skipped/already-have-h1: {len(skipped)}")
+if noh2: print(f"no-h2-title (skipped, not guessed): {', '.join(noh2)}")
+if noanchor: print(f"no <main> anchor: {', '.join(noanchor)}")
← 43bec6d backlog: loop #6 (noindex internal pages) + consolidate depl
·
back to Thesetdecorator
·
backlog: loop #7 (sr-only h1) + queue visible-h1 follow-up + 72384df →