SVG fill:none Shapes Have No Clickable Interior
TLDR
An unfilled SVG shape is only clickable on its stroke, so a rectangle with a 2px outline offers a 2px target and an inert interior. The fix is pointer-events: all scoped to closed user-drawn shapes only. Open paths must keep stroke-only hits, or the phantom fill region of an L-shaped wire starts stealing clicks.
The bug class
Vector editors draw diagram shapes as outlines: fill="none", visible stroke. SVG's default hit-testing mode is pointer-events: visiblePainted, and "painted" means exactly what got painted. No fill painted, no fill hits.
The user experience is a shape that ignores clicks everywhere except a 2px band. On a trackpad it feels flaky. On a touch screen it is effectively unselectable. Users do not file this as "pointer-events misconfiguration." They file it as "selection is broken."
Why the platform produces it
The default is not wrong, it is conservative. visiblePainted makes hit-testing match what the eye sees: you click what is drawn. For filled shapes that is intuitive. For unfilled shapes the drawn thing really is just the stroke, so the spec-correct behavior and the user's mental model diverge.
Every SVG editor rediscovers this. The usual escape hatches are invisible hitbox clones (a second, fatter, transparent copy of every element) or bounding-box hit regions. Both work. Both add DOM and bookkeeping.
The fix
For closed shapes there is a one-rule fix, no extra DOM:
#canvas rect[id^="el_"],
#canvas ellipse[id^="el_"],
#canvas circle[id^="el_"],
#canvas polygon[id^="el_"] {
pointer-events: all;
}
pointer-events: all makes both the fill region and the stroke respond to the pointer regardless of what is painted. The interior of an unfilled rectangle becomes clickable, which is what every user expected on day one.
The id-prefix selector scopes the rule to user-drawn content. Grid lines, guides, and preview elements keep their defaults.
The trap in the general fix
The tempting version is one selector for everything, paths included. Do not.
For hit-testing purposes an open path has a fill region: the area enclosed as if the path were closed by a straight line from end to start. An L-shaped wire therefore owns an invisible triangle between its two legs. With pointer-events: all, clicking in that empty triangle selects the wire, and in a dense schematic the phantom regions overlap everything.
So the rule splits by topology. Closed shapes (rect, ellipse, circle, polygon) get interior hits. Open strokes (wires, freehand ink) keep visiblePainted, and get their fat invisible hitbox clones instead, which widen the stroke target without inventing a fill region.
Preventing the class
The check to internalize: every time an element is created with fill="none", something must answer "how does a finger select this." Either the shape is closed and gets interior hits, or it is open and gets a widened hit surface. A drawing tool that commits unfilled geometry without answering that question ships the bug by default.
The lesson
Hit-testing is a designed surface, not a side effect of rendering. The platform's default ties the two together, and the moment your visuals use outlines, the two must be untied deliberately, with different answers for closed and open geometry.