{"id":5330,"date":"2025-03-18T12:54:44","date_gmt":"2025-03-18T17:54:44","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=5330"},"modified":"2025-04-04T09:01:15","modified_gmt":"2025-04-04T14:01:15","slug":"overlapping-inline-backgrounds","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/overlapping-inline-backgrounds\/","title":{"rendered":"A Deep Dive into the Inline Background Overlap Problem"},"content":{"rendered":"\n<p>A&nbsp;<a href=\"https:\/\/x.com\/LukyVJ\/status\/1894338305795244316\">tweet by Lucas Bonomi<\/a>&nbsp;got me thinking about this problem: how to get a semitransparent background following some inline text with padding, but without the overlap problem that can be seen in the image below.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"250\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/416732169-9cbb369e-7dab-4ad1-b345-aefbdfaa50c9.png?resize=1024%2C250&#038;ssl=1\" alt=\"Screenshot showing three lines of text, middle aligned, each with its own semitransparent background and padding. The padding on each line leads to intersection, and where we have intersection, the semi-transparent background becomes more opaque. The challenge is to get this result without the increase in alpha in the intersection areas.\" class=\"wp-image-5334\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/416732169-9cbb369e-7dab-4ad1-b345-aefbdfaa50c9.png?resize=1024%2C250&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/416732169-9cbb369e-7dab-4ad1-b345-aefbdfaa50c9.png?resize=300%2C73&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/416732169-9cbb369e-7dab-4ad1-b345-aefbdfaa50c9.png?resize=768%2C187&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/416732169-9cbb369e-7dab-4ad1-b345-aefbdfaa50c9.png?w=1066&amp;ssl=1 1066w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">the problem at hand: the overlapping parts appear darker because of the layered opacity<\/figcaption><\/figure>\n\n\n\n<p><a href=\"https:\/\/frontendmasters.com\/blog\/author\/temaniafif\/\">Temani Afif<\/a> had already <a href=\"https:\/\/x.com\/ChallengesCss\/status\/1894354015531450664\">suggested<\/a> using an SVG&nbsp;<code>filter<\/code>&nbsp;solution, and that was my first instinct too.<\/p>\n\n\n\n<p>While the initial problem has a pretty simple solution, more complex variations lead me down a deep rabbit hole and I thought the journey was worth sharing in an article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-initial-problem-and-exact-particular-solution\">The initial problem and exact particular solution<\/h2>\n\n\n\n<p>We start with some middle-aligned text wrapped inside a&nbsp;<code>p<\/code>&nbsp;and a&nbsp;<code>span<\/code>. The&nbsp;<code>span<\/code>&nbsp;gets <code>padding<\/code>, <code>border-radius<\/code>,&nbsp;and a semi-transparent&nbsp;<code>background<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">p<\/span> &gt; <span class=\"hljs-selector-tag\">span<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: .<span class=\"hljs-number\">25em<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">5px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>\/ var(--a, .<span class=\"hljs-number\">7<\/span>));\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#fff<\/span>;\n  <span class=\"hljs-attribute\">box-decoration-break<\/span>: clone\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We&#8217;re also setting&nbsp;<code>box-decoration-break: clone<\/code>&nbsp;so that each wrapped line gets its own&nbsp;<code>padding<\/code>&nbsp;and corner rounding (this is a very neat CSS feature that&#8217;s&nbsp;<a href=\"https:\/\/css-tricks.com\/decorating-lines-of-text-with-box-decoration-break\/\">worth looking into<\/a>&nbsp;if you&#8217;re not familiar with it).<\/p>\n\n\n\n<p>The result of the above code looks as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419917091-aeb8f280-1a3b-4827-85c1-c6bf6425fb96.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of text, middle aligned, each with its own semitransparent background and padding. The padding on each line leads to intersection, and where we have intersection, the semi-transparent background becomes more opaque. This is basically the same as the problem illustrated by the challenge image.\" class=\"wp-image-5335\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419917091-aeb8f280-1a3b-4827-85c1-c6bf6425fb96.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419917091-aeb8f280-1a3b-4827-85c1-c6bf6425fb96.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419917091-aeb8f280-1a3b-4827-85c1-c6bf6425fb96.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>what the above CSS gives us: the overlap problem<\/em><\/figcaption><\/figure>\n\n\n\n<p>This is pretty much the same as the screenshot Lucas posted, so let&#8217;s see how we can fix it with an SVG&nbsp;<code>filter<\/code>!<\/p>\n\n\n\n<p>The first step is to make the&nbsp;<code>background<\/code>&nbsp;of the&nbsp;<code>span<\/code>&nbsp;opaque by setting&nbsp;<code>--a<\/code>&nbsp;to&nbsp;<code>1<\/code>. This gets rid of the overlap increasing alpha problem because there is no more transparency. To restore that transparency, we use an SVG&nbsp;<code>filter<\/code>. We&#8217;ll get to that in a moment, but for now, these are the styles we add:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* same other styles as before *\/<\/span>\n<span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">--a<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">filter<\/span>: <span class=\"hljs-built_in\">url<\/span>(#alpha)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The SVG&nbsp;<code>filter<\/code>&nbsp;needs to live inside an&nbsp;<code>svg<\/code>&nbsp;element. Since this&nbsp;<code>svg<\/code>&nbsp;element only contains our&nbsp;<code>filter<\/code>&nbsp;and no actual SVG graphics to be displayed on the screen, it is functionally the same as a&nbsp;<code>style<\/code>&nbsp;element, so there&#8217;s no need for it to be visible\/ take up space in the document flow.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">'true'<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">'alpha'<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- filter content goes here --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">svg<\/span><span class=\"hljs-selector-attr\">&#91;height=<span class=\"hljs-string\">'0'<\/span>]<\/span><span class=\"hljs-selector-attr\">&#91;aria-hidden=<span class=\"hljs-string\">'true'<\/span>]<\/span> { <span class=\"hljs-attribute\">position<\/span>: fixed }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The first primitive,&nbsp;<a href=\"https:\/\/webplatform.github.io\/docs\/svg\/elements\/feComponentTransfer\/\"><code>feComponentTransfer<\/code><\/a>, takes the&nbsp;<code>SourceAlpha<\/code>&nbsp;(basically, the&nbsp;<code>filter<\/code>&nbsp;input, with the RGB channels of all pixels zeroed, all pixels become black, but keep their alpha) as input (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/in\"><code>in<\/code><\/a>) and scales it to the desired alpha, basically giving us the semitransparent version of the shape of the&nbsp;<code>span<\/code>&nbsp;background. This is because the input alpha is&nbsp;<code>1<\/code>&nbsp;within the&nbsp;<code>span<\/code>&nbsp;background area and&nbsp;<code>0<\/code>&nbsp;outside it. Multiplying the desired alpha with&nbsp;<code>1<\/code>&nbsp;leaves it unchanged, while multiplying it with&nbsp;<code>0<\/code>&#8230; well, zeroes it.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">'true'<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">'alpha'<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">'SourceAlpha'<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">'back'<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'linear'<\/span> <span class=\"hljs-attr\">slope<\/span>=<span class=\"hljs-string\">'.7'<\/span>\/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We&#8217;ve also named the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/result\"><code>result<\/code><\/a>&nbsp;of this primitive&nbsp;<code>back<\/code>&nbsp;so we can reference it later in primitives not immediately folowing this particular&nbsp;<code>feComponentTransfer<\/code>&nbsp;one.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419932978-df9f8eed-00ef-45b7-a954-bd8e7cbcf753-1.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing the semitransparent background for the same lines of text as before, but without the actual text and without any increase in alpha in the intersection areas.\" class=\"wp-image-5337\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419932978-df9f8eed-00ef-45b7-a954-bd8e7cbcf753-1.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419932978-df9f8eed-00ef-45b7-a954-bd8e7cbcf753-1.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419932978-df9f8eed-00ef-45b7-a954-bd8e7cbcf753-1.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>result of the first filter step: the semitransparent black background<\/em><\/figcaption><\/figure>\n\n\n\n<p>Now we have the semi-transparent multi-line&nbsp;<code>span<\/code>&nbsp;background with no increase in alpha in the overlap areas. But we still need to get the text and add it on top of it.<\/p>\n\n\n\n<p>Next, we have a&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/feColorMatrix\"><code>feColorMatrix<\/code><\/a>&nbsp;primitive that uses the&nbsp;<a href=\"https:\/\/bsky.app\/profile\/anatudor.bsky.social\/post\/3kqibnwoquk2m\">green channel as an alpha mask<\/a>&nbsp;(the second value on the last row of the matrix is the only non-zero one) and maxes out (sets to&nbsp;<code>100%<\/code>) all RGB channels of the output (last column, first three rows), basically&nbsp;<a href=\"https:\/\/bsky.app\/profile\/anatudor.bsky.social\/post\/3kojyswsn2q2w\">painting the output<\/a>&nbsp;white with an alpha equal to the input green channel value. This means the result is full transparency where the input&#8217;s green channel is zero (everywhere outside the white text) and opaque white where it&#8217;s maxed out (just for the white text).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"alpha\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceAlpha\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"linear\"<\/span> <span class=\"hljs-attr\">slope<\/span>=<span class=\"hljs-string\">\".7\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 1 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 1 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that by default, the inputs of any primitives other than the very first one in the&nbsp;<code>filter<\/code>&nbsp;get set to the result of the primitive right before, so for this&nbsp;<code>feColorMatrix<\/code>&nbsp;primitive we need to explicitly set the input&nbsp;<code>in<\/code>&nbsp;to&nbsp;<code>SourceGraphic<\/code>.<\/p>\n\n\n\n<p>Also note that there&#8217;s a reason behind using the green channel to extract the text. This is because when using Chrome and a wide gamut display, we may hit&nbsp;<a href=\"https:\/\/issues.chromium.org\/issues\/373410239\">a bug<\/a>&nbsp;which causes&nbsp;<code>feColorMatrix<\/code>&nbsp;to find for example red in what&#8217;s <code>0%<\/code> red, <code>100%<\/code> green and <code>0%<\/code> blue. And it&#8217;s not just that, but extracting the red channel out of&nbsp;<code>100%<\/code> red, <code>0%<\/code> green and&nbsp;<code>0%<\/code> blue doesn&#8217;t give us&nbsp;<code>100%<\/code> red, but a lower value.<\/p>\n\n\n\n<p>To get an idea of just how bad the problem is, check out the comparison screenshot below &#8211; everything should have all channels either maxed out or zeroed (like on the left), there should be no in betweens (like on the right).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"547\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?resize=1024%2C547&#038;ssl=1\" alt=\"Comparative screenshots for various tests: extracting just the individual channels, their negations, unions, intersections, XORs, as well as extracting them as alpha masks. On the left, we have the extected result: extract 100% out of each channel that's maxed out, 0% out of those zeroed. On the right (wide gamut case), we however find red, green, blue where these channels have been zeroed.\" class=\"wp-image-5338\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?resize=1024%2C547&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?resize=300%2C160&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?resize=768%2C410&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?resize=1536%2C820&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420838686-97a8bda8-f32c-4e6b-8ce7-4a2ff63feeb3.png?w=1572&amp;ssl=1 1572w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\"><em>expected vs. wide gamut problem (<a href=\"https:\/\/cdpn.io\/pen\/debug\/KwKvRKr\">live test<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<p>After a bunch of tests, it results the problem is less noticeable when using the green channel (compared to when using the blue or red channels), so we&#8217;re trying to limit this bug on the hardware where it&#8217;s possible to hit it.<\/p>\n\n\n\n<p>We now have just the white text:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419943848-cc3f8df9-621a-4c95-9227-9b64c8abd320.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing just the white text for the same lines as before, no background at all.\" class=\"wp-image-5339\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419943848-cc3f8df9-621a-4c95-9227-9b64c8abd320.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419943848-cc3f8df9-621a-4c95-9227-9b64c8abd320.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419943848-cc3f8df9-621a-4c95-9227-9b64c8abd320.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>result of the second filter step: just the white text<\/em><\/figcaption><\/figure>\n\n\n\n<p>The final step is to place the semi-transparent black background underneath it (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/in2\"><code>in2<\/code><\/a>&nbsp;specifies the bottom layer):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"alpha\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceAlpha\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"linear\"<\/span> <span class=\"hljs-attr\">slope<\/span>=<span class=\"hljs-string\">\".7\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 1 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 1 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feBlend<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I see&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/feMerge\"><code>feMerge<\/code><\/a>&nbsp;often used for this, but here we only have two layers, so I find&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/feBlend\"><code>feBlend<\/code><\/a>&nbsp;(with the default&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/mode\"><code>mode<\/code><\/a>&nbsp;of&nbsp;<code>normal<\/code>&nbsp;which just places the top layer&nbsp;<code>in<\/code>&nbsp;over the bottom layer&nbsp;<code>in2<\/code>) a much simpler solution.<\/p>\n\n\n\n<p>Note that we&#8217;re not specifying&nbsp;<code>in<\/code>&nbsp;explicitly because, by default, it&#8217;s the result of the previous primitive, the&nbsp;<code>feColorMatrix<\/code>. This is also why we didn&#8217;t bother with setting the&nbsp;<code>result<\/code>&nbsp;attribute like we did for the first primitive, the&nbsp;<code>feComponentTransfer<\/code>&nbsp;one because the output of this&nbsp;<code>feColorMatrix<\/code> primitive only gets fed automatically into the&nbsp;<code>in<\/code>&nbsp;input of the final primitive and nowhere else after that.<\/p>\n\n\n\n<p>Cool, right?<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419953162-16382e11-1781-4ac9-af5b-3d49ac010d7a.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of text, middle aligned, each with its own semitransparent background and padding. The padding on each line leads to intersection, but now there is no more increase in alpha in the intersection areas.\" class=\"wp-image-5340\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419953162-16382e11-1781-4ac9-af5b-3d49ac010d7a.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419953162-16382e11-1781-4ac9-af5b-3d49ac010d7a.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419953162-16382e11-1781-4ac9-af5b-3d49ac010d7a.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the desired result (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/gbOwxGL\">live demo<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"expanding-the-problem-scope\">Expanding the problem scope<\/h2>\n\n\n\n<p>I thought this was a neat trick worth sharing, so I posted about it on social media, which lead to an interesting conversation on Mastodon.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"a-related-problem\">A related problem<\/h3>\n\n\n\n<p>Patrick H. Lauke&nbsp;<a href=\"https:\/\/mastodon.social\/@patrick_h_lauke\/114064700058785709\">pointed me<\/a>&nbsp;to&nbsp;<a href=\"https:\/\/codepen.io\/patrickhlauke\/pen\/jOzJwvZ\">a CodePen demo<\/a>&nbsp;he had made a few years back, higlighting a related problem I wasn&#8217;t hitting with the quick demo I had shared: the background of the later lines covering up the text of the ones right before them.<\/p>\n\n\n\n<p>My demo wasn&#8217;t hitting this problem because I had tried to stay reasonably close to the initial challenge screenshot, so I hadn&#8217;t used a big enough&nbsp;<code>padding<\/code>&nbsp;to run into it. But let&#8217;s say we increase the&nbsp;<code>padding<\/code>&nbsp;of the&nbsp;<code>span<\/code>&nbsp;from&nbsp;<code>.25em<\/code>&nbsp;to&nbsp;<code>.5em<\/code>&nbsp;(and also remove the&nbsp;<code>filter<\/code>&nbsp;to make the problem more obvious).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419959733-8c866d6c-e940-4e24-b27e-294025425e69.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of text, middle aligned, each with its own fully opaque black background and padding. The padding on each line leads to its red background partly covering the white text of the line above.\" class=\"wp-image-5341\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419959733-8c866d6c-e940-4e24-b27e-294025425e69.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419959733-8c866d6c-e940-4e24-b27e-294025425e69.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419959733-8c866d6c-e940-4e24-b27e-294025425e69.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the bigger padding problem<\/em><\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-simplest-case-separate-spans-opaque-backgrounds-black-white-text\">The simplest case: separate spans, opaque backgrounds, black\/ white text<\/h3>\n\n\n\n<p>We first consider the case when we only have separate words wrapped in spans with opaque backgrounds and the text is either black or white (or at least very close). In this very simple case, a properly set&nbsp;<code>mix-blend-mode<\/code>&nbsp;on&nbsp;<code>span<\/code>&nbsp;elements (<code>darken<\/code>&nbsp;for black text,&nbsp;<code>lighten<\/code>&nbsp;for white) suffices, there&#8217;s no need for an SVG&nbsp;<code>filter<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"342\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419973578-7e5dd953-22e5-4106-8a96-ed4a240ffcfe.png?resize=800%2C342&#038;ssl=1\" alt=\"Screenshot showing a multi-line paragraph with isolated words highlighted by being wrapped in spans that get a background contrasting with both the backdrop and the text. Both the light theme case (left, black text, white backdrop, pink highlight) and the dark theme case (right, white text, black backdrop, crimson highlight) are included. The highlights expand out quite a bit outside the words they're meant to pop, overlapping some of the neighbouring ones, but they always show behind the text.\" class=\"wp-image-5342\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419973578-7e5dd953-22e5-4106-8a96-ed4a240ffcfe.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419973578-7e5dd953-22e5-4106-8a96-ed4a240ffcfe.png?resize=300%2C128&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419973578-7e5dd953-22e5-4106-8a96-ed4a240ffcfe.png?resize=768%2C328&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>isolated spans on opaque contrasting background<\/em><\/figcaption><\/figure>\n\n\n\n<p>Both&nbsp;<code>darken<\/code>&nbsp;and&nbsp;<code>lighten<\/code>&nbsp;value work on a per pixel, per channel basis. For each pixel of the input, they take either the minimum (<code>darken<\/code>) or the maximum (<code>lighten<\/code>) channel value betwen the two blended layers to produce the result.<\/p>\n\n\n\n<p>Black always has all channels smaller or at most equal to those of anything else. So when we blend any background layer with black text using the&nbsp;<code>darken<\/code>&nbsp;blend mode, the result always shows the black text where there is overlap because the&nbsp;<code>0%<\/code>-valued channels of the black text are always the result of the minimum computation.<\/p>\n\n\n\n<p>White always has all channels bigger or at most equal to those of anything else. So when we blend any background layer with white text using the&nbsp;<code>lighten<\/code>&nbsp;blend mode, the result always shows the white text where there is overlap because the&nbsp;<code>100%<\/code>-valued channels of the white text are always the result of the maximum computation.<\/p>\n\n\n\n<p>Now this works fine as it is when we don&#8217;t have any backdrop behind or when the backdrop is either white for black text or black for white text. In other cases, for example if we have a busy image behind, things don&#8217;t look as good as the&nbsp;<code>span<\/code>&nbsp;elements also get blended with the image backdrop.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"342\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419983532-e37313d6-3ab8-4f77-850f-84b6ff6cdb41.png?resize=800%2C342&#038;ssl=1\" alt=\"Screenshot showing the same multi-line paragraph with isolated words highlighted by being wrapped in spans that get a background contrasting with both the backdrop and the text in both the light and dark theme case. However, now the backdrop is a busy image and we can see how the highlights blend with it, instead of just being placed on top and covering it.\" class=\"wp-image-5343\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419983532-e37313d6-3ab8-4f77-850f-84b6ff6cdb41.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419983532-e37313d6-3ab8-4f77-850f-84b6ff6cdb41.png?resize=300%2C128&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419983532-e37313d6-3ab8-4f77-850f-84b6ff6cdb41.png?resize=768%2C328&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>isolated spans on busy background problem<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Luckily, the fix is straightforward: we just need to set&nbsp;<code>isolation: isolate<\/code>&nbsp;on the parent paragraph!<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"342\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419990476-c8acfa01-453b-46be-a940-f2f7d9919e13.png?resize=800%2C342&#038;ssl=1\" alt=\"Screenshot showing the same multi-line paragraph with isolated words highlighted by being wrapped in spans that get a background contrasting with both the backdrop and the text in both the light and dark theme case. The backdrop is now a busy image, lighter or darker depending on the theme, but the highlights are simply on top, they don't get blended with it anymore.\" class=\"wp-image-5344\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419990476-c8acfa01-453b-46be-a940-f2f7d9919e13.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419990476-c8acfa01-453b-46be-a940-f2f7d9919e13.png?resize=300%2C128&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/419990476-c8acfa01-453b-46be-a940-f2f7d9919e13.png?resize=768%2C328&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>isolated spans on busy background solution (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/ogNzGpw\">live demo<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"slightly-more-complex-long-wrapping-span-opaque-background-black-white-text\">Slightly more complex: long wrapping span, opaque background, black\/ white text<\/h3>\n\n\n\n<p>In this case, the&nbsp;<code>mix-blend-mode<\/code>&nbsp;solution isn&#8217;t enough anymore because the point of it was to blend the&nbsp;<code>span<\/code>&nbsp;background with the text of the parent paragraph that gets covered. But now it&#8217;s the&nbsp;<code>span<\/code>&#8216;s own text that gets covered by the&nbsp;<code>background<\/code>&nbsp;of its next line.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"264\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420042481-0b030caf-58d6-4dcd-8e37-9c1ffe97a930.png?resize=800%2C264&#038;ssl=1\" alt=\"Screenhot showing a multi-line paragraph with a long portion highlighted by being wrapped in a span that gets a background contrasting with both the backdrop and the text. Both the light theme case (left, black text, light image backdrop, pink highlight) and the dark theme case (right, white text, dark image backdrop, crimson highlight) are included. The highlights expand out quite a bit outside the portion they're meant to pop, overlapping some of the neighbouring words and lines, yet they always show behind the text of the paragraph around. Unfortunately, that's not enough, as they are painted above the text on the previous line that's also wrapped in the same span.\" class=\"wp-image-5345\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420042481-0b030caf-58d6-4dcd-8e37-9c1ffe97a930.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420042481-0b030caf-58d6-4dcd-8e37-9c1ffe97a930.png?resize=300%2C99&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420042481-0b030caf-58d6-4dcd-8e37-9c1ffe97a930.png?resize=768%2C253&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>long wrapping span problem in spite of mix-blend-mode<\/em><\/figcaption><\/figure>\n\n\n\n<p>To get around this, we wrap the entire&nbsp;<code>span<\/code>&nbsp;in another&nbsp;<code>span<\/code>&nbsp;and set the <code>padding<\/code> and <code>background<\/code> only on the outer&nbsp;<code>span<\/code>&nbsp;(<code>p &gt; span<\/code>). This causes the black\/white text of the inner&nbsp;<code>span<\/code>&nbsp;as well as that of the paragraph around the spans to get blended with the outer&nbsp;<code>span<\/code>&nbsp;background.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"423\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420043339-b2559f56-e965-4ae6-812a-75c0d41c8275.png?resize=800%2C423&#038;ssl=1\" alt=\"Screenhot showing a multi-line paragraph with a long portion highlighted by being wrapped in a span that gets a background contrasting with both the backdrop and the text. Both the light theme case (left, black text, light image backdrop, pink highlight) and the dark theme case (right, white text, dark image backdrop, crimson highlight) are included. The highlights expand out quite a bit outside the portion they're meant to pop, overlapping some of the neighbouring words and lines, yet they always show behind the text of the paragraph around and the text on the previous line that's also wrapped in the same span.\" class=\"wp-image-5346\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420043339-b2559f56-e965-4ae6-812a-75c0d41c8275.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420043339-b2559f56-e965-4ae6-812a-75c0d41c8275.png?resize=300%2C159&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420043339-b2559f56-e965-4ae6-812a-75c0d41c8275.png?resize=768%2C406&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>long wrapping span nesting solution (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/azbpKpg\">live demo<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<p>If you&#8217;ve checked the above demo in Firefox, you may have noticed that it doesn&#8217;t work. This is due to&nbsp;<a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1951653\">bug 1951653<\/a>.<\/p>\n\n\n\n<p>In the particular case when the&nbsp;<em>entire<\/em>&nbsp;text in the paragraph is wrapped in a&nbsp;<code>span<\/code>, we can avoid the Firefox bug by setting the&nbsp;<code>mix-blend-mode<\/code>&nbsp;property&nbsp;<em>only<\/em>&nbsp;on the inner&nbsp;<code>span<\/code>&nbsp;(<code>span span<\/code>).<\/p>\n\n\n\n<p>However, in the case above, where we also have paragraph text outside the outer&nbsp;<code>span<\/code>&nbsp;too, this unfortunately still leaves us with the problem of that text before the long&nbsp;<code>span<\/code>&nbsp;getting covered by the background of the next&nbsp;<code>span<\/code>&nbsp;line.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"264\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420044455-77799210-3b25-4312-98a7-4cc296a9cdba.png?resize=800%2C264&#038;ssl=1\" alt=\"Screenhot showing a multi-line paragraph with a long portion highlighted by being wrapped in a span that gets a background contrasting with both the backdrop and the text. Both the light theme case (left, black text, light image backdrop, pink highlight) and the dark theme case (right, white text, dark image backdrop, crimson highlight) are included. The highlights expand out quite a bit outside the portion they're meant to pop, overlapping some of the neighbouring words and lines, yet they always show behind text on the previous line that's also wrapped in the same span. Unfortunately, not also below the text of the paragraph before this long rapping span.\" class=\"wp-image-5347\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420044455-77799210-3b25-4312-98a7-4cc296a9cdba.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420044455-77799210-3b25-4312-98a7-4cc296a9cdba.png?resize=300%2C99&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420044455-77799210-3b25-4312-98a7-4cc296a9cdba.png?resize=768%2C253&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>Firefox workaround not good enough if there&#8217;s paragraph text before the long wrapping span<\/em><\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-most-complex-case-transparent-background-neither-the-text-nor-the-background-are-black-white\">The most complex case: transparent background where neither the text nor the background are black\/white<\/h3>\n\n\n\n<p>In this case, the blending solution isn&#8217;t enough anymore and we need an SVG&nbsp;<code>filter<\/code>&nbsp;one.<\/p>\n\n\n\n<p>Going back to our original demo, we need to apply the solution from the previous case: wrap the&nbsp;<code>span<\/code>&nbsp;in another, set the&nbsp;<code>padding<\/code>&nbsp;and&nbsp;<code>background<\/code>&nbsp;only on the outer one (<code>p &gt; span<\/code>), blend&nbsp;<em>only<\/em>&nbsp;the inner&nbsp;<code>span<\/code>&nbsp;element with the outer one to ensure our solution works cross-browser (since we have white text, we use the&nbsp;<code>lighten<\/code>&nbsp;mode) and prevent blending with anything outside the containing paragraph&nbsp;<code>p<\/code>&nbsp;by setting&nbsp;<code>isolation: isolate<\/code>&nbsp;on it.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#fff<\/span>;\n  <span class=\"hljs-attribute\">isolation<\/span>: isolate;\n  <span class=\"hljs-attribute\">filter<\/span>: <span class=\"hljs-built_in\">url<\/span>(#alpha)\n}\n\n<span class=\"hljs-selector-tag\">p<\/span> &gt; <span class=\"hljs-selector-tag\">span<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: .<span class=\"hljs-number\">5em<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">5px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-number\">#000<\/span>;\n  <span class=\"hljs-attribute\">box-decoration-break<\/span>: clone;\n\t\n  span { <span class=\"hljs-attribute\">mix-blend-mode<\/span>: lighten }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420278763-d87f1df8-cc99-4e42-9aab-03662273003a.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of text, middle aligned, each with its own semitransparent background and padding. The padding on each line leads to intersection, not just between padding areas on adjacent lines, but also between padding and text, but now there is no more increase in alpha in the padding intersection areas and all the background is always behind all the text.\" class=\"wp-image-5348\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420278763-d87f1df8-cc99-4e42-9aab-03662273003a.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420278763-d87f1df8-cc99-4e42-9aab-03662273003a.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420278763-d87f1df8-cc99-4e42-9aab-03662273003a.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the desired result in the bigger padding case (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/pvormNg\">live demo<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<p>But what we want here is to move away from black\/ white text and background, so let&#8217;s see how to do that.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"set-rgba-values-in-the-svg-filter\">Set RGBA values in the SVG filter<\/h4>\n\n\n\n<p>If we wanted to have a background that&#8217;s not semi-transparent black, but a semi-transparent dark blue, let&#8217;s say&nbsp;<code>rgb(25 25 112)<\/code>&nbsp;(which can also be written as&nbsp;<code>rgb(9.8% 9.8% 43.9%)<\/code>), as well as gold-orange text, let&#8217;s say&nbsp;<code>rgb(255 165 0)<\/code>&nbsp;(which can also be written as&nbsp;<code>rgb(100% 64.7% 0%)<\/code>), then we use&nbsp;<code>feColorMatrix<\/code>&nbsp;as the first primitive as well and alter the final column values on the first three matrix rows for both the first matrix giving us the background and the second one giving us the text to use the decimal representation of the three percentage RGB values:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"alpha\"<\/span> <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span>\n      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0  .098 \n              0 0 0 0  .098 \n              0 0 0 0  .439 \n              0 0 0 .7 0\"<\/span>\n      <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span>\n    \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span>\n      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>\n      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1 \n              0 0 0 0 .647 \n              0 0 0 0 0 \n              0 1 0 0 0\"<\/span>\n    \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feBlend<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Other than the&nbsp;<code>id<\/code>, we&#8217;ve now also set&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/color-interpolation-filters\">another attribute<\/a>&nbsp;on the&nbsp;<code>filter<\/code>&nbsp;element. We aren&#8217;t going into it because I don&#8217;t really understand much about it, but just know that this attribute with this value needs to be added on any SVG&nbsp;<code>filter<\/code>&nbsp;that messes with the RGB channels. Otherwise, the result won&#8217;t be consistent between browsers (the default is&nbsp;<code>linearRGB<\/code>&nbsp;in theory, but only the&nbsp;<code>sRGB<\/code>&nbsp;value seems to work in Safari) and it may not match expectations (the&nbsp;<code>sRGB<\/code>&nbsp;value is the one that gives us the result we want). Previously, having just white text on a black background, we didn&#8217;t really need it and it was safe to skip it, but now we have to include it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420321413-7ec2fa9c-4667-40ef-a0a0-5192a5333d30.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of golden text, middle aligned, each with its own semitransparent dark blue background and padding. The padding on each line leads to intersection, not just between padding areas on adjacent lines, but also between padding and text, but there is no increase in alpha in the padding intersection areas and all the background is always behind all the text.\" class=\"wp-image-5349\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420321413-7ec2fa9c-4667-40ef-a0a0-5192a5333d30.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420321413-7ec2fa9c-4667-40ef-a0a0-5192a5333d30.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420321413-7ec2fa9c-4667-40ef-a0a0-5192a5333d30.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>golden text on dark blue background using the method of setting the RGB values in the SVG filter (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/yyLgWjQ\">live demo<\/a>)<\/em><\/figcaption><\/figure>\n\n\n\n<p>The problem with this solution is that it involves hardcoding the RGBA values for both the&nbsp;<code>span<\/code>&nbsp;background and text in the SVG&nbsp;<code>filter<\/code>, meaning we can&#8217;t control them from the CSS.<\/p>\n\n\n\n<p>Let&#8217;s try another approach!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"set-rgba-values-upstream-of-the-svg-filter\">Set RGBA values upstream of the SVG filter<\/h4>\n\n\n\n<p>First, we set them as custom properties upstream of the&nbsp;<code>svg<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--a<\/span>: .<span class=\"hljs-number\">5<\/span>;\n  <span class=\"hljs-attribute\">--back-c<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">112<\/span>\/ var(--a));\n  <span class=\"hljs-attribute\">--text-c<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">255<\/span> <span class=\"hljs-number\">165<\/span> <span class=\"hljs-number\">0<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then we modify the&nbsp;<code>filter<\/code>&nbsp;a bit. We use&nbsp;<code>SourceAlpha<\/code>&nbsp;to give us the background area, though we still extract the text area via a&nbsp;<code>feColorMatrix<\/code>&nbsp;primitive and save it as&nbsp;<code>text<\/code>, but this time we don&#8217;t care about the RGB values, we won&#8217;t use them anyway. We also flood the entire&nbsp;<code>filter<\/code>&nbsp;area with&nbsp;<code>--back-c<\/code>&nbsp;and&nbsp;<code>--text-c<\/code>&nbsp;(using&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/feFlood\"><code>feFlood<\/code><\/a>), but then, out of the entire area, we only keep what&#8217;s at the intersection (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Attribute\/operator#fecomposite\"><code>operator='in'<\/code><\/a>&nbsp;of&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/feComposite\"><code>feComposite<\/code><\/a>) with the&nbsp;<code>SourceAlpha<\/code>&nbsp;and&nbsp;<code>text<\/code>&nbsp;areas respectively. Finally, we stack these intersections (via&nbsp;<code>feBlend<\/code>), with the text on top.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"alpha\"<\/span> <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFlood<\/span> <span class=\"hljs-attr\">flood-color<\/span>=<span class=\"hljs-string\">\"var(--back-c)\"<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"SourceAlpha\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span>\n      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>\n      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 \n              0 0 0 0 \n              0 0 0 0 0 \n              0 1 0 0 0\"<\/span>\n      <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n    \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFlood<\/span> <span class=\"hljs-attr\">flood-color<\/span>=<span class=\"hljs-string\">\"var(--text-c)\"<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feBlend<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This allows us to control both the text and background from the CSS.<\/p>\n\n\n\n<p>However, the values of&nbsp;<code>--back-c<\/code>&nbsp;and&nbsp;<code>--text-c<\/code>&nbsp;are those of the&nbsp;<code>feFlood<\/code>&nbsp;primitive, not those on the element the&nbsp;<code>filter<\/code>&nbsp;applies to. So for any different text or background, we need to have a different <code>filter<\/code>.<\/p>\n\n\n\n<p>If that&#8217;s difficult to grasp, let&#8217;s say we want two different options, the same golden-orange text on a dark blue background and also dark blue text on a pink background.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--a<\/span>: .<span class=\"hljs-number\">7<\/span>;\n\t\n  <span class=\"hljs-attribute\">--back-c-1<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">112<\/span>\/ var(--a));\n  <span class=\"hljs-attribute\">--text-c-1<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">255<\/span> <span class=\"hljs-number\">165<\/span> <span class=\"hljs-number\">0<\/span>);\n\t\n  <span class=\"hljs-attribute\">--back-c-2<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">255<\/span> <span class=\"hljs-number\">105<\/span> <span class=\"hljs-number\">180<\/span>\/ var(--a));\n  <span class=\"hljs-attribute\">--text-c-2<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">112<\/span>);\n\t\n  <span class=\"hljs-attribute\">--back-c<\/span>: <span class=\"hljs-built_in\">var<\/span>(--back-c-<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--text-c<\/span>: <span class=\"hljs-built_in\">var<\/span>(--text-c-<span class=\"hljs-number\">1<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we can change&nbsp;<code>--back-c<\/code>&nbsp;and&nbsp;<code>--text-c<\/code>&nbsp;on the second paragraph:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">p<\/span><span class=\"hljs-selector-pseudo\">:nth-child(2)<\/span> {\n  <span class=\"hljs-attribute\">--back-c<\/span>: <span class=\"hljs-built_in\">var<\/span>(--back-c-<span class=\"hljs-number\">2<\/span>);\n  <span class=\"hljs-attribute\">--text-c<\/span>: <span class=\"hljs-built_in\">var<\/span>(--text-c-<span class=\"hljs-number\">2<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But changing these variables on the second paragraph&nbsp;<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/JojEqBo\">doesn&#8217;t do anything<\/a>&nbsp;for the result of the SVG&nbsp;<code>filter<\/code>&nbsp;applied to it because the values for&nbsp;<code>--back-c<\/code>&nbsp;and&nbsp;<code>--text-c<\/code>&nbsp;that get used by the&nbsp;<code>filter<\/code>&nbsp;are&nbsp;<em>always<\/em>&nbsp;those set upstream from it on the&nbsp;<code>body<\/code>.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='239' src='https:\/\/videopress.com\/embed\/qcsrlKdK?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>the problem seen in DevTools<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Unfortunately, this is just how things are for SVG filters, even though CSS ones don&#8217;t have this limitation, like the comparison below shows.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"778\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421036374-6abf5652-0b1c-4fbe-89dd-e381a3cc1900.png?resize=800%2C778&#038;ssl=1\" alt=\"Screenshot illustrating the above. `--c` is set to `orangered` on the body and this is the value used for the drop shadow created by the SVG filter, regardless of what value `--c` has on the element the SVG filter is applied on. By contrast, when using a CSS drop shadow filter, the value of `--c` is the one set on the element the filter is applied on.\" class=\"wp-image-5350\" style=\"width:593px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421036374-6abf5652-0b1c-4fbe-89dd-e381a3cc1900.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421036374-6abf5652-0b1c-4fbe-89dd-e381a3cc1900.png?resize=300%2C292&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421036374-6abf5652-0b1c-4fbe-89dd-e381a3cc1900.png?resize=768%2C747&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">CSS vs. SVG drop-shadow filter using a variable for&nbsp;<code>flood-color<\/code>&nbsp;(<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/ZYEJBZr\">live demo<\/a>)<\/figcaption><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\" id=\"set-rgb-values-in-the-css-fix-alpha-in-the-svg-filter\">Set RGB values in the CSS, fix alpha in the SVG filter<\/h4>\n\n\n\n<p>Amelia Bellamy-Royds&nbsp;<a href=\"https:\/\/mastodon.social\/@AmeliaBR@front-end.social\/114065645349479429\">suggested<\/a>&nbsp;a&nbsp;<code>feComponentTransfer<\/code>&nbsp;<a href=\"https:\/\/codepen.io\/AmeliaBR\/pen\/XJWjEmM?editors=1100\">approach<\/a>&nbsp;that allows setting the palette from the CSS and then using the SVG&nbsp;<code>filter<\/code>&nbsp;only to take care of the increase in alpha where there is overlap.<\/p>\n\n\n\n<p>What Amelia&#8217;s&nbsp;<code>filter<\/code>&nbsp;does is use&nbsp;<code>feComponentTransfer<\/code>&nbsp;to preserve the alpha of everything that&#8217;s fully transparent (the area outside the span) or fully opaque (the text), but map a bunch of alpha values in between to the desired background alpha&nbsp;<code>a<\/code>. This should also catch and map the background overlap alpha (which is&nbsp;<code>a + a - a*a = 2*a - a*a<\/code>&nbsp;&#8211; for more details, see this&nbsp;<a href=\"https:\/\/css-tricks.com\/adventures-in-css-semi-transparency-land\/\">Adventures in CSS Semi-Transparency Land<\/a>&nbsp;article) to&nbsp;<code>a<\/code>.<\/p>\n\n\n\n<p>This is a very smart solution and it seems to work really well for this particular background and text case as well as for similar cases. But there are still issues, points where it breaks.<\/p>\n\n\n\n<p>First off, if we increase the alpha to something like&nbsp;<code>.75<\/code>, we start seeing an overlap.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"192\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420356221-86ba9f46-d026-4138-9975-04f6f8acddf8.png?resize=800%2C192&#038;ssl=1\" alt=\"Screenshot showing four lines of golden text, middle aligned, each with its own semitransparent dark blue background and padding. The padding on each line leads to intersection, and where we have intersection, the semi-transparent background becomes more opaque.\" class=\"wp-image-5351\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420356221-86ba9f46-d026-4138-9975-04f6f8acddf8.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420356221-86ba9f46-d026-4138-9975-04f6f8acddf8.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420356221-86ba9f46-d026-4138-9975-04f6f8acddf8.png?resize=768%2C184&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>overlap problem becoming visible when alpha is bumped up to&nbsp;<code>.75<\/code><\/em><\/figcaption><\/figure>\n\n\n\n<p>My first instinct was to do what Amelia also suggests doing in the comments to her version &#8211; increase the number of intervals as the alpha gets closer to the ends of the&nbsp;<code>[0, 1]<\/code>&nbsp;interval.<\/p>\n\n\n\n<p>Since I&#8217;m using <a href=\"https:\/\/pugjs.org\/api\/getting-started.html\">Pug<\/a> to generate the markup anyway, I figured this would be a good way to first measure how large the base intervals would need to be &#8211; and by that I mean the minimum between the distance between the ends of the&nbsp;<code>[0, 1]<\/code>&nbsp;interval and the desired alpha as well as the overlap alpha.<\/p>\n\n\n\n<p>We&#8217;re excluding&nbsp;<code>2*a - a*a<\/code>&nbsp;and&nbsp;<code>1 - a<\/code>&nbsp;from the minimum computation since&nbsp;<code>a<\/code>&nbsp;is subunitary, so&nbsp;<code>a<\/code>&nbsp;is always bigger than&nbsp;<code>a*a<\/code>, which results in&nbsp;<code>a<\/code>&nbsp;being always smaller than&nbsp;<code>2*a - a*a = a*(2 - a)<\/code>, which also results in&nbsp;<code>1 + a*a - 2*a<\/code>&nbsp;being smaller than&nbsp;<code>1 - a<\/code>.<\/p>\n\n\n\n<p>Then we get how many such base intervals&nbsp;<code>u<\/code>&nbsp;we could fit between&nbsp;<code>0<\/code>&nbsp;and&nbsp;<code>1<\/code>, round up this number (<code>n<\/code>) and then generate the list of alpha values (for&nbsp;<code>tableValues<\/code>) which remains&nbsp;<code>0<\/code>&nbsp;and&nbsp;<code>1<\/code>&nbsp;at the ends, but is set to&nbsp;<code>a<\/code>&nbsp;everywhere in between.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">- let u = Math.min(a, 1 + a*a - 2*a);\n- let n = Math.ceil(1\/u);\n- let v = new Array(n + 1).fill(0).map((_, i) =&gt; i*(n - i) ? a : i\/n)\n\nfeFuncA(type='table' tableValues=v.join(' '))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This does indeed fix the background overlap problem for any alpha, though it still means we need different filters for different alphas. Here is what gets generated for a few different alpha values:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-comment\">&lt;!-- a = .8 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> \n         <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 .8 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .75 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 .75 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .65 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .65 .65 .65 .65 .65 .65 .65 .65 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .5 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .5 .5 .5 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .35 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .35 .35 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .2 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .2 .2 .2 .2 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .1 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .1 .1 .1 .1 .1 .1 .1 .1 .1 1'<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- a = .05 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> \n         <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 .05 1'<\/span>\/&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We also have another bigger problem: due to font anti-aliasing, the&nbsp;<code>feComponentTransfer<\/code> messes up the text for lower value alphas and the lower the value, the worse the problem looks.<\/p>\n\n\n\n<p>Font anti-aliasing makes the edge pixels of text semi-transparent in order to avoid a&nbsp;<a href=\"https:\/\/bsky.app\/profile\/anatudor.bsky.social\/post\/3kzquolz3xs2c\">jagged, pixelated, ugly<\/a>, even broken look. For comparison, below is the same text without vs. with anti-aliasing, at normal size and scaled up 12 times:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"360\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420568656-a726cc27-9ef9-46a2-84d4-943370d2a18f.png?resize=800%2C360&#038;ssl=1\" alt=\"The text &quot;Pixels&quot; without (left) vs. with anti-aliasing (right), in normal size (top) and scaled up 12x times (bottom). The normal size text looks a bit rough in the no anti-aliasing version at normal size, whereas its anti-aliased version looks smooth. On zoom, we can see this is due to the no-anti-aliasing version having only fully opaque and fully transparent pixels, whereas the other version has its edges smoothened by semi-transparent pixels with various alpha levels.\" class=\"wp-image-5352\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420568656-a726cc27-9ef9-46a2-84d4-943370d2a18f.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420568656-a726cc27-9ef9-46a2-84d4-943370d2a18f.png?resize=300%2C135&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420568656-a726cc27-9ef9-46a2-84d4-943370d2a18f.png?resize=768%2C346&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>without vs. with anti-aliasing<\/em><\/figcaption><\/figure>\n\n\n\n<p>Those semi-transparent font edge pixels placed on top of the semi-transparent background also give us semi-transparent pixels. At the same time, our&nbsp;<code>filter<\/code>&nbsp;maps the alpha of more and more of the semi-transparent pixels of the input to the desired background alpha&nbsp;<code>a<\/code>&nbsp;as this&nbsp;<code>a<\/code>&nbsp;nears the ends of the&nbsp;<code>[0, 1]<\/code>&nbsp;interval. As&nbsp;<code>a<\/code>&nbsp;nears&nbsp;<code>0<\/code>, then almost all semi-transparent edge pixels get this very low&nbsp;<code>a<\/code>&nbsp;alpha, making them much more transparent than they should be and causing an eroded look for our text.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='296' src='https:\/\/videopress.com\/embed\/Ge3Bu3Oa?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>illustrating the problem caused by anti-aliasing (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/MYWoVKB\">live demo<\/a>)<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>I guess a simple fix for that would be to only map to the desired alpha&nbsp;<code>a<\/code>&nbsp;the smallest number of alpha points possible and let all others keep their initial alpha. This would mean that the first alpha point we map to the desired alpha&nbsp;<code>a<\/code>&nbsp;is equal to it or the nearest smaller than it, while the last one is equal to the overlap alpha&nbsp;<code>2*a - a*a<\/code>&nbsp;or the nearest bigger than it.<\/p>\n\n\n\n<p>For example, if the desired alpha&nbsp;<code>a<\/code>&nbsp;is&nbsp;<code>.2<\/code>, then the overlap alpha is&nbsp;<code>.2 + .2 - .2*.2 = .36<\/code>. The base interval&nbsp;<code>u<\/code>&nbsp;is&nbsp;<code>.2<\/code>,&nbsp;<code>n<\/code>&nbsp;is&nbsp;<code>1\/.2 = 5<\/code>, so we generate&nbsp;<code>n + 1 = 6<\/code>&nbsp;alpha points:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0 .2 .4 .6 .8 1<\/pre>\n\n\n\n<p>If before we mapped all those between&nbsp;<code>0<\/code>&nbsp;and&nbsp;<code>1<\/code>&nbsp;to the desired alpha&nbsp;<code>.2<\/code>, now we only map to the desired alpha&nbsp;<code>a<\/code>, those loosely matching the&nbsp;<code>[.2, .36]<\/code>&nbsp;interval &#8211; that is,&nbsp;<code>.2<\/code>&nbsp;and&nbsp;<code>.4<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0 .2 .2 .6 .8 1<\/pre>\n\n\n\n<p>In general, that means our values array would become:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">- let v = new Array(n + 1).fill(0);\n- v = v.map((_, i) =&gt; (i*(n - i) &amp;&amp; (i + 1)\/n &gt; a &amp;&amp; (i - 1)\/n <span class=\"hljs-tag\">&lt; <span class=\"hljs-attr\">a<\/span>*(<span class=\"hljs-attr\">2<\/span> <span class=\"hljs-attr\">-<\/span> <span class=\"hljs-attr\">a<\/span>)) ? <span class=\"hljs-attr\">a<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">i<\/span>\/<span class=\"hljs-attr\">n<\/span>);<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Probably ensuring the values outside the interval mapped to&nbsp;<code>a<\/code>&nbsp;are evenly distributed would be the more correct solution, but this simpler trick also seems to work really well when it comes to fixing the text erosion problem.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='296' src='https:\/\/videopress.com\/embed\/2vnkFRZP?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>testing a basic fix for the antialiasing problem (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/QwWgxMG\">live demo<\/a>)<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>But you may have noticed there&#8217;s still a problem and this is not an SVG&nbsp;<code>filter<\/code>&nbsp;one, it comes from the CSS.<\/p>\n\n\n\n<p>To make it more obvious, let&#8217;s put result right next to what we got via the earlier method of seting the RGBA values from the SVG&nbsp;<code>filter<\/code>&nbsp;&#8211; can you see it?<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='410' src='https:\/\/videopress.com\/embed\/Pz5L7juY?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>setting RGBA values in SVG filter method vs. RGB in CSS plus alpha fixing via SVG filter method (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/qEBjwzx\">live demo<\/a>)<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>If you can&#8217;t spot it in the recording above, how about when we have a diagonal middle split in between the result we get when we bake into the filter all RGBA values and the result we get with this alpha fix method via&nbsp;<code>feComponentTransfer<\/code>?<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"374\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420592423-5ee58be4-e3b6-475d-8353-dbdd07b28506.png?resize=800%2C374&#038;ssl=1\" alt=\"Split comparison screenshot. The paragraph box is split into two triangles, lightly separated by a gap along the secondary diagonal. In the top left triangle, we have the result obtained using the method of hardcoding the RGBA values for both the text and the background into the SVG filter. In the bottom right one, we have the result obtained using the method of setting the RGBA values in the CSS and then using an SVG filter to fix the overlap alpha to be the desired background alpha as well. Both use a background alpha of .85 and in his case, it looks like the text using the second method is a bit more faded.\" class=\"wp-image-5358\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420592423-5ee58be4-e3b6-475d-8353-dbdd07b28506.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420592423-5ee58be4-e3b6-475d-8353-dbdd07b28506.png?resize=300%2C140&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420592423-5ee58be4-e3b6-475d-8353-dbdd07b28506.png?resize=768%2C359&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>split comparison<\/em><\/figcaption><\/figure>\n\n\n\n<p>It&#8217;s pretty subtle here, but if you think it looks like this latest method is making the text a bit more faded, particularly at higher alpha values, you&#8217;re right.<\/p>\n\n\n\n<p>This is because the blending fix for the background overlapping text problem results in the text&nbsp;<code>color<\/code>&nbsp;not being preserved. This was precisely why we switched from a blending-only solution to an SVG&nbsp;<code>filter<\/code>&nbsp;one in the case when the text isn&#8217;t black or white (or close enough and the particular choice of text and background preserves the text post-blending exactly as it was set).<\/p>\n\n\n\n<p>A lot of text and background combinations don&#8217;t make this very obvious because, in order to have&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Accessibility\/Guides\/Understanding_WCAG\/Perceivable\/Color_contrast\">a good contrast ratio<\/a>, we often need either the text or the background behind it to be very dark or very bright &#8211; which means there&#8217;s a chance all three RGB channels of the text are either below or above the corresponding RGB channels of the background, or even if one of the channels is deviating on the other side, it&#8217;s not deviating enough to make a noticeable difference. But sometimes we can still see there&#8217;s a problem, as illustrated by the interactive demo below, which allows changing the palette.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_jEOwgoP\" src=\"\/\/codepen.io\/anon\/embed\/jEOwgoP?height=450&amp;theme-id=47434&amp;slug-hash=jEOwgoP&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed jEOwgoP\" title=\"CodePen Embed jEOwgoP\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>All of these palettes were chosen to have a good contrast ratio. Even so, there is some degree of text fading&nbsp;<em>for all of them<\/em>. And while it&#8217;s not easy to spot that for the first five, it&#8217;s way more noticeable for the second to last one and almost impossible to miss for the final one.<\/p>\n\n\n\n<p>Let&#8217;s take the second to last one, which uses a lighter blue than our initial palette, so it has a somewhat lower contrast making the problem more obvious. The higher the alpha gets, what should be golden text on a semitransparent deep blue background looks more pink-ish. This is due to the text being&nbsp;<code>rgb(100% 74.51% 4.31%)<\/code>&nbsp;and the background being&nbsp;<code>rgb(22.75% 21.18% 100%)<\/code>&nbsp;(we leave out the transparency for now and assume the alpha is&nbsp;<code>1<\/code>). Blending these using the&nbsp;<code>lighten<\/code>&nbsp;blend mode means taking the maximum value out of the two for each channel &#8211; that is,&nbsp;<code>100%<\/code>&nbsp;(<code>max(100%, 22.75%)<\/code>) for the red channel,&nbsp;<code>74.51%<\/code>&nbsp;(<code>max(74.51%, 21.18%)<\/code>) for the green one and&nbsp;<code>100%<\/code>&nbsp;(<code>max(4.31%, 100%)<\/code>) for the blue one. That means our text is&nbsp;<code>rgb(100% 74.51% 100%)<\/code>, a light pink, which is different from the&nbsp;<code>color<\/code>&nbsp;value of&nbsp;<code>rgb(100% 74.51% 4.31%)<\/code>&nbsp;(golden) we&#8217;ve set.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='285' src='https:\/\/videopress.com\/embed\/kNkKLwer?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>particular case of the second to last palette<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>The final text and background combination makes the problem even more clear. The higher the alpha gets, what should be lime text on a semitransparent blue background looks more like aqua text. This is due to the text being&nbsp;<code>rgb(0% 100% 0%)<\/code>&nbsp;and the background being&nbsp;<code>rgb(0% 0% 100%)<\/code>&nbsp;(again, we leave out the transparency for now and assume the alpha is&nbsp;<code>1<\/code>). Blending these using the&nbsp;<code>lighten<\/code>&nbsp;blend mode means taking the maximum value out of the two for each channel &#8211; that is,&nbsp;<code>0%<\/code>&nbsp;(<code>max(0%, 0%)<\/code>) for the red channel,&nbsp;<code>100%<\/code>&nbsp;(<code>max(100%, 0%)<\/code>) for the green one and&nbsp;<code>100%<\/code>&nbsp;(<code>max(0%, 100%)<\/code>) for the blue one. That means our text is&nbsp;<code>rgb(0% 100% 100%)<\/code>, so aqua, which is different from the&nbsp;<code>color<\/code>&nbsp;value of&nbsp;<code>rgb(0% 100% 0%)<\/code>&nbsp;(lime) we&#8217;ve set.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='285' src='https:\/\/videopress.com\/embed\/6vWnDPhz?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>particular case of the final palette<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>So what now? Well, the one solution I&#8217;ve been able to find is to pass in the text and background shapes separate from the RGBA values used for them. I&#8217;ve tried approaching this in multiple ways and ended up hitting bugs in all browsers. Tiling bugs in Safari and Chrome, a weird Windows-specific bug in Firefox, the same wide gamut bug mentioned before in Chrome&#8230; bugs everywhere.<\/p>\n\n\n\n<p>So now we&#8217;re not going through all of my failed experiments, of which there were many, we&#8217;re just looking at the one solution I&#8217;ve managed to get working reasonably well across various browser, OS and hardware combinations.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"set-shapes-and-rgba-values-in-the-css-pass-them-to-the-svg-filter-via-different-channels-alpha-points\">Set shapes and RGBA values in the CSS, pass them to the SVG filter via different channels\/ alpha points<\/h4>\n\n\n\n<p>The shape of the span background and that of the text get passed to the SVG filter using the&nbsp;<code>1<\/code>&nbsp;alpha point. That means we have white text on black background, all opaque, so we can extract it in the SVG by mapping all alpha points except&nbsp;<code>1<\/code>&nbsp;to&nbsp;<code>0<\/code>.<\/p>\n\n\n\n<p>We pass the text and background RGB values using the&nbsp;<code>.75<\/code>&nbsp;and&nbsp;<code>.25<\/code>&nbsp;alpha points &#8211; this allows us to extract them in the SVG&nbsp;<code>filter<\/code>&nbsp;by mapping their corresponding alpha points to&nbsp;<code>1<\/code>, while all other alpha points are&nbsp;<code>0<\/code>.<\/p>\n\n\n\n<p>Finally, we pass the alpha value to the SVG via the green channel, using the&nbsp;<code>.5<\/code>&nbsp;alpha point. By mapping the&nbsp;<code>.5<\/code>&nbsp;alpha point to&nbsp;<code>1<\/code>, while all other alpha points get mapped to&nbsp;<code>0<\/code>, we can extract in the SVG&nbsp;<code>filter<\/code>&nbsp;the desired background alpha value via the green channel value.<\/p>\n\n\n\n<p>This means we have five alpha points (<code>0<\/code>,&nbsp;<code>.25<\/code>,&nbsp;<code>.5<\/code>,&nbsp;<code>.75<\/code>&nbsp;and&nbsp;<code>1<\/code>), so we&#8217;re going to need to use five values for the&nbsp;<code>tableValues<\/code>&nbsp;attribute of&nbsp;<code>feFuncA<\/code>, all of them zeroed, except the one corresponding to the point we&#8217;re interested in and which we map to&nbsp;<code>1<\/code>.<\/p>\n\n\n\n<p>In order to do this, we first add an absolutely positioned, non-clickable pseudo on the&nbsp;<code>p<\/code>&nbsp;element. This pseudo has a&nbsp;<code>border<\/code>&nbsp;and two shadows (an outer one and an&nbsp;<code>inset<\/code>&nbsp;one) and is offset outwards (using a negative&nbsp;<code>inset<\/code>) to compensate for both the&nbsp;<code>inset<\/code>&nbsp;shadow and the&nbsp;<code>border<\/code>, so that there is no visible part of this pseudo intersecting the&nbsp;<code>span<\/code>&nbsp;background shape.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">--a<\/span>: <span class=\"hljs-number\">0.7<\/span>;\n  <span class=\"hljs-attribute\">--text-c<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">255<\/span> <span class=\"hljs-number\">165<\/span> <span class=\"hljs-number\">0<\/span>);\n  <span class=\"hljs-attribute\">--back-c<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">25<\/span> <span class=\"hljs-number\">112<\/span>);\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n\n  &amp;::after {\n    <span class=\"hljs-attribute\">position<\/span>: absolute;\n    <span class=\"hljs-attribute\">inset<\/span>: -<span class=\"hljs-number\">2em<\/span>;\n    <span class=\"hljs-attribute\">border<\/span>: solid <span class=\"hljs-number\">1em<\/span> <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">0%<\/span> calc(var(--a) * <span class=\"hljs-number\">100%<\/span>) <span class=\"hljs-number\">0%<\/span>\/ <span class=\"hljs-number\">0.5<\/span>);\n    <span class=\"hljs-attribute\">box-shadow<\/span>: inset <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1em<\/span> <span class=\"hljs-built_in\">rgba<\/span>(from var(--text-c) r g b\/ <span class=\"hljs-number\">0.75<\/span>),\n      <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1em<\/span> <span class=\"hljs-built_in\">rgba<\/span>(from var(--back-c) r g b\/ <span class=\"hljs-number\">0.25<\/span>);\n    <span class=\"hljs-attribute\">pointer-events<\/span>: none;\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"\"<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The first shadow is an&nbsp;<code>inset<\/code>&nbsp;one using the desired text RGB value and a&nbsp;<code>.75<\/code>&nbsp;alpha, which allows us to pass the RGB value to the SVG&nbsp;<code>filter<\/code>&nbsp;via the&nbsp;<code>.75<\/code>&nbsp;alpha point. The second shadow is an outer one using the desired background RGB value and a&nbsp;<code>.25<\/code>&nbsp;alpha, which allows us to pass the RGB value to the SVG&nbsp;<code>filter<\/code>&nbsp;via the&nbsp;<code>.25<\/code>&nbsp;alpha point.<\/p>\n\n\n\n<p>The&nbsp;<code>border-color<\/code>&nbsp;uses the desired&nbsp;<code>span<\/code>&nbsp;background alpha value on the green channel (we&#8217;re using the green channel due to the same Chrome wide gamut bug mentioned earlier in this article) and has a&nbsp;<code>.5<\/code>&nbsp;alpha. This allows us to pass to the SVG&nbsp;<code>filter<\/code>&nbsp;the value of the desired&nbsp;<code>span<\/code>&nbsp;background alpha as the green channel value using the&nbsp;<code>.5<\/code>&nbsp;alpha point.<\/p>\n\n\n\n<p>The negative&nbsp;<code>inset<\/code>&nbsp;(<code>-2em<\/code>) is set to compensate for both the&nbsp;<code>inset<\/code>&nbsp;shadow (with a&nbsp;<code>1em<\/code>&nbsp;spread) and for the&nbsp;<code>border<\/code>&nbsp;(with a&nbsp;<code>1em<\/code>&nbsp;width) because it&#8217;s very important that none of the visible parts of the pseudo (the&nbsp;<code>border<\/code>&nbsp;and the&nbsp;<code>box-shadow<\/code>&nbsp;using the&nbsp;<code>.25<\/code>,&nbsp;<code>.5<\/code>&nbsp;and&nbsp;<code>.75<\/code>&nbsp;alpha points) intersect the shape of the&nbsp;<code>span<\/code>&nbsp;background (using the&nbsp;<code>1<\/code>&nbsp;alpha point).<\/p>\n\n\n\n<p>The&nbsp;<code>pointer-events: none<\/code>&nbsp;property is there in order to avoid any interference with the&nbsp;<code>span<\/code>&nbsp;text selection. We could have also used&nbsp;<code>z-index: -1<\/code>, since there is no intersection between the visible parts of the pseudo and the&nbsp;<code>span<\/code>&nbsp;background shape. Both of them do the job and in this case, it really doesn&#8217;t matter which we choose to use.<\/p>\n\n\n\n<p>What we have so far definitely doesn&#8217;t look great, but&#8230; we&#8217;re getting there!<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622735-172f79d1-5f49-4c70-9b01-c9cde68974c0.png?resize=800%2C400&#038;ssl=1\" alt=\"Screenshot showing the result of the above CSS. We have four lines of white text, middle aligned, each with its own black background and padding. Although the background of each line overlaps the text of the adjacent ones, the text is shown everywhere on top. Around these lines of text, without touhing them, we have three nested frames. The innermost one has an alpha of .75 and the RGB value we want for the text in the final version. The middle one has an alpha of .5 and has the red and blue channels zeroed, while the green one has the value of the desired background alpha. The outer one has an alpha of .25 and the RGB value we want for the background.\" class=\"wp-image-5359\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622735-172f79d1-5f49-4c70-9b01-c9cde68974c0.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622735-172f79d1-5f49-4c70-9b01-c9cde68974c0.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622735-172f79d1-5f49-4c70-9b01-c9cde68974c0.png?resize=768%2C384&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>before applying any filter<\/em><\/figcaption><\/figure>\n\n\n\n<p>Moving on to the&nbsp;<code>filter<\/code>, we start in a similar manner as before, by getting the&nbsp;<code>opaque<\/code>&nbsp;part. To do so, we preserve just just the fifth alpha point (<code>1<\/code>), while mapping all others to&nbsp;<code>0<\/code>. Everything that intially has an alpha of&nbsp;<code>0<\/code>&nbsp;(transparent part inside the frames around the&nbsp;<code>span<\/code>&nbsp;shape),&nbsp;<code>.25<\/code>&nbsp;(outermost dark blue frame),&nbsp;<code>.5<\/code>&nbsp;(middle green frame) or&nbsp;<code>.75<\/code>&nbsp;(innermost golden frame) becomes transparent.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">'0'<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">'true'<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">'go'<\/span> <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">'sRGB'<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">'opaque'<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'table'<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">'0 0 0 0 1'<\/span>\/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We&#8217;ve saved this result as&nbsp;<code>opaque<\/code>&nbsp;for when we need to use it later.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622938-0a6221ab-593a-4f45-93ca-1dc28820428e.png?resize=800%2C400&#038;ssl=1\" alt=\"Screenshot showing what we get after the first  primitive: just the four lines of white text on black packground.\" class=\"wp-image-5360\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622938-0a6221ab-593a-4f45-93ca-1dc28820428e.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622938-0a6221ab-593a-4f45-93ca-1dc28820428e.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420622938-0a6221ab-593a-4f45-93ca-1dc28820428e.png?resize=768%2C384&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the opaque result<\/em><\/figcaption><\/figure>\n\n\n\n<p>Next, from the initial&nbsp;<code>filter<\/code>&nbsp;input, we extract the background RGB area by mapping the second (<code>.25<\/code>) alpha point to&nbsp;<code>1<\/code>, while mapping all others to&nbsp;<code>0<\/code>. Note that we don&#8217;t want the input of the second primitive to be the&nbsp;<code>result<\/code>&nbsp;of the first one, but the&nbsp;<code>filter<\/code>&nbsp;input, so we explicitly specify <code>in<\/code>&nbsp;as&nbsp;<code>SourceGraphic<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span> <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In theory, this second&nbsp;<code>feComponentTransfer<\/code>&nbsp;extracts second just the background RGB area (pseudo outer shadow area, using the second alpha point,&nbsp;<code>.25<\/code>). In practice, can you see what else it has picked up?<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420623150-858f198f-0791-40de-8dae-7a4af30623f7.png?resize=800%2C400&#038;ssl=1\" alt=\"Screenshot showing what we get after the second  primitive: just the outermost frame, the one holding the background RGB, but now with its alpha set to , fully opaque.\" class=\"wp-image-5361\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420623150-858f198f-0791-40de-8dae-7a4af30623f7.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420623150-858f198f-0791-40de-8dae-7a4af30623f7.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420623150-858f198f-0791-40de-8dae-7a4af30623f7.png?resize=768%2C384&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the outer frame using the background RGB<\/em><\/figcaption><\/figure>\n\n\n\n<p>If you cannot pick it up (it&#8217;s not easy), let&#8217;s remove the image backdrop and circle the problem areas:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420624040-f48d5f15-0298-4ea1-923a-3dfcbdd96b92.png?resize=800%2C400&#038;ssl=1\" alt=\"Screenshot showing the same outermost frame with the background RGB, now on a white background that allows us to see that in the middle of the rectangle this frame is placed around, there are a few stray black pixels.\" class=\"wp-image-5362\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420624040-f48d5f15-0298-4ea1-923a-3dfcbdd96b92.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420624040-f48d5f15-0298-4ea1-923a-3dfcbdd96b92.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/420624040-f48d5f15-0298-4ea1-923a-3dfcbdd96b92.png?resize=768%2C384&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>highlighting the problem areas<\/em><\/figcaption><\/figure>\n\n\n\n<p>Those black pixels it picks up are again due to anti-aliasing. At the rounded corners of the&nbsp;<code>span<\/code>&nbsp;background lines, we have semitransparent pixels in order for these corners to look smooth, not jagged. But then our second&nbsp;<code>feComponentTransfer<\/code> maps the pixels in the&nbsp;<code>[0, .25]<\/code>&nbsp;interval to&nbsp;<code>[0, 1]<\/code> and the pixels in the&nbsp;<code>[.25, .5]<\/code> interval to&nbsp;<code>[1, 0]<\/code>. And this doesn&#8217;t catch just the pixels of the pseudo&#8217;s outer shadow using the&nbsp;<code>.25<\/code>&nbsp;alpha point, but also the pixels in the&nbsp;<code>[0, .5]<\/code>&nbsp;interval at those rounded corners of those&nbsp;<code>span<\/code>&nbsp;background lines, which get a non-zero alpha too.<\/p>\n\n\n\n<p>Now in our particular case where we have a black&nbsp;<code>span<\/code>&nbsp;background, we can safely just ignore those pixels when moving on to the next step. But if we were to have a red background there, things would be very different and those pixels could cause a lot of trouble.<\/p>\n\n\n\n<p>That&#8217;s because at the next step we expand the background RGB frame we got to cover the entire&nbsp;<code>filter<\/code>&nbsp;area and we do that with a&nbsp;<code>feMorphology<\/code>&nbsp;primitive using the&nbsp;<code>dilate<\/code>&nbsp;operation. What this does is the following: for every channel of every pixel, it takes the&nbsp;<em>maximum<\/em>&nbsp;of all the values of that channel for the pixels lying within the specified&nbsp;<code>radius<\/code>&nbsp;(from the current pixel) along both the&nbsp;<em>x<\/em>&nbsp;and the&nbsp;<em>y<\/em>&nbsp;axes in both the negative and positive direction.<\/p>\n\n\n\n<p>Below, you can see how this works for a channel whose values are either maxed out (<code>1<\/code>) or zeroed (<code>0<\/code>). For every pixel of the input (green outline around the current one), the corresponding output value for the same channel is the maximum of all the values for that channel in the vicinity of the current pixel (within the red square).<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='603' src='https:\/\/videopress.com\/embed\/SVWv7R53?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>how dilation works in the general case<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>For our purpose, we first care about the alpha channel, since this turns opaque all transparent pixels that are within the specified&nbsp;<code>radius<\/code>&nbsp;from any opaque one along both axes in both directions, effectively dilating our frame to fill the area inside it.<\/p>\n\n\n\n<p>But the maximum computation happens for the RGB channels too. Black has zero for all RGB channels, so those stray pixels don&#8217;t affect the result of the maximum computation since every single one of the RGB channels of the frame is above zero, which makes them be the result of the maximum for every single one of the RGB channels.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><mark class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/mark><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that the&nbsp;<code>filter<\/code>&nbsp;now has&nbsp;<code>primitiveUnits<\/code>&nbsp;set to&nbsp;<code>objectBoundingBox<\/code>&nbsp;so values for attributes such as the&nbsp;<code>radius<\/code>&nbsp;attribute of&nbsp;<code>feMorphology<\/code>&nbsp;are not pixel values anymore, but relative to the&nbsp;<code>filter<\/code>&nbsp;input box size. This is because the size of our&nbsp;<code>filter<\/code>&nbsp;area is given by its input, whose exact pixel size is determined by the text content which we have no way of knowing. So we switch to relative units.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='338' src='https:\/\/videopress.com\/embed\/y9XaZrfy?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>dilating the frame to fill the filter area (black span background)<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>There are two things to keep in mind here.<\/p>\n\n\n\n<p>One, I&#8217;m not exactly happy to have to use such a relatively large dilation value, as it can negatively impact performance (at least from the tests on my laptop, the performance hit is obvious in both Firefox and Epiphany for the final demo). But unfortunately, my initial idea of extracting small squares in the top left corner and then tiling them ran into at least one different bug in every browser on at least one OS, so I guess this dilation was the only option left.<\/p>\n\n\n\n<p>Two, if we had a red (<code>rgb(100% 0% 0%)<\/code>) instead of a black (<code>rgb(0% 0% 0%)<\/code>) background, then the maxed up red channel would cause trouble since&nbsp;<code>100%<\/code>&nbsp;is a bigger value than the&nbsp;<code>9.8%<\/code>&nbsp;of the frame (desired RGB being&nbsp;<code>rgb(9.8% 9.8% 43.9%)<\/code>), so then we&#8217;d end up with those pesky corner pixels bloating up and turning the intersection with the dilated frame purple, a mix (<code>rgb(max(100%, 9.8%) max(0%, 9.8%) max(0%, 43.9%))<\/code>) between the red channel of the initial red&nbsp;<code>span<\/code>&nbsp;background and the green and blue channels of the frame (which has the desired RGB value for the background and whose red channel we&#8217;d lose this way).<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='338' src='https:\/\/videopress.com\/embed\/WKB25WuV?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>dilating the frame to fill the filter area (red span background)<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>In such a case where a red input area would &#8220;contaminate&#8221; our desired background RGB, we&#8217;d first need to apply a small erosion to get rid of those pesky corner pixels&nbsp;<em>before<\/em>&nbsp;we apply the dilation. Erosion works in a similar manner to dilation, except we take the&nbsp;<em>minimum<\/em>&nbsp;channel value of all pixels within the set&nbsp;<code>radius<\/code>&nbsp;along both axes in both directions.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='600' src='https:\/\/videopress.com\/embed\/GWdvgYeJ?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption><em>how erosion works in the general case<\/em><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>In our case, we care about the alpha channel erosions, all the transparent pixels around zeroing the alpha of those few ones we didn&#8217;t really mean to pick up.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">'.01'<\/span>\/&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that&nbsp;<code>erode<\/code>&nbsp;is the default&nbsp;<code>operator<\/code>, so we don&#8217;t need to explicitly set it.<\/p>\n\n\n\n<p>Back to our case, after dilating the frame to fill the entire&nbsp;<code>filter<\/code>&nbsp;area with the desired background RGB and saving this result as&nbsp;<code>back-rgb<\/code>, we extract (again, out of the initial&nbsp;<code>filter<\/code>&nbsp;input) the desired alpha as the green channel value of the pseudo border with a&nbsp;<code>.5<\/code>&nbsp;alpha. This means another&nbsp;<code>feComponentTransfer<\/code>, this time one mapping all alpha points to&nbsp;<code>0<\/code>, except for the third one (<code>.5<\/code>), which gets mapped to&nbsp;<code>1<\/code>&nbsp;(though in this one case the exact alpha it gets mapped to doesn&#8217;t really matter as long as its non-zero).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This gives us a green frame (red and blue channels zeroed, green channel set to the value of the desired alpha for the background of the&nbsp;<code>span<\/code>&nbsp;lines):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421526552-d7aa5011-b420-42ad-86e9-7c218867d0f0.png?resize=800%2C416&#038;ssl=1\" alt=\"Screenshot showing what we get after the third  primitive: just the middle frame, the one holding the background alpha on the green channel, but now with its alpha set to , fully opaque.\" class=\"wp-image-5363\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421526552-d7aa5011-b420-42ad-86e9-7c218867d0f0.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421526552-d7aa5011-b420-42ad-86e9-7c218867d0f0.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421526552-d7aa5011-b420-42ad-86e9-7c218867d0f0.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the middle frame using the desired alpha on the green channel<\/em><\/figcaption><\/figure>\n\n\n\n<p>Now you can probably guess what follows: we&nbsp;<code>dilate<\/code>&nbsp;this green frame to cover the entire&nbsp;<code>filter<\/code>&nbsp;area. Again, we have those stray black pixels, but since they&#8217;re black, their channel values just get discarded when we perform the dilation, so we don&#8217;t need that erosion step in between.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We don&#8217;t save the&nbsp;<code>result<\/code>&nbsp;of this primitive this time, but we&#8217;ll get to that in a moment. This is what we have now &#8211; not too exciting yet, though things are about to change.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421563823-bef2a12e-63c3-48ae-8c03-ab698869dae1-1.png?resize=800%2C512&#038;ssl=1\" alt=\"Screenshot showing the middle frame from before (holding the background alpha on the green channel) dilated in all directions to the point it has filled the entire filter area.\" class=\"wp-image-5365\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421563823-bef2a12e-63c3-48ae-8c03-ab698869dae1-1.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421563823-bef2a12e-63c3-48ae-8c03-ab698869dae1-1.png?resize=300%2C192&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421563823-bef2a12e-63c3-48ae-8c03-ab698869dae1-1.png?resize=768%2C492&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>middle frame dilated to fill entire filter area<\/em><\/figcaption><\/figure>\n\n\n\n<p>Next, we use&nbsp;<code>feColorMatrix<\/code>&nbsp;to give this layer covering the entire&nbsp;<code>filter<\/code>&nbsp;area an alpha equal to that of its green channel. This is why we don&#8217;t save the result of the second&nbsp;<code>feMorphology<\/code>&nbsp;&#8211; because we only feed it into the input of the very next primitive,&nbsp;<code>feColorMatrix<\/code>&nbsp;and then we don&#8217;t need it anywhere after that. We don&#8217;t care about the RGB values of the&nbsp;<code>result<\/code>, only about the alpha, so we just zero them all.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Basically, what this&nbsp;<code>feColorMatrix<\/code>&nbsp;does is set the output alpha channel to be equal to the input green channel (well, to&nbsp;<code>1<\/code>&nbsp;multiplied with the input green channel), regardless of the values of the other input channels (red, blue, alpha). This way, we recover the alpha channel from the green one.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421564769-35c90577-cc28-417d-8f11-fd28e1baaf26.png?resize=800%2C512&#038;ssl=1\" alt=\"Screenshot showing the fill from the previous step, now with the green channel value transferred onto the alpha channel, giving us a semi-transparent fill, of the alpha we want for the  background.\" class=\"wp-image-5366\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421564769-35c90577-cc28-417d-8f11-fd28e1baaf26.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421564769-35c90577-cc28-417d-8f11-fd28e1baaf26.png?resize=300%2C192&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421564769-35c90577-cc28-417d-8f11-fd28e1baaf26.png?resize=768%2C492&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">alpha value finally on the alpha channel<\/figcaption><\/figure>\n\n\n\n<p>Next step is to intersect the previously saved&nbsp;<code>back-rgb<\/code>&nbsp;result with this one, so we keep the RGB channels of that layer and the alpha channel of this one.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>What happens here is the alphas of the two input layers (<code>1<\/code>&nbsp;for&nbsp;<code>back-rgb<\/code>&nbsp;and the desired&nbsp;<code>span<\/code>&nbsp;background alpha for the other) are multiplied to give us the output alpha. At the same time, we only keep the RGB values of the top one (<code>back-rgb<\/code>) for the output.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421566248-cb88abc1-50fa-407c-9820-6484b5272908.png?resize=800%2C512&#038;ssl=1\" alt=\"Screenshot showing us the result of compositing the full filter area fill background RGB and background alpha layers. This has the desired background RGB and the desired background alpha.\" class=\"wp-image-5367\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421566248-cb88abc1-50fa-407c-9820-6484b5272908.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421566248-cb88abc1-50fa-407c-9820-6484b5272908.png?resize=300%2C192&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421566248-cb88abc1-50fa-407c-9820-6484b5272908.png?resize=768%2C492&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">the desired semi-transparent background, filling the filter area<\/figcaption><\/figure>\n\n\n\n<p>We now have the entire filter area covered by a layer with the desired RGBA for the&nbsp;<code>span<\/code>&nbsp;background lines, so the next step is to restrict it to the area of those&nbsp;<code>span<\/code>&nbsp;lines,&nbsp;<code>opaque<\/code>. That is, only keep it at the intersection with that area and save the&nbsp;<code>result<\/code>&nbsp;as&nbsp;<code>back<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>It finally looks like we&#8217;re getting somewhere!<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421567092-20782bfb-8bd8-47a4-955d-1f7e8f7638b2.png?resize=800%2C416&#038;ssl=1\" alt=\"Screenshot showing us the result of the latest compositing step: the background RGBA layer at the intersection with the initial opaque shape of the  background area.\" class=\"wp-image-5368\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421567092-20782bfb-8bd8-47a4-955d-1f7e8f7638b2.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421567092-20782bfb-8bd8-47a4-955d-1f7e8f7638b2.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421567092-20782bfb-8bd8-47a4-955d-1f7e8f7638b2.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">the semi-transparent dark blue background of the&nbsp;<code>span<\/code><\/figcaption><\/figure>\n\n\n\n<p>Next, we can move on to the text!<\/p>\n\n\n\n<p>We start by extracting the text RGB area by mapping the fourth (<code>.75<\/code>) alpha point to&nbsp;<code>1<\/code>, while mapping all others to&nbsp;<code>0<\/code>. Again, we explicitly specify <code>in<\/code> as <code>SourceGraphic<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This gives us yet another frame, this time one in the gold we want for the text.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421568337-289ed7d9-92db-4c73-b5e0-9fcd041b98b3.png?resize=800%2C416&#038;ssl=1\" alt=\"What we get after the fourth  primitive: just the innermost frame, the one holding the text RGB, but now with its alpha set to , fully opaque.\" class=\"wp-image-5369\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421568337-289ed7d9-92db-4c73-b5e0-9fcd041b98b3.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421568337-289ed7d9-92db-4c73-b5e0-9fcd041b98b3.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421568337-289ed7d9-92db-4c73-b5e0-9fcd041b98b3.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">the inner frame using the text RGB<\/figcaption><\/figure>\n\n\n\n<p>Just like we did for the other frames, we&nbsp;<code>dilate<\/code>&nbsp;this one too in order to make it fill the entire&nbsp;<code>filter<\/code>&nbsp;area and save this result as&nbsp;<code>text-rgb<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569320-65eb1151-e0fa-4d6e-a024-25b6ccadde1c.png?resize=800%2C512&#038;ssl=1\" alt=\"Screenshot showing the inner frame from before (holding the text RGB value) dilated in all directions to the point it has filled the entire filter area.\" class=\"wp-image-5370\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569320-65eb1151-e0fa-4d6e-a024-25b6ccadde1c.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569320-65eb1151-e0fa-4d6e-a024-25b6ccadde1c.png?resize=300%2C192&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569320-65eb1151-e0fa-4d6e-a024-25b6ccadde1c.png?resize=768%2C492&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">inner frame dilated to fill entire filter area<\/figcaption><\/figure>\n\n\n\n<p>Then we extract the text shape from the&nbsp;<code>opaque<\/code>&nbsp;layer, just like we did before, using the green channel like an alpha mask.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>My expectation was that this would give us just the text shape like below, which is what happens in Chrome.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569898-090dbf24-dc65-4dff-830b-b538d97c6128.png?resize=800%2C416&#038;ssl=1\" alt=\"Screenshot showing the Chrome result when extracting the green channel as an alpha mask from the initial opaque portion of the input, the one having white text (all three RGB channels maxed out) on black background (all three RGB channels zeroed). This is just the actal text area in this case, the green channel of the frames appears to have been thrown out when thir alphas got zeroed at the first step which gave us just the fully opaque area.\" class=\"wp-image-5371\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569898-090dbf24-dc65-4dff-830b-b538d97c6128.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569898-090dbf24-dc65-4dff-830b-b538d97c6128.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421569898-090dbf24-dc65-4dff-830b-b538d97c6128.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">the text shape extracted using the green channel (Chrome)<\/figcaption><\/figure>\n\n\n\n<p>However, Firefox does something interesting here and thinking it through, I&#8217;m not entirely sure it&#8217;s wrong.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421581023-73fac2b0-fa13-4029-b449-71036fdc48b7.png?resize=800%2C416&#038;ssl=1\" alt=\"Screenshot showing the Chrome result when extracting the green channel as an alpha mask from the initial opaque portion of the input, the one having white text (all three RGB channels maxed out) on black background (all three RGB channels zeroed). This is not just the white text in this case, but also the frames as the filter input had non-zero green channel values there and unlike Chrome, Firefox doesn't seem to have discarded their RGB values when zeroing the frame alphas at the first step extracting just the fully opaque portion.\" class=\"wp-image-5372\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421581023-73fac2b0-fa13-4029-b449-71036fdc48b7.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421581023-73fac2b0-fa13-4029-b449-71036fdc48b7.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421581023-73fac2b0-fa13-4029-b449-71036fdc48b7.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>Firefox extracting more than just the text shapes<\/em><\/figcaption><\/figure>\n\n\n\n<p>What seems to happen is that Chrome forgets all about the RGB values of the semi-transparent areas of the pseudo and just zeroes them when zeroing their alphas in the first <code>feComponentTransfer<\/code> primitive to extract the&nbsp;<code>opaque<\/code>&nbsp;part (the&nbsp;<code>span<\/code>&nbsp;with white text on solid black background). Then when using the green channel as an alpha mask on the opaque part, all that&#8217;s not transparent is the white text, where the green channel is maxed out.<\/p>\n\n\n\n<p>However, Firefox doesn&#8217;t seem to throw away the RGB values of those semi-transparent frames created by the&nbsp;<code>border<\/code>&nbsp;and&nbsp;<code>box-shadow<\/code>&nbsp;on the pseudo, even if it also zeroes their alphas via the first primitive as well. So even though the&nbsp;<code>opaque<\/code>&nbsp;result looks the same in both browsers, it&#8217;s not really the same. Then when we get to this latest&nbsp;<code>feColorMatrix<\/code>&nbsp;step, Firefox finds green in those now fully transparent frames because even though their alpha got zeroed to get the&nbsp;<code>opaque<\/code>&nbsp;result, their RGB values got preserved.<\/p>\n\n\n\n<p>Whichever browser is right, there&#8217;s a very simple way to get the result we want cross-browser: intersect what we have now with the&nbsp;<code>opaque<\/code>&nbsp;result. It doesn&#8217;t even matter the RGB values of which layer we choose to preserve as a result of this intersection because we won&#8217;t be using them anyway.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The next step is to keep the&nbsp;<code>text-rgb<\/code>&nbsp;layer only at the intersection with the text we just got.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421573835-3385e69e-5c01-4d25-9863-281e7d5e4a23.png?resize=800%2C416&#038;ssl=1\" alt=\"Screenshot showing the golden fll layer kept just at the intersection with the text shape.\" class=\"wp-image-5373\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421573835-3385e69e-5c01-4d25-9863-281e7d5e4a23.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421573835-3385e69e-5c01-4d25-9863-281e7d5e4a23.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/421573835-3385e69e-5c01-4d25-9863-281e7d5e4a23.png?resize=768%2C399&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><em>the golden text of the&nbsp;<code>span<\/code><\/em><\/figcaption><\/figure>\n\n\n\n<p>Finally, we place this on top of the&nbsp;<code>back<\/code>&nbsp;layer with a&nbsp;<code>feBlend<\/code>, just like we did before.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-hidden<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">filter<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"go\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">color-interpolation-filters<\/span>=<span class=\"hljs-string\">\"sRGB\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    <span class=\"hljs-attr\">primitiveUnits<\/span>=<span class=\"hljs-string\">\"objectBoundingBox\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">  &gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 0 1\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 1 0 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 1 0 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"back-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComponentTransfer<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"SourceGraphic\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feFuncA<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"table\"<\/span> <span class=\"hljs-attr\">tableValues<\/span>=<span class=\"hljs-string\">\"0 0 0 1 0\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">feComponentTransfer<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feMorphology<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"dilate\"<\/span> <span class=\"hljs-attr\">radius<\/span>=<span class=\"hljs-string\">\".5\"<\/span> <span class=\"hljs-attr\">result<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feColorMatrix<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">      <span class=\"hljs-attr\">values<\/span>=<span class=\"hljs-string\">\"0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 0 0 0 0 <\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">              0 1 0 0 0\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">    \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"opaque\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feComposite<\/span> <span class=\"hljs-attr\">in<\/span>=<span class=\"hljs-string\">\"text-rgb\"<\/span> <span class=\"hljs-attr\">operator<\/span>=<span class=\"hljs-string\">\"in\"<\/span> \/&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">feBlend<\/span> <span class=\"hljs-attr\">in2<\/span>=<span class=\"hljs-string\">\"back\"<\/span> \/&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">filter<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svg<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is our final result!<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XJWaMMb\" src=\"\/\/codepen.io\/anon\/embed\/XJWaMMb?height=450&amp;theme-id=47434&amp;slug-hash=XJWaMMb&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XJWaMMb\" title=\"CodePen Embed XJWaMMb\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>This allows us to have full control from the CSS over the text and background RGB, as well as over the background alpha, without needing to hardcode any of them in the SVG&nbsp;<code>filter<\/code>, which means we don&#8217;t need a different SVG&nbsp;<code>filter<\/code>&nbsp;if we want to set a different value for any of them on one o the elements the filter is applied to.<\/p>\n\n\n\n<p>Now you may be thinking&#8230; well, this looks ugly with those semi-transparent frames before the filter is&nbsp;<code>applied<\/code>, so what if the filter fails? Well, the fix is really simple.&nbsp;<code>clip-path<\/code>&nbsp;gets applied after <code>filter<\/code>, so we can clip out those frames. They still get used for the&nbsp;<code>filter<\/code>&nbsp;if the&nbsp;<code>filter<\/code>&nbsp;is applied, but if it fails, we are still left with the very reasonable choice of white text on black background.<\/p>\n\n\n\n<p>The following demo has different text and background combinations for each paragraph. All paragraphs use the exact same&nbsp;<code>filter<\/code>&nbsp;(the one above), they just have different values for&nbsp;<code>--text-c<\/code>,&nbsp;<code>--back-c<\/code>&nbsp;and&nbsp;<code>--a<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_EaxmJag\" src=\"\/\/codepen.io\/anon\/embed\/EaxmJag?height=450&amp;theme-id=47434&amp;slug-hash=EaxmJag&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed EaxmJag\" title=\"CodePen Embed EaxmJag\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>`box-decoration-break: clone;` in CSS can help us make for interesting backgrounds across lines of text that break, but when opacity gets involved, things can get complicated.<\/p>\n","protected":false},"author":32,"featured_media":5425,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[258,7,91],"class_list":["post-5330","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-blockquote","tag-css","tag-svg"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Frame-1.jpg?fit=2023%2C1392&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5330","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/32"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=5330"}],"version-history":[{"count":19,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5330\/revisions"}],"predecessor-version":[{"id":5531,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5330\/revisions\/5531"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/5425"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=5330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=5330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=5330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}