SVG fill:none Shapes Have No Clickable Interior
TLDR
SVG elements configured withfill="none" ignore pointer clicks inside their bounding interior because the default browser hit-testing mode is pointer-events: visiblePainted. Under visiblePainted, pointer events fire only on visually painted strokes (e.g., a 2px stroke outline). Applying pointer-events: all to user-drawn closed shapes (rect, ellipse, polygon) makes their interior clickable, while scoping open paths (wire, path) to stroke-only hitboxes to prevent phantom hit regions.
| SVG Geometry Category | Default Hit Behavior | CSS Optimization | Target Hit Area |
|---|---|---|---|
Closed Shapes (rect, circle) | Stroke outline only | pointer-events: all | Full interior area + stroke |
Open Paths (wire, path) | Stroke outline only | Invisible fat stroke clone | Stroke target + fat hit buffer |
Problem statement: the hollow element hit defect
In schema and diagram editors, shapes (rectangles, containers, components) are styled with outlined borders and transparent interiors: fill="none", stroke="#333".
Users clicking inside an unfilled rectangle to select or drag it find that the click passes through to elements underneath.
To select the shape, users are forced to click directly on the 2px stroke border, a difficult target on touch screens and trackpads.
Technical failure mode: visiblePainted hit specifications
The W3C SVG specification defaults pointer testing to pointer-events: visiblePainted.
For an SVG shape with fill="none", no pixels are painted inside its interior bounding area. The browser evaluates the interior as pointer-transparent.
Applying pointer-events: all across all SVG elements blindly breaks open paths: for an L-shaped wire, pointer-events: all treats the invisible triangle connecting its endpoints as a filled interior, causing wires to intercept clicks meant for adjacent components.
The fix & architecture: topology-aware CSS hit rules
Step 1: CSS scoping for closed shapes
Applypointer-events: all strictly to closed geometric elements (rect, circle, ellipse, polygon):
/ Enable interior hit-testing for user-drawn closed canvas shapes /
#canvas rect[id^="el_"],
#canvas circle[id^="el_"],
#canvas ellipse[id^="el_"],
#canvas polygon[id^="el_"] {
pointer-events: all;
}
This makes transparent interiors responsive to mouse and touch events without adding extra DOM nodes.
Step 2: fat hitbox clones for open paths
For open wires and polylines, retainvisiblePainted and generate a secondary, transparent stroke clone with a wider stroke width ($12\text{px}$) to expand the hit target without creating phantom interior hit regions:
<!-- Open Wire Geometry: Thin visible wire + Fat transparent hit stroke -->
<g class="wire-group">
<!-- Invisible fat hit stroke -->
<path d="M 10 10 L 100 10" stroke="transparent" stroke-width="12" pointer-events="stroke" />
<!-- Visible thin wire -->
<path d="M 10 10 L 100 10" stroke="#00e5ff" stroke-width="2" pointer-events="none" />
</g>
Rule of thumb: Applypointer-events: allto transparent closed shapes (rect,circle) for interior clickability. Use widened transparent stroke clones for open wire paths.