diff --git a/.changeset/giant-oranges-like.md b/.changeset/giant-oranges-like.md new file mode 100644 index 000000000..5a023e836 --- /dev/null +++ b/.changeset/giant-oranges-like.md @@ -0,0 +1,5 @@ +--- +"@aragon/gov-ui-kit": patch +--- + +Fix SVG build config with missing `prefixIds` plugin diff --git a/.storybook/main.ts b/.storybook/main.ts index 57ab05c49..72607deb0 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -5,6 +5,7 @@ import type { RollupOptions } from 'rollup'; import { mergeConfig } from 'vite'; import { viteStaticCopy } from 'vite-plugin-static-copy'; import svgr from 'vite-plugin-svgr'; +import svgoConfig from '../svgo.config.js'; const config: StorybookConfig = { stories: ['../docs/**/*.@(md|mdx)', '../src/**/*.stories.@(js|jsx|ts|tsx)', '../src/**/*.@(md|mdx)'], @@ -24,7 +25,12 @@ const config: StorybookConfig = { viteFinal: (viteConfig) => { // Add source-map-js alias and plugins for importing svg files and copying fonts const plugins = [ - svgr({ include: '**/*.svg' }), + // Run the same SVGO pass (including id namespacing, see svgo.config.js) as the + // published library build, so Storybook previews match production markup. + svgr({ + include: '**/*.svg', + svgrOptions: { plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'], svgoConfig }, + }), viteStaticCopy({ targets: [{ src: './src/theme/fonts/*.ttf', dest: './fonts' }] }), ]; const resolve = { alias: { 'source-map-js': 'source-map' } }; diff --git a/package.json b/package.json index a17f0cc4d..f389563fd 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,8 @@ "@rollup/plugin-typescript": "^12.3.0", "@storybook/addon-docs": "^10.4.6", "@storybook/react-vite": "^10.4.6", + "@svgr/plugin-jsx": "^8.1.0", + "@svgr/plugin-svgo": "^8.1.0", "@svgr/rollup": "^8.1.0", "@tailwindcss/postcss": "^4.3.1", "@tailwindcss/typography": "^0.5.20", @@ -133,6 +135,7 @@ "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-visualizer": "^7.0.1", "storybook": "^10.4.6", + "svgo": "^3.3.3", "tailwindcss": "^4.3.1", "ts-jest": "^29.4.11", "tslib": "^2.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ded77d11a..704b74c8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,6 +161,12 @@ importers: '@storybook/react-vite': specifier: ^10.4.6 version: 10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rollup@4.62.0)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@2.8.8)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) + '@svgr/plugin-jsx': + specifier: ^8.1.0 + version: 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': + specifier: ^8.1.0 + version: 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) '@svgr/rollup': specifier: ^8.1.0 version: 8.1.0(rollup@4.62.0)(typescript@5.9.3) @@ -248,6 +254,9 @@ importers: storybook: specifier: ^10.4.6 version: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@2.8.8)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + svgo: + specifier: ^3.3.3 + version: 3.3.3 tailwindcss: specifier: ^4.3.1 version: 4.3.1 diff --git a/rollup.config.mjs b/rollup.config.mjs index 340101776..a933236d1 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -8,6 +8,7 @@ import svgr from '@svgr/rollup'; import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import postcss from 'rollup-plugin-postcss'; import { visualizer } from 'rollup-plugin-visualizer'; +import svgoConfig from './svgo.config.js'; const require = createRequire(import.meta.url); const tsConfig = require('./tsconfig.json'); @@ -52,19 +53,7 @@ export default [ ], }), images({ include: ['**/*.png', '**/*.jpg'] }), - svgr({ - // Keep the viewBox so icons scale instead of getting clipped when - // rendered at a size other than their intrinsic 16px (e.g. size-3 in - // small buttons). SVGO's preset-default removes it by default. - svgoConfig: { - plugins: [ - { - name: 'preset-default', - params: { overrides: { removeViewBox: false } }, - }, - ], - }, - }), + svgr({ svgoConfig }), terser(), ], }, diff --git a/svgo.config.js b/svgo.config.js new file mode 100644 index 000000000..4790514a1 --- /dev/null +++ b/svgo.config.js @@ -0,0 +1,26 @@ +// Shared SVGO config used by the Rollup library build (rollup.config.mjs), the +// Storybook/dev Vite build (.storybook/main.ts) and the svgIds test, so icon/illustration +// SVGs are optimized identically everywhere and this file is the single source of truth +// for the id collision fix below. Authored as CommonJS so all three can import it. +/** @type {import('svgo').Config} */ +module.exports = { + plugins: [ + { + name: 'preset-default', + params: { + overrides: { + // Keep the viewBox so icons scale instead of getting clipped when + // rendered at a size other than their intrinsic 16px (e.g. size-3 in + // small buttons). SVGO's preset-default removes it by default. + removeViewBox: false, + }, + }, + }, + // Icons/illustrations are inlined as markup in the DOM, so ``, + // ``, etc. resolve against the whole document's id space, not + // per-. Without this, every source file that (independently) authors the + // same short id collides with every other one as soon as two such components are + // rendered on the same page, and `url(#a)` silently resolves to the wrong element. + 'prefixIds', + ], +};