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 style attributes and <style> blocks for later analysis
  • SVG metadata including role, <title>, <desc>, and aria-labelledby relationships
  • 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:

  • @import rules that could load external stylesheets dynamically
  • url() 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 /css2 endpoint 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 and data:text/html payloads 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-labelledby attribute 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-controls attribute
  • 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-root with a supported data-motion-mode (such as step or reveal)
  • 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-motion media 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.py provides a zero-dependency validation solution for diagram HTML output in the cathrynlavery/diagram-design repository.
  • DiagramParser incrementally extracts unsafe tags, executable attributes, and motion metadata using Python's standard HTMLParser.
  • check_css_references() blocks dangerous CSS constructs like @import and restricts remote fonts to approved Google Fonts URLs only.
  • check_svgs() enforces accessible SVG structure requiring role="img", sequential <title> and <desc> elements, and valid aria-labelledby references.
  • check_scripts() limits documents to a single canonical script that must match the template in assets/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →