square / maker

Maker Design System by Square
https://square.github.io/maker/styleguide/latest-stable/
Other
63 stars 14 forks source link

refactor(image): improve Image loading #512

Closed berwyn closed 1 year ago

berwyn commented 1 year ago

Describe the problem this PR addresses

With the latest release version, image loading has a few snags. This is especially apparent in Safari, where the user agent frequently loads two or more of the images. In my example case, I have an MImage with src, srcset, and sizes all set, which causes Safari to load two images from the srcset in two resolutions. Additionally, the internal cache inside MImage creates an <image> tag that loads its src, meaning that every image on the page is loading three separate copies of the same image at different resolutions. This has a huge impact in page load performance, as the number of in-flight requests as well as the amount of data loaded can become quite significant on image-heavy pages.

You can observe this in Safari's network tab using the "group by resource" option. Here, you can see two of the variants are loaded as a result of the <img> itself, while a 3rd is loaded from MImage's hidden <image> tag.

image

Describe the changes in this PR

This PR does a couple things to MImage that change implementation details without touching the public API

The end result is that user agents like Safari will now properly load exactly one candidate from the src+srcset tags.

Here, you can see Safari's network tab with the same settings shows exactly one version of the image loaded:

image

Other information

An interesting aside, but the lazyloading process used the Image constructor, which maps to the long-deprecated <image> tag. Normally, if this element was attached to the DOM, the user agent would convert it to an <img>, but in this case because it remains outside the DOM tree it uses the legacy <image> behaviour and would not parse srcset/sizes values!

github-actions[bot] commented 1 year ago

Deployed Styleguide and Lab.

Notes
  1. Links may take a few minutes to update after PR is opened or commit is pushed.
  2. Links may become invalidated after PR is merged or closed.
  3. Links for all releases and open PRs can be found on the Maker Deploys page.

github-actions[bot] commented 1 year ago

πŸ“Š Package size report   -0.02%↓

File Before After
components/Image/script.js 6.7 kB 0.6%↑6.8 kB
Total (Includes all files) 1.8 MB -0.02%↓1.8 MB
Tarball size 343.5 kB -0.09%↓343.2 kB
Unchanged files | File | Size | | -------------------------------------------------------------------------------------------- | --------: | | `components/Accordion/index.js` | `46 B` | | `components/Accordion/script.js` | `5.2 kB` | | `components/Accordion/styles.css` | `235 B` | | `components/ActionBar/index.js` | `46 B` | | `components/ActionBar/script.js` | `14.6 kB` | | `components/ActionBar/styles.css` | `5.7 kB` | | `components/Badge/index.js` | `46 B` | | `components/Badge/script.js` | `5.9 kB` | | `components/Badge/styles.css` | `1.8 kB` | | `components/Blade/index.js` | `46 B` | | `components/Blade/script.js` | `7.0 kB` | | `components/Blade/styles.css` | `837 B` | | `components/BlockFormControlLayout/index.js` | `46 B` | | `components/BlockFormControlLayout/script.js` | `1.8 kB` | | `components/BlockFormControlLayout/styles.css` | `234 B` | | `components/Button/index.js` | `46 B` | | `components/Button/script.js` | `9.2 kB` | | `components/Button/styles.css` | `7.1 kB` | | `components/Calendar/index.js` | `46 B` | | `components/Calendar/script.js` | `7.4 kB` | | `components/Calendar/styles.css` | `2.1 kB` | | `components/Card/index.js` | `46 B` | | `components/Card/script.js` | `2.4 kB` | | `components/Card/styles.css` | `603 B` | | `components/Checkbox/index.js` | `46 B` | | `components/Checkbox/script.js` | `3.9 kB` | | `components/Checkbox/styles.css` | `1.5 kB` | | `components/Choice/index.js` | `46 B` | | `components/Choice/script.js` | `5.7 kB` | | `components/Choice/styles.css` | `1.7 kB` | | `components/Container/index.js` | `46 B` | | `components/Container/script.js` | `4.9 kB` | | `components/Container/styles.css` | `1.2 kB` | | `components/Dialog/index.js` | `46 B` | | `components/Dialog/script.js` | `9.1 kB` | | `components/Dialog/styles.css` | `1.5 kB` | | `components/Divider/index.js` | `46 B` | | `components/Divider/script.js` | `2.7 kB` | | `components/Divider/styles.css` | `189 B` | | `components/Icon/index.js` | `46 B` | | `components/Icon/script.js` | `3.1 kB` | | `components/Icon/styles.css` | `188 B` | | `components/Image/index.js` | `46 B` | | `components/Image/styles.css` | `724 B` | | `components/ImageUploader/index.js` | `46 B` | | `components/ImageUploader/script.js` | `10.8 kB` | | `components/ImageUploader/styles.css` | `2.2 kB` | | `components/InlineFormControlLayout/index.js` | `46 B` | | `components/InlineFormControlLayout/script.js` | `2.5 kB` | | `components/InlineFormControlLayout/styles.css` | `315 B` | | `components/Input/index.js` | `46 B` | | `components/Input/script.js` | `4.3 kB` | | `components/Input/styles.css` | `2.7 kB` | | `components/Link/index.js` | `46 B` | | `components/Link/script.js` | `3.1 kB` | | `components/Link/styles.css` | `220 B` | | `components/Loading/index.js` | `46 B` | | `components/Loading/script.js` | `2.2 kB` | | `components/Loading/styles.css` | `1.2 kB` | | `components/Menu/index.js` | `46 B` | | `components/Menu/script.js` | `10.9 kB` | | `components/Menu/styles.css` | `4.2 kB` | | `components/Modal/index.js` | `46 B` | | `components/Modal/script.js` | `11.3 kB` | | `components/Modal/styles.css` | `1.1 kB` | | `components/Notice/index.js` | `46 B` | | `components/Notice/script.js` | `4.6 kB` | | `components/Notice/styles.css` | `1.1 kB` | | `components/Pill/index.js` | `46 B` | | `components/Pill/script.js` | `2.8 kB` | | `components/Pill/styles.css` | `370 B` | | `components/PinInput/index.js` | `46 B` | | `components/PinInput/script.js` | `6.3 kB` | | `components/PinInput/styles.css` | `1.5 kB` | | `components/Popover/index.js` | `46 B` | | `components/Popover/script.js` | `10.9 kB` | | `components/Popover/styles.css` | `518 B` | | `components/ProgressBar/index.js` | `46 B` | | `components/ProgressBar/script.js` | `3.5 kB` | | `components/ProgressBar/styles.css` | `1.2 kB` | | `components/ProgressCircle/index.js` | `46 B` | | `components/ProgressCircle/script.js` | `4.0 kB` | | `components/ProgressCircle/styles.css` | `760 B` | | `components/Radio/index.js` | `46 B` | | `components/Radio/script.js` | `3.6 kB` | | `components/Radio/styles.css` | `1.5 kB` | | `components/Row/index.js` | `46 B` | | `components/Row/script.js` | `2.5 kB` | | `components/Row/styles.css` | `663 B` | | `components/SegmentedControl/index.js` | `46 B` | | `components/SegmentedControl/script.js` | `3.2 kB` | | `components/SegmentedControl/styles.css` | `1.4 kB` | | `components/Select/index.js` | `46 B` | | `components/Select/script.js` | `5.3 kB` | | `components/Select/styles.css` | `3.7 kB` | | `components/Skeleton/index.js` | `46 B` | | `components/Skeleton/script.js` | `4.3 kB` | | `components/Skeleton/styles.css` | `978 B` | | `components/StarRating/index.js` | `46 B` | | `components/StarRating/script.js` | `6.2 kB` | | `components/StarRating/styles.css` | `322 B` | | `components/Stepper/index.js` | `46 B` | | `components/Stepper/script.js` | `5.7 kB` | | `components/Stepper/styles.css` | `1.2 kB` | | `components/Text/index.js` | `46 B` | | `components/Text/script.js` | `4.8 kB` | | `components/Text/styles.css` | `4.7 kB` | | `components/Textarea/index.js` | `46 B` | | `components/Textarea/script.js` | `3.6 kB` | | `components/Textarea/styles.css` | `1.9 kB` | | `components/TextButton/index.js` | `46 B` | | `components/TextButton/script.js` | `3.9 kB` | | `components/TextButton/styles.css` | `2.1 kB` | | `components/Theme/index.js` | `46 B` | | `components/Theme/script.js` | `18.2 kB` | | `components/Theme/styles.css` | `264 B` | | `components/Thumbnails/index.js` | `46 B` | | `components/Thumbnails/script.js` | `3.5 kB` | | `components/Thumbnails/styles.css` | `458 B` | | `components/Toast/index.js` | `46 B` | | `components/Toast/script.js` | `9.1 kB` | | `components/Toast/styles.css` | `2.1 kB` | | `components/Toggle/index.js` | `46 B` | | `components/Toggle/script.js` | `3.5 kB` | | `components/Toggle/styles.css` | `2.7 kB` | | `components/TouchCapture/index.js` | `25 B` | | `components/TouchCapture/script.js` | `3.5 kB` | | `components/Transition/index.js` | `25 B` | | `components/Transition/script.js` | `2.5 kB` | | `components/TransitionCollapse/index.js` | `25 B` | | `components/TransitionCollapse/script.js` | `3.1 kB` | | `components/TransitionFadeIn/index.js` | `25 B` | | `components/TransitionFadeIn/script.js` | `2.3 kB` | | `components/TransitionResize/index.js` | `25 B` | | `components/TransitionResize/script.js` | `3.6 kB` | | `components/TransitionResponsive/index.js` | `25 B` | | `components/TransitionResponsive/script.js` | `2.2 kB` | | `components/TransitionSpringLeft/index.js` | `25 B` | | `components/TransitionSpringLeft/script.js` | `2.3 kB` | | `components/TransitionSpringUp/index.js` | `25 B` | | `components/TransitionSpringUp/script.js` | `2.3 kB` | | `components/TransitionStack/index.js` | `25 B` | | `components/TransitionStack/script.js` | `2.3 kB` | | `components/TransitionStaggered/index.js` | `25 B` | | `components/TransitionStaggered/script.js` | `2.5 kB` | | `components/VerticalDivider/index.js` | `46 B` | | `components/VerticalDivider/script.js` | `1.7 kB` | | `components/VerticalDivider/styles.css` | `239 B` | | [`LICENSE`](https://github.com/square/maker/blob/fix/safari-image-loading/LICENSE) | `552 B` | | [`package.json`](https://github.com/square/maker/blob/fix/safari-image-loading/package.json) | `5.6 kB` | | [`README.md`](https://github.com/square/maker/blob/fix/safari-image-loading/README.md) | `1.0 kB` | | `utils/assert.js` | `1.0 kB` | | `utils/constants.js` | `689 B` | | `utils/css-validator.js` | `933 B` | | `utils/debug.js` | `984 B` | | `utils/get-contrast.js` | `1.4 kB` | | `utils/maker-colors.js` | `4.1 kB` | | `utils/RenderFn.js` | `766 B` | | `utils/transitions.js` | `4.2 kB` |
Hidden files | File | Before | After | | --------------------------------------------------- | --------: | --------------------------: | | `components/Accordion/script.js.map` | `21.4 kB` | `21.4 kB` | | `components/Accordion/styles.css.map` | `5.9 kB` | `5.9 kB` | | `components/ActionBar/script.js.map` | `56.0 kB` | `56.0 kB` | | `components/ActionBar/styles.css.map` | `18.9 kB` | `18.9 kB` | | `components/Badge/script.js.map` | `21.6 kB` | `21.6 kB` | | `components/Badge/styles.css.map` | `5.2 kB` | `5.2 kB` | | `components/Blade/script.js.map` | `26.7 kB` | `26.7 kB` | | `components/Blade/styles.css.map` | `5.0 kB` | `5.0 kB` | | `components/BlockFormControlLayout/script.js.map` | `8.5 kB` | `8.5 kB` | | `components/BlockFormControlLayout/styles.css.map` | `767 B` | `767 B` | | `components/Button/script.js.map` | `35.4 kB` | `35.4 kB` | | `components/Button/styles.css.map` | `18.8 kB` | `18.8 kB` | | `components/Calendar/script.js.map` | `28.7 kB` | `28.7 kB` | | `components/Calendar/styles.css.map` | `10.1 kB` | `10.1 kB` | | `components/Card/script.js.map` | `11.8 kB` | `11.8 kB` | | `components/Card/styles.css.map` | `1.6 kB` | `1.6 kB` | | `components/Checkbox/script.js.map` | `18.6 kB` | `18.6 kB` | | `components/Checkbox/styles.css.map` | `3.7 kB` | `3.7 kB` | | `components/Choice/script.js.map` | `25.1 kB` | `25.1 kB` | | `components/Choice/styles.css.map` | `7.7 kB` | `7.7 kB` | | `components/Container/script.js.map` | `19.1 kB` | `19.1 kB` | | `components/Container/styles.css.map` | `5.5 kB` | `5.5 kB` | | `components/Dialog/script.js.map` | `33.2 kB` | `33.2 kB` | | `components/Dialog/styles.css.map` | `9.4 kB` | `9.4 kB` | | `components/Divider/script.js.map` | `12.7 kB` | `12.7 kB` | | `components/Divider/styles.css.map` | `1.7 kB` | `1.7 kB` | | `components/Icon/script.js.map` | `13.7 kB` | `13.7 kB` | | `components/Icon/styles.css.map` | `2.2 kB` | `2.2 kB` | | `components/Image/script.js.map` | `23.4 kB` | -0.74%↓`23.2 kB` | | `components/Image/styles.css.map` | `5.4 kB` | -4.47%↓`5.2 kB` | | `components/ImageUploader/script.js.map` | `43.4 kB` | `43.4 kB` | | `components/ImageUploader/styles.css.map` | `19.5 kB` | `19.5 kB` | | `components/InlineFormControlLayout/script.js.map` | `12.7 kB` | `12.7 kB` | | `components/InlineFormControlLayout/styles.css.map` | `1.4 kB` | `1.4 kB` | | `components/Input/script.js.map` | `21.1 kB` | `21.1 kB` | | `components/Input/styles.css.map` | `5.7 kB` | `5.7 kB` | | `components/Link/script.js.map` | `14.2 kB` | `14.2 kB` | | `components/Link/styles.css.map` | `3.4 kB` | `3.4 kB` | | `components/Loading/script.js.map` | `11.0 kB` | `11.0 kB` | | `components/Loading/styles.css.map` | `2.3 kB` | `2.3 kB` | | `components/Menu/script.js.map` | `41.6 kB` | `41.6 kB` | | `components/Menu/styles.css.map` | `13.8 kB` | `13.8 kB` | | `components/Modal/script.js.map` | `42.1 kB` | `42.1 kB` | | `components/Modal/styles.css.map` | `16.7 kB` | `16.7 kB` | | `components/Notice/script.js.map` | `18.5 kB` | `18.5 kB` | | `components/Notice/styles.css.map` | `5.1 kB` | `5.1 kB` | | `components/Pill/script.js.map` | `13.3 kB` | `13.3 kB` | | `components/Pill/styles.css.map` | `2.3 kB` | `2.3 kB` | | `components/PinInput/script.js.map` | `24.5 kB` | `24.5 kB` | | `components/PinInput/styles.css.map` | `7.9 kB` | `7.9 kB` | | `components/Popover/script.js.map` | `38.8 kB` | `38.8 kB` | | `components/Popover/styles.css.map` | `6.9 kB` | `6.9 kB` | | `components/ProgressBar/script.js.map` | `15.6 kB` | `15.6 kB` | | `components/ProgressBar/styles.css.map` | `3.4 kB` | `3.4 kB` | | `components/ProgressCircle/script.js.map` | `17.4 kB` | `17.4 kB` | | `components/ProgressCircle/styles.css.map` | `4.2 kB` | `4.2 kB` | | `components/Radio/script.js.map` | `17.5 kB` | `17.5 kB` | | `components/Radio/styles.css.map` | `3.6 kB` | `3.6 kB` | | `components/Row/script.js.map` | `11.8 kB` | `11.8 kB` | | `components/Row/styles.css.map` | `2.0 kB` | `2.0 kB` | | `components/SegmentedControl/script.js.map` | `14.8 kB` | `14.8 kB` | | `components/SegmentedControl/styles.css.map` | `3.9 kB` | `3.9 kB` | | `components/Select/script.js.map` | `24.2 kB` | `24.2 kB` | | `components/Select/styles.css.map` | `6.8 kB` | `6.8 kB` | | `components/Skeleton/script.js.map` | `17.8 kB` | `17.8 kB` | | `components/Skeleton/styles.css.map` | `3.0 kB` | `3.0 kB` | | `components/StarRating/script.js.map` | `23.6 kB` | `23.6 kB` | | `components/StarRating/styles.css.map` | `6.8 kB` | `6.8 kB` | | `components/Stepper/script.js.map` | `22.2 kB` | `22.2 kB` | | `components/Stepper/styles.css.map` | `6.5 kB` | `6.5 kB` | | `components/Text/script.js.map` | `22.4 kB` | `22.4 kB` | | `components/Text/styles.css.map` | `11.6 kB` | `11.6 kB` | | `components/Textarea/script.js.map` | `17.9 kB` | `17.9 kB` | | `components/Textarea/styles.css.map` | `4.1 kB` | `4.1 kB` | | `components/TextButton/script.js.map` | `18.1 kB` | `18.1 kB` | | `components/TextButton/styles.css.map` | `5.2 kB` | `5.2 kB` | | `components/Theme/script.js.map` | `57.0 kB` | `57.0 kB` | | `components/Theme/styles.css.map` | `3.8 kB` | `3.8 kB` | | `components/Thumbnails/script.js.map` | `15.6 kB` | `15.6 kB` | | `components/Thumbnails/styles.css.map` | `3.0 kB` | `3.0 kB` | | `components/Toast/script.js.map` | `38.7 kB` | `38.7 kB` | | `components/Toast/styles.css.map` | `14.6 kB` | `14.6 kB` | | `components/Toggle/script.js.map` | `18.3 kB` | `18.3 kB` | | `components/Toggle/styles.css.map` | `4.5 kB` | `4.5 kB` | | `components/TouchCapture/script.js.map` | `12.0 kB` | `12.0 kB` | | `components/Transition/script.js.map` | `10.8 kB` | `10.8 kB` | | `components/TransitionCollapse/script.js.map` | `13.4 kB` | `13.4 kB` | | `components/TransitionFadeIn/script.js.map` | `10.5 kB` | `10.5 kB` | | `components/TransitionResize/script.js.map` | `14.4 kB` | `14.4 kB` | | `components/TransitionResponsive/script.js.map` | `10.3 kB` | `10.3 kB` | | `components/TransitionSpringLeft/script.js.map` | `10.6 kB` | `10.6 kB` | | `components/TransitionSpringUp/script.js.map` | `10.5 kB` | `10.5 kB` | | `components/TransitionStack/script.js.map` | `10.2 kB` | `10.2 kB` | | `components/TransitionStaggered/script.js.map` | `11.1 kB` | `11.1 kB` | | `components/VerticalDivider/script.js.map` | `8.9 kB` | `8.9 kB` | | `components/VerticalDivider/styles.css.map` | `760 B` | `760 B` | | `utils/assert.js.map` | `3.9 kB` | `3.9 kB` | | `utils/constants.js.map` | `2.5 kB` | `2.5 kB` | | `utils/css-validator.js.map` | `3.3 kB` | `3.3 kB` | | `utils/debug.js.map` | `3.4 kB` | `3.4 kB` | | `utils/get-contrast.js.map` | `5.7 kB` | `5.7 kB` | | `utils/maker-colors.js.map` | `15.6 kB` | `15.6 kB` | | `utils/RenderFn.js.map` | `2.8 kB` | `2.8 kB` | | `utils/transitions.js.map` | `16.9 kB` | `16.9 kB` |

πŸ€– This report was automatically generated by pkg-size-action

github-actions[bot] commented 1 year ago

:tada: This PR is included in version 16.2.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: