<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Svg/icons</title>
    <description>The latest articles on DEV Community by Svg/icons (@svgicons).</description>
    <link>https://dev.to/svgicons</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940768%2F093b279b-947a-4e08-b433-4da6f4e803c7.png</url>
      <title>DEV Community: Svg/icons</title>
      <link>https://dev.to/svgicons</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svgicons"/>
    <language>en</language>
    <item>
      <title>Why Icons Lose Contrast on Translucent Cards</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:17:57 +0000</pubDate>
      <link>https://dev.to/svgicons/why-icons-lose-contrast-on-translucent-cards-3512</link>
      <guid>https://dev.to/svgicons/why-icons-lose-contrast-on-translucent-cards-3512</guid>
      <description>&lt;p&gt;An icon can look clear in a component preview and become difficult to see when the background changes.&lt;/p&gt;

&lt;p&gt;In our test, the same download icon went from a minimum sampled contrast of &lt;strong&gt;4.42:1 on a light background to 1.04:1 on a dark background&lt;/strong&gt;. Its SVG, color and size stayed the same.&lt;/p&gt;

&lt;p&gt;The difference came from the translucent card behind it.&lt;/p&gt;

&lt;p&gt;We tested five backgrounds, then applied a simple correction: an opaque white surface behind the icon, while keeping the rest of the card translucent.&lt;/p&gt;

&lt;h2&gt;
  
  
  One icon, five backgrounds
&lt;/h2&gt;

&lt;p&gt;We used the &lt;a href="https://svgicons.com/icon/113147/download" rel="noopener noreferrer"&gt;Download icon from Lucide on SVGicons&lt;/a&gt;, by Lucide Contributors, under the &lt;a href="https://github.com/lucide-icons/lucide/blob/main/LICENSE" rel="noopener noreferrer"&gt;ISC license&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every scene uses the same settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Icon:&lt;/strong&gt; 32 × 32 pixels, colored &lt;code&gt;#64748b&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Button:&lt;/strong&gt; 48 × 48 pixels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Card background:&lt;/strong&gt; white at 38% opacity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backdrop blur:&lt;/strong&gt; 12 pixels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVG geometry and card layout:&lt;/strong&gt; unchanged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original button has a transparent background. The corrected version adds an opaque white background to that button.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Light background
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fer4p8zhhou3o3elilvo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fer4p8zhhou3o3elilvo0.png" alt="Download icon on a light background, before and after adding an opaque white button" width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original icon is already easy to distinguish against this light card. Adding an opaque white button makes only a modest difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum sampled contrast: 4.42:1 → 4.76:1.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Dark background
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9eqbd0t9ia2y5ayje0ol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9eqbd0t9ia2y5ayje0ol.png" alt="Download icon on a dark background: contrast improves from 1.04:1 to 4.76:1 with an opaque white butto" width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over a dark background, the translucent card becomes darker too. The slate-colored icon now sits against a similar rendered color, making it much harder to distinguish.&lt;/p&gt;

&lt;p&gt;The white button restores separation without changing the SVG.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum sampled contrast: 1.04:1 → 4.76:1.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Mid-tone background
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28y8io8trm6jpw7jtvvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28y8io8trm6jpw7jtvvy.png" alt="Download icon on a mid-tone background, before and after adding an opaque white button." width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A background does not have to be very dark to reduce contrast. This mid-tone scene also brings the card’s rendered color closer to the icon’s color.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum sampled contrast: 2.72:1 → 4.76:1.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Gradient background
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5f128u28jyouom0xtax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5f128u28jyouom0xtax.png" alt="Download icon on a gradient background, before and after adding an opaque white button." width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a gradient, the colors next to the icon’s strokes depend on their position. Measuring against a color picked elsewhere on the card can miss the weakest local contrast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum sampled contrast: 1.68:1 → 4.76:1.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Photographic background
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flcxu7bawsl45ynz3tcn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flcxu7bawsl45ynz3tcn2.png" alt="Download icon over a photograph, before and after adding an opaque white button." width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A photograph introduces variations in brightness and color. Blur softens those variations, but does not guarantee sufficient contrast around the icon.&lt;/p&gt;

&lt;p&gt;The corrected button keeps the photograph visible through the card while giving the icon a predictable local background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum sampled contrast: 1.58:1 → 4.76:1.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photograph: &lt;a href="https://commons.wikimedia.org/wiki/File:Fronalpstock_big.jpg" rel="noopener noreferrer"&gt;Fronalpstock big.jpg&lt;/a&gt;, by Hannes Röst / Wikimedia Commons, licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/3.0/" rel="noopener noreferrer"&gt;CC BY-SA 3.0&lt;/a&gt;. We used a centered cover crop of the 1280 × 575 thumbnail. The photographic crop and its adaptations in these comparison images retain that license.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;These are the &lt;strong&gt;minimum sampled contrast ratios&lt;/strong&gt; for the recorded settings, rounded to two decimal places.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Background&lt;/th&gt;
&lt;th&gt;Original button&lt;/th&gt;
&lt;th&gt;Opaque white button&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Light — &lt;code&gt;#f4f4f5&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;4.42:1&lt;/td&gt;
&lt;td&gt;4.76:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dark — &lt;code&gt;#18181b&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.04:1&lt;/td&gt;
&lt;td&gt;4.76:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid-tone — &lt;code&gt;#94a3b8&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;2.72:1&lt;/td&gt;
&lt;td&gt;4.76:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Light-to-navy gradient&lt;/td&gt;
&lt;td&gt;1.68:1&lt;/td&gt;
&lt;td&gt;4.76:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Photograph&lt;/td&gt;
&lt;td&gt;1.58:1&lt;/td&gt;
&lt;td&gt;4.76:1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same SVG produces very different results because the background immediately around its strokes changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an opaque button helps
&lt;/h2&gt;

&lt;p&gt;A white translucent card is not a solid white background. Its final appearance depends on what is behind it and how the browser renders the backdrop filter.&lt;/p&gt;

&lt;p&gt;Comparing the original icon with solid white would therefore give the wrong picture. Comparing it with the raw photograph would also skip the card’s transparency and blur.&lt;/p&gt;

&lt;p&gt;The correction gives the icon an &lt;strong&gt;opaque local surface&lt;/strong&gt;. The rest of the card remains translucent, but the backdrop can no longer change the color immediately around the strokes.&lt;/p&gt;

&lt;p&gt;In these five recorded cases, the white surface produces a minimum sampled contrast of &lt;strong&gt;4.76:1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;White is not a universal requirement. A dark surface and a light icon can serve the same purpose. The important part is choosing and testing the foreground and background together.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reusable HTML/CSS example
&lt;/h2&gt;

&lt;p&gt;This minimal example demonstrates the correction using the same Download geometry. It is not the complete measured scene.&lt;/p&gt;

&lt;p&gt;Connect the button to your application’s download handler and keep its accessible name specific to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"file-card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Field notes&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"download"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
    &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Download field notes"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt;
      &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
      &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
      &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt;
      &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt;
      &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
      &lt;span class="na"&gt;focusable=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;g&lt;/span&gt;
        &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
        &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
        &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
        &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
        &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M12 15V3m9 12v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"m7 10l5 5l5-5"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.file-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.38&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;backdrop-filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.download&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;place-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.download&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.download&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nx"&gt;#2563eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An accessible name and visual contrast solve different problems.&lt;/strong&gt; The &lt;code&gt;aria-label&lt;/code&gt; identifies the action for assistive technology. It does not make a low-contrast icon easier to see.&lt;/p&gt;

&lt;p&gt;The SVG is hidden from assistive technology because the button already supplies the action’s name. The focus indicator needs its own contrast check against the surrounding interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we measured
&lt;/h2&gt;

&lt;p&gt;We sampled colors from browser captures after transparency and blur had been rendered.&lt;/p&gt;

&lt;p&gt;For each case, we captured:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The normal scene with the icon visible.&lt;/li&gt;
&lt;li&gt;The same scene with only the SVG hidden using &lt;code&gt;visibility: hidden&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A separate mask of the SVG geometry to locate the solid cores of its strokes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hiding the SVG preserved the card, button, filters and layout. This provided the composited background for comparison.&lt;/p&gt;

&lt;p&gt;The script compared foreground stroke-core pixels with the nearest fully unpainted adjacent background pixels within four device pixels. Antialiased stroke edges were excluded so blended fringe colors were not treated as the icon’s intended color.&lt;/p&gt;

&lt;p&gt;Contrast was calculated using the WCAG relative-luminance formula. Sample coordinates, RGB values and minimum locations were recorded.&lt;/p&gt;

&lt;p&gt;The test used Chromium 156.0.8078.4 on Windows, Playwright 1.64.1, a 1600 × 1120 viewport, device scale 1, forced sRGB and a fixed photographic crop.&lt;/p&gt;

&lt;p&gt;The original and corrected rows occupied slightly different subpixel positions, so their accepted stroke-core sample counts differed. These measurements are finite samples, not an exhaustive analysis of every pixel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these numbers do—and do not—mean
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/WAI/WCAG21/Techniques/general/G207" rel="noopener noreferrer"&gt;W3C technique G207&lt;/a&gt; discusses a contrast ratio of at least &lt;strong&gt;3:1&lt;/strong&gt; for icons required to understand content.&lt;/p&gt;

&lt;p&gt;It also considers whether an icon remains understandable when its insufficiently contrasting portions are treated as invisible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A low sampled value is not an automatic failure verdict for the entire icon.&lt;/strong&gt; Our script measures contrast; it does not decide whether the remaining shape still communicates its meaning.&lt;/p&gt;

&lt;p&gt;This case study does not establish full WCAG compliance. We did not measure card text, control boundaries, hover and focus contrast, other browsers, display scales or forced-color modes. No human screen-reader session was performed.&lt;/p&gt;

&lt;p&gt;The results apply to the recorded configuration. Changes to opacity, blur, icon color, image crop or layout require another check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take into your next interface
&lt;/h2&gt;

&lt;p&gt;When placing an icon on a translucent surface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test it over the backgrounds users will actually encounter.&lt;/li&gt;
&lt;li&gt;Check the rendered colors next to the icon, rather than the card’s CSS tint alone.&lt;/li&gt;
&lt;li&gt;Do not assume backdrop blur will preserve contrast.&lt;/li&gt;
&lt;li&gt;Consider an opaque local surface when the background can vary.&lt;/li&gt;
&lt;li&gt;Check accessible naming, keyboard focus and interaction states separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SVG is only one part of the result. Its surroundings determine whether people can see it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>accessibily</category>
      <category>css</category>
      <category>design</category>
    </item>
    <item>
      <title>Why Dark Mode Tests Don’t Cover Windows Contrast Themes</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Sat, 24 Oct 2026 14:43:13 +0000</pubDate>
      <link>https://dev.to/svgicons/why-dark-mode-tests-dont-cover-windows-contrast-themes-gkl</link>
      <guid>https://dev.to/svgicons/why-dark-mode-tests-dont-cover-windows-contrast-themes-gkl</guid>
      <description>&lt;p&gt;A toolbar can look correct in light and dark mode and still become difficult to use with a Windows contrast theme.&lt;/p&gt;

&lt;p&gt;The missing check is &lt;strong&gt;forced colors&lt;/strong&gt;: a browser mode that applies a restricted, user-selected palette to the page.&lt;/p&gt;

&lt;p&gt;For SVG icons, this raises a different question from ordinary theme support:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the surrounding interface changes to system colors, does the icon remain recognizable—and does the control still look like a control?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article explains the documented behavior and a practical way to check it. It does not present results from a completed Windows browser comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the rendered control
&lt;/h2&gt;

&lt;p&gt;A contrast calculation evaluates a foreground against a background. But the colors you authored are not necessarily the colors the browser paints in forced colors mode.&lt;/p&gt;

&lt;p&gt;A button might normally use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a colored background;&lt;/li&gt;
&lt;li&gt;a white icon;&lt;/li&gt;
&lt;li&gt;a subtle shadow;&lt;/li&gt;
&lt;li&gt;a different background for its selected state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the browser applies the user's palette, some of those distinctions can change or disappear.&lt;/p&gt;

&lt;p&gt;The useful unit to review is therefore the whole control: its icon, label, boundary, focus indicator, and state.&lt;/p&gt;

&lt;p&gt;An SVG can remain present in the DOM while becoming difficult to distinguish visually. A button can remain clickable while losing the visual cues that explain what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes in forced colors?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/forced-colors" rel="noopener noreferrer"&gt;&lt;code&gt;forced-colors&lt;/code&gt; media feature&lt;/a&gt; detects whether the browser is enforcing a restricted palette.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forced-colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Targeted adjustments for the user's palette */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDN documents adjustments to text, backgrounds, borders, and SVG paint properties. It also documents the removal of box shadows and non-URL background images, including CSS gradients.&lt;/p&gt;

&lt;p&gt;Two details matter for icons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A URL-based SVG background is not automatically removed by the rule that removes gradients.&lt;/strong&gt; Its artwork may still become difficult to see against the changed surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SVG has specific color-adjustment behavior.&lt;/strong&gt; The CSS specification describes &lt;code&gt;preserve-parent-color&lt;/code&gt;, which allows embedded SVG to receive its parent's adjusted text color while otherwise preserving artwork colors.&lt;/p&gt;

&lt;p&gt;That makes blanket statements such as “forced colors recolors every SVG path” unreliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep monochrome action icons connected to text
&lt;/h2&gt;

&lt;p&gt;For a monochrome icon inside a button, using &lt;code&gt;currentColor&lt;/code&gt; for the appropriate fill or stroke lets the artwork follow the control's text color.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DownloadButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"action"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;
        &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
        &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
        &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
        &lt;span class="na"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M12 3v12m-5-5 5 5 5-5M5 17v4h14v-4"&lt;/span&gt;
          &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
          &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
          &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
          &lt;span class="na"&gt;strokeLinejoin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download report&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SVG does not introduce a separate text color. Its stroke follows the inherited &lt;code&gt;color&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This example uses simple illustrative geometry. When adapting an existing icon, preserve the distinction between fill and stroke. Replacing every paint value indiscriminately can change the artwork.&lt;/p&gt;

&lt;p&gt;For a broader explanation of integration methods, see &lt;a href="https://dev.to/svgicons/same-svg-different-runtime-why-css-inline-svg-and-are-not-equivalent-551d"&gt;Same SVG, Different Runtime&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The important reminder here is that an external SVG loaded through &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; does not inherit the surrounding button's text color into its internal paths. Adding &lt;code&gt;currentColor&lt;/code&gt; inside that file does not create the same inheritance relationship as inline SVG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check whether the button boundary survives
&lt;/h2&gt;

&lt;p&gt;An icon is only one part of the control.&lt;/p&gt;

&lt;p&gt;If a button depends entirely on a shadow to separate it from its surroundings, removing that shadow can make the boundary unclear.&lt;/p&gt;

&lt;p&gt;A transparent border reserves space in the ordinary design. A targeted rule can then give that border a system color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#17324d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#edf3f9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.375rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.action&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.action&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forced-colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ButtonText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.action&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;outline-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Highlight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ButtonText&lt;/code&gt; and &lt;code&gt;Highlight&lt;/code&gt; refer to the active system palette.&lt;/p&gt;

&lt;p&gt;Treat this as a starting pattern to inspect in context. A default button, a disabled button, and a selected toggle have different communication requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t preserve the entire design with an opt-out
&lt;/h2&gt;

&lt;p&gt;This rule may appear to solve unexpected changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;forced-color-adjust&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it disables automatic color adjustment throughout that subtree.&lt;/p&gt;

&lt;p&gt;That can preserve the same color combinations the user enabled a contrast theme to avoid.&lt;/p&gt;

&lt;p&gt;Keep any exception narrowly scoped. A color swatch may need to preserve a particular color because that color is the information. An ordinary toolbar usually benefits from following the user's palette.&lt;/p&gt;

&lt;p&gt;If an exception is necessary, check both the preserved artwork and its contrast against the surrounding interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test states, not just screenshots of the default theme
&lt;/h2&gt;

&lt;p&gt;A useful review should answer specific questions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Icon&lt;/td&gt;
&lt;td&gt;Can its shape still be distinguished from the surface?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label&lt;/td&gt;
&lt;td&gt;Is the action understandable if the icon is unclear?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boundary&lt;/td&gt;
&lt;td&gt;Can users identify the clickable control?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyboard focus&lt;/td&gt;
&lt;td&gt;Is the focused control visibly indicated?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected state&lt;/td&gt;
&lt;td&gt;Can users distinguish selected from unselected?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disabled state&lt;/td&gt;
&lt;td&gt;Is the unavailable state still recognizable?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a toggle, exposing &lt;code&gt;aria-pressed&lt;/code&gt; communicates its state to assistive technology. Sighted users also need a visible indication that survives the active palette.&lt;/p&gt;

&lt;p&gt;Similarly, an accessible name on an icon-only button does not resolve an invisible icon for a sighted user.&lt;/p&gt;

&lt;p&gt;Both forms of communication need review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use emulation during development, then check Windows
&lt;/h2&gt;

&lt;p&gt;Browser emulation is useful for finding problems early.&lt;/p&gt;

&lt;p&gt;You can confirm that the browser reports forced colors with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(forced-colors: active)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;true&lt;/code&gt; result confirms that the media query matches. It does not confirm that the interface is usable.&lt;/p&gt;

&lt;p&gt;For a Windows-focused check, use actual Windows contrast themes as well:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the interface in a browser you support.&lt;/li&gt;
&lt;li&gt;Enable a contrast theme with a dark canvas.&lt;/li&gt;
&lt;li&gt;Inspect the controls and navigate with the keyboard.&lt;/li&gt;
&lt;li&gt;Repeat with a contrast theme with a light canvas.&lt;/li&gt;
&lt;li&gt;Check selected, disabled, hover, and focus states where applicable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Record the Windows version, browser version, theme name, and whether the test used OS settings or emulation.&lt;/p&gt;

&lt;p&gt;Label screenshots accordingly. An emulated browser capture should not be presented as a capture of an actual Windows contrast theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make an icon comparison reproducible
&lt;/h2&gt;

&lt;p&gt;If you compare integration methods, keep the artwork constant.&lt;/p&gt;

&lt;p&gt;Choose a small group of monochrome icons from one family and save:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the exact SVG files;&lt;/li&gt;
&lt;li&gt;their source URLs;&lt;/li&gt;
&lt;li&gt;the applicable license information;&lt;/li&gt;
&lt;li&gt;any changes made to fill, stroke, or color;&lt;/li&gt;
&lt;li&gt;the component code and test conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then compare inline SVG, &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, and URL-based CSS backgrounds using the same geometry.&lt;/p&gt;

&lt;p&gt;This separates differences caused by integration from differences introduced while preparing the assets.&lt;/p&gt;

&lt;p&gt;A collection exported from SVGicons can provide the source assets, but the exported files are only the beginning. The component that renders them still needs inspection in the target environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add forced colors to the review
&lt;/h2&gt;

&lt;p&gt;Light and dark themes check the palettes your application provides.&lt;/p&gt;

&lt;p&gt;Forced colors checks how the interface behaves with a palette chosen by the user.&lt;/p&gt;

&lt;p&gt;Before shipping an icon-based control, make sure its meaning, boundary, focus, and state remain available under that condition too.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/forced-colors" rel="noopener noreferrer"&gt;MDN: forced-colors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drafts.csswg.org/css-color-adjust-1/#forced-color-adjust-prop" rel="noopener noreferrer"&gt;CSS Color Adjustment: forced-color-adjust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Accessibility/PreserveParentColor/explainer.md" rel="noopener noreferrer"&gt;Microsoft Edge: preserve-parent-color explainer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://melanie-richards.com/blog/currentcolor-svg-hcm/" rel="noopener noreferrer"&gt;Melanie Richards: CurrentColor SVG in forced colors modes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/svgicons/same-svg-different-runtime-why-css-inline-svg-and-are-not-equivalent-551d"&gt;SVGicons: Same SVG, Different Runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>accessibilty</category>
      <category>svg</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Keep Dynamic Icon Groups from Breaking Your List Layout</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:34:09 +0000</pubDate>
      <link>https://dev.to/svgicons/keep-dynamic-icon-groups-from-breaking-your-list-layout-fo2</link>
      <guid>https://dev.to/svgicons/keep-dynamic-icon-groups-from-breaking-your-list-layout-fo2</guid>
      <description>&lt;p&gt;A list looks tidy when every row starts with one icon. Then one item gets three icons, another gets five, and the labels stop lining up.&lt;/p&gt;

&lt;p&gt;The tempting fix is to adjust the gap. That works for one row and breaks another. The gap was consistent already; the space before it was changing.&lt;/p&gt;

&lt;p&gt;Give every row the same leading slot, then put a limit on what can occupy it. In this React example, that means a shared CSS Grid column, a bounded icon stack, and an explicit rule for overflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjnq93xdhweqtj6d4rkz1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjnq93xdhweqtj6d4rkz1.gif" alt="Animated illustration of icon group alignment before and after using a fixed-width slot" width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the labels drift
&lt;/h2&gt;

&lt;p&gt;Suppose a row contains an icon group followed by its label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"source-row"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IconGroup&lt;/span&gt; &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row-content"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a content-sized group in a flex row, the label follows the group's width. One badge takes less space than three overlapping badges. Even with identical icon sizes and gaps, the labels start at different positions.&lt;/p&gt;

&lt;p&gt;That is reasonable flex behavior. It becomes a problem when the design expects a common label column across independent rows.&lt;/p&gt;

&lt;p&gt;A concrete instance appears in &lt;a href="https://github.com/yetone/magpie/issues/2498" rel="noopener noreferrer"&gt;Magpie issue #2498&lt;/a&gt;: model rows have a single provider icon, while routing groups have stacked icons. The issue describes reserving a common leading width. This tutorial uses that reported problem as a starting point, with an independent teaching implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the stack policy before choosing the width
&lt;/h2&gt;

&lt;p&gt;A fixed column alone does not stop its children from overflowing. Decide how many positions the component can display first.&lt;/p&gt;

&lt;p&gt;For this example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Number of sources&lt;/th&gt;
&lt;th&gt;Visible content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Empty slot, with the label still aligned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1–3&lt;/td&gt;
&lt;td&gt;All source icons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 or more&lt;/td&gt;
&lt;td&gt;First two icons and a counter for the remaining sources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five sources therefore render as two icons and &lt;code&gt;+3&lt;/code&gt;. The counter occupies the third position; it is not appended as a fourth item.&lt;/p&gt;

&lt;p&gt;Use 28px badges, with each overlapping badge starting 16px after the previous one. Three positions occupy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;28 + (3 - 1) × 16 = 60px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 64px slot leaves 4px of spare space. With a 16px root font size, those dimensions are &lt;code&gt;1.75rem&lt;/code&gt;, &lt;code&gt;1rem&lt;/code&gt;, and &lt;code&gt;4rem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Choose a width from the actual badge geometry and overflow policy. A &lt;code&gt;3rem&lt;/code&gt; slot, for example, would be too small for this particular stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the React component
&lt;/h2&gt;

&lt;p&gt;The example uses three Tabler SVGs retrieved through the SVGicons catalogue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://svgicons.com/icon/205669/folder" rel="noopener noreferrer"&gt;Folder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgicons.com/icon/203679/bell" rel="noopener noreferrer"&gt;Bell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgicons.com/icon/205533/file-settings" rel="noopener noreferrer"&gt;File Settings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are by Paweł Kuna and distributed under the &lt;a href="https://github.com/tabler/tabler-icons/blob/main/LICENSE" rel="noopener noreferrer"&gt;MIT licence&lt;/a&gt;. Keep the copyright and licence notice with redistributed assets.&lt;/p&gt;

&lt;p&gt;Save them in your application's public directory so they are available at &lt;code&gt;/icons/folder.svg&lt;/code&gt;, &lt;code&gt;/icons/bell.svg&lt;/code&gt;, and &lt;code&gt;/icons/file-settings.svg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The layout does not require a particular icon library. Using local image files keeps SVG markup out of the component's layout logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;IconGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon-slot"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon-stack"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt;
            &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"badge count"&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;99+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`+&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SourceList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"source-list"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"source-row"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IconGroup&lt;/span&gt; &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row-content"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row-label"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
                &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`. Sources: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
                &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;. No connected sources&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each member needs a stable, unique &lt;code&gt;id&lt;/code&gt;, a readable &lt;code&gt;name&lt;/code&gt;, and a trusted local &lt;code&gt;src&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/folder.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alerts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alerts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/bell.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Configuration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/file-settings.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;archive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;archive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Archive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/folder.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;monitoring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monitoring&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Monitoring&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/bell.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;documents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Documents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Project activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Workspace overview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;operations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operations across all connected workspaces&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;monitoring&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No connected sources yet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SourceList&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source names are exposed once as text. The overlapping images and counter are hidden from assistive technology, avoiding a second announcement of the same source information.&lt;/p&gt;

&lt;p&gt;This approach suits a static list. If users need to inspect or select individual sources, provide a visible details view with equivalent keyboard access. A tiny overlapped badge should not be the only way to reach an action.&lt;/p&gt;

&lt;p&gt;Localize the source sentence and counter formatting for your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reserve the column and contain the stack
&lt;/h2&gt;

&lt;p&gt;Apply these styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.source-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--badge-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--stack-step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--icon-slot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.source-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-slot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;column-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;padding-block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;border-block-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e3e8f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.icon-slot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-slot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.icon-stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--stack-step&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="py"&gt;inset-block-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;place-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dce8ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dcf5ec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#efe3ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.125rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.125rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.625rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#17233b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row-label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anywhere&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sr-only&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;clip-path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two separate constraints here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The shared &lt;code&gt;--icon-slot&lt;/code&gt; keeps the label position consistent across rows.&lt;/li&gt;
&lt;li&gt;The component's three-position limit keeps the stack within that slot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An empty group keeps its wrapper, so zero icons does not become another alignment exception.&lt;/p&gt;

&lt;p&gt;The absolutely positioned badges need an explicit stack height because they do not contribute to their parent's normal flow height. Later badges paint over earlier ones in this simple stack.&lt;/p&gt;

&lt;p&gt;The label track uses &lt;code&gt;minmax(0, 1fr)&lt;/code&gt; so its minimum track size does not default to a content-based minimum. &lt;code&gt;min-inline-size: 0&lt;/code&gt; lets the content wrapper shrink, while &lt;code&gt;overflow-wrap: anywhere&lt;/code&gt; allows long unbroken identifiers to wrap.&lt;/p&gt;

&lt;p&gt;These choices address different parts of overflow handling. Replacing the icon column alone will not fix every long-text case. See MDN's references for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/grid-template-columns" rel="noopener noreferrer"&gt;grid track sizing&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/min-width" rel="noopener noreferrer"&gt;minimum widths&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If labels must stay on one line, use a deliberate truncation design instead. Make the full text available through an accessible details interaction; do not rely only on a mouse tooltip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the same rule on narrow screens and in RTL
&lt;/h2&gt;

&lt;p&gt;As the container narrows, the fixed slot stays put and the text wraps in the remaining track. Top alignment keeps a multi-line label beside the top of its icon stack.&lt;/p&gt;

&lt;p&gt;If a compact design needs a smaller slot, change its geometry for the entire list. For example, 24px badges with a 12px step occupy 48px across three positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.source-list.compact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--badge-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--stack-step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--icon-slot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never make this choice independently for each row, or the common column disappears again. Check that the counter remains readable in the smaller badges.&lt;/p&gt;

&lt;p&gt;For right-to-left layout, place &lt;code&gt;dir="rtl"&lt;/code&gt; on the list or an ancestor. Grid's inline direction places the leading column on the right. &lt;code&gt;inset-inline-start&lt;/code&gt; moves the badge stack from that same side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rtl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SourceList&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use real translated labels when reviewing the result. Changing direction on English text checks the layout rule, but does not verify Arabic or Hebrew typography, punctuation, or mixed-direction identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the relationship that matters
&lt;/h2&gt;

&lt;p&gt;A screenshot can catch an obvious regression. A geometry assertion can also express the actual requirement: every label container has the same leading edge.&lt;/p&gt;

&lt;p&gt;Here is a Playwright test for a page containing one &lt;code&gt;.source-list&lt;/code&gt;. Serve the component on the &lt;code&gt;/&lt;/code&gt; route and configure Playwright's &lt;code&gt;baseURL&lt;/code&gt; to point to your development server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;420&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1100&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ltr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rtl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`label alignment: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setViewportSize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.source-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;edges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.row-content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;evaluateAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rtl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nx"&gt;direction&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeLessThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check that every badge stays within its slot, images have loaded, and the document has no horizontal overflow. A component can keep its labels aligned while its icons still collide with them.&lt;/p&gt;

&lt;p&gt;Use fixtures with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero, one, two, three and five sources.&lt;/li&gt;
&lt;li&gt;A very large source count.&lt;/li&gt;
&lt;li&gt;A multi-line label.&lt;/li&gt;
&lt;li&gt;A long unbroken identifier.&lt;/li&gt;
&lt;li&gt;Larger text.&lt;/li&gt;
&lt;li&gt;Both LTR and RTL directions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check keyboard navigation if the rows contain links or buttons. Keep screenshot baselines only after reviewing the initial renders, and rerun the checks in your supported browser engines.&lt;/p&gt;

&lt;p&gt;The useful invariant is simple: source count may change the content of the icon slot, but it must not change where the label begins. Once that rule is explicit, new data has much less room to rearrange the list.&lt;/p&gt;

</description>
      <category>react</category>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Your SVG Icon Migration Worked. So Why Are Some Icons Missing?</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:52:16 +0000</pubDate>
      <link>https://dev.to/svgicons/your-svg-icon-migration-worked-so-why-are-some-icons-missing-1ok</link>
      <guid>https://dev.to/svgicons/your-svg-icon-migration-worked-so-why-are-some-icons-missing-1ok</guid>
      <description>&lt;p&gt;&lt;em&gt;Your SVG files are valid. Your toolbar icons look fine. But some dropdown menus are suddenly missing their icons. Here's why that happens—and how to catch it before release.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Migrating from CSS sprites to SVG icons sounds straightforward.&lt;/p&gt;

&lt;p&gt;You replace the old assets, update the components, check that the new icons render, and move on.&lt;/p&gt;

&lt;p&gt;Everything looks fine until someone opens a dropdown menu.&lt;/p&gt;

&lt;p&gt;The toolbar icons are there. The menu labels are visible. The actions still work. But the icons inside the menu have disappeared.&lt;/p&gt;

&lt;p&gt;The strange part? &lt;strong&gt;The missing icons may already exist in your SVG sprite.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This isn't necessarily an asset problem. It can be a migration problem: one part of your application uses the new rendering system while another still expects the old one.&lt;/p&gt;

&lt;p&gt;Let's look at a real-world example, reproduce the failure in React, and build a practical checklist for avoiding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A real migration bug: the icons existed, but the menus were blank
&lt;/h2&gt;

&lt;p&gt;In August 2026, a contributor reported an issue in Euro-Office DocumentServer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Euro-Office/DocumentServer/issues/313" rel="noopener noreferrer"&gt;Dropdown menu items still render icons the old way — every menu icon is blank&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The application had been migrating from CSS/PNG sprites to SVG symbols.&lt;/p&gt;

&lt;p&gt;Its toolbar buttons had already moved to the new SVG rendering approach, but dropdown menu items still generated the old CSS sprite markup.&lt;/p&gt;

&lt;p&gt;The PNG sprite assets that those menus depended on were no longer being generated or shipped.&lt;/p&gt;

&lt;p&gt;The result was predictable once the cause was identified: buttons displayed their icons, while dropdown menus showed empty spaces.&lt;/p&gt;

&lt;p&gt;According to the issue report, 1,092 menu items declared icons across five editors. The report also found that the SVG symbols needed by the document editor's menus were largely already available.&lt;/p&gt;

&lt;p&gt;The issue was subsequently closed.&lt;/p&gt;

&lt;p&gt;What makes this case interesting isn't the missing artwork. It's the difference between migrating icon files and migrating the components that render them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A migration can be complete at the asset level and still be incomplete at the interface level.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reproducing the problem in a React application
&lt;/h2&gt;

&lt;p&gt;To make the issue easier to understand, we built a small, independent React and TypeScript demonstration.&lt;/p&gt;

&lt;p&gt;It contains a document-style interface with toolbar buttons and a dropdown menu.&lt;/p&gt;

&lt;p&gt;The same actions—Copy, Paste, Save, and Download—appear in both places.&lt;/p&gt;

&lt;p&gt;In the incomplete migration, the toolbar uses SVG symbols correctly. The dropdown still uses legacy CSS sprite references.&lt;/p&gt;

&lt;p&gt;The result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before and after: the toolbar works in both versions, but the dropdown icons only appear after correcting the rendering code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgo7w9h1rkd0gl1nuzdps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgo7w9h1rkd0gl1nuzdps.png" alt="Before and after SVG migration: toolbar icons work in both versions, but dropdown icons are missing before the fix" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the menu items don't disappear. Their text and actions remain available.&lt;/p&gt;

&lt;p&gt;Only the icons are missing.&lt;/p&gt;

&lt;p&gt;This is why the problem can escape ordinary functional testing. A test that checks whether a menu opens or an action runs may still pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Why CSS sprites and SVG symbols aren't interchangeable
&lt;/h2&gt;

&lt;p&gt;Consider how an application might have rendered icons before the migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  The old CSS sprite approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"legacy-icon legacy-copy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.legacy-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/icons.png")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.legacy-copy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-40px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The icon depends on an external PNG image and CSS rules that select the correct position within that image.&lt;/p&gt;

&lt;p&gt;If the PNG sprite or its CSS rules are removed, the element can still exist in the DOM.&lt;/p&gt;

&lt;p&gt;But nothing draws the icon.&lt;/p&gt;

&lt;p&gt;That's an important distinction: the markup hasn't disappeared, and the browser doesn't necessarily report a JavaScript error.&lt;/p&gt;

&lt;h3&gt;
  
  
  The new SVG symbol approach
&lt;/h3&gt;

&lt;p&gt;An SVG sprite can define reusable symbols:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt;
  &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position:absolute;width:0;height:0;overflow:hidden"&lt;/span&gt;
  &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;symbol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"icon-copy"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt;
      &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt;
      &lt;span class="na"&gt;rx=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
      &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
      &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
      &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt;
      &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M16 8V4H4v12h4"&lt;/span&gt;
      &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
      &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
      &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/symbol&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A component can reference that symbol with &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#icon-copy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the symbol is defined within the same document. An external SVG sprite can also be used, but the URL and symbol IDs must resolve correctly.&lt;/p&gt;

&lt;p&gt;The difference is fundamental.&lt;/p&gt;

&lt;p&gt;The CSS sprite approach relies on background images and positioning rules. The SVG symbol approach relies on valid symbol references and SVG rendering.&lt;/p&gt;

&lt;p&gt;Removing the old sprite files doesn't automatically update the components that reference them.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What an incomplete migration looks like
&lt;/h2&gt;

&lt;p&gt;Here's the problem in our demonstration.&lt;/p&gt;

&lt;p&gt;The toolbar has been migrated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ToolbarButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;use&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#icon-copy"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Copy
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the dropdown still renders a legacy element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MenuAction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"legacy-icon legacy-copy"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      Copy
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The toolbar doesn't need the old PNG sprites anymore.&lt;/p&gt;

&lt;p&gt;The menu still does.&lt;/p&gt;

&lt;p&gt;Once those assets and styles are removed, the menu icon becomes invisible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ssk3mq1mboshnresra6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ssk3mq1mboshnresra6.png" alt="React application showing working toolbar icons and missing dropdown menu icons." width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot, the toolbar icons are visible, but the dropdown entries have no icons.&lt;/p&gt;

&lt;p&gt;There's no need to invent a missing asset or artificially hide anything to reproduce the failure.&lt;/p&gt;

&lt;p&gt;The old markup simply no longer has a working rendering mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing the menu
&lt;/h3&gt;

&lt;p&gt;The immediate correction is to update the menu to reference the SVG symbol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MenuAction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;use&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#icon-copy"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Copy
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The icon now renders through the same mechanism as the toolbar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe7i1uka7dpzy9kz8xr8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe7i1uka7dpzy9kz8xr8f.png" alt="React application showing restored SVG icons in the dropdown menu after migration." width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But manually fixing individual menus doesn't address the larger problem: how do you stop different components from implementing icon rendering differently?&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Use one icon component across your interface
&lt;/h2&gt;

&lt;p&gt;A shared icon component is one practical way to reduce inconsistencies.&lt;/p&gt;

&lt;p&gt;In React, a simple implementation might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IconName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paste&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;download&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IconProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IconName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;IconProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
      &lt;span class="na"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;use&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`#icon-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the toolbar and the menu can now render the same symbol through the same component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Icon&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"copy"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  Copy
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't eliminate every possible icon bug.&lt;/p&gt;

&lt;p&gt;An incorrect symbol ID, a missing sprite, or incompatible styling can still cause problems.&lt;/p&gt;

&lt;p&gt;But it reduces the number of places where icon rendering behaviour must be maintained.&lt;/p&gt;

&lt;p&gt;For icon-only controls, remember that the button itself still needs an accessible name—for example, through &lt;code&gt;aria-label&lt;/code&gt;. The decorative SVG shouldn't be the only source of that name.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Finding legacy icon references before they reach production
&lt;/h2&gt;

&lt;p&gt;After updating your main components, search the codebase for references to the old rendering system.&lt;/p&gt;

&lt;p&gt;Don't limit the search to image files.&lt;/p&gt;

&lt;p&gt;Old rendering logic may still exist in templates, dropdown components, CSS classes, or conditional code paths.&lt;/p&gt;

&lt;p&gt;For example, with ripgrep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rg &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'background-image|background-position'&lt;/span&gt; src
rg &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'legacy-icon|iconCls|menu-item-icon'&lt;/span&gt; src
rg &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'\.png'&lt;/span&gt; src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These searches are intentionally broad.&lt;/p&gt;

&lt;p&gt;A match doesn't automatically indicate a migration bug. CSS backgrounds and PNG files may still be legitimate elsewhere in the application.&lt;/p&gt;

&lt;p&gt;The goal is to identify code that still renders icons using a mechanism scheduled for removal.&lt;/p&gt;

&lt;p&gt;A useful component inventory can help:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;SVG migration&lt;/th&gt;
&lt;th&gt;Visual verification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Toolbar buttons&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;Passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dropdown menus&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;Passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context menus&lt;/td&gt;
&lt;td&gt;Needs review&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dialog buttons&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;Passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected states&lt;/td&gt;
&lt;td&gt;Needs review&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disabled states&lt;/td&gt;
&lt;td&gt;Needs review&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important thing is to inspect each type of component, not just the most visible one.&lt;/p&gt;

&lt;p&gt;Dropdowns and context menus are particularly easy to overlook because they aren't always visible during normal page loading.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Test what users actually see
&lt;/h2&gt;

&lt;p&gt;A successful build doesn't guarantee that icons are visible.&lt;/p&gt;

&lt;p&gt;Neither does checking that an SVG element exists in the DOM.&lt;/p&gt;

&lt;p&gt;For example, this markup is syntactically valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#icon-does-not-exist"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if the referenced symbol doesn't exist, it won't display the intended icon.&lt;/p&gt;

&lt;p&gt;Likewise, a valid SVG reference can still produce an invisible icon because of color, sizing, clipping, or styling.&lt;/p&gt;

&lt;p&gt;That's why icon migrations benefit from multiple kinds of verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the SVG references
&lt;/h3&gt;

&lt;p&gt;Confirm that every symbol ID used by your components actually exists.&lt;/p&gt;

&lt;p&gt;Be especially careful when icons are assembled dynamically from names or application state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the rendered components
&lt;/h3&gt;

&lt;p&gt;Inspect the real interface, including menus that only appear after interaction.&lt;/p&gt;

&lt;p&gt;Verify that the expected SVG geometry is rendered, rather than checking only for the presence of an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compare screenshots
&lt;/h3&gt;

&lt;p&gt;Capture representative interface states before and after the migration.&lt;/p&gt;

&lt;p&gt;For example, with Playwright:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shows the migrated menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveScreenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;actions-menu.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example assumes the application's Actions control opens a properly identified menu and that a reviewed baseline screenshot has already been created.&lt;/p&gt;

&lt;p&gt;Screenshot tests are particularly useful because they can reveal a missing icon even when the associated button or menu action continues to work.&lt;/p&gt;

&lt;p&gt;Our demonstration was checked using Chromium and Playwright, including tests that inspect rendered icon geometry and screenshots.&lt;/p&gt;

&lt;p&gt;That doesn't replace cross-browser testing in a production application, but it provides a useful starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Don't forget themes and component states
&lt;/h2&gt;

&lt;p&gt;Migrating to SVG symbols also changes how icon styling is managed.&lt;/p&gt;

&lt;p&gt;For monochrome icons, &lt;code&gt;currentColor&lt;/code&gt; can simplify theme support by allowing the artwork to inherit the surrounding text color.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#27364b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.action&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the referenced artwork uses &lt;code&gt;stroke="currentColor"&lt;/code&gt; or &lt;code&gt;fill="currentColor"&lt;/code&gt; appropriately, the icon can follow the component's color.&lt;/p&gt;

&lt;p&gt;Dark mode can then adjust that color through the surrounding component or theme.&lt;/p&gt;

&lt;p&gt;However, this only works when the SVG artwork and its styling are compatible with the chosen approach.&lt;/p&gt;

&lt;p&gt;During verification, check more than the default light theme.&lt;/p&gt;

&lt;p&gt;Include dark mode, hover, disabled, active, and selected states where relevant.&lt;/p&gt;

&lt;p&gt;Also test smaller icon sizes. An icon that looks correct at 32 pixels may be difficult to distinguish at 16 pixels.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Prevent the old rendering system from coming back
&lt;/h2&gt;

&lt;p&gt;Once the migration is complete, consider adding a lightweight automated check for obsolete markup.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/components/Toolbar.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/components/Dropdown.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;legacy-icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Legacy icon reference found in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This intentionally simple example checks only the specified files.&lt;/p&gt;

&lt;p&gt;A production implementation should cover all relevant source directories and account for the project's template and component patterns.&lt;/p&gt;

&lt;p&gt;The purpose isn't to reject every use of CSS backgrounds.&lt;/p&gt;

&lt;p&gt;It's to prevent a retired icon-rendering mechanism from being reintroduced accidentally.&lt;/p&gt;

&lt;p&gt;Combine that source check with browser tests for components that use icons.&lt;/p&gt;

&lt;p&gt;The two approaches catch different classes of problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. A practical checklist for SVG icon migrations
&lt;/h2&gt;

&lt;p&gt;Before marking the migration complete, verify the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Every icon-rendering component has been identified.&lt;/li&gt;
&lt;li&gt;[ ] Obsolete CSS sprite references have been reviewed.&lt;/li&gt;
&lt;li&gt;[ ] All required SVG symbol IDs exist.&lt;/li&gt;
&lt;li&gt;[ ] Toolbar, dropdown, and context menu icons render correctly.&lt;/li&gt;
&lt;li&gt;[ ] Light and dark themes have been checked.&lt;/li&gt;
&lt;li&gt;[ ] Disabled, selected, and hover states remain readable.&lt;/li&gt;
&lt;li&gt;[ ] Representative screenshots have been compared.&lt;/li&gt;
&lt;li&gt;[ ] Automated checks can detect deprecated icon markup.&lt;/li&gt;
&lt;li&gt;[ ] The migration has been tested beyond the main toolbar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When choosing or replacing icons during this process, consistency matters too.&lt;/p&gt;

&lt;p&gt;Mixing unrelated icon families can introduce differences in stroke weight, proportions, and visual alignment—even when the migration itself works technically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons.com&lt;/a&gt; can help developers explore and compare SVG icons when preparing a consistent set for an application.&lt;/p&gt;

&lt;p&gt;Asset selection and rendering verification are separate responsibilities, and both deserve attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;A missing icon doesn't always mean a missing file.&lt;/p&gt;

&lt;p&gt;Sometimes the artwork is available, the SVG sprite is valid, and most of the interface works perfectly.&lt;/p&gt;

&lt;p&gt;The problem is that one component still expects an icon-rendering system that no longer exists.&lt;/p&gt;

&lt;p&gt;That's why an icon migration shouldn't be considered complete until the actual interface has been verified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't just check whether your SVG files migrated. Check whether every component migrated with them.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Euro-Office/DocumentServer/issues/313" rel="noopener noreferrer"&gt;Euro-Office DocumentServer — Issue #313&lt;/a&gt; — real-world report of missing dropdown icons after an incomplete SVG sprite migration.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons.com&lt;/a&gt; — SVG icon discovery and selection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The React demonstration and screenshots described in this article are an independent educational reproduction, not screenshots from the Euro-Office application.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>svg</category>
      <category>react</category>
      <category>fronted</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Choose a Consistent SVG Icon Set for a React UI</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Mon, 19 Oct 2026 22:24:39 +0000</pubDate>
      <link>https://dev.to/svgicons/how-to-choose-a-consistent-svg-icon-set-for-a-react-ui-5274</link>
      <guid>https://dev.to/svgicons/how-to-choose-a-consistent-svg-icon-set-for-a-react-ui-5274</guid>
      <description>&lt;p&gt;Finding an icon is usually straightforward. Finding six icons that look like they belong in the same interface takes a little more work.&lt;/p&gt;

&lt;p&gt;Consider a file manager built with React. You might need icons for folders, files, search, settings, and a few common actions.&lt;/p&gt;

&lt;p&gt;Each icon can look perfectly fine on its own. But put them next to each other and small differences start to appear: one has a heavier stroke, another uses different proportions, and a third looks noticeably smaller despite having the same CSS dimensions.&lt;/p&gt;

&lt;p&gt;The problem isn't necessarily the individual icons. It's the way they work together.&lt;/p&gt;

&lt;p&gt;Here's a practical approach to selecting a consistent set, using a React filebar as an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with the interface, not the icon library
&lt;/h2&gt;

&lt;p&gt;Before searching for icons, write down what the interface actually needs.&lt;/p&gt;

&lt;p&gt;For a file-management UI, that might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A folder&lt;/li&gt;
&lt;li&gt;A document&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Navigation actions&lt;/li&gt;
&lt;li&gt;File-related actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simple inventory makes the selection process easier.&lt;/p&gt;

&lt;p&gt;It also helps avoid a common problem: choosing attractive icons individually and then discovering that they don't form a coherent set.&lt;/p&gt;

&lt;p&gt;Think about where each icon will appear. A small toolbar button has different requirements from an illustration or a large empty-state graphic.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose a visual family
&lt;/h2&gt;

&lt;p&gt;The simplest way to maintain consistency is to start with one icon family.&lt;/p&gt;

&lt;p&gt;For our React filebar example, we used icons from Lucide.&lt;/p&gt;

&lt;p&gt;Using one family provides a useful baseline for stroke style, proportions and general visual language.&lt;/p&gt;

&lt;p&gt;That doesn't mean every icon automatically works well with every other icon. Some symbols have more visual weight because of their shape or complexity.&lt;/p&gt;

&lt;p&gt;A magnifying glass may look heavier than a simple arrow, even when both share the same stroke width.&lt;/p&gt;

&lt;p&gt;You still need to compare them at the size they'll actually use.&lt;/p&gt;

&lt;p&gt;A few things worth checking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stroke weight.&lt;/strong&gt; Do the outlines look equally heavy?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proportions.&lt;/strong&gt; Do the symbols occupy a similar amount of visual space?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexity.&lt;/strong&gt; Is one icon much more detailed than its neighbors?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recognition.&lt;/strong&gt; Can users understand the meaning without having to think about it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Style.&lt;/strong&gt; Are you accidentally mixing filled, outlined and multicolor designs?&lt;/p&gt;

&lt;p&gt;The goal isn't mathematical uniformity. It's visual consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Search within the same family
&lt;/h2&gt;

&lt;p&gt;Searching a large icon collection becomes more useful when you can narrow the results to a specific family.&lt;/p&gt;

&lt;p&gt;For our demonstration, we used the SVGicons CLI to explore public icon metadata.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;svgicons search &lt;span class="s2"&gt;"folder"&lt;/span&gt; &lt;span class="nt"&gt;--anonymous&lt;/span&gt; &lt;span class="nt"&gt;--icon-set&lt;/span&gt; lucide-svg-icons &lt;span class="nt"&gt;--limit&lt;/span&gt; 10 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And another search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;svgicons search &lt;span class="s2"&gt;"search"&lt;/span&gt; &lt;span class="nt"&gt;--anonymous&lt;/span&gt; &lt;span class="nt"&gt;--icon-set&lt;/span&gt; lucide-svg-icons &lt;span class="nt"&gt;--limit&lt;/span&gt; 10 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--icon-set&lt;/code&gt; option restricts the search to a particular collection.&lt;/p&gt;

&lt;p&gt;This helps when you want a consistent group rather than the most popular result from every available library.&lt;/p&gt;

&lt;p&gt;The JSON output also makes the results easier to inspect in a terminal workflow or an automated process.&lt;/p&gt;

&lt;p&gt;You can then compare candidates by name, collection and other available metadata.&lt;/p&gt;

&lt;p&gt;An important distinction: &lt;strong&gt;anonymous search returns metadata, not the raw SVG files&lt;/strong&gt;. Downloading SVG assets through the relevant SVGicons export workflows requires the appropriate Pro access.&lt;/p&gt;

&lt;p&gt;In our example, the React application used existing, locally adapted Lucide components. The CLI was used for discovery and comparison, not to claim a completed anonymous SVG export.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Don't assume the first result is the right one
&lt;/h2&gt;

&lt;p&gt;Icon search is useful, but rankings aren't a design decision.&lt;/p&gt;

&lt;p&gt;The first result may have the correct name while being visually unsuitable for your interface.&lt;/p&gt;

&lt;p&gt;For example, a search for "search" might return several variations of magnifying-glass icons, including symbols intended for different contexts.&lt;/p&gt;

&lt;p&gt;Inspecting additional results can help.&lt;/p&gt;

&lt;p&gt;With the SVGicons CLI, pagination is available using &lt;code&gt;--limit&lt;/code&gt; and &lt;code&gt;--offset&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;svgicons search &lt;span class="s2"&gt;"search"&lt;/span&gt; &lt;span class="nt"&gt;--anonymous&lt;/span&gt; &lt;span class="nt"&gt;--icon-set&lt;/span&gt; lucide-svg-icons &lt;span class="nt"&gt;--limit&lt;/span&gt; 10 &lt;span class="nt"&gt;--offset&lt;/span&gt; 10 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requests another page of results rather than relying only on the first ten matches.&lt;/p&gt;

&lt;p&gt;The exact ordering of search results can change, so avoid building your selection process around a fixed ranking.&lt;/p&gt;

&lt;p&gt;Once you have a candidate, verify that it communicates the action you intended.&lt;/p&gt;

&lt;p&gt;A visually consistent icon that suggests the wrong action is still a bad choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Compare icons together at their actual size
&lt;/h2&gt;

&lt;p&gt;This is where many icon-selection workflows fall short.&lt;/p&gt;

&lt;p&gt;An icon previewed individually at a large size may look completely different when placed inside a compact toolbar.&lt;/p&gt;

&lt;p&gt;Before making a final decision, put the icons next to one another.&lt;/p&gt;

&lt;p&gt;For a React interface, a small reusable component can help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToolbarButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ToolbarButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ToolbarButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use your selected icon components within the same button structure.&lt;/p&gt;

&lt;p&gt;This makes it easier to compare the icons without differences in padding or button dimensions distracting you.&lt;/p&gt;

&lt;p&gt;Look at the toolbar as a whole.&lt;/p&gt;

&lt;p&gt;Does one symbol dominate? Does another almost disappear? Are some icons harder to distinguish at smaller sizes?&lt;/p&gt;

&lt;p&gt;You may need to choose a different symbol within the same family.&lt;/p&gt;

&lt;p&gt;That's usually preferable to mixing unrelated libraries just to find a shape you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Keep accessibility in the component contract
&lt;/h2&gt;

&lt;p&gt;Icon consistency isn't only visual.&lt;/p&gt;

&lt;p&gt;If an icon is the only content of a button, the button needs an accessible name.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Open settings"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SettingsIcon&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the button communicates the action, while the SVG is decorative.&lt;/p&gt;

&lt;p&gt;This prevents the screen reader from having to interpret an unlabeled graphic.&lt;/p&gt;

&lt;p&gt;For icons that convey meaningful information independently, the accessibility approach may be different.&lt;/p&gt;

&lt;p&gt;The important point is to decide which element owns the accessible meaning instead of leaving it ambiguous.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Verify the finished React interface
&lt;/h2&gt;

&lt;p&gt;For our SVGicons demonstration, we used an existing React filebar built with six Lucide icons.&lt;/p&gt;

&lt;p&gt;The purpose wasn't to show that React could render SVG components. That's already well established.&lt;/p&gt;

&lt;p&gt;We wanted to see how a group of icons, selected with a consistent visual language, worked in a realistic interface.&lt;/p&gt;

&lt;p&gt;The workflow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the icon needs of the filebar.&lt;/li&gt;
&lt;li&gt;Explore candidates using SVGicons CLI.&lt;/li&gt;
&lt;li&gt;Compare icons within the Lucide family.&lt;/li&gt;
&lt;li&gt;Review the existing licensed React components.&lt;/li&gt;
&lt;li&gt;Check how they look together in the real interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a practical example of why icon discovery and final UI review should be treated as connected steps.&lt;/p&gt;

&lt;p&gt;You can explore the React example here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/svgicons-com/svgicons-react-filebar" rel="noopener noreferrer"&gt;SVGicons React Filebar on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository also includes the relevant licensing notices for the icon assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. A checklist before shipping
&lt;/h2&gt;

&lt;p&gt;Before committing your final icon choices, ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do the icons communicate their intended actions?&lt;/li&gt;
&lt;li&gt;Do they belong to a consistent visual family?&lt;/li&gt;
&lt;li&gt;Are their proportions balanced at the intended size?&lt;/li&gt;
&lt;li&gt;Are they readable at small sizes?&lt;/li&gt;
&lt;li&gt;Are icon-only controls accessible?&lt;/li&gt;
&lt;li&gt;Are the icon licenses compatible with your project?&lt;/li&gt;
&lt;li&gt;Have you checked the actual application instead of relying only on individual previews?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need an elaborate design-system review for every small interface.&lt;/p&gt;

&lt;p&gt;But even a few minutes spent comparing icons together can prevent a lot of inconsistent UI details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;A large icon collection makes finding candidates easier. It doesn't automatically make choosing them easier.&lt;/p&gt;

&lt;p&gt;The useful workflow is to &lt;strong&gt;search, narrow down, compare, and verify in context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For our React filebar, SVGicons CLI helped explore and compare icon metadata, while the existing Lucide components let us review the final result in a real interface.&lt;/p&gt;

&lt;p&gt;That separation matters: discovering an icon, obtaining its asset, and integrating it into an application are related steps, but they're not the same operation.&lt;/p&gt;

&lt;p&gt;A good icon set isn't just a collection of attractive symbols. It's a set of decisions that makes the interface feel coherent.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/svgicons-com/cli" rel="noopener noreferrer"&gt;SVGicons CLI — GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/svgicons-com/svgicons-react-filebar" rel="noopener noreferrer"&gt;React Filebar Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lucide.dev" rel="noopener noreferrer"&gt;Lucide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>svg</category>
      <category>frontend</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>SVG Is the Source, Not Necessarily the Runtime Format</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Tue, 13 Oct 2026 16:46:35 +0000</pubDate>
      <link>https://dev.to/svgicons/svg-is-the-source-not-necessarily-the-runtime-format-1879</link>
      <guid>https://dev.to/svgicons/svg-is-the-source-not-necessarily-the-runtime-format-1879</guid>
      <description>&lt;p&gt;&lt;em&gt;Why sharing the same SVG doesn't guarantee identical icons across iOS and Android.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An SVG icon looks perfect in the browser. You export it for Android and iOS, integrate it into both applications, and expect the same result.&lt;/p&gt;

&lt;p&gt;But the icons don't quite match.&lt;/p&gt;

&lt;p&gt;One looks slightly heavier. Another appears smaller. A color behaves differently. And the two platforms don't even use the same asset format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem isn't necessarily the SVG. It's what happens to the SVG after export.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For cross-platform applications, SVG can serve as the single source of truth without being the format that actually gets rendered at runtime.&lt;/p&gt;

&lt;p&gt;That distinction matters more than it might seem.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. One source, different rendering pipelines
&lt;/h2&gt;

&lt;p&gt;SVG is an excellent format for maintaining a shared icon library.&lt;/p&gt;

&lt;p&gt;It provides resolution-independent geometry, supports reusable vector artwork, and makes it possible to manage icons independently of the platforms that consume them.&lt;/p&gt;

&lt;p&gt;A typical workflow might look like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared SVG source → Platform-specific assets → Native rendering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The original artwork stays the same, but the delivery format changes.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Possible asset format&lt;/th&gt;
&lt;th&gt;Rendering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web&lt;/td&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;td&gt;Browser SVG renderer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;VectorDrawable XML&lt;/td&gt;
&lt;td&gt;Android graphics system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;PDF vector asset&lt;/td&gt;
&lt;td&gt;Native image rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;Custom SF Symbol&lt;/td&gt;
&lt;td&gt;Apple's symbol rendering system&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These aren't interchangeable formats.&lt;/p&gt;

&lt;p&gt;They have different capabilities, restrictions, and rendering behaviors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharing the source doesn't mean sharing the renderer.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Android: SVG becomes VectorDrawable
&lt;/h2&gt;

&lt;p&gt;Android Studio includes Vector Asset Studio, which can import compatible SVG files and convert them into Android's &lt;code&gt;VectorDrawable&lt;/code&gt; format.&lt;/p&gt;

&lt;p&gt;Instead of loading the original SVG directly, an application can reference the generated XML drawable.&lt;/p&gt;

&lt;p&gt;Consider a simple SVG circle:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;circle cx="12" cy="12" r="8" fill="#2563EB"/&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;During conversion, that geometry can become a path inside an Android vector drawable.&lt;/p&gt;

&lt;p&gt;The generated resource contains elements such as &lt;code&gt;vector&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;fillColor&lt;/code&gt;, and &lt;code&gt;pathData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A simplified example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;path android:fillColor="#2563EB" android:pathData="M20,12A8,8 0,1 1,4 12A8,8 0,1 1,20 12Z"/&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The appearance may remain very similar, but the representation is no longer SVG.&lt;/p&gt;

&lt;p&gt;And conversion has limitations.&lt;/p&gt;

&lt;p&gt;Android's SVG importer doesn't support every SVG feature. Effects such as blur filters, pattern fills, and certain other advanced features may need to be simplified or removed.&lt;/p&gt;

&lt;p&gt;Even supported geometry deserves visual inspection after conversion.&lt;/p&gt;

&lt;p&gt;A successful import doesn't necessarily mean the icon looks exactly as intended.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://developer.android.com/studio/write/vector-asset-studio" rel="noopener noreferrer"&gt;Android Vector Asset Studio documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. iOS: Native vector assets and SF Symbols aren't the same thing
&lt;/h2&gt;

&lt;p&gt;On iOS, the workflow depends on how the icon will be used.&lt;/p&gt;

&lt;p&gt;For a conventional image asset, one approach is to export the artwork from SVG to vector PDF and include it in an Xcode asset catalog.&lt;/p&gt;

&lt;p&gt;This lets developers maintain SVG as the original design source while delivering an asset through Apple's native image system.&lt;/p&gt;

&lt;p&gt;Custom SF Symbols follow a different process.&lt;/p&gt;

&lt;p&gt;Apple provides a template-based workflow for creating custom symbols. Developers can modify the exported SVG template, preserve its required structure, and validate it using the SF Symbols application or Xcode.&lt;/p&gt;

&lt;p&gt;This allows custom symbols to participate in features such as different symbol weights, scales, and rendering modes.&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;an arbitrary SVG file is not automatically a valid custom SF Symbol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A conventional vector image and a custom symbol have different requirements.&lt;/p&gt;

&lt;p&gt;Choosing between them depends on whether the icon simply needs to appear as artwork or integrate more deeply with Apple's symbol system.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://developer.apple.com/documentation/technologyoverviews/custom-sf-symbols" rel="noopener noreferrer"&gt;Apple — Creating custom symbols&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Three things conversion doesn't guarantee
&lt;/h2&gt;

&lt;p&gt;Even when the source artwork is correct, three areas deserve particular attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual weight
&lt;/h3&gt;

&lt;p&gt;Two icons can share identical geometry and still feel different in their interfaces.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because perceived weight depends on more than path coordinates.&lt;/p&gt;

&lt;p&gt;Stroke thickness, scaling, optical alignment, available space, and neighboring UI elements all influence how an icon appears.&lt;/p&gt;

&lt;p&gt;A 24×24 SVG may look balanced in a web toolbar but slightly too heavy next to native iOS controls.&lt;/p&gt;

&lt;p&gt;The solution isn't necessarily to modify the original icon.&lt;/p&gt;

&lt;p&gt;Sometimes a platform-specific size or optical adjustment produces a more consistent result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency is about perceived appearance, not just matching coordinates.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Color layers
&lt;/h3&gt;

&lt;p&gt;Color is another potential source of differences.&lt;/p&gt;

&lt;p&gt;An SVG may define multiple fills, transparency, gradients, or other graphical features.&lt;/p&gt;

&lt;p&gt;But native asset formats don't always interpret or support those features in the same way.&lt;/p&gt;

&lt;p&gt;Android VectorDrawable supports several common vector features, but not the full SVG specification.&lt;/p&gt;

&lt;p&gt;On iOS, rendering behavior also depends on the asset type and whether the image is displayed as an original image, a template image, or a symbol with a specific rendering mode.&lt;/p&gt;

&lt;p&gt;An icon intended to inherit a UI color might accidentally retain its original fill.&lt;/p&gt;

&lt;p&gt;A multicolor symbol may require additional configuration.&lt;/p&gt;

&lt;p&gt;These differences become especially noticeable when supporting light and dark appearances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naming
&lt;/h3&gt;

&lt;p&gt;Visual consistency is only part of the problem.&lt;/p&gt;

&lt;p&gt;Naming consistency matters too.&lt;/p&gt;

&lt;p&gt;Imagine that the same icon exists under three identifiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SVG source: &lt;code&gt;settings-outline.svg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Android resource: &lt;code&gt;ic_settings_outline&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;iOS asset: &lt;code&gt;SettingsOutline&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different naming conventions aren't inherently wrong. Each platform has its own conventions.&lt;/p&gt;

&lt;p&gt;The problem starts when the relationship between those identifiers becomes unclear.&lt;/p&gt;

&lt;p&gt;A stable naming or mapping strategy helps teams track which assets come from the same source.&lt;/p&gt;

&lt;p&gt;That becomes particularly important when updating hundreds of icons or automating asset generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A practical example: one settings icon, two platforms
&lt;/h2&gt;

&lt;p&gt;Imagine a project that uses a shared 24×24 settings icon.&lt;/p&gt;

&lt;p&gt;The source file is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;icons/settings-outline.svg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It contains the icon's geometry, viewBox, and color definitions.&lt;/p&gt;

&lt;p&gt;For Android, the SVG is imported into Vector Asset Studio and converted to a drawable resource:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;res/drawable/ic_settings_outline.xml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For iOS, the same source artwork can be exported to a PDF vector asset:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Assets.xcassets/SettingsOutline.imageset/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The application now has two native resources derived from one source.&lt;/p&gt;

&lt;p&gt;But before shipping, the team should compare them in context.&lt;/p&gt;

&lt;p&gt;Does the Android icon appear heavier?&lt;/p&gt;

&lt;p&gt;Does the iOS version have the same apparent size?&lt;/p&gt;

&lt;p&gt;Are both icons aligned correctly inside their buttons?&lt;/p&gt;

&lt;p&gt;Do their colors behave as intended in light and dark appearances?&lt;/p&gt;

&lt;p&gt;The important point is that &lt;strong&gt;both assets can be technically valid without being visually equivalent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A reliable workflow includes a visual review after conversion, not just a successful export.&lt;/p&gt;

&lt;p&gt;And if the artwork changes, both native assets should be regenerated from the same updated SVG source.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. A small validation checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping icons across native platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Keep the original SVG as the reference artwork.&lt;/li&gt;
&lt;li&gt;[ ] Check which SVG features each conversion tool supports.&lt;/li&gt;
&lt;li&gt;[ ] Verify viewport dimensions and scaling.&lt;/li&gt;
&lt;li&gt;[ ] Compare stroke weight and apparent icon size.&lt;/li&gt;
&lt;li&gt;[ ] Validate fills, transparency, and color behavior.&lt;/li&gt;
&lt;li&gt;[ ] Test light and dark appearances where applicable.&lt;/li&gt;
&lt;li&gt;[ ] Maintain consistent asset identifiers or mappings.&lt;/li&gt;
&lt;li&gt;[ ] Preview icons inside actual UI components.&lt;/li&gt;
&lt;li&gt;[ ] Regenerate platform assets whenever the source changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For larger icon libraries, these checks can become part of an automated asset pipeline.&lt;/p&gt;

&lt;p&gt;Automated conversion and validation can detect unsupported features or missing files. Visual regression tests can help identify unexpected appearance changes.&lt;/p&gt;

&lt;p&gt;Human review still matters when evaluating optical balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Source consistency isn't rendering consistency
&lt;/h2&gt;

&lt;p&gt;Using a shared SVG library is a strong foundation for cross-platform development.&lt;/p&gt;

&lt;p&gt;It reduces duplication, makes artwork easier to maintain, and provides a clear reference for designers and developers.&lt;/p&gt;

&lt;p&gt;Sites such as &lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons.com&lt;/a&gt; can help developers discover reusable open-source SVG artwork to use as a starting point.&lt;/p&gt;

&lt;p&gt;But selecting or exporting the SVG is only the beginning of the asset pipeline.&lt;/p&gt;

&lt;p&gt;Native platforms have their own rendering systems, asset formats, and interface conventions.&lt;/p&gt;

&lt;p&gt;Trying to force every platform to use exactly the same runtime representation isn't always necessary — or desirable.&lt;/p&gt;

&lt;p&gt;The better goal is to maintain a common source while validating each platform's output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One source. Multiple outputs. Consistent visual intent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The SVG is the starting point. The rendering pipeline determines what users actually see.&lt;/p&gt;




&lt;h3&gt;
  
  
  Further reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio/write/vector-asset-studio" rel="noopener noreferrer"&gt;Android Developers — Vector Asset Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/technologyoverviews/custom-sf-symbols" rel="noopener noreferrer"&gt;Apple Developer — Creating custom symbols&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;How do you manage SVG-to-native icon conversion in your projects? Do you automate the exports, or maintain separate assets for each platform?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>svg</category>
      <category>android</category>
      <category>ios</category>
      <category>mobile</category>
    </item>
    <item>
      <title>SVG 2 and Accessible Icons: What Developers Need to Know in 2026</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:02:58 +0000</pubDate>
      <link>https://dev.to/svgicons/svg-2-and-accessible-icons-what-developers-need-to-know-in-2026-4fol</link>
      <guid>https://dev.to/svgicons/svg-2-and-accessible-icons-what-developers-need-to-know-in-2026-4fol</guid>
      <description>&lt;p&gt;On October 6, 2026, the W3C published a new Candidate Recommendation Snapshot of SVG 2.&lt;/p&gt;

&lt;p&gt;It's an important milestone for a format we use every day, particularly in web interfaces and icon libraries.&lt;/p&gt;

&lt;p&gt;But one detail caught our attention: &lt;strong&gt;using multiple &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;desc&amp;gt;&lt;/code&gt; elements for localization is still marked as “at risk.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That raises a practical question: how should developers handle accessible SVG icons today?&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "at risk" actually mean?
&lt;/h2&gt;

&lt;p&gt;SVG 2 is still a Candidate Recommendation, not a final W3C Recommendation.&lt;/p&gt;

&lt;p&gt;The specification identifies features that may be removed if implementation support proves insufficient.&lt;/p&gt;

&lt;p&gt;Among them is the ability to include multiple &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;desc&amp;gt;&lt;/code&gt; elements to provide localized descriptions.&lt;/p&gt;

&lt;p&gt;This doesn't mean that &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;desc&amp;gt;&lt;/code&gt; themselves are being removed. They remain important SVG elements for accessibility.&lt;/p&gt;

&lt;p&gt;The uncertainty concerns using multiple instances of these elements for localization.&lt;/p&gt;

&lt;p&gt;It's also a reminder that "a feature appearing in a specification doesn't guarantee consistent browser support".&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every icon needs an accessible name
&lt;/h2&gt;

&lt;p&gt;Before adding accessibility attributes to an SVG, consider what the icon actually does.&lt;/p&gt;

&lt;p&gt;There are two common situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Decorative icons
&lt;/h3&gt;

&lt;p&gt;If an icon appears next to visible text that already conveys its meaning, it usually doesn't need to be announced separately.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt;
    &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
    &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;focusable=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M12 3v12m-5-5 5 5 5-5M4 18v3h16v-3"&lt;/span&gt;
          &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
          &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  Download
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the button already contains the text “Download.”&lt;/p&gt;

&lt;p&gt;Hiding the SVG from assistive technologies avoids announcing the same information twice.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Icon-only buttons
&lt;/h3&gt;

&lt;p&gt;Now consider a button displaying only a search icon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt;
    &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
    &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;focusable=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"11"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"11"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt;
            &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
            &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M16 16l5 5"&lt;/span&gt;
          &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The icon is hidden from the accessibility tree, but the button itself has an accessible name.&lt;/p&gt;

&lt;p&gt;This is generally a straightforward approach for icon-only controls.&lt;/p&gt;

&lt;p&gt;For a standalone informative SVG, a &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; can instead provide an accessible name, with &lt;code&gt;role="img"&lt;/code&gt; and &lt;code&gt;aria-labelledby&lt;/code&gt; where appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about multilingual interfaces?
&lt;/h2&gt;

&lt;p&gt;This is where the SVG 2 discussion becomes particularly relevant.&lt;/p&gt;

&lt;p&gt;Imagine an application available in English, French, and German.&lt;/p&gt;

&lt;p&gt;Rather than relying on multiple localized &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; elements inside a single SVG, the application can provide the accessible name in the current interface language.&lt;/p&gt;

&lt;p&gt;For example, in a localized React component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SearchButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
        &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
        &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
        &lt;span class="na"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;circle&lt;/span&gt; &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"11"&lt;/span&gt; &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"11"&lt;/span&gt; &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt;
                &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
                &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M16 16l5 5"&lt;/span&gt;
              &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application supplies the translated label, while the SVG geometry remains unchanged.&lt;/p&gt;

&lt;p&gt;This separates the icon's appearance from its accessible meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the actual result
&lt;/h2&gt;

&lt;p&gt;A technically valid SVG isn't necessarily accessible in every environment.&lt;/p&gt;

&lt;p&gt;When integrating icons into an application, it's worth checking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether icon-only controls have meaningful accessible names.&lt;/li&gt;
&lt;li&gt;Whether decorative icons are ignored by screen readers.&lt;/li&gt;
&lt;li&gt;Whether translated labels update correctly when the language changes.&lt;/li&gt;
&lt;li&gt;Whether the accessible names work with your target browsers and screen readers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browser developer tools can help inspect the accessibility tree, but testing with assistive technology remains valuable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small detail with a larger lesson
&lt;/h2&gt;

&lt;p&gt;The October 2026 SVG 2 update doesn't require developers to rewrite their icon libraries.&lt;/p&gt;

&lt;p&gt;It does highlight something worth remembering: &lt;strong&gt;SVG accessibility depends on how an icon is used, not just on the SVG markup itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://svgicons.com/" rel="noopener noreferrer"&gt;SVGicons&lt;/a&gt;, we work with SVG icons intended for integration into real applications. Keeping visual assets separate from application-level labels is a useful approach, especially when the same icon appears in different contexts or languages.&lt;/p&gt;

&lt;p&gt;SVG 2 will continue to evolve. In the meantime, clear accessible names, appropriate ARIA usage, and real-world testing remain good foundations.&lt;/p&gt;




&lt;p&gt;References&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/2026/CR-SVG2-20261006/" rel="noopener noreferrer"&gt;W3C — SVG 2 Candidate Recommendation, October 6, 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/SVG2/" rel="noopener noreferrer"&gt;W3C — SVG 2 Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>svg</category>
      <category>a11y</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to Give Your Claude API App Access to SVG Icons with MCP</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Sat, 10 Oct 2026 15:54:13 +0000</pubDate>
      <link>https://dev.to/svgicons/how-to-give-your-claude-api-app-access-to-svg-icons-with-mcp-1f9j</link>
      <guid>https://dev.to/svgicons/how-to-give-your-claude-api-app-access-to-svg-icons-with-mcp-1f9j</guid>
      <description>&lt;p&gt;When building an AI-powered application, connecting a language model to real development assets can be surprisingly useful.&lt;/p&gt;

&lt;p&gt;Consider a dashboard builder. A developer describes the interface they want, and the application suggests components, layouts, and icons.&lt;/p&gt;

&lt;p&gt;The layout suggestions might be reasonable. But what happens when the model needs to recommend actual SVG icons?&lt;/p&gt;

&lt;p&gt;Without access to an icon catalog, it may suggest generic names, invent identifiers, or mix incompatible icon styles.&lt;/p&gt;

&lt;p&gt;A better approach is to let the model search a real icon catalog.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll explore how to connect the Claude Messages API to the SVGicons MCP server, allowing Claude to discover icons and recommend consistent icon sets for a user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: AI can suggest icons, but can it find them?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building an analytics dashboard with five navigation items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Reports&lt;/li&gt;
&lt;li&gt;Activity&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You want recognizable icons, but you also want them to share a consistent visual style.&lt;/p&gt;

&lt;p&gt;You could manually search an icon library for each item, or ask Claude to suggest icon names.&lt;/p&gt;

&lt;p&gt;The second approach is convenient, but there's a limitation: a language model doesn't automatically know which icons are currently available in a particular catalog.&lt;/p&gt;

&lt;p&gt;It might recommend an identifier that doesn't exist or combine icons from unrelated families.&lt;/p&gt;

&lt;p&gt;Instead, we can connect Claude to a searchable icon catalog using the Model Context Protocol (MCP).&lt;/p&gt;

&lt;h2&gt;
  
  
  1. How the integration works
&lt;/h2&gt;

&lt;p&gt;MCP allows AI applications to interact with external tools and data sources through a standardized interface.&lt;/p&gt;

&lt;p&gt;Anthropic provides an MCP connector for the Claude Messages API. It can communicate with remote MCP servers without requiring you to implement a separate MCP client.&lt;/p&gt;

&lt;p&gt;We'll use the SVGicons server:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://svgicons.com/mcp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The architecture is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Application
       |
       v
Claude Messages API
       |
       v
MCP Connector
       |
       v
SVGicons MCP Server
       |
       v
Icon Catalog Search
       |
       v
Recommendations returned
to your application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application sends a request to Claude.&lt;/p&gt;

&lt;p&gt;Claude can use the available SVGicons tools to search for relevant icons, compare candidates, and return recommendations.&lt;/p&gt;

&lt;p&gt;The important difference is that recommendations can be based on actual catalog results rather than generated icon names.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What you'll need
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Anthropic API key with access to the Claude Messages API.&lt;/li&gt;
&lt;li&gt;An active SVGicons Pro subscription.&lt;/li&gt;
&lt;li&gt;A valid SVGicons access token for the authenticated MCP workflow.&lt;/li&gt;
&lt;li&gt;A terminal capable of running cURL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SVGicons Pro includes developer integrations such as MCP access.&lt;/p&gt;

&lt;p&gt;You can find subscription information on the &lt;a href="https://svgicons.com/pricing" rel="noopener noreferrer"&gt;SVGicons pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Anthropic API and SVGicons subscription are separate services, so their access requirements and charges are independent.&lt;/p&gt;

&lt;p&gt;Keep both credentials on your application backend rather than exposing them in browser-side code.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Connect Claude to the SVGicons MCP server
&lt;/h2&gt;

&lt;p&gt;The Claude Messages API uses two main fields for remote MCP integration:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mcp_servers&lt;/code&gt; defines the servers Claude can connect to.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tools&lt;/code&gt; defines which MCP tools Claude is allowed to use.&lt;/p&gt;

&lt;p&gt;For our example, we're interested in three SVGicons capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;search_icons&lt;/code&gt; — find icons matching search terms.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;search_icon_sets&lt;/code&gt; — discover available icon families.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;recommend_icons_for_ui&lt;/code&gt; — find candidates for UI elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll restrict Claude to these discovery tools rather than enabling every available server operation.&lt;/p&gt;

&lt;p&gt;Here's an example using cURL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.anthropic.com/v1/messages &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"anthropic-version: 2023-06-01"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"anthropic-beta: mcp-client-2025-11-20"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "YOUR_CLAUDE_MODEL",
    "max_tokens": 1500,
    "messages": [
      {
        "role": "user",
        "content": "Use SVGicons to find icons for an analytics dashboard with Overview, Reports, Activity, Settings, and Notifications. Prefer a consistent outline style. Return real catalog identifiers and explain the choices briefly."
      }
    ],
    "mcp_servers": [
      {
        "type": "url",
        "url": "https://svgicons.com/mcp",
        "name": "svgicons",
        "authorization_token": "YOUR_SVGICONS_ACCESS_TOKEN"
      }
    ],
    "tools": [
      {
        "type": "mcp_toolset",
        "mcp_server_name": "svgicons",
        "default_config": {
          "enabled": false
        },
        "configs": {
          "search_icons": {
            "enabled": true
          },
          "search_icon_sets": {
            "enabled": true
          },
          "recommend_icons_for_ui": {
            "enabled": true
          }
        }
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the placeholders with your Claude model ID and appropriate credentials.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;authorization_token&lt;/code&gt; field passes the access token used to authenticate with the remote MCP server. Ensure that your token is valid for this integration.&lt;/p&gt;

&lt;p&gt;In a production application, inject credentials securely from your backend rather than hardcoding them in a request payload stored in source control.&lt;/p&gt;

&lt;p&gt;Notice that we explicitly disable tools by default and enable only the three discovery operations.&lt;/p&gt;

&lt;p&gt;This gives Claude access to the capabilities needed for our task without exposing unrelated operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ask for consistent icon recommendations
&lt;/h2&gt;

&lt;p&gt;Connecting the server is only the first step.&lt;/p&gt;

&lt;p&gt;The quality of the recommendations also depends on how clearly you describe your interface requirements.&lt;/p&gt;

&lt;p&gt;Compare these two prompts.&lt;/p&gt;

&lt;p&gt;A generic request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find icons for my dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A more useful request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find icons for Overview, Reports, Activity, Settings, and Notifications. Prefer a single icon family, consistent stroke widths, and an outline style. Return only identifiers found in the SVGicons catalog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second prompt provides constraints that matter in real interface design.&lt;/p&gt;

&lt;p&gt;It asks Claude to consider consistency across the entire navigation system rather than choosing every icon independently.&lt;/p&gt;

&lt;p&gt;An application could also supply requirements such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI context:
- Desktop analytics dashboard
- Left navigation sidebar
- Icons displayed at 20 × 20 px
- Neutral, professional appearance

Requirements:
- Prefer a single icon family
- Consistent visual weight
- Recognizable navigation metaphors
- Return actual catalog identifiers
- Flag unavailable matches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't guarantee that every recommendation will be perfect.&lt;/p&gt;

&lt;p&gt;But it gives Claude a better starting point and lets the icon catalog provide the actual candidates.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Validate the results before using them
&lt;/h2&gt;

&lt;p&gt;One important principle when integrating AI into a development workflow is to separate recommendations from implementation.&lt;/p&gt;

&lt;p&gt;Even when Claude uses external tools, its final response should not automatically be treated as verified application data.&lt;/p&gt;

&lt;p&gt;For example, your application might receive suggestions corresponding to:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Navigation item&lt;/th&gt;
&lt;th&gt;Icon concept&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Overview&lt;/td&gt;
&lt;td&gt;Grid or dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reports&lt;/td&gt;
&lt;td&gt;Chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activity&lt;/td&gt;
&lt;td&gt;Activity indicator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings&lt;/td&gt;
&lt;td&gt;Gear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notifications&lt;/td&gt;
&lt;td&gt;Bell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These concepts are illustrative, not verified SVGicons catalog identifiers.&lt;/p&gt;

&lt;p&gt;Your application should use the identifiers returned by the actual tool searches.&lt;/p&gt;

&lt;p&gt;Before accepting a recommendation, verify that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The icon exists in the selected catalog.&lt;/li&gt;
&lt;li&gt;It belongs to the intended icon family.&lt;/li&gt;
&lt;li&gt;Its visual style is compatible with the other selections.&lt;/li&gt;
&lt;li&gt;Its licensing terms suit your project.&lt;/li&gt;
&lt;li&gt;Its SVG markup can be integrated safely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last point matters.&lt;/p&gt;

&lt;p&gt;For example, an icon containing hardcoded colors may not adapt correctly to a dark theme.&lt;/p&gt;

&lt;p&gt;An SVG intended for use with &lt;code&gt;currentColor&lt;/code&gt; may require different handling from one using explicit fills.&lt;/p&gt;

&lt;p&gt;Accessibility also remains the application's responsibility. An icon-only button still needs an accessible name.&lt;/p&gt;

&lt;p&gt;MCP can help you discover assets, but it doesn't eliminate the need to review how those assets are used.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Handle authentication and search failures
&lt;/h2&gt;

&lt;p&gt;A production integration needs to handle more than successful searches.&lt;/p&gt;

&lt;p&gt;You might encounter an expired access token, insufficient permissions, an unavailable server, or a search that returns no suitable icons.&lt;/p&gt;

&lt;p&gt;Your application should distinguish these situations rather than reporting every failure as "No icons found."&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authentication&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check your SVGicons access token.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;permission&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your account does not have access to this operation.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no_results&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Try different search terms or an alternative icon style.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service_unavailable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The icon search service is temporarily unavailable.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to complete the icon search.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is illustrative application logic, not a list of official SVGicons MCP error codes. Your backend should classify actual API and tool responses before invoking it.&lt;/p&gt;

&lt;p&gt;You should also consider logging failures, controlling request frequency, and applying sensible timeout limits.&lt;/p&gt;

&lt;p&gt;A failed catalog search should never silently turn into a fabricated icon recommendation.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. When is this approach useful?
&lt;/h2&gt;

&lt;p&gt;Connecting an icon catalog to Claude makes sense when your application needs to interpret design requirements expressed in natural language.&lt;/p&gt;

&lt;p&gt;Some examples include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted interface builders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A user describes a screen and receives suggested icons for its components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An assistant helps developers locate icons without switching between their editor and a browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design system assistants&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A tool recommends icons while following an established style or icon family.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application prototyping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Developers can quickly explore possible icon choices while generating an early interface.&lt;/p&gt;

&lt;p&gt;However, MCP isn't always the right solution.&lt;/p&gt;

&lt;p&gt;If your application already knows exactly which icon identifiers it needs, a direct API call or a static icon package may be simpler and more predictable.&lt;/p&gt;

&lt;p&gt;The benefit of MCP becomes clearer when the request involves interpreting intent, searching external data, and comparing alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Keep the developer in control
&lt;/h2&gt;

&lt;p&gt;The most useful AI development tools don't necessarily automate every decision.&lt;/p&gt;

&lt;p&gt;They reduce repetitive work while keeping important choices visible.&lt;/p&gt;

&lt;p&gt;For icon selection, a practical workflow might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Developer describes the interface

2. Claude interprets the requirements

3. SVGicons MCP searches the catalog

4. Claude recommends matching icons

5. Application validates identifiers

6. Developer reviews the selections

7. Approved assets enter the UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach combines natural-language interaction with a real source of design assets.&lt;/p&gt;

&lt;p&gt;Claude handles interpretation and recommendations.&lt;/p&gt;

&lt;p&gt;SVGicons supplies searchable icon resources.&lt;/p&gt;

&lt;p&gt;Your application controls validation and integration.&lt;/p&gt;

&lt;p&gt;That separation makes the workflow easier to understand, maintain, and extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Giving an AI application access to real design assets is more useful than asking a model to invent resources from memory.&lt;/p&gt;

&lt;p&gt;With the Claude Messages API and an MCP server such as SVGicons, developers can connect natural-language requests to an actual SVG icon catalog.&lt;/p&gt;

&lt;p&gt;The integration is relatively small, but it introduces an important architectural principle: use the language model to interpret requirements, and use external tools to retrieve verifiable data.&lt;/p&gt;

&lt;p&gt;For UI development, that can mean less time searching for icons and more consistent results across an interface.&lt;/p&gt;

&lt;p&gt;The developer still makes the final design decisions, but the discovery process becomes much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;For additional configuration details and integration examples, see the official SVGicons guide:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://svgicons.com/articles/claude-api-mcp-connector-svgicons" rel="noopener noreferrer"&gt;Claude API MCP Connector — SVGicons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons — SVG Icon Catalog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://svgicons.com/pricing" rel="noopener noreferrer"&gt;SVGicons Plans and Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/agents-and-tools/mcp-connector" rel="noopener noreferrer"&gt;Anthropic — MCP Connector Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you're building an AI-assisted development tool, would you let the model choose icons automatically, or would you prefer it to suggest candidates for developers to review?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>mcp</category>
    </item>
    <item>
      <title>From Icon Search to React Components: Building a Reusable Icon Collection</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Thu, 08 Oct 2026 22:15:54 +0000</pubDate>
      <link>https://dev.to/svgicons/from-icon-search-to-react-components-building-a-reusable-icon-collection-5mb</link>
      <guid>https://dev.to/svgicons/from-icon-search-to-react-components-building-a-reusable-icon-collection-5mb</guid>
      <description>&lt;p&gt;Finding an SVG icon is easy. Keeping a consistent set of icons across an evolving React application takes a little more planning.&lt;/p&gt;

&lt;p&gt;Consider a dashboard with navigation, billing, notifications, and account settings. Each screen needs icons, but downloading them one by one quickly leads to a folder of unrelated assets.&lt;/p&gt;

&lt;p&gt;A more practical approach is to build a collection specifically for the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a collection for your UI
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://svgicons.com/project-kits" rel="noopener noreferrer"&gt;SVGicons&lt;/a&gt;, you can create a named &lt;strong&gt;Icon Collection&lt;/strong&gt; and add icons directly from search results or individual icon pages.&lt;/p&gt;

&lt;p&gt;For a dashboard, you might create a collection called &lt;code&gt;Dashboard Navigation&lt;/code&gt; and select icons for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Billing&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The collection preserves the original icon references, including their source sets and license information.&lt;/p&gt;

&lt;p&gt;You can also reorder icons, remove unused entries, and review which icon families are represented.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose how your icons behave
&lt;/h2&gt;

&lt;p&gt;Before exporting, two settings are particularly useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Color policy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a typical React interface, &lt;code&gt;currentColor&lt;/code&gt; allows compatible icons to inherit their color from CSS.&lt;/p&gt;

&lt;p&gt;That means a navigation icon can follow the color of its surrounding UI without requiring a separate colored SVG file.&lt;/p&gt;

&lt;p&gt;SVGicons also supports &lt;code&gt;preserve&lt;/code&gt; for original colors and &lt;code&gt;strip&lt;/code&gt; for workflows that require normalized paint attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naming policy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can choose consistent naming conventions, including &lt;code&gt;kebab&lt;/code&gt;, &lt;code&gt;camel&lt;/code&gt;, and &lt;code&gt;pascal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For React components, PascalCase is a natural choice: &lt;code&gt;ArrowLeft&lt;/code&gt;, &lt;code&gt;CreditCard&lt;/code&gt;, or &lt;code&gt;Settings&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Export components instead of converting every SVG
&lt;/h2&gt;

&lt;p&gt;Once the collection is ready, SVGicons can generate an archive containing React TypeScript components.&lt;/p&gt;

&lt;p&gt;Depending on the selected export options, the archive can also contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Individual SVG files and an SVG sprite&lt;/li&gt;
&lt;li&gt;JSON metadata and license manifests&lt;/li&gt;
&lt;li&gt;Vue, Svelte, Solid, or Blade components&lt;/li&gt;
&lt;li&gt;Storybook galleries and npm package scaffolds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This avoids manually converting every selected SVG into a frontend component.&lt;/p&gt;

&lt;p&gt;The exported files can then be added to your project and managed alongside your application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Keep the collection as your UI evolves
&lt;/h2&gt;

&lt;p&gt;A dashboard rarely stays unchanged.&lt;/p&gt;

&lt;p&gt;When a new feature needs an icon, you can return to the collection, review the existing visual style, and add another selection.&lt;/p&gt;

&lt;p&gt;SVGicons also provides a VS Code extension that lets Pro users access collections and add icons directly from the editor.&lt;/p&gt;

&lt;p&gt;The collection remains available for future exports, while the generated files live in your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small detail worth checking
&lt;/h2&gt;

&lt;p&gt;Before shipping, review the collection's icon sets and license summary. Grouping icons into one archive does not automatically make their licenses identical.&lt;/p&gt;

&lt;p&gt;Creating and organizing collections is free with a Member account. Adding icons and exporting collections use Pro credits, and free Member accounts include 15 credits to explore the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;A reusable icon collection is not just a convenient way to download multiple SVG files.&lt;/p&gt;

&lt;p&gt;It gives your project a defined selection of icons, consistent export settings, and a practical route from visual selection to frontend components.&lt;/p&gt;

&lt;p&gt;Explore &lt;a href="https://svgicons.com/project-kits" rel="noopener noreferrer"&gt;SVGicons Project Kits&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>svg</category>
    </item>
    <item>
      <title>SVG 2 in 2026: What Icon Designers and Developers Should Know</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Thu, 08 Oct 2026 20:54:22 +0000</pubDate>
      <link>https://dev.to/svgicons/svg-2-in-2026-what-icon-designers-and-developers-should-know-58eh</link>
      <guid>https://dev.to/svgicons/svg-2-in-2026-what-icon-designers-and-developers-should-know-58eh</guid>
      <description>&lt;p&gt;SVG has been part of web development for years. It's lightweight, scalable, and particularly well suited for icons and interface graphics.&lt;/p&gt;

&lt;p&gt;On October 6, 2026, the W3C published an updated &lt;strong&gt;Candidate Recommendation Snapshot of SVG 2&lt;/strong&gt;, bringing corrections and clarifications intended to improve interoperability, especially between web browsers.&lt;/p&gt;

&lt;p&gt;It's a positive step for the SVG standard. But what does it actually mean for designers, developers, and icon libraries?&lt;/p&gt;

&lt;p&gt;And does everyone need to change the way they work with SVG?&lt;/p&gt;

&lt;h2&gt;
  
  
  SVG 2 is evolving, not starting from scratch
&lt;/h2&gt;

&lt;p&gt;SVG 2 builds on SVG 1.1, the specification behind many of the features developers already use today.&lt;/p&gt;

&lt;p&gt;The goal isn't to reinvent vector graphics. It's to modernize the specification, clarify existing behavior, and improve integration with other web technologies.&lt;/p&gt;

&lt;p&gt;The October 2026 publication continues that process.&lt;/p&gt;

&lt;p&gt;A Candidate Recommendation Snapshot is an important milestone in the W3C standards process, but it isn't the same as a final Recommendation.&lt;/p&gt;

&lt;p&gt;It also doesn't mean that every SVG 2 feature suddenly became available in every browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The specification defines how something should work. Browser implementations determine how reliably it works in practice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction matters when SVG graphics become part of a real application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's different in SVG 2?
&lt;/h2&gt;

&lt;p&gt;Several changes introduced during the development of SVG 2 are particularly relevant to people working with icons and user interfaces.&lt;/p&gt;

&lt;p&gt;These are broader developments since SVG 1.1, not features introduced specifically on October 6, 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Closer integration with CSS
&lt;/h3&gt;

&lt;p&gt;SVG 2 brings SVG geometry and styling closer to the CSS model.&lt;/p&gt;

&lt;p&gt;For example, certain geometric attributes, including dimensions and positions, are defined as CSS geometry properties.&lt;/p&gt;

&lt;p&gt;This makes it possible to control more aspects of SVG graphics through stylesheets, where supported.&lt;/p&gt;

&lt;p&gt;For developers building adaptable interfaces, closer integration between SVG and CSS is a useful direction.&lt;/p&gt;

&lt;p&gt;However, support for individual properties still needs to be checked before relying on them in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Modernized references
&lt;/h3&gt;

&lt;p&gt;SVG 2 also modernizes how elements reference other resources.&lt;/p&gt;

&lt;p&gt;For example, older SVG markup may contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;"#search-icon"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modern SVG markup can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#search-icon"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The older &lt;code&gt;xlink:href&lt;/code&gt; syntax is deprecated, although it remains recognized for backward compatibility.&lt;/p&gt;

&lt;p&gt;This relatively small change illustrates how SVG continues to evolve alongside the rest of the web platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Clearer rules for reusable graphics
&lt;/h3&gt;

&lt;p&gt;Reusability has always been one of SVG's strengths.&lt;/p&gt;

&lt;p&gt;Elements such as &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; allow developers to define graphics once and reference them elsewhere.&lt;/p&gt;

&lt;p&gt;SVG 2 refines the specification around reusable elements, including their sizing, styling, and rendering behavior.&lt;/p&gt;

&lt;p&gt;For icon systems, these details matter because the same graphical asset may appear in many different contexts.&lt;/p&gt;

&lt;p&gt;The goal is greater consistency, although actual behavior still depends on browser implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this mean for icon libraries?
&lt;/h2&gt;

&lt;p&gt;For most developers using SVG icons today, the immediate impact is limited.&lt;/p&gt;

&lt;p&gt;Existing SVG files don't suddenly become obsolete because the specification has been updated.&lt;/p&gt;

&lt;p&gt;Simple, well-structured SVG icons remain a practical choice for modern interfaces.&lt;/p&gt;

&lt;p&gt;What matters most is still familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Icons should remain sharp at different sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Colors and strokes should behave predictably.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Icons should remain visually coherent across an interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility:&lt;/strong&gt; SVG assets should render reliably in the environments where they're used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility:&lt;/strong&gt; Icons should be implemented appropriately, whether decorative or meaningful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These requirements aren't new. But they remain just as important while the underlying standard evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should developers start using every SVG 2 feature?
&lt;/h2&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;A feature being defined in a specification doesn't automatically make it the best choice for every project.&lt;/p&gt;

&lt;p&gt;Before adopting a newer SVG capability, it's worth asking a few practical questions.&lt;/p&gt;

&lt;p&gt;Does it solve a real problem? Is it supported in the browsers your users rely on? Can the same result be achieved with a simpler, well-established approach?&lt;/p&gt;

&lt;p&gt;For an icon library, predictability is often more valuable than using the newest available syntax.&lt;/p&gt;

&lt;p&gt;There's little benefit in introducing additional complexity if it doesn't improve the final experience.&lt;/p&gt;

&lt;p&gt;Developers interested in implementation progress can consult the &lt;a href="https://wpt.fyi/results/svg" rel="noopener noreferrer"&gt;Web Platform Tests results for SVG&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These results provide a useful starting point for understanding browser conformance, although application-specific testing remains important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking ahead
&lt;/h2&gt;

&lt;p&gt;The October 2026 update is a positive development for the SVG ecosystem.&lt;/p&gt;

&lt;p&gt;It demonstrates that SVG is still being refined as part of the modern web platform, with ongoing attention to interoperability and implementation consistency.&lt;/p&gt;

&lt;p&gt;For designers and developers, this is worth following even when there's no immediate need to change an existing workflow.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons.com&lt;/a&gt;, we believe that simple, reliable, and easy-to-use SVG icons remain essential, regardless of how the specification evolves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A more capable SVG standard is good news. But reliable rendering and predictable integration are what make SVG icons useful.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/news/2026/updated-candidate-recommendation-scalable-vector-graphics-svg-2/" rel="noopener noreferrer"&gt;W3C — Updated Candidate Recommendation: SVG 2 (October 6, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/2026/CR-SVG2-20261006/" rel="noopener noreferrer"&gt;SVG 2 — Candidate Recommendation Snapshot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/2026/CR-SVG2-20261006/changes.html" rel="noopener noreferrer"&gt;W3C — Changes from SVG 1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wpt.fyi/results/svg" rel="noopener noreferrer"&gt;Web Platform Tests — SVG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Published by &lt;a href="https://svgicons.com" rel="noopener noreferrer"&gt;SVGicons.com&lt;/a&gt; — SVG icons for designers and developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>svg</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>css</category>
    </item>
    <item>
      <title>Why Icon Shape Still Matters</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Wed, 07 Oct 2026 12:59:51 +0000</pubDate>
      <link>https://dev.to/svgicons/why-icon-shape-still-matters-2d56</link>
      <guid>https://dev.to/svgicons/why-icon-shape-still-matters-2d56</guid>
      <description>&lt;p&gt;Consistency is one of the foundations of a good icon system.&lt;/p&gt;

&lt;p&gt;Icons should share proportions, stroke weight, optical balance, and visual language. When they belong to the same interface, they should feel like members of the same family.&lt;/p&gt;

&lt;p&gt;But consistency can also remove information.&lt;/p&gt;

&lt;p&gt;A recent discussion around increasingly standardized app icon shapes highlights an interesting design tradeoff: when every icon is placed inside the same outer shape, the silhouette stops helping us recognize it.&lt;/p&gt;

&lt;p&gt;And that means the glyph inside has to work harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  An icon is more than its glyph
&lt;/h2&gt;

&lt;p&gt;When we think about an icon, we often focus on the symbol itself.&lt;/p&gt;

&lt;p&gt;A magnifying glass means search.&lt;/p&gt;

&lt;p&gt;A trash can means delete.&lt;/p&gt;

&lt;p&gt;A gear suggests settings.&lt;/p&gt;

&lt;p&gt;But recognition does not come from the internal symbol alone.&lt;/p&gt;

&lt;p&gt;Several visual signals work together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the outer silhouette&lt;/li&gt;
&lt;li&gt;the internal glyph&lt;/li&gt;
&lt;li&gt;color&lt;/li&gt;
&lt;li&gt;negative space&lt;/li&gt;
&lt;li&gt;proportions&lt;/li&gt;
&lt;li&gt;visual weight&lt;/li&gt;
&lt;li&gt;sometimes even texture or depth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The brain does not necessarily process these elements separately. It recognizes the whole object.&lt;/p&gt;

&lt;p&gt;That makes silhouette surprisingly important.&lt;/p&gt;

&lt;p&gt;A circle, star, shield, folder, cloud, or irregular outline can often be recognized before we consciously inspect what is inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standardization removes one recognition cue
&lt;/h2&gt;

&lt;p&gt;Imagine six icons with completely different outer shapes.&lt;/p&gt;

&lt;p&gt;Even in monochrome, their silhouettes already give us six distinct visual objects.&lt;/p&gt;

&lt;p&gt;Now place those same concepts inside six identical rounded-square containers.&lt;/p&gt;

&lt;p&gt;The layout becomes cleaner.&lt;/p&gt;

&lt;p&gt;The icons align perfectly.&lt;/p&gt;

&lt;p&gt;The interface feels more controlled.&lt;/p&gt;

&lt;p&gt;But one layer of differentiation has disappeared.&lt;/p&gt;

&lt;p&gt;The outer shape now communicates almost nothing because it is identical everywhere.&lt;/p&gt;

&lt;p&gt;Recognition depends much more heavily on the internal glyph.&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The more standardized the container becomes, the more distinctive the symbol inside needs to be.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistency and recognizability are different goals
&lt;/h2&gt;

&lt;p&gt;This is an important distinction for icon designers.&lt;/p&gt;

&lt;p&gt;Consistency answers questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do these icons look like they belong together?&lt;/li&gt;
&lt;li&gt;Do they use the same visual grammar?&lt;/li&gt;
&lt;li&gt;Do they align correctly?&lt;/li&gt;
&lt;li&gt;Do they have similar optical weight?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recognizability asks something different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can users distinguish one icon from another quickly?&lt;/li&gt;
&lt;li&gt;Can the icon still be identified at a glance?&lt;/li&gt;
&lt;li&gt;Does it remain recognizable at small sizes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A perfectly consistent icon set can still contain icons that are difficult to distinguish.&lt;/p&gt;

&lt;p&gt;In fact, excessive standardization can sometimes make that problem worse.&lt;/p&gt;

&lt;p&gt;If every icon uses the same container, stroke weight, corner radius, visual density, and construction rules, individual symbols may begin to look too similar.&lt;/p&gt;

&lt;p&gt;Consistency reduces noise.&lt;/p&gt;

&lt;p&gt;But some visual differences are useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The glyph has to carry more meaning
&lt;/h2&gt;

&lt;p&gt;When the silhouette is fixed, the internal glyph becomes more important.&lt;/p&gt;

&lt;p&gt;That has practical consequences.&lt;/p&gt;

&lt;p&gt;Small differences between symbols matter more.&lt;/p&gt;

&lt;p&gt;Negative space matters more.&lt;/p&gt;

&lt;p&gt;The overall mass of the glyph matters more.&lt;/p&gt;

&lt;p&gt;And at small sizes, unnecessary detail becomes even more expensive.&lt;/p&gt;

&lt;p&gt;Consider two icons that both use the same rounded-square container.&lt;/p&gt;

&lt;p&gt;If their internal symbols are also similar, perhaps both using three horizontal lines with only small differences, users may need additional time to distinguish them.&lt;/p&gt;

&lt;p&gt;But if one symbol has a strong diagonal structure and the other has a clear circular structure, recognition becomes much faster.&lt;/p&gt;

&lt;p&gt;The container may be identical.&lt;/p&gt;

&lt;p&gt;The internal visual signature is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  This matters beyond app icons
&lt;/h2&gt;

&lt;p&gt;The same principle applies to many interface systems.&lt;/p&gt;

&lt;p&gt;Toolbar icons often share the same bounding box.&lt;/p&gt;

&lt;p&gt;Sidebar icons usually occupy identical slots.&lt;/p&gt;

&lt;p&gt;Design systems frequently enforce consistent icon dimensions.&lt;/p&gt;

&lt;p&gt;Component libraries normalize padding and alignment.&lt;/p&gt;

&lt;p&gt;All of that is useful.&lt;/p&gt;

&lt;p&gt;But once the external geometry becomes standardized, differentiation must happen inside that structure.&lt;/p&gt;

&lt;p&gt;This is especially important for large icon libraries.&lt;/p&gt;

&lt;p&gt;As the number of icons grows, designers cannot rely only on aesthetic consistency.&lt;/p&gt;

&lt;p&gt;They also need sufficient visual distance between related symbols.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;import vs export&lt;/li&gt;
&lt;li&gt;upload vs download&lt;/li&gt;
&lt;li&gt;duplicate vs copy&lt;/li&gt;
&lt;li&gt;expand vs maximize&lt;/li&gt;
&lt;li&gt;archive vs inbox&lt;/li&gt;
&lt;li&gt;refresh vs sync&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These concepts are already semantically close.&lt;/p&gt;

&lt;p&gt;If their silhouettes, proportions, and internal constructions are also too similar, recognition becomes harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful design question
&lt;/h2&gt;

&lt;p&gt;When reviewing an icon set, we often ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this icon fit the visual system?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It may be useful to add another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If every surrounding visual cue disappeared, would this symbol still be easy to recognize?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or more specifically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the container shape were identical for every icon, would the glyph still be distinctive enough?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question changes how we evaluate icons.&lt;/p&gt;

&lt;p&gt;It pushes us to think not only about visual consistency, but also about information density and recognition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistency should support meaning, not erase it
&lt;/h2&gt;

&lt;p&gt;Standardization is not the enemy of good icon design.&lt;/p&gt;

&lt;p&gt;Without shared rules, icon sets quickly become chaotic.&lt;/p&gt;

&lt;p&gt;But the goal of those rules should be to create a coherent visual language without eliminating the differences users rely on.&lt;/p&gt;

&lt;p&gt;An icon system has to balance two forces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency helps users understand that icons belong together.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinctiveness helps users understand that they mean different things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good icon design needs both.&lt;/p&gt;

&lt;p&gt;When the outer shape becomes standardized, the responsibility simply moves inward.&lt;/p&gt;

&lt;p&gt;The glyph has to work harder.&lt;/p&gt;

</description>
      <category>design</category>
      <category>uiux</category>
      <category>webdev</category>
      <category>icons</category>
    </item>
    <item>
      <title>When SVG Optimization Fails, Your Pipeline Shouldn’t</title>
      <dc:creator>Svg/icons</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:22:00 +0000</pubDate>
      <link>https://dev.to/svgicons/when-svg-optimization-fails-your-pipeline-shouldnt-2ab7</link>
      <guid>https://dev.to/svgicons/when-svg-optimization-fails-your-pipeline-shouldnt-2ab7</guid>
      <description>&lt;p&gt;SVG optimization is usually treated as a simple step in an asset pipeline.&lt;/p&gt;

&lt;p&gt;An SVG comes in.&lt;br&gt;&lt;br&gt;
An optimizer removes unnecessary data.&lt;br&gt;&lt;br&gt;
A smaller SVG comes out.&lt;/p&gt;

&lt;p&gt;But real-world SVG files are not always simple.&lt;/p&gt;

&lt;p&gt;They may contain embedded styles, unusual selectors, metadata, generated markup, accessibility attributes, or constructs that an optimizer does not fully understand.&lt;/p&gt;

&lt;p&gt;When that happens, an important question appears:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should an optimization failure make the asset itself fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In many pipelines, the answer is effectively yes.&lt;/p&gt;

&lt;p&gt;It probably shouldn’t be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization and validity are different questions
&lt;/h2&gt;

&lt;p&gt;Consider four operations that are often mixed together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PARSE → OPTIMIZE → VALIDATE → RENDER TEST&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They may happen one after another, but they answer very different questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parse
&lt;/h3&gt;

&lt;p&gt;Can the system read the SVG structure?&lt;/p&gt;

&lt;p&gt;If parsing fails, the document may genuinely be malformed or unsupported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimize
&lt;/h3&gt;

&lt;p&gt;Can parts of the document be simplified or removed safely?&lt;/p&gt;

&lt;p&gt;This is a transformation.&lt;/p&gt;

&lt;p&gt;Failure here does not necessarily mean anything is wrong with the original asset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;

&lt;p&gt;Does the SVG satisfy the rules required by the application or project?&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required &lt;code&gt;viewBox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;forbidden external resources&lt;/li&gt;
&lt;li&gt;naming conventions&lt;/li&gt;
&lt;li&gt;accessibility requirements&lt;/li&gt;
&lt;li&gt;maximum dimensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are project constraints, not optimization rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render test
&lt;/h3&gt;

&lt;p&gt;Does the SVG actually produce the expected visual result?&lt;/p&gt;

&lt;p&gt;This can reveal problems that neither parsing nor structural validation can detect.&lt;/p&gt;

&lt;p&gt;These four stages should not be treated as interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A recent SVGO example
&lt;/h2&gt;

&lt;p&gt;A recent SVGO issue illustrates the distinction.&lt;/p&gt;

&lt;p&gt;An SVG containing CSS pseudo-element selectors such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.icon::before { content: ""; }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;can currently cause the default optimization process to throw an error because part of the internal CSS processing does not support those selectors.&lt;/p&gt;

&lt;p&gt;The interesting part is not whether pseudo-elements are common inside SVG files.&lt;/p&gt;

&lt;p&gt;The interesting part is what the failure means.&lt;/p&gt;

&lt;p&gt;The SVG can still be parseable.&lt;/p&gt;

&lt;p&gt;It may still be renderable.&lt;/p&gt;

&lt;p&gt;It may still satisfy every requirement of the application using it.&lt;/p&gt;

&lt;p&gt;Yet the optimization stage cannot process one part of its stylesheet.&lt;/p&gt;

&lt;p&gt;That is an &lt;strong&gt;optimizer limitation&lt;/strong&gt;, not automatically an &lt;strong&gt;asset failure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Related issue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/svg/svgo/issues/2308" rel="noopener noreferrer"&gt;https://github.com/svg/svgo/issues/2308&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure state matters
&lt;/h2&gt;

&lt;p&gt;Automated pipelines often reduce everything to two states:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUCCESS&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;FAILURE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But asset processing usually needs more nuance.&lt;/p&gt;

&lt;p&gt;A better model could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsed successfully&lt;/li&gt;
&lt;li&gt;Optimization skipped or partially completed&lt;/li&gt;
&lt;li&gt;Validation passed&lt;/li&gt;
&lt;li&gt;Render test passed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The asset can then continue through the pipeline while the optimization warning is recorded separately.&lt;/p&gt;

&lt;p&gt;For many systems, this is more useful than rejecting the file completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizers should be conservative transformers
&lt;/h2&gt;

&lt;p&gt;An optimizer performs transformations.&lt;/p&gt;

&lt;p&gt;That means its safest behavior when encountering something it cannot confidently transform is often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;leave it unchanged.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is very different from silently rewriting unfamiliar syntax.&lt;/p&gt;

&lt;p&gt;A conservative optimizer can effectively say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I cannot safely optimize this rule, so I will preserve it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rest of the SVG may still contain dozens of optimizations that can be performed safely.&lt;/p&gt;

&lt;p&gt;This creates a useful engineering boundary:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unknown syntax → Preserve → Continue processing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;instead of:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unknown syntax → Reject entire asset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second behavior can make automated workflows unnecessarily fragile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipelines should preserve the reason for failure
&lt;/h2&gt;

&lt;p&gt;There is another important consequence.&lt;/p&gt;

&lt;p&gt;When a build system reports:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SVG FAILED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;the developer has very little information.&lt;/p&gt;

&lt;p&gt;Was the XML invalid?&lt;/p&gt;

&lt;p&gt;Did optimization fail?&lt;/p&gt;

&lt;p&gt;Did validation reject the file?&lt;/p&gt;

&lt;p&gt;Did the renderer produce a broken result?&lt;/p&gt;

&lt;p&gt;Those are completely different problems.&lt;/p&gt;

&lt;p&gt;A better pipeline exposes the actual stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PARSE&lt;/strong&gt; ✓&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OPTIMIZE&lt;/strong&gt; ⚠ Unsupported CSS selector&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VALIDATE&lt;/strong&gt; ✓&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RENDER&lt;/strong&gt; ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the developer knows that the asset works, but one optimization could not be applied.&lt;/p&gt;

&lt;p&gt;That distinction becomes increasingly important as asset pipelines grow more automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat optimization as an enhancement
&lt;/h2&gt;

&lt;p&gt;The simplest design principle is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization should usually improve a valid asset, not define whether the asset is valid.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are exceptions.&lt;/p&gt;

&lt;p&gt;A team may deliberately require every SVG to pass a particular optimizer before deployment.&lt;/p&gt;

&lt;p&gt;That can be a perfectly valid project policy.&lt;/p&gt;

&lt;p&gt;But it should be an explicit policy.&lt;/p&gt;

&lt;p&gt;It should not happen accidentally because one transformation tool became the de facto validator for the entire pipeline.&lt;/p&gt;

&lt;p&gt;For SVG workflows, keeping responsibilities separate produces clearer failures, safer transformations, and pipelines that are much easier to debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parse → Optimize → Validate → Render test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Four stages.&lt;/p&gt;

&lt;p&gt;Four different responsibilities.&lt;/p&gt;

&lt;p&gt;And an optimization failure does not always mean the SVG has failed.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
