← back to Thesetdecorator
TSD a11y: add autocomplete to personal-identity form fields (WCAG 1.3.5, DTD verdict A)
818a6517244c7558944ebce90800feb7418932f9 · 2026-06-03 12:39:36 -0700 · Steve
Conservative scope — tagged 11 unambiguous personal-identity inputs across
submit/job-board/decorator (email->email, tel->tel, name->name, website->url,
agency->organization). Deliberately skipped ambiguous/context fields
(display_name, shared_folder_url, job metadata, verify/linkedin/headshot URLs,
bio) where a wrong token degrades autofill UX — risk I can't verify
autonomously. Idempotent scripts/form-autocomplete.py; verified correct tokens,
well-formed tags, ambiguous fields untouched. Invisible. Static — queued deploy.
Files touched
M public/decorator.htmlM public/job-board.htmlM public/submit.htmlA scripts/form-autocomplete.py
Diff
commit 818a6517244c7558944ebce90800feb7418932f9
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 3 12:39:36 2026 -0700
TSD a11y: add autocomplete to personal-identity form fields (WCAG 1.3.5, DTD verdict A)
Conservative scope — tagged 11 unambiguous personal-identity inputs across
submit/job-board/decorator (email->email, tel->tel, name->name, website->url,
agency->organization). Deliberately skipped ambiguous/context fields
(display_name, shared_folder_url, job metadata, verify/linkedin/headshot URLs,
bio) where a wrong token degrades autofill UX — risk I can't verify
autonomously. Idempotent scripts/form-autocomplete.py; verified correct tokens,
well-formed tags, ambiguous fields untouched. Invisible. Static — queued deploy.
---
public/decorator.html | 10 +++----
public/job-board.html | 6 ++--
public/submit.html | 6 ++--
scripts/form-autocomplete.py | 65 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/public/decorator.html b/public/decorator.html
index d8a817d..dd119ae 100644
--- a/public/decorator.html
+++ b/public/decorator.html
@@ -58,8 +58,8 @@
<form method="dialog">
<h2 style="margin-top:0">Claim this profile</h2>
<p class="muted" style="font-size:0.9rem">We'll verify you're <strong id="claim-target-name"></strong> by checking the link you provide against the IMDb entry. Once verified, your bio, city, agency, contact, headshot, and additional credits replace the placeholder fields here.</p>
- <p><label>Your name<br><input id="claim-name" type="text" required style="width:100%"></label></p>
- <p><label>Your email (where verification confirmation goes)<br><input id="claim-email" type="email" required style="width:100%"></label></p>
+ <p><label>Your name<br><input id="claim-name" type="text" required style="width:100%" autocomplete="name"></label></p>
+ <p><label>Your email (where verification confirmation goes)<br><input id="claim-email" type="email" required style="width:100%" autocomplete="email"></label></p>
<p><label>Verification link — IMDb / agency / SDSA profile that proves this is you<br><input id="claim-verify" type="url" required style="width:100%" placeholder="https://www.imdb.com/name/nm…"></label></p>
<hr>
<p><label>City<br>
@@ -73,10 +73,10 @@
<option>Other</option>
</select>
</label></p>
- <p><label>Agency or representation<br><input id="claim-agency" type="text" style="width:100%" placeholder="e.g. UTA · Gersh · Verve"></label></p>
+ <p><label>Agency or representation<br><input id="claim-agency" type="text" style="width:100%" placeholder="e.g. UTA · Gersh · Verve" autocomplete="organization"></label></p>
<p><label>Public LinkedIn URL (optional)<br><input id="claim-linkedin" type="url" style="width:100%"></label></p>
- <p><label>Public phone (optional, shown on profile)<br><input id="claim-phone" type="tel" style="width:100%"></label></p>
- <p><label>Public email (optional, shown on profile)<br><input id="claim-pubemail" type="email" style="width:100%"></label></p>
+ <p><label>Public phone (optional, shown on profile)<br><input id="claim-phone" type="tel" style="width:100%" autocomplete="tel"></label></p>
+ <p><label>Public email (optional, shown on profile)<br><input id="claim-pubemail" type="email" style="width:100%" autocomplete="email"></label></p>
<p><label>Headshot URL (optional)<br><input id="claim-headshot" type="url" style="width:100%"></label></p>
<p><label>Short bio (optional, 1-3 sentences)<br><textarea id="claim-bio" rows="3" style="width:100%"></textarea></label></p>
<div style="display:flex;gap:0.5rem;justify-content:flex-end;margin-top:1rem">
diff --git a/public/job-board.html b/public/job-board.html
index f6ef4ab..dd6b00e 100644
--- a/public/job-board.html
+++ b/public/job-board.html
@@ -132,10 +132,10 @@
<option>union hall</option>
</select>
</label></p>
- <p><label>Contact value<br><input id="j-contact" type="text" required placeholder="email / phone / agency name" maxlength="200"></label></p>
+ <p><label>Contact value<br><input id="j-contact" type="text" required placeholder="email / phone / agency name" maxlength="200" autocomplete="name"></label></p>
<hr style="grid-column:1/-1;border:none;border-top:1px solid var(--rule);margin:0.5rem 0">
- <p><label>Your name<br><input id="j-sname" type="text" required maxlength="80"></label></p>
- <p><label>Your email<br><input id="j-semail" type="email" required maxlength="120"></label></p>
+ <p><label>Your name<br><input id="j-sname" type="text" required maxlength="80" autocomplete="name"></label></p>
+ <p><label>Your email<br><input id="j-semail" type="email" required maxlength="120" autocomplete="email"></label></p>
<p style="grid-column:1/-1"><label>Your union/role (optional)<br><input id="j-sunion" type="text" placeholder="e.g. IATSE Local 800 / Production Designer" maxlength="60"></label></p>
</div>
<p style="display:flex;gap:0.5rem;align-items:center;margin-top:1rem"><button class="btn primary" type="submit">Submit for moderation</button> <span id="j-status" class="muted" style="font-size:0.85rem"></span></p>
diff --git a/public/submit.html b/public/submit.html
index 051ba19..f319106 100644
--- a/public/submit.html
+++ b/public/submit.html
@@ -101,7 +101,7 @@
<div class="row">
<div>
<label>Your Google email (the one that owns the Drive folder)</label>
- <input type="email" name="email" required placeholder="you@gmail.com">
+ <input type="email" name="email" required placeholder="you@gmail.com" autocomplete="email">
</div>
<div>
<label>Display name on the card (optional)</label>
@@ -115,11 +115,11 @@
<div class="row">
<div>
<label>Website / portfolio (optional)</label>
- <input type="url" name="website" placeholder="https://yourportfolio.com">
+ <input type="url" name="website" placeholder="https://yourportfolio.com" autocomplete="url">
</div>
<div>
<label>Real name (for tax records — never public)</label>
- <input type="text" name="real_name" placeholder="Steve Photographer">
+ <input type="text" name="real_name" placeholder="Steve Photographer" autocomplete="name">
</div>
</div>
<div>
diff --git a/scripts/form-autocomplete.py b/scripts/form-autocomplete.py
new file mode 100644
index 0000000..02e6085
--- /dev/null
+++ b/scripts/form-autocomplete.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+"""Idempotent: add autocomplete to unambiguous personal-identity form fields
+(WCAG 1.3.5 Identify Input Purpose, DTD verdict A — conservative scope).
+
+Tokens are added ONLY to fields that unambiguously collect the user's own
+identity/contact data, where the token is mechanically certain. Ambiguous /
+context-dependent fields (display_name, shared_folder_url, job metadata,
+verify/linkedin/headshot URLs, bio) are deliberately left untouched — a wrong
+token degrades UX via incorrect autofill, and autofill can't be verified in an
+autonomous run. Invisible change (attribute only).
+
+Matching is by the input's id= or name= (whichever the page uses). Idempotent:
+skips an input that already has autocomplete. Reversible.
+ python3 scripts/form-autocomplete.py
+"""
+import os, re
+
+PUB = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "public")
+
+# file -> { id_or_name : token } (conservative, unambiguous personal-identity fields only)
+PLAN = {
+ "submit.html": {
+ "email": "email", "website": "url", "real_name": "name",
+ },
+ "job-board.html": {
+ "j-contact": "name", "j-sname": "name", "j-semail": "email",
+ },
+ "decorator.html": {
+ "claim-name": "name", "claim-email": "email", "claim-agency": "organization",
+ "claim-phone": "tel", "claim-pubemail": "email",
+ },
+}
+
+def add_token(html, key, token):
+ """Add autocomplete=token to the <input> whose id= OR name= equals key,
+ if it doesn't already have an autocomplete attr. Returns (html, changed)."""
+ # match a single <input ...> tag that contains id="key" or name="key"
+ pat = re.compile(r'<input\b(?=[^>]*\b(?:id|name)=["\']' + re.escape(key) + r'["\'])[^>]*>', re.IGNORECASE)
+ m = pat.search(html)
+ if not m:
+ return html, "absent"
+ tag = m.group(0)
+ if re.search(r'\bautocomplete=', tag, re.IGNORECASE):
+ return html, "already"
+ newtag = tag[:-1].rstrip() + f' autocomplete="{token}">'
+ return html[:m.start()] + newtag + html[m.end():], "changed"
+
+total_changed, report = 0, []
+for fname, fields in PLAN.items():
+ path = os.path.join(PUB, fname)
+ if not os.path.exists(path):
+ report.append(f"{fname}: MISSING FILE"); continue
+ html = open(path, encoding="utf-8").read()
+ per = []
+ for key, token in fields.items():
+ html, status = add_token(html, key, token)
+ per.append(f"{key}->{token}:{status}")
+ if status == "changed":
+ total_changed += 1
+ open(path, "w", encoding="utf-8").write(html)
+ report.append(f"{fname}: " + ", ".join(per))
+
+print(f"total inputs tagged: {total_changed}")
+for r in report:
+ print(" " + r)
← 443a9c7 TSD SEO: backfill og:site_name on 23 pages with OG blocks bu
·
back to Thesetdecorator
·
backlog: loop #10 (og:site_name + WCAG autocomplete) + deplo c738710 →