mikaelvesavuori / figmagic

Figmagic is the missing piece between DevOps and design: Generate design tokens, export graphics, and extract design token-driven React components from your Figma documents.
https://docs.figmagic.com
MIT License
807 stars 71 forks source link

feat: optimizing SVG graphics #177

Closed opauloh closed 1 year ago

opauloh commented 1 year ago

Hi everyone

This PR adds a step that optimizes SVGs downloaded from Figma by using the SVGO API.

That ends up benefiting the final users as it helps on reducing bundle sizes and also makes it easier for animations by optimizing the number of paths.

The benefit can already be seen on simple SVGs, but the more complex SVG, the bigger the benefits. The difference is also huge when importing illustrations from Figma.

Example of an SVG downloaded from Figma:

SVG xml:

<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.2656 21.6875L25.2344 14.7083L21.2031 21.6875L12.974 16.9323L18.8073 6.82812C19.186 6.17855 19.7279 5.63927 20.3793 5.26377C21.0307 4.88828 21.769 4.68962 22.5208 4.6875H27.9479C28.6998 4.68962 29.4381 4.88828 30.0895 5.26377C30.7409 5.63927 31.2828 6.17855 31.6615 6.82812L37.5 16.9323L29.2656 21.6875ZM25.2344 12.6302L29.6458 20.2604L36.0677 16.5521L30.7552 7.34896C30.4699 6.85693 30.0605 6.44839 29.5679 6.16415C29.0753 5.8799 28.5167 5.72992 27.9479 5.72917H22.5208C21.9513 5.72953 21.3919 5.87931 20.8983 6.16354C20.4048 6.44778 19.9945 6.85652 19.7083 7.34896L14.4011 16.5521L20.8333 20.2604L25.2344 12.6302Z" fill="black"/>
<path d="M25.2344 14.1875H25.1823C25.1138 14.181 25.0472 14.161 24.9865 14.1286C24.9257 14.0962 24.872 14.0521 24.8284 13.9988C24.7848 13.9455 24.7522 13.8841 24.7325 13.8182C24.7128 13.7522 24.7064 13.683 24.7135 13.6146C24.7135 13.4115 25.3021 8.5625 31.0365 6.59896C31.1018 6.57377 31.1715 6.56202 31.2415 6.56439C31.3114 6.56676 31.3802 6.58321 31.4437 6.61277C31.5071 6.64232 31.564 6.68436 31.6109 6.73638C31.6577 6.7884 31.6936 6.84933 31.7163 6.91554C31.7391 6.98174 31.7483 7.05185 31.7434 7.12168C31.7384 7.19152 31.7195 7.25964 31.6876 7.32197C31.6557 7.38431 31.6116 7.43958 31.5579 7.48449C31.5042 7.5294 31.442 7.56301 31.375 7.58333C26.2604 9.33854 25.7552 13.6771 25.75 13.724C25.7359 13.851 25.6756 13.9685 25.5805 14.054C25.4854 14.1395 25.3622 14.187 25.2344 14.1875Z" fill="black"/>
<path d="M36.2761 23.4375L26.7396 21.7188L29.224 20.5052L36.5156 16.2969L39.1198 14.5469L36.2761 23.4375ZM30.0261 21.2708L35.5625 22.2552L37.2188 17.099L37.0417 17.2188L30.0261 21.2708Z" fill="black"/>
<path d="M40.6823 44.7135L29.0104 44.6823V35.1771H37.0677L33.0573 28.1875L41.3021 23.4635L47.1302 33.599C47.5049 34.2511 47.7021 34.9901 47.7021 35.7422C47.7021 36.4943 47.5049 37.2333 47.1302 37.8854L44.4062 42.5729C44.0279 43.2251 43.4846 43.7662 42.8309 44.1419C42.1773 44.5177 41.4362 44.7148 40.6823 44.7135ZM30.0521 43.6406L40.6771 43.6719C41.2452 43.6711 41.8033 43.5217 42.2958 43.2385C42.7882 42.9552 43.198 42.548 43.4844 42.0573L46.2083 37.3698C46.4921 36.8771 46.6415 36.3186 46.6415 35.75C46.6415 35.1814 46.4921 34.6229 46.2083 34.1302L40.9375 24.9062L34.5 28.5938L38.8854 36.2448H30.0677L30.0521 43.6406Z" fill="black"/>
<path d="M40.6719 44.7135H40.5729C40.5054 44.7005 40.4412 44.6742 40.3839 44.6361C40.3266 44.5981 40.2774 44.5491 40.2392 44.492C40.2009 44.4348 40.1744 44.3707 40.161 44.3032C40.1477 44.2358 40.1478 44.1663 40.1615 44.099C41.2031 38.7969 37.7136 36.1771 37.6823 36.151C37.6195 36.1143 37.5652 36.0648 37.5227 36.0058C37.4802 35.9468 37.4505 35.8795 37.4356 35.8083C37.4207 35.7371 37.4209 35.6636 37.4361 35.5925C37.4513 35.5213 37.4813 35.4542 37.5241 35.3954C37.5668 35.3365 37.6214 35.2873 37.6844 35.2508C37.7473 35.2144 37.8172 35.1915 37.8895 35.1837C37.9618 35.1759 38.035 35.1833 38.1042 35.2055C38.1735 35.2277 38.2374 35.2641 38.2917 35.3125C38.4583 35.4323 42.3646 38.3542 41.1823 44.3021C41.1572 44.4188 41.0929 44.5233 41 44.5982C40.907 44.6731 40.7912 44.7138 40.6719 44.7135Z" fill="black"/>
<path d="M30.25 47.2917L24 40.375L30.25 32.9792L30.0469 35.7396V44.1458L30.25 47.2917ZM25.3958 40.3542L29.0417 44.375V44.1615V36.0625L25.3958 40.3542Z" fill="black"/>
<path d="M20.9271 44.8281L9.26563 44.7917C8.51316 44.7872 7.77507 44.5852 7.12519 44.2058C6.47532 43.8265 5.93645 43.2832 5.5625 42.6302L2.86979 37.9427C2.49505 37.2915 2.29782 36.5534 2.29782 35.8021C2.29782 35.0508 2.49505 34.3126 2.86979 33.6615L8.76042 23.5469L16.974 28.3281L12.9167 35.2917L20.974 35.3281L20.9271 44.8281ZM9.13542 25L3.78646 34.151C3.50264 34.6437 3.35324 35.2023 3.35324 35.7708C3.35324 36.3394 3.50264 36.898 3.78646 37.3906L6.48438 42.0781C6.76127 42.5768 7.16454 42.9938 7.65365 43.2873C8.14277 43.5808 8.70051 43.7404 9.27083 43.75L19.8906 43.7917L19.9271 36.375L11.1094 36.3333L15.5469 28.7135L9.13542 25Z" fill="black"/>
<path d="M9.3021 36.8125C6.95687 36.7834 4.70723 35.8787 2.99481 34.276C2.89132 34.1848 2.82827 34.0562 2.81948 33.9185C2.81069 33.7809 2.85689 33.6453 2.94793 33.5417C3.03917 33.4382 3.16774 33.3751 3.30542 33.3663C3.44311 33.3576 3.57865 33.4037 3.68231 33.4948C7.71876 37.0469 11.6406 35.401 11.8021 35.3281C11.9292 35.2722 12.0733 35.269 12.2027 35.3193C12.3321 35.3696 12.4363 35.4693 12.4922 35.5964C12.5481 35.7234 12.5513 35.8675 12.501 35.997C12.4507 36.1264 12.3511 36.2305 12.224 36.2865C11.292 36.6447 10.3005 36.8232 9.3021 36.8125Z" fill="black"/>
<path d="M18.2552 30.5052L15.9688 28.9427L8.70313 24.7188L5.88022 23.3125L14.9948 21.3854L18.2552 30.5052ZM9.02084 23.7344L9.21355 23.8281L16.2083 27.9063L14.3177 22.5938L9.02084 23.7344Z" fill="black"/>
</svg>

SVG Image: Recycle

Example of the same SVG after the optimization:

SVG xml

<svg width="50" height="50" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="m29.266 21.688-4.032-6.98-4.03 6.98-8.23-4.756 5.833-10.104a4.313 4.313 0 0 1 3.714-2.14h5.427a4.313 4.313 0 0 1 3.713 2.14L37.5 16.932l-8.234 4.756Zm-4.032-9.058 4.412 7.63 6.422-3.708-5.313-9.203a3.25 3.25 0 0 0-2.807-1.62H22.52a3.255 3.255 0 0 0-2.813 1.62L14.4 16.552l6.432 3.708 4.401-7.63Z" fill="#000"/>
  <path d="M25.234 14.188h-.052a.52.52 0 0 1-.468-.573c0-.204.588-5.053 6.323-7.016a.52.52 0 1 1 .338.984c-5.115 1.756-5.62 6.094-5.625 6.141a.521.521 0 0 1-.516.463Zm11.042 9.25-9.536-1.72 2.484-1.213 7.292-4.208 2.604-1.75-2.844 8.89Zm-6.25-2.167 5.537.984 1.656-5.156-.177.12-7.016 4.052Zm10.656 23.443-11.672-.032v-9.505h8.058l-4.01-6.99 8.244-4.724L47.13 33.6a4.302 4.302 0 0 1 0 4.286l-2.724 4.688a4.296 4.296 0 0 1-3.724 2.14Zm-10.63-1.073 10.625.03a3.257 3.257 0 0 0 2.807-1.614l2.724-4.687a3.245 3.245 0 0 0 0-3.24l-5.27-9.224-6.438 3.688 4.385 7.65h-8.817l-.016 7.397Z" fill="#000"/>
  <path d="M40.672 44.714h-.1a.518.518 0 0 1-.41-.615c1.041-5.302-2.448-7.922-2.48-7.948a.52.52 0 1 1 .61-.839c.166.12 4.073 3.042 2.89 8.99a.52.52 0 0 1-.51.412ZM30.25 47.292 24 40.375l6.25-7.396-.203 2.76v8.407l.203 3.146Zm-4.854-6.938 3.646 4.021v-8.313l-3.646 4.292Zm-4.469 4.474-11.661-.036a4.296 4.296 0 0 1-3.704-2.162L2.87 37.943a4.292 4.292 0 0 1 0-4.282l5.89-10.114 8.214 4.781-4.057 6.964 8.057.036-.047 9.5ZM9.135 25l-5.349 9.151a3.245 3.245 0 0 0 0 3.24l2.698 4.687a3.25 3.25 0 0 0 2.787 1.672l10.62.042.036-7.417-8.818-.042 4.438-7.62L9.135 25Z" fill="#000"/>
  <path d="M9.302 36.813a9.4 9.4 0 0 1-6.307-2.537.52.52 0 0 1 .687-.781c4.037 3.552 7.959 1.906 8.12 1.833a.524.524 0 0 1 .422.958 7.909 7.909 0 0 1-2.922.526Zm8.953-6.308-2.286-1.562-7.266-4.224-2.823-1.407 9.115-1.927 3.26 9.12Zm-9.234-6.77.193.093 6.994 4.078-1.89-5.312-5.297 1.14Z" fill="#000"/>
</svg>

SVG Image: Recycle

Final result:

The SVG image remains the same, but the markup is optimized and more readable.

Added the optimization as default but added the --noOptimizeSVG CLI option for users to disable the optimization if needed (It can also be disabled by setting optimizeSVG: false on the figmarc file)

Description of the changes made

Checklist

mikaelvesavuori commented 1 year ago

This is a smart idea, but I am concerned about bundling even more dependencies; by the way this uses svgo as a dev dependency which wouldn't work?

On that note, is svgo "bundleable" with Webpack?

This is one more thing that could be provided possibly as a guide rather than a feature.

sonarcloud[bot] commented 1 year ago

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

opauloh commented 1 year ago

On that note, is svgo "bundleable" with Webpack?

Hey, yes it should be on 'dependencies' instead of devDependencies to be "bundleable" by webpack, I just updated the PR

opauloh commented 1 year ago

This is a smart idea, but I am concerned about bundling even more dependencies; by the way this uses svgo as a dev dependency which wouldn't work?

It's "bundleable", and it significantly increases the package size: from 218 kb to 1.49 MB,

Current:

Screen Shot 2022-10-27 at 5 03 32 PM

With SVGO:

Screen Shot 2022-10-27 at 5 03 58 PM

On my note, I wasn't so worried about bundle size because Figmagic is a dev dependency and won't get bundled for end users, and on the other side, the SVGs are bundled, and having them optimized by default could make the trade-off worthwhile.

opauloh commented 1 year ago

This is one more thing that could be provided possibly as a guide rather than a feature.

That might be a good idea 💡

mikaelvesavuori commented 1 year ago

OK, @opauloh. While I love your work, I feel like this one isn't in line with design goals like keeping this light and dependency-less. It's a bit of a dud that Figmagic uses Webpack at all, but it comes down to bundling all of the files of the project rather than, as is normal, also any third party dependencies. This design goal may not have been clear, but I feel it's the right call—svgo optimization I am happy to support with a dedicated guide that we can link to in the regular README.

Once again, it's never "fun" saying no, but in this case we retain the clarity of the project while easing production-grade use for our users if we can assist them, but it's not part of Figmagic itself.

opauloh commented 1 year ago

Thanks for your kindly anwer @mikaelvesavuori , I agree with you, and realized I had precipitated myself in opened the PR (we could had discussed that on an issue first).

I'll be happy to add an guide on how to optimize svgs later, thanks