How lint-render.py Detects Visual Clipping in SVG Diagrams

The lint-render.py script is a render-time linter that launches headless Chromium to capture screenshots of SVG diagrams, temporarily releases overflow constraints on the SVG and its ancestors, and performs pixel-level image diffing to detect strokes, markers, and filter bleed that static HTML analysis cannot identify.

The cathrynlavery/diagram-design repository uses scripts/lint-render.py as a critical quality assurance tool to ensure diagrams render completely without hidden visual truncation. Unlike static analyzers that inspect DOM geometry, this script validates the actual painted output by comparing constrained and unconstrained renderings of each diagram.

The Clipping Detection Pipeline

The script implements a multi-stage visual regression system that systematically unlocks overflow constraints and measures the visual difference. According to the source code in scripts/lint-render.py, the process involves JavaScript injection, staged screenshot capture, and pixel comparison across multiple scales.

SVG Survey and Enumeration (lines 124-144)

The process begins with SURVEY_JS, a JavaScript payload executed in the Playwright browser context. This script enumerates every <svg> element on the page and records:

  • Element dimensions and position
  • Current overflow CSS properties
  • The complete ancestor chain that might impose clipping constraints

The resulting metadata drives the subsequent detection stages, ensuring the linter examines every potential clipping boundary in the DOM hierarchy.

Staged Overflow Release (lines 133-138, 188-210)

The clipping_findings function coordinates a systematic release of constraints to isolate the source of any clipping:

  • Stage 0: Examines the SVG element itself if it lacks overflow: visible, capturing a baseline screenshot and a comparison after forcing visibility
  • Ancestor Stages: For each clipping ancestor up to MAX_ANCESTOR_STAGES, the script injects RELEASE_JS (lines 188-210) to mutate the ancestor's CSS to overflow: visible, then captures additional screenshots

This staged approach identifies whether clipping originates from the SVG viewport itself or from a parent container higher in the DOM tree.

Multi-Scale Pixel Diffing (lines 76-84, 226-260)

To catch both subtle artifacts and major off-screen spills, the compare_release function executes comparisons at two scales defined by DIFF_SCALES = (1, 0.25) (lines 76-84). The pure-JavaScript DIFF_JS algorithm (lines 226-260) performs pixel-level comparison between the constrained and released screenshots, applying these thresholds:

  • CHANNEL_THRESHOLD: Minimum intensity difference per color channel to register as a changed pixel
  • MIN_DIFF_PIXELS: Minimum number of differing pixels required to flag a spill

The algorithm ignores the interior of the released bounding box plus a guard band, focusing exclusively on new ink appearing outside legitimate boundaries. This detects paint extending beyond viewports—including stroke extensions, marker overflows, and filter effects like drop-shadow bleed—that DOM methods like getBoundingClientRect would miss.

Running the Clipping Linter

The script provides several execution modes for integration into development workflows.

Inspect a single diagram:

python3 scripts/lint-render.py skills/diagram-design/assets/example-venn.html

When clipping is detected, the output reports specific overflow measurements:


skills/diagram-design/assets/example-venn.html: clipped: example-venn.html paints outside its box: right by 7px (23 px of ink cut off at 1x)

Process all shipped assets:

python3 scripts/lint-render.py --all

This scans every example-*.html and template*.html file under skills/diagram-design/assets/ (defined in shipped_assets at lines 85-92), producing a summary:


Summary: 42 file(s) rendered, 3 finding(s), 0 note(s) (not checked, not failed).

Validate detection logic:

python3 scripts/lint-render.py --self-test

The self-test mode constructs synthetic SVG fixtures from SVG_CASES (starting at line 101) to verify the pipeline against 48 known clipping scenarios, ensuring calibration before production use.

Integration with the Diagram Design Workflow

While companion script scripts/lint-skin.py handles static accessibility and skin analysis, lint-render.py specifically guarantees visual completeness. The script additionally verifies page-wide overflow, missing assets, network isolation, and DOM restoration (lines 80-89) to prevent environmental factors from generating false positives.

Summary

  • lint-render.py operates as a render-time visual linter using headless Chromium and Playwright to validate actual painted output
  • The detection pipeline surveys SVGs via SURVEY_JS, releases overflow constraints using RELEASE_JS, and performs pixel diffing with DIFF_JS
  • Key thresholds CHANNEL_THRESHOLD and MIN_DIFF_PIXELS filter significant visual differences from rendering noise
  • Multi-scale analysis at 1x and 0.25x resolution catches both fine-detail bleed and large off-screen rendering errors
  • Staged ancestor checking up to MAX_ANCESTOR_STAGES identifies clipping regardless of whether it originates from the SVG viewport or parent containers

Frequently Asked Questions

What types of clipping can lint-render.py detect that static analysis misses?

Static analyzers rely on DOM geometry methods like getBoundingClientRect, which only measure element bounding boxes. The pixel-based approach in lint-render.py catches visual artifacts extending beyond these boxes, including stroke widths that overflow the viewBox, marker symbols positioned outside element bounds, and CSS filter effects (such as drop shadows or blurs) that bleed past container limits into hidden overflow areas.

How does the script avoid false positives from legitimate design choices?

The DIFF_JS algorithm ignores the interior of the released overflow box plus a configurable guard band, focusing exclusively on new ink appearing outside expected boundaries. Additionally, the script verifies network isolation and DOM restoration (lines 80-89) to ensure that dynamic content loading or state mutations do not trigger spurious clipping alerts during the capture process.

Why does the linter check multiple ancestor stages rather than just the SVG element?

SVG diagrams frequently nest within containers that impose independent overflow constraints. The MAX_ANCESTOR_STAGES logic implemented via RELEASE_JS (lines 188-210) walks up the DOM tree because clipping ancestors—whether immediate parents or distant layout containers—can truncate diagram content. This comprehensive ancestry verification ensures detection of clipping regardless of where in the hierarchy the CSS restriction originates.

What do the scale factors (1 and 0.25) in DIFF_SCALES accomplish?

According to lines 76-84, repeating the screenshot comparison at both full resolution (1x) and quarter resolution (0.25x) serves distinct detection purposes. The 1x scale captures fine detail like single-pixel strokes and subtle filter bleed, while the 0.25x scale reveals larger off-screen spills that might be mathematically present but visually insignificant at high resolution. This dual-pass approach ensures comprehensive coverage of potential rendering defects across different magnitudes.

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 →