Skip to content

fix: support suppressing generated svg dimensions#433

Open
miguelcobain wants to merge 1 commit intounplugin:mainfrom
miguelcobain:custom-svg-dimension-suppression
Open

fix: support suppressing generated svg dimensions#433
miguelcobain wants to merge 1 commit intounplugin:mainfrom
miguelcobain:custom-svg-dimension-suppression

Conversation

@miguelcobain
Copy link
Copy Markdown

Description

Fixes collection-specific suppression of generated SVG dimensions so custom SVG collections can preserve non-square artwork sizing, such as horizontal brand logos.

Today, unplugin-icons always ends up emitting root width/height attributes for generated components unless sizing is disabled globally with scale: 0. That works well for normal icons, but it breaks CSS sizing patterns like width: 11rem; height: auto; for custom logo artwork because the outer <svg> keeps a square intrinsic size.

This PR keeps the existing customization API and makes iconCustomizer() and query params honor width: 'unset' / height: 'unset' (and 'none') by removing the root <svg> dimensions after Iconify renders the SVG.

Included in this PR:

  • root-SVG dimension suppression in the shared loader path
  • support for suppression via both iconCustomizer() and query params
  • preservation of existing precedence: defaults -> iconCustomizer() -> query params
  • focused tests covering custom collections, override behavior, and one-sided suppression cases
  • README docs for using unset / none with custom logo collections

Linked Issues

Fixes #353

Additional context

Reviewers may want to focus on the suppression semantics in src/core/loader.ts, especially the one-sided cases:

  • if one dimension is suppressed and the other was not explicitly set, both dimensions are removed
  • if one dimension is suppressed and the other is explicitly set to a real value, only the suppressed dimension is removed

Validation I ran locally:

  • vitest --run test/loader.test.ts
  • eslint src/core/loader.ts test/loader.test.ts

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@userquin
Copy link
Copy Markdown
Member

userquin commented Apr 14, 2026

/cc @cyberalien Maybe we need this at iconify utils instead only here, this way we can reuse this at UnoCSS preset-icons and also at iconify, can you take a look at this 🙏 ?

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 14, 2026

Open in StackBlitz

npm i https://pkg.pr.new/unplugin-icons@433

commit: 28b40fd

@cyberalien
Copy link
Copy Markdown
Contributor

I'm not sure its even worth the effort. This is a one time use case for a custom icon. Why not just embed icon without unplugin?

@miguelcobain
Copy link
Copy Markdown
Author

miguelcobain commented Apr 15, 2026

@cyberalien I agree embedding directly is a fine workaround for a one-off asset. My thinking was more about custom collections in general: they can include non-square SVGs like logos, wordmarks, flags, or other artwork where width: ...; height: auto; should follow the viewBox ratio.

To me that nudges custom collections from "bring your SVG icons" a bit closer to "bring your SVGs", without adding much complexity.

This PR doesn’t add a new API or change defaults, it just makes the existing customization path honor explicit width/height = unset|none by omitting those attributes on the root <svg>. So the normal icon case stays unchanged, and only users opting into that behavior are affected.

@cyberalien
Copy link
Copy Markdown
Contributor

You are right. I didn't realise Unplugin Icons generates square icons only. That's bad and needs fixing.

@cyberalien
Copy link
Copy Markdown
Contributor

Did more tests, sizing is broken if one attribute is set.

No attributes: height is set to 1.2em, width is calculated using icon's width/height ratio. So the only scenario where it works correctly.

If height is set, width is completely broken: calculated using icon's width/height ratio from 1.2em, not from height attribute.

If width is set, height is broken, same as above.

unset not only doesn't work, it sets both width and height to 1.2em, ignoring icon's width/height ratio.

Tested by replacing icons in Vue example to fa6-regular/comments, which is a 640 x 512 icon, so it has 1.25 width/height ratio.

@miguelcobain
Copy link
Copy Markdown
Author

Just to be clear, you tested that with this branch?

@cyberalien
Copy link
Copy Markdown
Contributor

Just to be clear, you tested that with this branch?

No, I've tested with default to make sure things are broken.

Now I've tested with this branch:

  • Setting one attribute - still broken
  • unset value - fixed

@miguelcobain
Copy link
Copy Markdown
Author

I confirmed that behavior locally, and your summary matches what the code is doing.

Looking at the code path, I think if this is fixed in @iconify/utils at the shared loader level, unplugin-icons probably wouldn’t need its own implementation change for it.

unplugin-icons is basically forwarding through loadNodeIcon() and then compiling the returned SVG, so this branch is mainly compensating for the current upstream loader behavior around unset.

I’ll take a look at solving it upstream instead, since that seems like the cleaner place to address it.

@miguelcobain
Copy link
Copy Markdown
Author

@cyberalien @iconify/utils has a specific test that contradicts your expectations:

test('loadIcon with unset width', async () => {
	const result = await loadNodeIcon('flat-color-icons', 'up-right', {
		customizations: {
			additionalProps: {
				width: 'unset',
			},
		},
	});
	expect(result).toBeTruthy();
	expect(result && result.includes('width="')).toBeFalsy();
	expect(result && result.includes('height="1em"')).toBeTruthy();
	expect(result && result.includes('height="')).toBeFalsy();
});

So, it expects the height to be the default 1em, even when we pass width: 'unset'.

Is it ok to change this test? It might be a "breaking" change in that sense.

@cyberalien
Copy link
Copy Markdown
Contributor

Yes, if whole parsing logic is fixed, that test can be fixed too.

That test was intentional, but I don't remember what it was fixing.

@cyberalien
Copy link
Copy Markdown
Contributor

Merged pull request to Utils and tested it, works correctly with main branch, without this pull request. So it was fixed in Utils.

I've also pushed a PR that replaces square icons with non-square icons in Vue example to test generated components: #434

@cyberalien
Copy link
Copy Markdown
Contributor

Related to this issue: can anyone test @iconify/utils@dev, which contains fix for this issue, with UnoCSS?

I've tried doing it, but experiencing massive amount of errors trying to build UnoCSS from main branch, cannot even get to testing dev version of utils. Tried fixing some build errors, ran into more and more other build errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

About icon aspect ratio

3 participants