Mata pengguna dasbor keuangan mencari tiga angka lebih dulu: berapa isinya sekarang, berapa yang masuk, dan berapa yang keluar. Bagian ini merakit ketiganya menjadi kartu ringkasan yang ditempatkan di puncak area utama. Di balik tampilannya yang sederhana ada tiga teknik yang patut dicontoh ke proyek lain: grid adaptif tanpa satu pun media query, tipografi angka tabular-nums agar digit Rupiah sejajar, dan panah tren yang digambar CSS lewat ::before tanpa ikon gambar.

Deretan Kartu yang Beradaptasi #

Tiga kartu berdiri di atas satu deklarasi grid:

css
.dasbor-ringkasan {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--jarak-4);
}

Fungsi minmax(210px, 1fr) memberi tiap kartu lebar minimum 210 px; auto-fit membuat browser menghitung sendiri berapa kolom yang muat. Pada layar lebar ketiganya berdampingan, pada tablet bisa tersisa dua, dan pada ponsel kartu menumpuk satu kolom — semuanya tanpa media query. Identitas tiap kartu datang dari garis tebal empat piksel di tepi kirinya:

css
.kartu-ringkasan {
  display: grid; gap: 2px; padding: var(--jarak-4) var(--jarak-5);
  border: 1px solid var(--warna-garis); border-left: 4px solid var(--warna-utama);
}
.kartu-ringkasan--masuk { border-left-color: var(--warna-masuk); }
.kartu-ringkasan--keluar { border-left-color: var(--warna-keluar); }

Token --warna-utama, --warna-masuk, dan --warna-keluar yang sama dipakai ulang oleh lencana status di Bagian 4, sehingga bahasa visual dasbor konsisten dari atas ke bawah.

Angka sebagai Tokoh Utama dengan tabular-nums #

Hierarki visual kartu ini sengaja timpang: label kecil dan redup, angka besar dan tebal. Perbedaan peran itu diterjemahkan langsung ke ukuran dan warna:

css
.kartu-label { font-size: 13px; font-weight: 600; color: var(--warna-teks-lembut); }
.kartu-angka {
  font-size: clamp(1.5rem, 2.5vw, 1.85rem);
  font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

Deklarasi clamp(1.5rem, 2.5vw, 1.85rem) menjaga angka tetap besar di ponsel namun tidak membesar tanpa batas di monitor lebar. Nilai tabular-nums memaksa setiap digit menempati lebar yang sama. Tanpa properti itu, deretan angka seperti Rp9.485.000 terlihat sedikit bergelombang karena digit 1 lebih ramping dari digit 0; dengan properti itu, semua angka terbaca seperti kolom laporan keuangan yang dirapikan mesin hitung. Kebiasaan yang sama akan kita terapkan pada kolom Jumlah di tabel transaksi.

Panah Tren lewat ::before #

Di bawah tiap angka ada baris tren, misalnya “12,4% dari bulan lalu”. Panahnya bukan gambar dan bukan elemen tambahan, melainkan konten yang dibangkitkan CSS:

css
.tren--naik::before { content: "▲ "; font-size: 0.8em; }
.tren--turun::before { content: "▼ "; font-size: 0.8em; }
.tren--positif { color: var(--warna-masuk); }
.tren--negatif { color: var(--warna-keluar); }

Perhatikan bahwa arah dan penilaian dipisah menjadi dua pasang class. Kartu Pemasukan memakai tren--naik tren--positif: panah ke atas berwarna hijau karena pendapatan naik adalah kabar baik. Kartu Pengeluaran justru memakai tren--naik tren--negatif: panahnya tetap ke atas karena pengeluaran naik, tetapi warnanya merah karena itu kabar buruk. Pemisahan ini membuat CSS tetap benar saat data berubah — cukup tukar satu class tanpa menyentuh aturan gayanya. Panah juga bersifat dekoratif; makna lengkapnya sudah tertulis pada teks persentase, sehingga pembaca layar tidak kehilangan informasi apa pun.

css
/* ============================================================
   Dashboard Keuangan — stylesheet utama
   Urutan: token → base → cangkang aplikasi → sidebar →
   kepala aplikasi → kartu ringkasan → tabel transaksi →
   lencana status → responsif → fokus dan gerak
   ============================================================ */

/* ---------- 1. Design token ---------- */
:root {
  /* Warna semantik dasbor */
  --warna-latar: #f1f5f9;       --warna-permukaan: #ffffff;
  --warna-teks: #0f172a;        --warna-teks-lembut: #64748b;
  --warna-garis: #e2e8f0;       --warna-utama: #0f766e;
  --warna-utama-pekat: #115e59; --warna-utama-lembut: #ccfbf1;
  --warna-utama-tinta: #134e4a; --warna-fokus: #0d9488;
  --warna-samping: #0f172a;     --warna-samping-teks: #cbd5e1;
  --warna-samping-hover: rgba(255, 255, 255, 0.08);
  --warna-masuk: #15803d;       --warna-masuk-latar: #dcfce7;
  --warna-keluar: #b91c1c;      --warna-keluar-latar: #fee2e2;

  /* Skala spasi, basis empat dan delapan piksel */
  --jarak-1: 4px;  --jarak-2: 8px;
  --jarak-3: 12px; --jarak-4: 16px;
  --jarak-5: 24px; --jarak-6: 32px;

  /* Radius sudut dan bayangan */
  --radius-kecil: 8px; --radius: 12px; --radius-penuh: 999px;
  --bayangan-1: 0 1px 2px rgba(15, 23, 42, 0.06);
  --bayangan-2: 0 12px 28px rgba(15, 23, 42, 0.22);

  /* Tipografi, gerak, dan kerangka */
  --font-dasar: system-ui, "Segoe UI", sans-serif;
  --durasi: 180ms; --pelunakan: ease-out;
  --lebar-samping: 248px;
}

/* ---------- 2. Base ---------- */
*, *::before, *::after { box-sizing: border-box; }

body { margin: 0; font-family: var(--font-dasar); line-height: 1.6; background-color: var(--warna-latar); color: var(--warna-teks); }

h1, h2, p, ul { margin: 0; }

ul { padding: 0; list-style: none; }

button { font: inherit; }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ---------- 3. Cangkang aplikasi ---------- */
.cangkang-app {
  display: grid;
  grid-template-areas: "samping kepala" "samping utama";
  grid-template-columns: var(--lebar-samping) 1fr;
  grid-template-rows: auto 1fr;
  height: 100dvh;
}

.sidebar { grid-area: samping; }

.kepala-app { grid-area: kepala; }

.isi-utama { grid-area: utama; overflow-y: auto; display: grid; gap: var(--jarak-5); align-content: start; padding: var(--jarak-5); }

.isi-kaki { font-size: 12.5px; color: var(--warna-teks-lembut); text-align: center; }

/* ---------- 4. Sidebar dan navigasi ---------- */
.sidebar {
  display: flex; flex-direction: column; gap: var(--jarak-5); overflow-y: auto;
  padding: var(--jarak-5) var(--jarak-4);
  background-color: var(--warna-samping); color: var(--warna-samping-teks);
}

.brand { display: flex; align-items: center; gap: var(--jarak-3); }

.brand-tanda { flex: none; width: 38px; height: 38px; border-radius: 10px; background-image: linear-gradient(135deg, #2dd4bf, #0f766e); }

.brand-nama { color: #ffffff; font-size: 17px; font-weight: 800; line-height: 1.3; }

.brand-sub { display: block; font-size: 11.5px; font-weight: 500; color: var(--warna-samping-teks); }

.menu-judul { padding-inline: var(--jarak-3); font-size: 11.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: #64748b; }

.menu-app { display: grid; gap: 4px; margin-top: var(--jarak-2); }

.menu-tautan {
  display: flex; align-items: center; gap: var(--jarak-3); padding: 10px var(--jarak-3);
  border-radius: var(--radius-kecil); text-decoration: none;
  color: var(--warna-samping-teks); font-size: 14.5px; font-weight: 600;
  transition: background-color var(--durasi) var(--pelunakan), color var(--durasi) var(--pelunakan);
}

.menu-tautan:hover { background-color: var(--warna-samping-hover); color: #ffffff; }

.menu-tautan.is-aktif { background-color: var(--warna-utama); color: #ffffff; }

.sidebar-kaki { margin-top: auto; padding: var(--jarak-3); border-top: 1px solid rgba(255, 255, 255, 0.12); font-size: 12.5px; }

/* ---------- 5. Kepala aplikasi ---------- */
.kepala-app {
  display: flex; align-items: center; gap: var(--jarak-4); padding: var(--jarak-4) var(--jarak-5);
  background-color: var(--warna-permukaan); border-bottom: 1px solid var(--warna-garis);
}

.kepala-sapaan { font-size: 12.5px; color: var(--warna-teks-lembut); }

.kepala-judul { font-size: 20px; font-weight: 800; letter-spacing: -0.01em; line-height: 1.3; }

.kepala-periode { margin-left: auto; padding: 6px 14px; border-radius: var(--radius-penuh); background-color: var(--warna-utama-lembut); color: var(--warna-utama-tinta); font-size: 13px; font-weight: 700; }

.tombol-samping { display: none; }

/* ---------- 6. Kartu ringkasan ---------- */
.dasbor-ringkasan {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--jarak-4);
}

.kartu-ringkasan {
  display: grid; gap: 2px; padding: var(--jarak-4) var(--jarak-5);
  background-color: var(--warna-permukaan); border-radius: var(--radius);
  border: 1px solid var(--warna-garis); border-left: 4px solid var(--warna-utama);
  box-shadow: var(--bayangan-1);
}

.kartu-ringkasan--masuk { border-left-color: var(--warna-masuk); }

.kartu-ringkasan--keluar { border-left-color: var(--warna-keluar); }

.kartu-label { font-size: 13px; font-weight: 600; color: var(--warna-teks-lembut); }

.kartu-angka { font-size: clamp(1.5rem, 2.5vw, 1.85rem); font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

.kartu-tren { font-size: 13px; font-weight: 600; color: var(--warna-teks-lembut); }

.tren--naik::before { content: "▲ "; font-size: 0.8em; }

.tren--turun::before { content: "▼ "; font-size: 0.8em; }

.tren--positif { color: var(--warna-masuk); }

.tren--negatif { color: var(--warna-keluar); }
html
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dashboard Keuangan — DompetKu</title>
  <link rel="stylesheet" href="css/styles.css">
  <script src="js/main.js" defer></script>
</head>
<body>
  <div class="cangkang-app">
    <aside class="sidebar" id="sidebar-nav" aria-label="Panel navigasi dasbor">
      <div class="brand">
        <span class="brand-tanda" aria-hidden="true"></span>
        <span class="brand-nama">DompetKu<span class="brand-sub">Catatan keuangan pribadi</span></span>
      </div>
      <nav aria-label="Menu utama">
        <p class="menu-judul" id="judul-menu">Menu</p>
        <ul class="menu-app" aria-labelledby="judul-menu">
          <li><a href="#ringkasan" class="menu-tautan is-aktif" aria-current="page">Ringkasan</a></li>
          <li><a href="#transaksi" class="menu-tautan">Transaksi</a></li>
          <li><a href="#" class="menu-tautan">Anggaran</a></li>
          <li><a href="#" class="menu-tautan">Tabungan</a></li>
          <li><a href="#" class="menu-tautan">Laporan</a></li>
        </ul>
        <p class="menu-judul" id="judul-menu-lain">Lainnya</p>
        <ul class="menu-app" aria-labelledby="judul-menu-lain">
          <li><a href="#" class="menu-tautan">Pengaturan</a></li>
          <li><a href="#" class="menu-tautan">Bantuan</a></li>
        </ul>
      </nav>
      <p class="sidebar-kaki">Versi latihan 1.0</p>
    </aside>

    <button type="button" class="tirai-samping" id="tirai-samping" aria-label="Tutup panel navigasi"></button>

    <header class="kepala-app">
      <button type="button" class="tombol-samping" id="tombol-samping" aria-expanded="false" aria-controls="sidebar-nav">☰ Menu</button>
      <div class="kepala-teks">
        <p class="kepala-sapaan">Selamat datang kembali, Rani</p>
        <h1 class="kepala-judul">Ringkasan Keuangan</h1>
      </div>
      <p class="kepala-periode">November 2026</p>
    </header>

    <main class="isi-utama" id="ringkasan">
      <section class="dasbor-ringkasan" aria-label="Kartu ringkasan keuangan">
        <article class="kartu-ringkasan">
          <p class="kartu-label">Saldo saat ini</p>
          <p class="kartu-angka">Rp9.485.000</p>
          <p class="kartu-tren tren tren--turun tren--negatif">0,8% dari bulan lalu</p>
        </article>
        <article class="kartu-ringkasan kartu-ringkasan--masuk">
          <p class="kartu-label">Pemasukan bulan ini</p>
          <p class="kartu-angka">Rp11.670.000</p>
          <p class="kartu-tren tren tren--naik tren--positif">12,4% dari bulan lalu</p>
        </article>
        <article class="kartu-ringkasan kartu-ringkasan--keluar">
          <p class="kartu-label">Pengeluaran bulan ini</p>
          <p class="kartu-angka">Rp2.185.000</p>
          <p class="kartu-tren tren tren--naik tren--negatif">5,2% dari bulan lalu</p>
        </article>
      </section>

      <section class="seksi-transaksi" id="transaksi" aria-labelledby="judul-transaksi">
        <div class="seksi-kepala">
          <h2 class="seksi-judul" id="judul-transaksi">Transaksi Terakhir</h2>
          <a class="tautan-lain" href="#">Lihat semua transaksi</a>
        </div>
        <div class="tabel-gulir">
          <table class="tabel-transaksi">
            <caption class="sr-only">Delapan transaksi terakhir pada November 2026</caption>
            <thead>
              <tr>
                <th scope="col">Tanggal</th>
                <th scope="col">Keterangan</th>
                <th scope="col" class="kolom-kategori">Kategori</th>
                <th scope="col" class="kolom-jumlah">Jumlah</th>
                <th scope="col">Status</th>
              </tr>
            </thead>
            <tbody>
              <tr><td class="kolom-tanggal">18 Nov</td><td>Penjualan barang bekas</td><td class="kolom-kategori">Lainnya</td><td class="kolom-jumlah sel-masuk">+Rp420.000</td><td><span class="lencana-status lencana-status--masuk">Masuk</span></td></tr>
              <tr><td class="kolom-tanggal">15 Nov</td><td>Ongkir belanja online</td><td class="kolom-kategori">Kebutuhan</td><td class="kolom-jumlah sel-keluar">−Rp35.000</td><td><span class="lencana-status lencana-status--keluar">Keluar</span></td></tr>
              <tr><td class="kolom-tanggal">14 Nov</td><td>Makan siang kantor</td><td class="kolom-kategori">Makanan</td><td class="kolom-jumlah sel-keluar">−Rp275.000</td><td><span class="lencana-status lencana-status--keluar">Keluar</span></td></tr>
              <tr><td class="kolom-tanggal">11 Nov</td><td>Tagihan listrik dan air</td><td class="kolom-kategori">Tagihan</td><td class="kolom-jumlah sel-keluar">−Rp486.000</td><td><span class="lencana-status lencana-status--keluar">Keluar</span></td></tr>
              <tr><td class="kolom-tanggal">08 Nov</td><td>Proyek desain lepas</td><td class="kolom-kategori">Penghasilan</td><td class="kolom-jumlah sel-masuk">+Rp2.750.000</td><td><span class="lencana-status lencana-status--masuk">Masuk</span></td></tr>
              <tr><td class="kolom-tanggal">05 Nov</td><td>Langganan streaming</td><td class="kolom-kategori">Hiburan</td><td class="kolom-jumlah sel-keluar">−Rp149.000</td><td><span class="lencana-status lencana-status--keluar">Keluar</span></td></tr>
              <tr><td class="kolom-tanggal">03 Nov</td><td>Belanja bulanan supermarket</td><td class="kolom-kategori">Kebutuhan</td><td class="kolom-jumlah sel-keluar">−Rp1.240.000</td><td><span class="lencana-status lencana-status--keluar">Keluar</span></td></tr>
              <tr><td class="kolom-tanggal">02 Nov</td><td>Gaji bulanan</td><td class="kolom-kategori">Penghasilan</td><td class="kolom-jumlah sel-masuk">+Rp8.500.000</td><td><span class="lencana-status lencana-status--masuk">Masuk</span></td></tr>
            </tbody>
          </table>
        </div>
      </section>

      <p class="isi-kaki">Angka pada dasbor ini adalah contoh latihan; total pemasukan dikurangi pengeluaran cocok dengan saldo yang ditampilkan.</p>
    </main>
  </div>
</body>
</html>

Ringkasan #

Tiga kartu ringkasan kini menjadi mercusuar visual halaman: grid adaptif menata posisinya, digit tabular menjaga angka tetap sejajar, dan panah ::before menghitung arah serta penilaian tren secara terpisah. Puncak hierarki area utama sudah berdiri. Bagian 4 menuruni satu tingkat: tabel transaksi dengan kepala kolom yang menempel saat digulir dan lencana status berkontras baik.