How `self_check.py` Validates Diagram HTML Output: A Complete Technical Breakdown
The self_check.py script in the cathrynlavery/diagram-design repository implements a deterministic, dependency-free validation pipeline that inspects generated HTML files for security vulnerabilities, SVG accessibility compliance, and motion control contract adherence.
The diagram-design repository provides a framework for generating accessible, animated diagrams. The self_check.py validator, located at skills/diagram-design/scripts/self_check.py, serves as the gatekeeper to ensure every output file meets strict safety and semantic standards without requiring external dependencies.
HTML Parsing and Data Collection
The validation process begins with the DiagramParser class, a specialized subclass of Python's standard HTMLParser. This parser incrementally scans the markup to collect potential violations and metadata before any rule enforcement begins.
Extracting Unsafe Elements and Attributes
As DiagramParser walks the document tree in the verify() function, it captures several categories of content into discrete collections:
- Unsafe tags such as
<base>,<embed>, and<object>that could introduce security risks - Executable attributes matching the
on*pattern (e.g.,onclick,onload) that could execute arbitrary JavaScript - External references extracted from
src,href, and similar attributes for subsequent validation - CSS fragments found in inline
styleattributes and<style>blocks for later analysis - SVG metadata including
role,<title>,<desc>, andaria-labelledbyrelationships - Motion markup via
data-motion-*attributes to identify animated diagrams - Script blocks to enforce single-script limitations
Security and Resource Validation
CSS Reference Sanitization with check_css_references()
The check_css_references() function normalizes CSS escape sequences before applying security policies. According to the source code implementation, it specifically rejects:
@importrules that could load external stylesheets dynamicallyurl()functions pointing to non-fragment targets (remote resources)image-set()declarations that could load unauthorized image sources- Any remote URL that does not match the approved Google Fonts
/css2endpoint pattern
Generic Reference Verification
The reference_error() function validates every captured src and href attribute against a strict whitelist:
- Empty or fragment-only URLs (e.g.,
#section) are permitted for internal navigation javascript:URLs anddata:text/htmlpayloads are blocked immediately as XSS vectors- Non-image data URIs are rejected to prevent code injection
- Remote URLs are restricted solely to the approved Google Fonts stylesheet for
<link rel="stylesheet">elements
SVG Accessibility and Semantic Requirements
check_svgs() Implementation
The check_svgs() function enforces WCAG-compliant SVG structure through several concrete checks:
- The document must contain at least one SVG that is not hidden via
aria-hidden="true" - Every SVG must declare
role="img"to expose it as an image to assistive technologies - A non-empty
<title>element must exist, followed immediately by a<desc>element to provide accessible names and descriptions - The
aria-labelledbyattribute must correctly reference the IDs of the<title>and<desc>elements, and these IDs must be prefixed (not bare strings) to ensure uniqueness in the DOM
Script and Motion Contract Enforcement
Canonical Script Validation with check_scripts()
The check_scripts() function enforces strict limitations on JavaScript execution within diagram files:
- Only a single
<script>element is permitted per document - The script tag must be explicitly closed (not self-closing) to ensure proper parsing across browsers
- The element must carry exactly the canonical
data-diagram-controlsattribute - The script body must byte-match the canonical motion controller stored in
assets/template-motion.html
Motion Controls and Accessibility with check_motion()
When motion-related markup is detected in the parsed content, check_motion() validates complex animation contracts:
- Exactly one root element must declare
data-motion-rootwith a supporteddata-motion-mode(such assteporreveal) - Step counts must be ASCII decimal integers within the 0–8 range
- The item budget is capped at 12 elements maximum
- Semantic steps must form a contiguous sequence without gaps (1, 2, 3, not 1, 3, 4)
- No more than two items may share the same step value
- Controlled modes require proper control buttons, action attributes, and a live status element
- CSS must include
prefers-reduced-motionmedia queries and print media fallbacks - A
<noscript>explanation must be present for users with JavaScript disabled
Command-Line Interface and CI Integration
The main() entry point parses file paths from command-line arguments and iterates over each target. For every file, it invokes verify(path), which orchestrates the full validation pipeline and aggregates errors into a single list.
# Validate a single diagram file
python3 skills/diagram-design/scripts/self_check.py path/to/diagram.html
# Batch validation of multiple outputs
python3 skills/diagram-design/scripts/self_check.py diagram1.html diagram2.html diagram3.html
The script outputs OK for files passing all checks or FAIL followed by specific violation messages. This deterministic output makes it suitable for CI/CD integration as a pre-commit hook or build gate, aborting builds when contract violations are detected.
Summary
self_check.pyprovides a zero-dependency validation solution for diagram HTML output in thecathrynlavery/diagram-designrepository.DiagramParserincrementally extracts unsafe tags, executable attributes, and motion metadata using Python's standardHTMLParser.check_css_references()blocks dangerous CSS constructs like@importand restricts remote fonts to approved Google Fonts URLs only.check_svgs()enforces accessible SVG structure requiringrole="img", sequential<title>and<desc>elements, and validaria-labelledbyreferences.check_scripts()limits documents to a single canonical script that must match the template inassets/template-motion.html.check_motion()validates animation budgets (≤12 items), step contiguity (0–8), and accessibility fallbacks for reduced-motion preferences.
Frequently Asked Questions
What specific CSS patterns does self_check.py block?
According to the implementation in check_css_references(), the validator explicitly rejects @import rules, url() functions targeting non-fragment URLs, image-set() declarations, and any remote stylesheet that does not match the approved Google Fonts /css2 endpoint pattern. It normalizes CSS escape sequences before performing these comparisons to prevent evasion attempts.
How does the validator ensure SVG accessibility?
The check_svgs() function requires that SVGs declare role="img", contain a non-empty <title> immediately followed by <desc>, and use prefixed (non-bare) IDs in aria-labelledby references. It also verifies that at least one SVG in the document is not hidden via aria-hidden="true", ensuring screen readers can access the diagram content.
Can self_check.py be used in automated CI pipelines?
Yes. The script accepts multiple file paths as command-line arguments and processes them sequentially without external dependencies. It exits with a FAIL status and descriptive error messages when violations are detected, making it suitable for pre-commit hooks or build automation where any non-OK output should abort the deployment process.
What motion animation constraints does the validator enforce?
The check_motion() function validates that animations have exactly one root element with a supported data-motion-mode, step counts as ASCII decimals between 0–8, a maximum of 12 items, contiguous semantic steps without gaps, and no more than two items per step. It also requires proper accessibility fallbacks including prefers-reduced-motion media queries and <noscript> explanations for users with JavaScript disabled.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →