nuxt / icon

The <Icon> component, supporting Iconify, Emojis and custom components.
https://stackblitz.com/edit/nuxt-icon-playground?file=app.vue
MIT License
924 stars 50 forks source link

Auto-optimized sprites using `<symbol>` and `<use>` #256

Open ShayanTheNerd opened 1 week ago

ShayanTheNerd commented 1 week ago

Astro Icon is a similar module for Astro projects that also utilizes Iconify under the hood. It automatically optimizes repeated references to the same icon on a page, using a simple approach that results in smaller HTML documents (official documentation).

In a nutshell, the first time the <Icon> component is included on a page, it defines a sprite <symbol> with a unique ID and immediately renders that symbol with the <use> element. If the same icon is referenced again, <Icon> will render only a <use> element, reducing the overall size of the HTML document by referencing the existing <symbol>.

With that said:

<Icon name="logo" />
<!-- First usage generates the following HTML -->
<svg data-icon="logo">
  <symbol id="ai:uniqueid"><!-- contents of logo.svg --></symbol>
  <use xlink:href="#ai:uniqueid"></use>
</svg>

<Icon name="logo" />
<!-- Additional usage generates the following HTML -->
<svg data-icon="logo">
  <use xlink:href="#ai:uniqueid"></use>
</svg>

It'd be nice to have this feature in Nuxt Icon module as well.

antfu commented 1 week ago

Sounds like an interesting idea to avoid duplication. However, I am not sure how to handle the cases when the first icon gets unmounted while the second remains.

On the other hand, I think the CSS mode solves this problem is a much better way (which is already the the default). Wonder what's the block prevent you from using the CSS mode?

ShayanTheNerd commented 6 days ago

Wonder what's the block prevent you from using the CSS mode?

167 and fine-grained control over the customization of icons, especially the local ones.


I am not sure how to handle the cases when the first icon gets unmounted while the second remains.

Perhaps by inserting a hidden <svg> element right after the starting <body> tag. This <svg> would contain a <symbol> for each icon used in the current HTML document. This way, all icons are referenced by <use>, and removing one doesn't break its duplications elsewhere in the document.

<!-- First usage -->
<Icon name="logo" />
<!-- Additional usage-->
<Icon name="logo" />

<!-- Generates the following HTML -->
<body>
   <svg hidden aria-hidden="true">
      <symbol id="ai:uniqueid"><!-- contents of logo.svg --></symbol>
   </svg>

   <!-- Some other markup -->

   <!-- Note that `href` suffices since `xlink:href` has been deprecated -->
   <svg data-icon="logo">
      <use href="#ai:uniqueid"></use>
   </svg>
   <svg data-icon="logo">
      <use href="#ai:uniqueid"></use>
   </svg>
</body>