Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(astro): add Built-in SVG component support #12067

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

stramel
Copy link
Contributor

@stramel stramel commented Sep 25, 2024

Changes

This PR implements Built-in SVG Components following up on the discussions on withastro/roadmap#699

By default, SVG files will be components and can be imported by referencing their file path and then treating them like a component. Take this for example:

previously:

---
import Image from 'astro:assets'
import logo from './path/to/svg/file.svg'
---

<div set:html={logo}  style={{height: '24px', width: '24px' }} />
<!-- or -->
<Image src={logo} width={24} height={24} />

now:

---
import Logo from './path/to/svg/file.svg';
---

<Logo size={24} />

GOALS:

  • Allow users to import and render local .svg files as if they were an Astro component
  • Allow passing props onto the root element while overriding the existing props
  • Automate best practices for to minimize performance footguns
    • Including many inlined on a page can hurt browser parsing performance. We can automatically reduce the number of nodes by using and .
    • Inline often have xmlns and xmlns:xlink and version attributes. These are not needed in HTML5 and we can automatically drop them to save a few bytes.
  • Support this in a backwards-compatible way, meaning the current public interface of an .svg import remains unchanged
  • Stretch: support rendering .svg components in frameworks without inlining them into the client side JavaScript
    • Creating framework components for .svg icons is an anti-pattern. It’s a terrible idea that bloats your JavaScript and creates all sorts of headaches for HMR and clientside performance in production.
    • If we provide a great component for each supported framework, we’ll make it easy for users to avoid this anti-pattern. It’s so common now because there aren’t many other good solutions.

This builds a foundation for us which can be extended in the future within the core astro and even by third-parties. For instance using astro-icon, which has been the critical plugin when it comes to icons, as a playground, I was able to extend the foundation built here to add dynamic icon import for Iconify.

FUTURE:

  • Optimization: Allow hooking in SVGO or other SVG Optimizers

/cc @natemoo-re

Testing

I have ensured that existing tests are functioning properly and added additional suite for SVG Components.

Manually, I have updated the blog example to include using the SVG components. I have also tested locally against astro-icon and its demo application. Will work on some testing for this change.

Docs

This will definitely need documentation added to it. Will need some assistance in writing it.

/cc @withastro/maintainers-docs for feedback!

Copy link

changeset-bot bot commented Sep 25, 2024

🦋 Changeset detected

Latest commit: 5f8997c

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added pkg: example Related to an example package (scope) pkg: astro Related to the core `astro` package (scope) semver: major Change triggers a `major` release labels Sep 25, 2024
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is blocked because it contains a major changeset. A reviewer will merge this at the next release if approved.

@stramel stramel marked this pull request as ready for review September 25, 2024 21:15
Copy link
Member

@natemoo-re natemoo-re left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few suggestions, but this looks really good!

Comment on lines +126 to +129
/**
* Bypasses automatic sprite optimization by directly inlinining the SVG
*/
inline?: boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After sitting with this for a while, inline behavior should be the default. There are a few edge-cases that have come up with dynamic client-side behavior in astro-icon, so while I still think the optimization is very useful, we should prioritize flexibility by keeping the sprite behavior opt-in.

Comment on lines +47 to +49
Object.defineProperty(Component, Symbol.for('nodejs.util.inspect.custom'), {
value: (_: any, opts: any, inspect: any) => inspect(meta, opts),
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how necessary this is, but maintaining the current console.log behavior seems like a good idea.

A comment here explaining why we're doing this would be helpful 👍

Comment on lines +46 to +49
// Attach file data for SVGs
if (fileMetadata.format === 'svg') {
emittedImage.contents = fileData;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love @Princesseuh's take on this. Any expected footguns with including the file content for SVGs in the image metadata?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a lot of SVGs, this can be problematic for SSR since it'll get in your bundle, that's my only concerns

Comment on lines +21 to +24
if (size) {
props.height = size;
props.width = size;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like size will override width and height attributes if present. I think size should only apply when they are undefined.

Comment on lines +114 to +120
it('has height and width overridden - size set', () => {
let $svg = $('#override-attrs svg');
assert.equal($svg.length, 1);
assert.equal($svg.attr('height'), '16');
assert.equal($svg.attr('width'), '16');
assert.equal(!!$svg.attr('size'), false);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test will need updating if the size behavior switches

assert.equal(!!$svg.attr('version'), false);
});
});
describe('additional props', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to assert that the default role="img" is overwritable. Now that I think of it, we should also double check that role="img" is the best default since I know aria-hidden and role="presentation" are also used sometimes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😩 this is the old logo

@@ -0,0 +1,54 @@
import { parse, renderSync } from 'ultrahtml';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a callout that I maintain ultrahtml—this could easily be swapped for another XML parser if preferred, but keep in mind that it will be bundled into the server runtime so we should try to keep a light footprint.

@@ -1,6 +1,7 @@
/// <reference types="vite/types/import-meta.d.ts" />
/// <reference path="./types/content.d.ts" />
/// <reference path="./types/actions.d.ts" />
/// <reference path="./jsx-runtime.d.ts" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed, was there something you were encountering?

Comment on lines +46 to +49
// Attach file data for SVGs
if (fileMetadata.format === 'svg') {
emittedImage.contents = fileData;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a lot of SVGs, this can be problematic for SSR since it'll get in your bundle, that's my only concerns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: astro Related to the core `astro` package (scope) pkg: example Related to an example package (scope) semver: major Change triggers a `major` release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants