Pola kedua yang akan kita rakit adalah kartu — komponen yang menampung produk, artikel, atau profil dalam satu wadah berulang. Kartu menyimpan dua tantangan layout klasik. Pertama, kartu-kartu berdampingan harus sama tinggi meski isinya berbeda panjang. Kedua, tombol aksi di dasar kartu harus sejajar rapi meski deskripsi di atasnya jumlah barisnya berbeda. Flexbox menjawab keduanya dengan dua teknik yang sudah kita kuasai: nilai default stretch dan margin-top: auto.

Struktur Kartu #

Kartu produk terurai menjadi tiga bagian dengan peran masing-masing:

BagianElemenPeran flexbox
Gambardiv.kartu-gambarArea tetap di atas; tingginya kita kunci
Isidiv.kartu-isiArea yang tumbuh menyerap ruang kosong (flex-grow: 1)
Aksidiv.kartu-aksiBaris harga + tombol, menempel di dasar via margin-top: auto

Kuncinya ada pada kartu itu sendiri: kartu bukan sekadar kotak, melainkan flex container berarah kolom. Bagian isi diberi tumbuh, bagian aksi ditempel ke dasar — dua keputusan yang bekerja pada sumbu vertikal kartu.

Markup satu kartu terlihat seperti ini, dan tiga kartu identik dibungkus satu container deretan:

html
<div class="deretan-kartu">
  <article class="kartu-produk">
    <div class="kartu-gambar kartu-gambar--gayo"></div>
    <div class="kartu-isi">
      <h3 class="kartu-nama">Kopi Gayo</h3>
      <p class="kartu-deskripsi">
        Proses semi-washed dari dataran tinggi Aceh,
        dengan keasaman sedang dan rasa bersih.
      </p>
    </div>
    <div class="kartu-aksi">
      <span class="kartu-harga">Rp45.000</span>
      <button class="kartu-tombol" type="button">Tambah</button>
    </div>
  </article>
</div>

Berikut gayanya, dari luar ke dalam:

css
.deretan-kartu {
  display: flex;
  gap: 16px;
}

.kartu-produk {
  flex: 1;
  display: flex;
  flex-direction: column;
  border: 1px solid #d1d5db;
  border-radius: 12px;
  overflow: hidden;
  background-color: #ffffff;
}

.kartu-gambar {
  height: 96px;
  flex-shrink: 0;
}

.kartu-gambar--gayo {
  background: linear-gradient(135deg, #6b4f2a, #c49a6c);
}

.kartu-isi {
  flex-grow: 1;
  padding: 14px 16px;
}

.kartu-aksi {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-top: 1px solid #e5e7eb;
}

Mari kita telaah keputusan pentingnya:

  1. .deretan-kartu sebagai container baris: tiga kartu disusun mendatar dengan gap: 16px. Tidak ada alignment yang ditulis — dan justru itu poinnya.
  2. .kartu-produk dengan flex: 1: ketiga kartu berbagi lebar sama berkat basis 0% dari chapter Shorthand Flex.
  3. Sama tinggi datang dari default: nilai awal align-items adalah stretch, sehingga semua kartu pada baris flex melar ke tinggi kartu tertinggi. Inilah jawaban flexbox untuk “kartu sama tinggi” tanpa satu deklarasi tambahan.
  4. .kartu-isi dengan flex-grow: 1: deskripsi pendek tidak membuat area isinya pendek; area isi menyerap seluruh ruang kosong di atas bagian aksi.
  5. .kartu-aksi dengan margin-top: auto: sebagai lapisan pengaman, sisa ruang apa pun yang tersisa diserap margin atas baris aksi, sehingga harga dan tombol selalu menempel di dasar kartu — meski tinggi kartu ditentukan oleh tetangganya yang deskripsinya lebih panjang.

Demo berikut memuat tiga kartu dengan panjang deskripsi yang sengaja dibuat berbeda — perhatikan garis dasar tombolnya:

Perkecil lebar demo dan amati kartu-kartu terus berbagi lebar secara rasio.

Catatan tentang area gambar: pada proyek nyata, .kartu-gambar biasanya menampung elemen <img> asli. Berikan height pasti pada areanya dan object-fit: cover pada gambarnya agar foto mengisi area tanpa melar atau gepeng — pembahasan lengkapnya menunggu di section Images dan Media. Demo chapter ini sengaja memakai gradien agar tetap berjalan mandiri tanpa berkas gambar.

Kesalahan Umum #

KesalahanAkibatnyaPerbaikannya
Lupa menjadikan kartu sebagai container kolommargin-top: auto dan flex-grow tidak bekerja; footer mengikuti aliran biasaBeri display: flex dan flex-direction: column pada kartu
Menyetel align-items: flex-start pada deretan kartuKartu berhenti melar; tinggi tiap kartu mengikuti isinya sendiriBiarkan nilai default stretch, atau setel eksplisit align-items: stretch
Hanya mengandalkan flex-grow tanpa margin-top: auto di footerFooter ikut rapi, tetapi menempel hanya bila area di atasnya benar-benar tumbuhPasang keduanya: isi yang tumbuh plus margin-top: auto sebagai pengaman

Satu catatan tambahan dari chapter Flex Basis: jika deskripsi berisi satu kata sangat panjang, kartu bisa meluber melebihi tetangganya. Tambahkan min-width: 0 pada area isi agar kontennya boleh terpotong.

Ringkasan #

Kartu yang rapi lahir dari dua lapis flexbox: deretan kartu sebagai container baris yang membagi lebar via flex: 1 dan menyamakan tinggi via stretch, lalu tiap kartu sebagai container kolom yang area isinya tumbuh dan tombol aksinya ditempel ke dasar dengan margin-top: auto. Dua pola terakhir — navigasi dan kartu — membuktikan bahwa teori section ini cukup untuk membangun komponen nyata. Chapter penutup berikutnya merangkum semuanya menjadi kerangka keputusan: kapan flexbox adalah alat yang tepat, dan kapan sebaiknya kita beralih ke grid.