Menu docs

Komponen · · 2 menit baca

Komponen e-commerce: kartu produk, badge diskon & rating bintang

Kode lengkap kartu produk toko online: rating bintang dua lapis tanpa gambar, aspect-ratio yang mencegah layout shift, line-clamp, dan state habis stok — editor + preview live berdampingan.

HTMLCSSE-commerceA11y
Contoh liveDirender langsung oleh komponennya — bukan gambar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<div class="grid">
  <article class="card">
    <div class="card__media">
      <span class="card__badge">-20%</span>
    </div>
    <div class="card__body">
      <p class="card__kicker">Arabika</p>
      <h3 class="card__name">Kopi Gayo Premium 250g</h3>

      <!-- Rating: 1 elemen isi di atas 1 elemen dasar, lebar = persen -->
      <span class="rating" role="img" aria-label="4,2 dari 5">
        <span class="rating__base">★★★★★</span>
        <span class="rating__fill" style="width: 84%">★★★★★</span>
      </span>

      <p class="card__price">
        <strong>Rp 85.000</strong>
        <s>Rp 106.000</s>
      </p>
      <button class="btn" type="button">+ Keranjang</button>
    </div>
  </article>

  <article class="card card--out">
    <div class="card__media">
      <span class="card__badge card__badge--out">Habis</span>
    </div>
    <div class="card__body">
      <p class="card__kicker">Robusta</p>
      <h3 class="card__name">Robusta Temanggung 500g</h3>
      <span class="rating" role="img" aria-label="5 dari 5">
        <span class="rating__base">★★★★★</span>
        <span class="rating__fill" style="width: 100%">★★★★★</span>
      </span>
      <p class="card__price"><strong>Rp 62.000</strong></p>
      <button class="btn" type="button" disabled>Stok habis</button>
    </div>
  </article>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
}

.card {
  border: 1px solid #e4e4e7;
  background: #fff;
  display: flex;
  flex-direction: column;
}

/* Rasio terkunci: grid tidak "loncat" saat gambar termuat */
.card__media {
  position: relative;
  aspect-ratio: 1 / 1;
  background: #fafafa;
}

.card__badge {
  position: absolute;
  top: 10px;
  left: 10px;
  background: #0d9488;
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
}
.card__badge--out { background: #71717a; }

.card__body { padding: 14px; }

.card__kicker {
  margin: 0;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #71717a;
}

.card__name {
  margin: 6px 0 8px;
  font-size: 14px;
  line-height: 1.35;
  /* Nama panjang tidak merusak tinggi kartu */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Rating bintang tanpa gambar & tanpa JavaScript */
.rating {
  position: relative;
  display: inline-block;
  font-size: 13px;
  line-height: 1;
  color: #d4d4d8;
}
.rating__fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  white-space: nowrap;
  color: #f59e0b;
}

.card__price {
  margin: 10px 0 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.card__price strong { font-size: 17px; }
.card__price s { font-size: 12px; color: #a1a1aa; }

.btn {
  margin-top: 12px;
  width: 100%;
  border: 0;
  background: #09090b;
  color: #fff;
  font-size: 12px;
  padding: 9px 12px;
  cursor: pointer;
}
.btn:hover { background: #0f766e; }
.btn:disabled {
  background: #f4f4f5;
  color: #a1a1aa;
  cursor: not-allowed;
}

Preview

Kartu produk adalah komponen paling banyak dilihat di sebuah toko online — ia muncul puluhan kali di setiap halaman katalog. Editor di atas berisi kode lengkapnya, dan panel kanan menjalankan kode itu juga: yang kamu baca persis yang kamu lihat.

Rating bintang tanpa gambar dan tanpa JavaScript

Trik yang kupakai di semua project: dua lapis teks bintang yang identik, satu di atas yang lain. Lapisan bawah abu-abu (★★★★★), lapisan atas berwarna emas dan dipotong dengan width persen:

<span class="rating" role="img" aria-label="4,2 dari 5">
  <span class="rating__base">★★★★★</span>
  <span class="rating__fill" style="width: 84%">★★★★★</span>
</span>

4,2 dari 5 = 84%. Rating setengah bintang bekerja otomatis, tanpa sprite, tanpa SVG, tanpa perhitungan di JavaScript. role="img" + aria-label membuat screen reader membacanya sebagai satu nilai, bukan sepuluh karakter bintang.

Tiga detail yang menjaga grid tetap rapi

  • aspect-ratio: 1 / 1 pada area gambar. Tanpa ini, kartu memendek sebelum gambar termuat lalu “meloncat” — layout shift yang merusak Core Web Vitals dan terasa murah.
  • -webkit-line-clamp: 2 pada nama produk. Nama panjang dipotong di dua baris, jadi semua kartu dalam satu baris grid punya tinggi yang sama.
  • grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)). Jumlah kolom menyesuaikan sendiri tanpa satu pun media query.

Status habis stok: nonaktifkan, jangan sembunyikan

Produk habis tetap ditampilkan (SEO dan pengunjung yang mau menunggu restock), tapi tombolnya disabled dan badge-nya berubah abu-abu. Yang penting: keadaan itu dikomunikasikan lewat atribut disabled, bukan hanya warna — tombol non-aktif otomatis dilewati keyboard dan diumumkan screen reader.

Harga: satu format, satu helper

Perhatikan struktur harga — harga aktif <strong>, harga coret <s>. Elemen <s> punya makna semantik “tidak lagi berlaku”, tepat untuk harga sebelum diskon. Formatnya selalu lewat helper Intl.NumberFormat yang sama (lihat build note Katalog + keranjang), jangan pernah diformat manual per tempat.

Pelajaran

  • Efek visual yang terlihat “butuh library” sering hanya butuh dua elemen dan satu overflow: hidden.
  • Kunci rasio dan tinggi teks sejak awal — layout shift adalah bug yang paling sering dianggap “wajar” padahal paling terasa.
  • Setiap state (habis, diskon, rating) harus punya penanda non-visual, bukan cuma warna.

Mau fitur seperti ini dibangun untuk bisnismu?