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

> Discover how lint-render.py detects visual clipping in SVG diagrams by rendering SVGs, releasing constraints, and performing pixel diffing for accurate results.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: how-to-guide
- Published: 2026-09-11

---

**The [`lint-render.py`](https://github.com/cathrynlavery/diagram-design/blob/main/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`](https://github.com/cathrynlavery/diagram-design/blob/main/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`](https://github.com/cathrynlavery/diagram-design/blob/main/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:

```bash
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:

```bash
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:

```bash
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`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) handles static accessibility and skin analysis, [`lint-render.py`](https://github.com/cathrynlavery/diagram-design/blob/main/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`](https://github.com/cathrynlavery/diagram-design/blob/main/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.