Clamping Order of rx and ry Defaults in Non‑Square SVG Rectangles
0 reputation · 16 Feb 2022, 21:39 UTC
0 reputation · 16 Feb 2022, 21:39 UTC
The SVG <rect> element defines corner rounding via rx and ry attributes, accompanied by a clamping rule that reduces any radius exceeding half the element’s width or height. When only rx is author-specified, the ry attribute defaults to the same value, but the specification does not explicitly state whether clamping occurs before or after this defaulting step. This ambiguity is acute on non‑square rectangles, where a width‑bounded clamp applied to rx may yield a different visual outcome than a height‑bounded clamp applied after ry inherits the original rx value. Additionally, SVG 2 permits rx and ry as presentation attributes stylable via CSS, yet browser implementation differs on whether these are treated as geometry properties or presentation attributes, and negative values are invalidated to an unspecified radius state. These intersecting rules produce inconsistent rendering across user agents when authoring pill‑shaped rectangles that require rx and ry set to exactly half the width and height.
Does the browser clamp rx to half the width before defaulting ry to that value, or does it default ry to the author‑specified rx before applying the height‑based clamp? How does this ordering differ between attribute syntax and corresponding CSS rx/ry properties in SVG 2‑compatible engines? What corner radius results when a negative rx value is author‑specified with ry omitted on a rectangle of non‑zero height?
35775 reputation · 16 Feb 2022, 23:28 UTC
ry to the author-specified rx value first, then clamps each radius independently — rx to half the rectangle width, ry to half the rectangle height.rx/ry ignored or not animatable), not a different clamping sequence.rx with ry omitted: The negative value is invalid and treated as unspecified. Both radii fall back to their initial value of 0 (square corners). There is no "unspecified radius state" that preserves a prior value.The SVG 1.1 and SVG 2 specs define the used-value computation for <rect> corner radii as:
rx, ry is specified, the other takes the same specified value.rx to min(rx, width/2) and ry to min(ry, height/2) independently.Step 2 uses the specified (pre-clamp) value. Therefore, on a width=100, height=40 rectangle with rx=30 and no ry:
ry receives the specified 30, not the width-clamped 30 (which happens to be the same here).ry to 20 (height/2).rx=30, ry=20 — a perfect stadium/pill shape.If clamping occurred before defaulting, ry would inherit the already-clamped rx and also be 30, which would then be clamped to 20 anyway in this example. The difference appears when the authored rx exceeds half the height but not half the width — the height clamp still applies to the inherited value.
A negative rx or ry is an invalid value. Per SVG 2 §5.3, an invalid value is treated as if the attribute were not specified. With rx=-5 and ry omitted, both attributes are effectively absent, so both used values become 0. The rectangle renders with sharp corners.
SVG 2 reclassifies rx and ry as geometry properties, making them stylable via CSS. The cascade order is:
In practice:
rx/ry work and produce identical results to attributes.rx/ry entirely; only presentation attributes apply.rx/ry require geometry-property support; SMIL <animate> works on attributes in more engines.To author a stadium shape that adapts to any aspect ratio without scripting:
<rect width="100" height="40" rx="50" />
/* or via CSS */
rect { rx: 50%; } /* see caution below */
Any rx ≥ max(width, height)/2 (or 50% if percentage resolves against the viewport diagonal) yields rx=width/2, ry=height/2 after independent clamping.
<rect width="100" height="40" rx="30"/> — measure corner radii (expect rx=30, ry=20).<rect width="100" height="40" rx="-5"/> — confirm square corners.rx="30" vs CSS rect { rx: 30px } in target browsers.getComputedStyle(rect).rx and .ry to confirm used values.rx/ry resolve against the viewport's normalized diagonal (√(width²+height²)/√2) in SVG 2, not the rectangle's own dimensions. rx: 50% does not reliably produce a pill on all viewports.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.