htmlcsssvgfirefoxstroke

svg element with stroke-width renders differently in firefox and chrome


There is a difference in rendering svg elements with stroke-width in firefox and chrome.

<html>
<body>
<svg width="400" height="110">
  <rect width="400" height="100" style="fill:white;stroke-width:3;stroke:red" />
</svg>
</body>
</html>

for example if you consider this code in chrome the real size of <rect> is equal 400 while in firefox it's 403px It's adding stroke-width to width attribute.

firefox

Is there any way for them to behave similarly? something like box-sizing: border-box could be nice.


Solution

  • There is no difference in the actual rendering. Both browsers show the stroke in the same position relative to the bounding box of the <svg> element, with the stroke centered on the geometrical boundary of the element. Both browsers clip the rendering at the bounding box of the <svg> element, so that for the top, left and right side only the inner half of the stroke is visible. For both, the size of the <svg> element is 400px * 110px.

    Only in the return value of rect.getBoundingClientRect(), which is used in the developer tools to present the size, differs.

    SVG itself has a method for measuring the bounding box of an element. In SVG 1.1, SVGLocatable.getBBox() excludes "stroking, clipping, masking and filter effects" and applies only to the geometry. In SVG 2, the interface has slightly changed. SVGGraphicsElement.getBBox(options) should take an options parameter to define whether the stroke, clip path or markers should be included. Default is to only measure the unstyled geometry. Neither Chrome nor Firefox implement this parameter.

    document.querySelector('rect').getBBox()
    > SVGRect {x: 0, y: 0, width: 400, height: 100}
    

    element.getBoundingClientRect() is defined in the CSSOM view module. To cover all cases, it is a bit more complicated, but basically it references back to element.getClientRects():

    If the element has an associated SVG layout box return a DOMRectList object containing a single DOMRect object that describes the bounding box of the element as defined by the SVG specification, applying the transforms that apply to the element and its ancestors.

    No reference of the getBBox() options parameter is made, which I understand to say it should behave like the default, without any parameter set. Chrome does that:

    document.querySelector('rect').getBoundingClientRect() 
    > DOMRect {x: 8, y: 8, width: 400, height: 100, top: 8, …}
    

    But Firefox returns

    document.querySelector('rect').getBoundingClientRect()
    > DOMRect { x: 6.5, y: 6.5, width: 403, height: 103, top: 6.5, right: 409.5, bottom: 109.5, left: 6.5 }
    

    including the stroke in the measurement.

    While currently this is a bug in Firefox, there is a discussion in the SVG spec working group where at least some argue the "decorated" box would be more usefull. Currently there seems to be no urgency in resolving the difference, and the discussion is left for a future SVG 2.1 version.