Выравнивание по низу карточки

При верстке карточек товаров для каталога или аналогичных блоков, описание в карточке может значительно отличаться по объему текста. В результате, контент после этого текста располагается на разной высоте, что в большинстве случаев неприемлемо. Как это исправить?

'1'

Варианты решения

Возможные, но некорректные решения

Первый вариант - блоку с текстом можно задать фиксированную высоту, но это будет некорректно, так как неизвестно, какой объем текста будет использоваться для описания.

Второй вариант - блоку, который нужно прижать к низу карточки, присвоить position: absolute; bottom: 0; и внизу карточки сделать увеличенный внутренний отступ padding, но опять же вариант некорректен, так как придется надеяться, что этого отступа всегда будет хватать.

Корректное решение

Для корректного решения данной задачи воспользуемся display: flex

Рассмотрим простой пример

'2'

<div class="simple">
  <div class="container">
    <div class="simple__flex">
      <div class="simple__card">
        <div class="simple__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur accusantium corrupti reiciendis nisi voluptatum nobis vitae et, quo quaerat excepturi tempora, libero perspiciatis quae voluptates qui dolores autem doloribus ab.</div>
        <div class="simple__button button"><a href="">Button</a></div>
      </div>
      <div class="simple__card">
        <div class="simple__text">Lorem ipsum dolor sit amet</div>
        <div class="simple__button button"><a href="">Button</a></div>
      </div>
      <div class="simple__card">
        <div class="simple__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum voluptate vero ipsa praesentium autem nihil.</div>
        <div class="simple__button button"><a href="">Button</a></div>
      </div>
      <div class="simple__card">
        <div class="simple__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum voluptate vero ipsa praesentium autem nihil. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum voluptate vero ipsa praesentium autem nihil.</div>
        <div class="simple__button button"><a href="">Button</a></div>
      </div>
    </div>
  </div>
</div>

Есть замечательное свойство flex-grow: 1;, которое позволяет элементу растягиваться и занимать все доступное свободное пространство в родительском блоке, а также есть сокращенная запись этого свойства - flex: 1;

Подробнее про flex

Чтобы свойство flex: 1; для блока <div class="simple__text"></div> заработало, необходимо родительскому блоку <div class="simple__card"></div> присвоить display: flex; и flex-direction: column; чтобы элементы располагались вертикально

Далее, если в ряду карточек у текста одинаковое количество строк, блоки с текстом будут одинаковой высоты, и нижний контент итак будет выровнен по низу карточки

Но если в ряду карточек имеются карточки с разным количеством текста, то карточки растягиваются по высоте самой высокой в ряду и внизу карточки образуется свободное пространство, так как у родительского блока <div class="simple__flex"></div> установлено display: flex;, a у него по умолчанию align-items: stretch;

Это свободное пространство занимаем блоком <div class="simple__text"></div> с помощью свойства flex: 1;, а нижний блок с контентом прижимается к низу карточки, чего мы и добивались

.simple__flex {
  display: flex;
  justify-content: space-between;
}
.simple__card {
  padding: 30px;
  width: calc(25% - 10px);
  display: flex;
  flex-direction: column; /* располагаем <div class="simple__text"></div> и <div class="simple__button"></div> вертикально */
}
.simple__text {
  flex: 1; /* растягиваем <div class="simple__text"></div> на все доступное свободное пространство в карточке <div class="simple__card"></div> */
}
.simple__button {
  margin-top: 15px;
}


Более сложный пример с адаптивной сеткой

'3'

Посмотрите пример на Codepen

Полный код примера

<section class="catalog">
  <div class="container">
    <div class="catalog__flex">
      <div class="catalog__row">
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1080" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog One</div>
              <div class="card__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur, maiores.</div>
              <div class="card__bottom">
                <div class="card__cost">$49</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1081" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog Two</div>
              <div class="card__text">Consectetur adipisicing elit</div>
              <div class="card__bottom">
                <div class="card__cost">$19</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1082" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog Three</div>
              <div class="card__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus, ipsum. Lorem ipsum dolor sit amet.</div>
              <div class="card__bottom">
                <div class="card__cost">$89</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1083" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog Four</div>
              <div class="card__text">Consectetur adipisicing elit</div>
              <div class="card__bottom">
                <div class="card__cost">$39</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1084" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog Five</div>
              <div class="card__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Modi et vel pariatur laborum sed nihil quisquam, neque rerum atque minus!</div>
              <div class="card__bottom">
                <div class="card__cost">$69</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
        <div class="catalog__col">
          <div class="catalog__card card">
            <div class="card__image"><img src="https://picsum.photos/1920/1085" alt=""/></div>
            <div class="card__body">
              <div class="card__title">Catalog Six</div>
              <div class="card__text">Consectetur adipisicing elit onsectetur adipisicing elit</div>
              <div class="card__bottom">
                <div class="card__cost">$99</div>
                <div class="card__button button"><a href="">Buy</a></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
@import url("https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;700&display=swap");
*,
*::before,
*::after {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  font-family: 'Rubik', sans-serif;
}
.container {
  padding: 0 30px;
  max-width: 1200px;
  margin: 0 auto;
}
.card {
  border-radius: 15px;
  overflow: hidden;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #fff;
}
.card__image {
  height: 200px;
  overflow: hidden;
  width: 100%;
  border-radius: 15px;
}
.card__image img {
  display: block;
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
  object-fit: cover;
}
.card__body {
  padding: 15px;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.card__title {
  font-size: 24px;
  text-align: center;
  margin-bottom: 15px;
}
.card__text {
  text-align: center;
  font-size: 16px;
  flex: 1;
  margin-bottom: 15px;
  font-weight: 300;
}
.card__bottom {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
}
.card__cost {
  font-size: 24px;
  color: #4a60d7;
  font-weight: 300;
}
.button {
  text-align: center;
}
.button a {
  display: inline-flex;
  padding: 0 15px;
  background: #4a60d7;
  align-items: center;
  justify-content: center;
  text-align: center;
  height: 40px;
  width: 100px;
  color: #fff;
  border-radius: 5px;
  text-decoration: none;
  transition: all 0.6s ease;
}
.button a:hover {
  background: #40ffdc;
}
.catalog {
  background: #f1f1f1;
  padding: 60px 0;
}
.catalog__title {
  margin-bottom: 60px;
}
.catalog__row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 -15px -30px;
}
.catalog__col {
  width: 33.33333333%;
  padding: 0 15px 30px;
}
@media (max-width: 991.98px) {
  .container {
    padding: 0 15px;
  }
  .popular__col {
    width: 50%;
  }
}
@media (max-width: 767.98px) {
  .catalog__col {
    width: 100%;
  }
}



Полезные ссылки

Подробнее про адаптивную сетку

MDN web docs - flex

MDN web docs - flex-grow