withastro / roadmap

Ideas, suggestions, and formal RFC proposals for the Astro project.
311 stars 30 forks source link

[RFC Stage 3]: Built-in SVG Components #1035

Open stramel opened 4 weeks ago

stramel commented 4 weeks ago

Summary

This RFC proposes adding native support for importing and rendering .svg files as Astro components, with optimized rendering techniques to minimize performance impact. It aims to allow .svg files to be treated as components that accept props and be optimized for repeated use on a page using <symbol> and <use> elements.

An SVG can be imported directly into an Astro component and used as a component that will only embed itself once.

---
import Logo from '../assets/logo.svg'
---

<Logo/>

Results in:

<svg height="24" width="24" role="img">
    <!-- SVG Content -->
</svg>

Links

abdo-spices commented 4 weeks ago

thanks, for hard working and supporting \ as I requested

matthewp commented 3 weeks ago

Can you explain how you are implementing the <use> behavior? Does the user need to author their SVG to be a sprite, and you detect that and use <use> Or it is a configuration to enable that, or a prop, or?

lorenzolewis commented 3 weeks ago

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

stramel commented 3 weeks ago

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

Yes, that will break the rest of the SVGs using that symbol. This is one of the nuances to using this sprite setup. Another option would be to put all the symbols into a single SVG that could be placed on the page (perhaps in a layout for simplicity). This would avoid that nuance but would complicate things in the future from a lazy loading perspective, generating the build, and would a slightly more code (though this is probably negligible)

stramel commented 3 weeks ago

Can you explain how you are implementing the <use> behavior? Does the user need to author their SVG to be a sprite, and you detect that and use <use> Or it is a configuration to enable that, or a prop, or?

The user doesn't need to author the SVG in any certain way for it to be used as a sprite.

The sprite behavior is:

All future SVGs will not create and add the <symbol> element leaving only the <use> element which references the original definition.

lorenzolewis commented 3 weeks ago

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

Yes, that will break the rest of the SVGs using that symbol. This is one of the nuances to using this sprite setup. Another option would be to put all the symbols into a single SVG that could be placed on the page (perhaps in a layout for simplicity). This would avoid that nuance but would complicate things in the future from a lazy loading perspective, generating the build, and would a slightly more code (though this is probably negligible)

I can imagine this will present some difficult support scenarios, especially if adding a framework-specific way on top of this is a future goal as you've mentioned in the original RFC.

Would there be any downside to having a separate SVG defining the symbols on the page without that specific element being responsible for rendering anything itself (i.e. only holding the symbols, not displaying them)?


Thinking outside the box a bit here, I could imagine a setup like this:

  1. User imports and uses an SVG. This is is just treated normally without any symbol or use injected.
  2. A user opts-in to the symbol and use setup by importing a special component and putting it in their page:
    
    ---
    import { SvgSymbol } from 'astro:assets' // naming tbd
    import Logo from '../assets/logo.svg';
    import Layout from './layouts/BaseLayout.astro'
    ---

I'm a cool page



With the above case, a user is specifically opting-in to the sprite behavior and it is clear where it is used. My mind is thinking a bit on how View Transitions were implemented: https://docs.astro.build/en/guides/view-transitions/#adding-view-transitions-to-a-page
stramel commented 3 weeks ago

There are more gotchas when using Sprites which is why we should make that opt-in rather than the default. Even with using a Sprite component to hold the aggregate of SVGs on the page.

Would there be any downside to having a separate SVG defining the symbols on the page without that specific element being responsible for rendering anything itself (i.e. only holding the symbols, not displaying them)?

As I was mentioning previously, this does complicate the implementation, could prove hard for SSR lazy-loading later, and very slight increase in code size.

That being said, I think it might be worth it for the trade-off for avoiding an edge-case on the Sprite usage and possible improvement for framework usage.

Would love @natemoo-re's thoughts on it.

alvinometric commented 3 weeks ago

Just wanted to say that doing this:

---
  import ThumbIcon from '../assets/icons/ui/thumbs-up.svg?raw';
---

<Fragment set:html={ThumbIcon} />

Has been pretty great for me. I like the idea of "it just works" rather than having to find this in the docs. But I'd rather not overcomplicate things if it creates problems elsewhere.

Lofty-Brambles commented 2 weeks ago

A QoL thing: It'd be nice if I can style the svg without having to use a global modifier, if I import it within the component.

stramel commented 2 weeks ago

A QoL thing: It'd be nice if I can style the svg without having to use a global modifier, if I import it within the component.

I'm not quite sure I'm following. Can you provide an example or for details?

Lofty-Brambles commented 2 weeks ago

I'm not quite sure I'm following. Can you provide an example or for details?

Yes!

---
import RandomSVG from ".@src/imgs/duck_icon.svg";
---

<RandomSVG />

<style>
    svg { /* This would target the svg element that this results in. */ }
</style>
stramel commented 6 days ago

I'm not quite sure I'm following. Can you provide an example or for details?

Yes!

---
import RandomSVG from ".@src/imgs/duck_icon.svg";
---

<RandomSVG />

<style>
    svg { /* This would target the svg element that this results in. */ }
</style>

I'm not sure there is much we can do about that. The styling is primarily leaning on the feature-set of Astro. You could use the style/class attributes.