fix(lint): enforce accessible SVG viewBox and title rules (#123)

* fix(lint): enforce viewBox presence and title length cap

- Add a11y check: non-decorative <svg> must carry a viewBox attribute
  with exactly four numeric tokens and positive width/height dimensions.
- Add a11y check: <title> text must be 60 characters or fewer, closing
  the gap between the CONTRIBUTING.md spec and linter enforcement.
- Update VALID_SVG in test-lint-a11y.py to include viewBox so all
  existing passing cases remain valid after the new checks land.
- Add three adversarial cases: missing-viewbox, invalid-viewbox,
  title-too-long — all verified against the gate suite (47/47 OK).

* fix(lint): accept SVG comma separators and reject non-finite viewBox values

- Re-split viewBox on [\s,]+ so comma-separated values (e.g. 0,0,800,600)
  are accepted as valid SVG
- Guard each component with math.isfinite() so values like inf and nan are
  rejected rather than passing the > 0 size check
- Add adversarial test cases: viewbox-comma-separated (pass),
  viewbox-nonfinite-width, viewbox-nonfinite-height (both fail)
- Bump plugin manifests to 2.4.1

* fix(lint): reject viewBox numbers with Python-only underscore separators

* test(lint): cover remaining viewBox cases

---------

Co-authored-by: Rupam Pal <rupam.pal@euromonitor.com>
This commit is contained in:
Cathryn Lavery
2026-08-19 19:53:43 -07:00
committed by GitHub
co-authored by Rupam Pal
parent cc2f51f3fd
commit 91fb4f243b
5 changed files with 98 additions and 5 deletions
+62 -1
View File
@@ -18,7 +18,7 @@ BUILD_ICONS = ROOT / "scripts/build-icons.py"
MOTION_TEMPLATE = ROOT / "skills/diagram-design/assets/template-motion.html"
VALID_SVG = """\
<svg xmlns="http://www.w3.org/2000/svg" role="img"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" role="img"
aria-labelledby="fixture-title fixture-desc">
<title id="fixture-title">Fixture diagram</title>
<desc id="fixture-desc">Diagram showing a fixture connected to a result.</desc>
@@ -156,6 +156,67 @@ def main() -> int:
'duplicate accessible-name id="fixture-desc" is not allowed',
directory,
)
require_failure(
"missing-viewbox",
VALID_SVG.replace(' viewBox="0 0 800 600"', ""),
"diagram <svg> must have a viewBox attribute",
directory,
)
require_failure(
"invalid-viewbox",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 -10 100"'),
'viewBox "0 0 -10 100" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_failure(
"viewbox-zero-height",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 800 0"'),
'viewBox "0 0 800 0" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_failure(
"viewbox-malformed-token",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 800px 600"'),
'viewBox "0 0 800px 600" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_failure(
"title-too-long",
VALID_SVG.replace("Fixture diagram", "A" * 61),
"<title> must be 60 characters or fewer (got 61)",
directory,
)
require_pass(
"viewbox-comma-separated",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0,0,800,600"').replace(
"fixture-title", "viewbox-comma-separated-title"
).replace("fixture-desc", "viewbox-comma-separated-desc"),
directory,
)
require_failure(
"viewbox-nonfinite-width",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 inf 600"').replace(
"fixture-title", "viewbox-nonfinite-width-title"
).replace("fixture-desc", "viewbox-nonfinite-width-desc"),
'viewBox "0 0 inf 600" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_failure(
"viewbox-nonfinite-height",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 800 inf"').replace(
"fixture-title", "viewbox-nonfinite-height-title"
).replace("fixture-desc", "viewbox-nonfinite-height-desc"),
'viewBox "0 0 800 inf" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_failure(
"viewbox-underscore-width",
VALID_SVG.replace('viewBox="0 0 800 600"', 'viewBox="0 0 1_000 600"').replace(
"fixture-title", "viewbox-underscore-width-title"
).replace("fixture-desc", "viewbox-underscore-width-desc"),
'viewBox "0 0 1_000 600" is not valid (expected "min-x min-y width height" with positive size)',
directory,
)
require_pass(
"decorative",
'<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true">'