Filter effect is spilling out

97 Views Asked by At

Consider a div with some bumped up text and a filter. The filter is restricted to 100%×100% of the objectBoundingBox.

However, when we apply a feFlood, we can see how this spills outside the element's padding-box (identical to the border-box) here. Why?

(the box-shadow highlights the boundary here)

@import url('https://fonts.googleapis.com/css2?family=Fjalla+One&display=swap');

body {
    font: 900 8em/ 1 fjalla one, 
        sans-serif;
}

svg { position: fixed }

div {
    margin: .375em;
    box-shadow: inset 0 0 0 2px #00f; /* padding-box boundary highlight */
    background: #00f7 content-box;
    filter: url(#f)
}

div:nth-of-type(2n) { padding-bottom: 1lh }
<svg width="0" height="0">
  <filter id="f" primitiveUnits="objectBoundingBox" 
          x="0%" y="0%" width="100%" height="100%">
    <feFlood flood-color="#f00"/>
    <feBlend in="SourceGraphic"/>
  </filter>
</svg>
<div>HELLO</div>
<div>HELLO</div>

This seems to depend on the line-height and on the particular font-family chosen (why?), but changing those is not a solution.

How do I fix this? It's particularly problematic when I want to apply a filter effect on half the height because this height is miscomputed, which triggers an avalanche of subsequent miscomputed filter effects.

@import url('https://fonts.googleapis.com/css2?family=Fjalla+One&display=swap');

body {
    font: 900 8em/ 1 fjalla one, 
        sans-serif;
}

svg { position: fixed }

div {
    margin: .375em;
    box-shadow: inset 0 0 0 2px #00f; /* padding-box boundary highlight */
    background: #00f7 content-box;
    filter: url(#f)
}

div:nth-of-type(2n) { padding-bottom: 1lh }
<svg width="0" height="0">
  <filter id="f" primitiveUnits="objectBoundingBox" 
          x="0%" y="0%" width="100%" height="100%">
    <feFlood flood-color="#f00" height=".5"/>
    <feBlend in="SourceGraphic"/>
  </filter>
</svg>
<div>HELLO</div>
<div>HELLO</div>

1

There are 1 best solutions below

3
Temani Afif On

I don't think this is related to the SVG. The SVG filter apply to the content area. It's the area that depends on the font properties and you cannot control it by changing the line-height (Can specific text character change the line height?)

The content area is that area you see when you select your text:

enter image description here

Or when you apply a background to an inline element:

@import url('https://fonts.googleapis.com/css2?family=Fjalla+One&display=swap');

body {
    font: 900 8em/ 1 fjalla one, 
        sans-serif;
}

svg { position: fixed }

div {
    margin: .375em;
    box-shadow: inset 0 0 0 2px #00f; /* padding-box boundary highlight */
    background: #00f7 content-box;
    filter: url(#f)
}

div + div {filter:none}

div + div span {background :red}
<svg width="0" height="0">
  <filter id="f" primitiveUnits="objectBoundingBox" 
          x="0%" y="0%" width="100%" height="100%">
    <feFlood flood-color="#f00"/>
    <feBlend in="SourceGraphic"/>
  </filter>
</svg>
<div>HELLO</div>
<div><span>HELLO</span></div>

As you can see, in all the cases the content area is overflowing the div because your line-height is defining the height of the line box to be smaller than the content area.

Not sure if you can change the behavior of the SVG filter but I am sure that you cannot control that content area.