-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
622 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"docs": patch | ||
--- | ||
|
||
Working with images |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@shopware-pwa/types": minor | ||
--- | ||
|
||
Improved media types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@shopware-pwa/helpers-next": patch | ||
--- | ||
|
||
Proper access for an URL of main product image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
--- | ||
head: | ||
- - meta | ||
- name: og:title | ||
content: "Best practices: Images" | ||
- - meta | ||
- name: og:description | ||
content: "Collection of good practices to manage images." | ||
- - meta | ||
- name: og:image | ||
content: "https://frontends-og-image.vercel.app/Best%20practices:%20**Images** 🖼.png?fontSize=110px" | ||
nav: | ||
position: 30 | ||
--- | ||
|
||
# Images | ||
|
||
Best practices for images. | ||
|
||
## Optimization | ||
|
||
Let's have a look on some good practices to help display images efficiently. | ||
|
||
### Image format & Compression | ||
|
||
Compression is the first step, relatively easy to achieve in order to reduce loading time by reducing file size, thus saving network traffic for images. | ||
|
||
**WebP** is a new format for images, developed by Google, in order to add an alternative for _png_ images, but with lossy compression, enhanced by some new techniques allowing to compress with different level selectively within the same image. As it's become fully supported in all modern browsers - it can be recommended. | ||
|
||
You can check how much you can save by using `webp` format instead of others raster-images formats. See how can it help you on [Thumbor](http://thumborize.globo.com/?url=https://frontends-demo.vercel.app). | ||
|
||
:::info Test different formats | ||
There are many image formats, which have different advantages, depending on images purposes. Probably you don't need `webp` files for vector images. Sometimes, when the high image quality is important, using lossy formats may not be a good idea. It always depends on the use case. | ||
|
||
There are many tools to check different image formats, but the great one is [Squoosh](https://squoosh.app/) which allows you to experiment with images interactively: | ||
|
||
 | ||
::: | ||
|
||
### Images hosting on CDN + Image processor | ||
|
||
Using Content Delivery Network platforms (CDN) helps to reduce network distance, by serving resources from the closest server for an user. | ||
|
||
Although it can be a standalone service, some platforms serves images with additional option of resizing on the fly, or being more general: processing the images, depending on provided query parameter, like `?width=400px`. Thanks to this, `<img>` element is more readable. | ||
|
||
```html | ||
<img | ||
src="https://images.swfrontends.com/frontends-unsplash.png?width=400px" | ||
srcset=" | ||
https://images.swfrontends.com/frontends-unsplash.png?width=400px 320w, | ||
https://images.swfrontends.com/frontends-unsplash.png?width=800px 720w | ||
" | ||
> | ||
``` | ||
|
||
Examples of open source image processors which can be used as a middleware to serve processed images: | ||
* [thumbor](https://www.thumbor.org/) | ||
* [lovell/sharp](https://github.com/lovell/sharp) | ||
* [imgproxy/imgproxy](https://github.com/imgproxy/imgproxy) | ||
|
||
|
||
## Responsive images | ||
|
||
Utilize [srcset](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset) attribute for `<img>` elements in order to load the image in size what is actually needed at the moment. | ||
Decide what metric (pixel ratio - DPR or width) is more appropriate for your users when defining breakpoints. | ||
|
||
Also, consider using `sizes` attribute which will indicate what image size is best to choose - if your images occupy less than 100% of viewport. The value can be defined in percentage of viewport width (`sizes="80vw"`) or fixed value (`sizes="600px"`) regardless the device size. Read more at [mdn web docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes). | ||
|
||
```html | ||
<img | ||
sizes="50vw" | ||
srcset="frontends-header-xs.webp 600w, rontends-header-md.webp 1200w, rontends-header-xl.webp 2000w" | ||
src="rontends-header-xs.webp" | ||
alt="..."> | ||
<!-- src fallback is set to be mobile first --> | ||
``` | ||
|
||
If you application serves many image formats and there is a significant part of users with older browsers, you can use [picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) element. | ||
|
||
In this example, browser will decide which image format is available to serve, otherwise the `<img>` will be picked as a fallback. | ||
|
||
```html | ||
<picture> | ||
<source | ||
type="image/avif" | ||
srcset=" | ||
https://images.swfrontends.com/frontends-unsplash-320.avif 320w, | ||
https://images.swfrontends.com/frontends-unsplash-720.avif 720w" | ||
> | ||
<source | ||
type="image/webp" | ||
srcset=" | ||
https://images.swfrontends.com/frontends-unsplash-320.webp 320w, | ||
https://images.swfrontends.com/frontends-unsplash-720.webp 720w" | ||
> | ||
<img | ||
src="https://images.swfrontends.com/frontends-unsplash.png" | ||
alt="Logo Shopware Frontends" | ||
> | ||
</picture> | ||
``` | ||
|
||
## Reduce Cumulative Layout Shift (CLS) | ||
|
||
When Images occupy a big amount of space on web pages, they are a common cause of high [CLS](https://web.dev/cls/) scores. | ||
|
||
* Always set `width` and `hight` attributes for your `<img>` elements, with values matching size of image source. So even if they are being loaded, the space of layout will be filled out. | ||
* Define CSS style to override `<img>` attributes (there is a moment when image element is available in DOM, and CSS is not loaded yet): | ||
```css | ||
img { | ||
max-width: 100%; | ||
height: auto; | ||
} | ||
``` | ||
* Try to use low-quality placeholders (based on svg, for example) to avoid having empty blank spaces within the layout: | ||
<div role="status" class="mt-4 max-w-sm p-4 animate-pulse md:p-6 "> | ||
<div class="flex items-center justify-center h-32 mb-4 bg-gray-300 rounded dark:bg-gray-700"> | ||
<svg class="w-12 h-12 text-gray-200 dark:text-gray-600" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" viewBox="0 0 640 512"><path d="M480 80C480 35.82 515.8 0 560 0C604.2 0 640 35.82 640 80C640 124.2 604.2 160 560 160C515.8 160 480 124.2 480 80zM0 456.1C0 445.6 2.964 435.3 8.551 426.4L225.3 81.01C231.9 70.42 243.5 64 256 64C268.5 64 280.1 70.42 286.8 81.01L412.7 281.7L460.9 202.7C464.1 196.1 472.2 192 480 192C487.8 192 495 196.1 499.1 202.7L631.1 419.1C636.9 428.6 640 439.7 640 450.9C640 484.6 612.6 512 578.9 512H55.91C25.03 512 .0006 486.1 .0006 456.1L0 456.1z"/></svg> | ||
</div> | ||
</div> | ||
|
||
|
||
## Speed up Largest Contentful Paint | ||
|
||
"[LCP](https://web.dev/lcp/) element has an image on around three quarters of pages" says the result of [Web research](https://almanac.httparchive.org/en/2021/media#images). Moreover, on 70.6% mobile pages, LCP element has an image. On desktops, the rate is even bigger: 79.4%. So we can assume, that bad LCP scores are based on low image performance. | ||
|
||
* Never use `loading="lazy"` on `<img>` elements if they are part of what an user see first on they viewport (consider editing the attributes for CMS elements in Shopware Experiences). | ||
* Utilize `fetchpriority="high"` on `<img>` also tells the browser, that the asset (LCP resource is prioritized) is important and should be taken care of as fast as possible. | ||
|
||
|
||
<PageRef page="../framework/images" title="Framework" sub="How to display images served by API" /> | ||
|
||
|
||
|
||
## Resources | ||
|
||
Collection of useful blog posts and articles about performance related to images. | ||
|
||
* https://web.dev/learn/images/ | ||
* https://austingil.com/better-html-images/ | ||
* https://www.smashingmagazine.com/2023/01/optimizing-image-element-lcp/ | ||
* https://web.dev/top-cwv-2023/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
--- | ||
head: | ||
- - meta | ||
- name: og:title | ||
content: "Working with Images" | ||
- - meta | ||
- name: og:description | ||
content: "How to display images served by API" | ||
- - meta | ||
- name: og:image | ||
content: "https://frontends-og-image.vercel.app/Working%20with%20**Images**.png?fontSize=110px" | ||
nav: | ||
position: 30 | ||
--- | ||
|
||
<script setup> | ||
import StackBlitzLiveExample from "../components/StackBlitzLiveExample.vue"; | ||
</script> | ||
|
||
# Working with Images | ||
|
||
This section covers topics related to images, with a focus on what comes from API. | ||
|
||
:::warning Not auto-loaded | ||
Although images are not always contained in API responses, we try to keep the composables logic aware of that and ready to load if they are needed. | ||
|
||
Which means if you need to work with images, ensure the requests contains additional [associations](https://shopware.stoplight.io/docs/store-api/cf710bf73d0cd-search-queries#associations). | ||
|
||
Example of request's payload with media association included, to avoid an empty `media` object within the response: | ||
|
||
```json | ||
{ | ||
"associations": { | ||
"media": {} | ||
} | ||
} | ||
``` | ||
::: | ||
|
||
## Structure of media objects | ||
|
||
Media objects can be used in many places, such as: | ||
|
||
* CMS objects (containing [CmsElementImage](https://github.com/shopware/frontends/blob/main/packages/composables/src/types/cmsElementTypes.ts#L71) element) | ||
* Product (cover image, image gallery, attributes in type media, etc.) | ||
* Category (main image, ...) | ||
* ... | ||
|
||
Regardless the outer container (see [ProductMedia](https://github.com/shopware/frontends/blob/main/packages/types/shopware-6-client/models/content/product/ProductMedia.d.ts#L8) as example) an image object can be wrapped with, the inner structure is reflected in type definition at [Media](https://github.com/shopware/frontends/blob/main/packages/types/shopware-6-client/models/content/media/Media.d.ts#L23) | ||
|
||
Let's have a look what's inside: | ||
|
||
```json | ||
{ | ||
// irrelevant data omitted | ||
... | ||
"mimeType": "image/webp", // mime-type of media object, supported by the Shopware 6 platform | ||
"fileExtension": "webp", | ||
"fileSize": 492024, | ||
"title": "Frontends Logo", | ||
"metaData": { | ||
"hash": "b795091b0a92b8a0605281f710dc1c28", | ||
"type": 2, | ||
"width": 3505, // original width | ||
"height": 5258 // original height | ||
}, | ||
"alt": "Shopware Frontends", | ||
"url": "http://localhost/media/shopware-frontends-4P8HWu_NRp4-unsplash.jpg", | ||
"fileName": "shopware-frontends-4P8HWu_NRp4-unsplash", | ||
"thumbnails": [ // list of resized images for previously configured ranges | ||
{ | ||
"width": 1920, | ||
"height": 1920, | ||
"url": "http://localhost/thumbnail/ainars-cekuls-4P8HWu_NRp4-unsplash_1920x1920.webp", | ||
}, | ||
{ | ||
// omitted irrelevant data | ||
"width": 800, | ||
"height": 800, | ||
"url": "http://localhost/thumbnail/ainars-cekuls-4P8HWu_NRp4-unsplash_800x800.webp", | ||
"apiAlias": "media_thumbnail" | ||
}, | ||
... | ||
] | ||
... | ||
} | ||
``` | ||
|
||
The media object, and its `thumbnails` list, contain all required information about the file to be used in the browser like URL and sizes. | ||
|
||
## Thumbnails and resolutions | ||
|
||
By default, every uploaded image is resized to the predefined width and height sizes (in pixels): | ||
* 1920x1920 | ||
* 800x800 | ||
* 400x400 | ||
|
||
In order to change those sizes, or add another one (also the quality, or to keep aspect ratio), the values need to be adjusted in administration panel, for specific media folder. | ||
|
||
 | ||
|
||
:::warning Image processing | ||
While a file is uploaded, it's been automatically resized for the current configuration in Administration > Media section. Thanks to this, the newly uploaded files will be available for all required dimensions. However keep in mind that if your settings have changes, the new dimensions won't be applied automatically for the old images. | ||
::: | ||
|
||
## Helpers | ||
|
||
There are few functions that could be used to extract some crucial information about the media in short way. Browse [Helpers > Media](../packages/helpers/index.html#media) category to see them all. | ||
|
||
Example how to work with Product's main image: | ||
|
||
```ts | ||
import type { Product } from "@shopware-pwa/types"; | ||
import { getMainImageUrl } from "@shopware-pwa/helpers-next"; | ||
|
||
|
||
const coverUrl = getMainImageUrl(product as Product); | ||
// coverUrl is now an URL to the resource (or undefined) | ||
``` | ||
|
||
## Responsive Images | ||
|
||
Having additional information about resized images (see `thumbnails` array in `Media` object), we are able to use them to define [srcset](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset) attribute for `<img>`. | ||
|
||
```vue{8} | ||
<script> | ||
import type { Product, Media } from "@shopware-pwa/types"; | ||
const product: Product = {} // an object omitted | ||
// get the cover media image (main image for a product) | ||
const coverMedia = product.cover?.media as Media | ||
// prepare `srcset` string for available thumbnails | ||
// let the breakpoints be for every width range | ||
const srcset = coverMedia?.thumbnails?.map((thumb) => `${thumb.url} ${thumb.width}w`).join(", ") | ||
</script> | ||
<template> | ||
<img | ||
:srcset="srcset" | ||
:src="coverMedia?.url" | ||
:alt="coverMedia?.alt" | ||
:title="coverMedia?.title" | ||
> | ||
</template> | ||
``` | ||
|
||
### Live example | ||
Have a look on live example: | ||
<StackBlitzLiveExample projectPath="shopware/frontends/tree/main/examples/responsive-images" openPath="/" /> | ||
<br/> | ||
|
||
The example above shows how to use dimension sizes configured in admin panel as ranges for viewport. However it can be adjusted to your needs. | ||
|
||
The `src` attribute points to the main image URL (not resized) as a fallback. | ||
|
||
As long as `thumbnails` array is fulfilled, the same strategy can be applied when we work with every `media` object for each entity available in Shopware 6. | ||
|
||
|
||
<PageRef page="../best-practices/images" title="Best Practices" sub="Best Practices to work with images" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
.DS_Store | ||
dist | ||
dist-ssr | ||
coverage | ||
*.local | ||
|
||
/cypress/videos/ | ||
/cypress/screenshots/ | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
Oops, something went wrong.