Создание простой карточки товара для интернет-магазина
Универсальная, адаптивная карточка товара — сложный блок, который может содержать много элементов в разных состояниях. Нужно продумывать всё многообразие сочетаний элементов и их состояний
Я думаю подготовить лонгрид про построение «взрослой» карточки, но это будет сильно позже. Сейчас хочу кратко описать примитивную конструкцию и на её примере обозначить вектор мысли относительно проектирования компонента
Задача
Сверстать карточку товара, которая будет содержать в себе:
- изображение;
- название товара;
- краткое описание;
- цену;
- кнопку добавления в корзину.
Карточка должна уметь трансформироваться из ячейки сетки в строковый блок без изменения структуры, сменой класса.
Общая структура
Карточка — .product
Содержимое делим на два больших контейнера: __ main и __aside. Первый представляет собой большой элемент-ссылку, ведущую на страницу товара. Второй содержит вспомогательные интерактивные элементы. В примере такой один, кнопка «В корзину», на практике может быть больше, например кнопки добавления в избранное или сохранения в закладках.
Внутри контейнера __main размещаем два дополнительных ящика: один для фотографии товара, второй для названия и описания.
Схематично структура выглядит так:

В разметке эдак:
<div class="product product_grid">
<a href="/" class="product__main">
<div class="product__outer-image-container"></div>
<div class="product__info-container"></div>
</a>
<div class="product__aside"></div>
</div>
Основной контейнер
.product__main — основной контейнер, ссылка, содержащая в себе фотографию, название товара и его краткое описание.
Я считаю плохим решением оборачивать фотографию и заголовок в отдельные ссылки. В таком случае площадь кликабельного поля снижается, между линками появляются провалы, переход на страницу товара требует внимательного прицеливания.
Недостатком большой ссылки-контейнера является усложнение выделения текста, но с этим, на мой взгляд, можно смириться.

Изображение
Контейнер удовлетворяет следующим условиям:
- изображению позволено иметь любые пропорции (минус головная боль контент-менеджеру);
- оно вставляется через тег
<img>, без использованияbackground.
Приведённый способ хорошо работает на сайтах где большая часть изображений товаров — предмет на белом фоне. В случае если вставлена обычная фотография и её пропорции не совпадают с пропорциями контейнера, будут появляться поля. Это не так эстетично, но не очень страшно.
Контейнер состоит из трёх секций:
<div class="product__outer-image-container">
<div class="product__medium-image-container">
<div class="product__internal-image-container">
<img src="https://nikolaywerner.ru/files/codepen/product.jpg" alt="" class="product__image">
</div>
</div>
</div>
Внешней секции прописываем position: relative и, опционально, ширину.
Средней секции указываем ширину в 100%, нулевую высоту и размер нижнего поля в процентах. padding-bottom: 70%; Так мы получим нижнее поле пропорциональное ширине секции.
Внутренней секции назначаем 100% высоту и ширину и абсолютное позиционирование. Также я предпочитаю делать эту секцию флекс-контейнером и указывать вертикальное и горизонтальное выравнивание внутри по центру.
Последний элемент — само изображение. Ему важно назначить максимальную ширину и высоту в 100%. Не width и height, а max-width и max-height. Это позволит фотографии, в зависимости от ориентации занять внутри максимально возможное пространство. Имэдж портретной ориентации упрётся в «пол» и «потолок» блока, фото пейзажной ориентации упрётся в «стены».

Итого стили выглядят примерно так:
.product__outer-image-container {
position: relative;
}
.product__medium-image-container {
width: 100%;
height: 0;
padding: 0 0 70%;
}
.product__internal-image-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
}
.product__image {
max-width: 100%;
max-height: 100%;
}
Изменение способов отображения
Способы отображения: .product_grid — сетка, .product_row — строка.
В первом случае элементы просто выстраиваются в колонку и занимают всю ширину .product
Строчная вёрстка чуть сложнее. В ней наша карточка становится флекс-контейнером. Аналогичным образом изменяется свойство display и у .product__main. Внутри основного контейнера жёстко задана ширина внешней обёртки изображения, а flex: 1; для блока с заголовком и описанием указывает занять оставшееся свободное пространство.
На узких экранах мобильных устройств строковая карточка может чрезмерно сжаться, даже несмотря на уменьшение кегля текста и размеров изображение. Поэтому самым простым вариантом адаптации для мобильного будет банальное выстраивание элементов в колонку, подобно карточке для сетки.
Отступы
Отступы между элементами предпочитаю задавать через соседние селекторы.
Допустим, между изображением и контейнером для описания требуется зазор в 12 пикселей. Можно задать верхний отступ для описания, но если вдруг у нас будет карточка без фотки (мало ли), сверху появится паразитный маргин. Если задать нижний отступ для изображения, скорее всего ничего страшного не случится (без названия карточек обычно не бывает), но я всё же предпочитаю соседние селекторы. С их помощью можно точно сказать: «описание, стоящее ровно после фотки — получай верхний маргин».
.product__outer-image-container + .product__info-container {
margin: 12px 0 0;
}
И под конец статьи повторю: не стоит слепо копировать данную инструкцию. Описанная карточка не учитывает много деталей, которые могут всплыть в том или ином контексте применения. Речь о некоторых базовых принципах, описанных на примере примитивного варианта компонента.
Посмотреть пример на кодпене.