Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Адаптивні зображення: picture, srcset та sizes». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Адаптивні зображення** дають браузеру змогу самостійно обрати потрібний файл через `srcset`, `sizes` і `picture`. ```html <img src="photo-small.jpg" srcset="photo-small.jpg 400w, photo-medium.jpg 800w, photo-large.jpg 1600w" sizes="(max-width: 640px) 100vw, 50vw" alt="Фото" > ``` **Ключове:** `srcset` + `sizes` для масштабування одного зображення. `picture` коли кадр або формат змінюється залежно від пристрою.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Адаптивні зображення** дають браузеру змогу самостійно обрати потрібний файл для кожного пристрою, враховуючи ширину вікна, щільність пікселів і підтримку форматів. ## Теорія ### TL;DR - `srcset` + `sizes`: перелічуєш доступні файли з їхніми розмірами, кажеш браузеру скільки місця займе зображення на екрані — він рахує і обирає сам. - `picture`: ти пишеш явні media queries, браузер точно їх виконує. Ти контролюєш, яке зображення завантажується на кожному breakpoint. - Одне зображення різних розмірів → `srcset`. Різні кадри або формати для різних пристроїв → `picture`. - Телефон 375px з 2x екраном потребує файл 750px, а не 375px. Браузер розраховує це через `sizes`. - Аналогія: `srcset` — меню з порціями, де офіціант сам обирає під твої потреби. `picture` — коли ти вказуєш пальцем на конкретну страву. ### Швидкий приклад ```html <!-- Браузер обирає зображення залежно від вікна і щільності пікселів --> <img src="photo-small.jpg" srcset="photo-small.jpg 400w, photo-medium.jpg 800w, photo-large.jpg 1600w" sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" alt="Фото продукту" > <!-- Телефон 375px (1x): photo-small.jpg (100vw = 375px, 400w підходить) --> <!-- Телефон 375px (2x): photo-medium.jpg (375 x 2 = 750px потрібно, 800w) --> <!-- Десктоп 1920px: photo-large.jpg (33vw = 640px, 1600w покриє 2x) --> ``` Атрибут `src` — запасний варіант для старих браузерів, що ігнорують `srcset`. ### Ключова різниця `srcset` з дескрипторами ширини каже браузеру, які файли є і яка ширина кожного. `sizes` каже, скільки місця зображення займе на екрані. Браузер комбінує обидва атрибути і обирає файл для завантаження. `picture` працює інакше: перебирає елементи `<source>` по media queries і завантажує перший, що збігся, а `srcset` всередині кожного source обробляє варіанти роздільної здатності. `srcset` для масштабування. `picture` коли сам вміст зображення змінюється. ### Коли що використовувати - `srcset` + `sizes`: фото товарів, зображення в блозі, hero-зображення, що пропорційно масштабуються - `picture`: hero-зображення з різними кадрами на мобільному і десктопі, перемикання форматів (WebP із запасним JPEG), коли вміст зображення змінюється залежно від пристрою - Обидва разом: `picture` вирішує який кадр показати, `srcset` всередині кожного `<source>` вирішує яку роздільну здатність цього кадру завантажити - Один `src`: SVG, іконки, дрібні декоративні зображення, що не змінюються залежно від пристрою ### Порівняльна таблиця | Аспект | `srcset` + `sizes` | Елемент `picture` | Один `src` | |--------|--------------------|--------------------|------------| | Використання | Одне зображення різних розмірів | Різні зображення per breakpoint | Статичне, незмінне | | Хто вирішує? | Браузер (viewport + DPR) | Ти (через media queries) | Немає вибору | | Економія трафіку | Висока | Висока | Немає | | Art direction | Ні | Так | Ні | | Перемикання форматів | Ні | Так | Ні | | Підходить для | Фото, галереї | Різні кадри, WebP/JPEG | Іконки, SVG | ### Як браузер обирає зображення Коли браузер бачить `srcset` + `sizes`, він читає `sizes`, щоб дізнатись ширину відображення в CSS-пікселях, множить на pixel ratio пристрою і шукає найменший файл у `srcset`, що покриє потрібну кількість пікселів без пікселізації. Для `picture` браузер перебирає елементи `<source>` зверху вниз, зупиняється на першому, де збіглися `media` і `type`, потім запускає той самий алгоритм для `srcset` всередині цього source. Рішення кешується і не переглядається без суттєвої зміни viewport. Важливий нюанс: браузер може враховувати кешовані файли і мережеві умови. Якщо великий файл вже є в кеші, він може використати його замість завантаження теоретично оптимального варіанта. ### Типові помилки **Помилка 1: Не вказаний атрибут `sizes`** ```html <!-- Неправильно: браузер вважає що зображення займає 100vw --> <img src="photo.jpg" srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w" alt="Фото" /> <!-- Правильно: описуємо реальну ширину відображення --> <img src="photo.jpg" srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w" sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" alt="Фото" /> ``` Без `sizes` мініатюра на десктопі 1440px викличе завантаження повнорозмірного файлу. Витрата трафіку цілком реальна. **Помилка 2: Змішування дескрипторів ширини і щільності пікселів** ```html <!-- Неправильно: браузер ігнорує весь srcset --> <img src="logo.png" srcset="logo.png 1x, logo-2x.png 2x, logo-wide.png 800w" alt="Логотип" /> <!-- Правильно: один тип дескрипторів --> <img src="logo.png" srcset="logo.png 1x, logo-2x.png 2x" alt="Логотип" /> ``` Дескриптори ширини (`400w`) потребують `sizes`. Дескриптори щільності (`2x`) абсолютні і не потребують. Якщо їх змішати, браузер повернеться до `src`. **Помилка 3: `sizes` не відповідає CSS** ```html <!-- Неправильно: CSS обмежує зображення 600px, а sizes каже 100vw --> <style>img { width: 100%; max-width: 600px; }</style> <img src="photo.jpg" srcset="photo-400.jpg 400w, photo-1200.jpg 1200w" sizes="100vw" alt="Фото" /> <!-- На десктопі 1440px: браузер завантажить photo-1200.jpg, --> <!-- але CSS відобразить його у 600px. Трафік витрачено даремно. --> <!-- Правильно: sizes відповідає CSS --> <img src="photo.jpg" srcset="photo-400.jpg 400w, photo-1200.jpg 1200w" sizes="(max-width: 640px) 100vw, 600px" alt="Фото" /> ``` **Помилка 4: `picture` для простого масштабування** ```html <!-- Надмірно складно для завдання, яке вирішує srcset --> <picture> <source media="(max-width: 640px)" srcset="photo-small.jpg" /> <source media="(max-width: 1024px)" srcset="photo-medium.jpg" /> <img src="photo-large.jpg" alt="Фото" /> </picture> <!-- Простіше і гнучкіше --> <img src="photo-small.jpg" srcset="photo-small.jpg 400w, photo-medium.jpg 800w, photo-large.jpg 1600w" sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" alt="Фото" /> ``` Закодовані media queries в `picture` перестають працювати для пристроїв поза очікуваними breakpoints. `srcset` дає браузеру адаптуватись до будь-якого екрану автоматично. **Помилка 5: Очікування, що телефон завантажить найменший файл** Телефон 375px з 2x екраном завантажить файл 800w, а не 400w. Математика: `375 x 2 = 750px` потрібно, тому браузер обирає 800w як найменший варіант без погіршення якості. Це правильна поведінка, не баг. При генерації файлів завжди враховуй DPR. ### Де зустрічається в реальних проєктах - Next.js компонент `<Image>`: генерує `srcset` автоматично, ти тільки вказуєш `sizes` і розміри - Shopify: `picture` + `srcset` для мобільного кадру і перемикання на WebP - WordPress з плагінами типу Smush: автоматично додають `srcset` до всіх завантажених зображень - Gatsby `gatsby-plugin-image`: генерує `srcset` із розмитими (blur) заповнювачами - Cloudinary та Imgix: CDN-сервіси, що генерують URL для `srcset` без ручного створення файлів - CSS фони: `image-set()` для тієї ж ідеї в CSS (`background-image: image-set(url(photo.jpg) 1x, url(photo-2x.jpg) 2x)`) ### Питання на співбесіді **Q:** Чому не можна змішувати дескриптори ширини (`400w`) і щільності пікселів (`2x`) в одному `srcset`? **A:** Кожен тип використовує різний алгоритм вибору. Дескриптори ширини потребують `sizes` для розрахунку ширини відображення. Дескриптори щільності абсолютні і не використовують `sizes`. Комбінація створює неоднозначність, яку браузер не вирішує, тому весь `srcset` ігнорується і браузер завантажує `src`. **Q:** Якщо я використовую `picture` з media queries, чи потрібний `srcset` всередині `<source>`? **A:** Так. `picture` вирішує, яку версію зображення використати (art direction). `srcset` всередині `<source>` обробляє варіанти роздільної здатності для цієї версії. На телефоні 2x браузер обирає мобільний source через `picture`, потім `srcset` обирає 2x варіант цього мобільного зображення. **Q:** Телефон завантажив більше зображення, ніж очікувалось. Що перевіряєш першим? **A:** Device pixel ratio. Більшість сучасних телефонів мають 2x або 3x, тому браузер запитує 750 до 1125px. Відкрий Chrome DevTools, вкладку Network, відфільтруй по Img і перевір який файл з `srcset` реально завантажився. **Q:** Що станеться, якщо жоден `<source>` в `<picture>` не збіжиться? **A:** Браузер використає `<img>` всередині `<picture>` як запасний варіант. Саме тому `<img>` обов'язковий всередині `<picture>`, а не опціональний. **Q:** (Рівень Senior) Дизайнер передав зображення 3000x2000px для адаптивної сторінки. Як підходиш до задачі? **A:** Перше питання: вміст зображення змінюється залежно від пристрою чи тільки розмір? Якщо тільки розмір — `srcset` + `sizes`. Якщо мобільний потребує портретний кадр — `picture` для art direction. Далі рахую реальні ширини відображення: мобільний 100vw (~375px), планшет 50vw (~512px), десктоп 33vw (~640px). Генерую файли 1x і 2x для кожного: шість файлів. Автоматизую через CDN або інструмент збірки, не вручну. Перевіряю в DevTools, що кожен пристрій завантажує правильний файл. ## Приклади ### srcset з sizes для товарної сітки ```html <!-- Повна ширина на мобільному, половина на планшеті, третина на десктопі --> <img src="product-800.jpg" srcset=" product-400.jpg 400w, product-800.jpg 800w, product-1600.jpg 1600w " sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" alt="Сині кросівки для бігу" > <!-- Телефон 375px (1x) → product-400.jpg (375px потрібно) --> <!-- Телефон 375px (2x) → product-800.jpg (750px потрібно) --> <!-- Десктоп 1440px (1x) → product-800.jpg (1440 x 0.33 = ~475px) --> <!-- Десктоп 1440px (2x) → product-1600.jpg (~950px потрібно) --> ``` Значення `sizes` повинні відповідати реальному CSS. Якщо сітка змінюється з трьох колонок на чотири, оновлюй і `sizes`. ### picture для art direction і WebP ```html <!-- Hero: портретний кадр на мобільному, ландшафт на десктопі, WebP де підтримується --> <picture> <!-- Мобільний: портретний кадр, WebP --> <source media="(max-width: 640px)" type="image/webp" srcset="hero-mobile.webp 375w, hero-mobile@2x.webp 750w" /> <!-- Мобільний: портретний кадр, JPEG --> <source media="(max-width: 640px)" srcset="hero-mobile.jpg 375w, hero-mobile@2x.jpg 750w" /> <!-- Десктоп: повний ландшафт, WebP --> <source type="image/webp" srcset="hero-desktop.webp 1200w, hero-desktop@2x.webp 2400w" /> <!-- Запасний для браузерів без підтримки picture --> <img src="hero-desktop.jpg" alt="Команда в офісі" /> </picture> <!-- Chrome на мобільному → hero-mobile.webp (або @2x) --> <!-- Safari на мобільному → hero-mobile.jpg --> <!-- IE11 → hero-desktop.jpg (ігнорує picture) --> ``` Браузер перебирає `<source>` по порядку. Перший, де збіглися і `media`, і `type`, виграє. ### Перемикання форматів без зміни кадру ```html <!-- Той самий кадр, просто WebP для браузерів що підтримують --> <picture> <source type="image/webp" srcset="photo.webp 400w, photo-2x.webp 800w, photo-3x.webp 1200w" sizes="(max-width: 640px) 100vw, 50vw" /> <img src="photo.jpg" srcset="photo.jpg 400w, photo-2x.jpg 800w, photo-3x.jpg 1200w" sizes="(max-width: 640px) 100vw, 50vw" alt="Пейзажне фото" /> </picture> <!-- Chrome, Firefox, сучасний Edge → WebP (зазвичай на 25-35% менший за JPEG) --> <!-- Safari 13 і старіший → JPEG через img srcset --> <!-- IE11 → photo.jpg із img src --> ``` Цей патерн — стандартний спосіб подавати сучасні формати з безпечним запасним варіантом. Серверна логіка не потрібна.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.