Proyek ini merakit DompetKu, sebuah dasbor keuangan pribadi: panel sidebar gelap di sisi kiri, kepala aplikasi dengan periode aktif, tiga kartu ringkasan (Saldo, Pemasukan, Pengeluaran), dan tabel transaksi berlencana status Masuk/Keluar. Dasbor adalah pola antarmuka yang berbeda dari halaman konten biasa karena ia harus mengisi tinggi layar penuh, mengelola gulir per area, dan menonjolkan angka sebagai tokoh utamanya. Kita membangunnya bertahap dalam lima bagian, mulai dari cangkang aplikasi berbasis grid areas, lalu navigasi, kartu ringkasan, tabel transaksi, dan ditutup dengan keruntuhan responsif ke bilah atas yang digerakkan JavaScript. Setiap bagian menyimpan snapshot berkasnya sendiri, sehingga kalian bisa mengikuti stylesheet tumbuh baris demi baris hingga kondisi akhirnya.
Perilaku yang Diharapkan #
Halaman akhirnya berisi sidebar navigasi, kepala aplikasi, tiga kartu ringkasan, tabel delapan transaksi terakhir, dan catatan kaki di dalam area utama. Perilaku yang bisa dicoba langsung:
- Cangkang aplikasi memakai
grid-template-areasdengan tinggi100dvh; kolom sidebar tetap terlihat sementara area utama menggulir sendiri, tanpa menggerakkan sidebar. - Kartu ringkasan menyusun grid
repeat(auto-fit, minmax(210px, 1fr))dan menonjolkan angka denganfont-variant-numeric: tabular-nums, sehingga deretan digit Rupiah sejajar sempurna. - Panah tren digambar lewat
::beforeberisi karakter panah; arah panah dan penilaian naik/turunnya dipisah menjadi dua class agar kartu Pengeluaran bisa berwarna merah meski panahnya menunjuk ke atas. - Tabel transaksi memiliki kepala kolom yang menempel (
position: sticky) saat area gulirnya dijalankan, jumlah rata kanan, serta lencana status berkontras baik. - Pada layar sempit, sidebar berubah menjadi panel geser dengan tirai gelap; tombol Menu di bilah atas membukanya dan
aria-expandedikut diperbarui.
Cuplikan berikut meniru versi ringkas dasbor akhir — perhatikan kartu ringkasan dan lencana statusnya:
Struktur Folder #
dashboard-keuangan/
├── _index.md
├── src/
│ ├── index.html
│ ├── css/
│ │ └── styles.css
│ └── js/
│ └── main.js
├── part-1-dashboard-grid/
│ ├── index.md
│ └── styles.css
├── part-2-sidebar-dan-navigation/
│ ├── index.md
│ ├── styles.css
│ └── beranda.html
├── part-3-summary-cards/
│ ├── index.md
│ ├── styles.css
│ └── beranda.html
├── part-4-transaction-table/
│ ├── index.md
│ ├── styles.css
│ └── beranda.html
└── part-5-responsive-dashboard/
├── index.md
├── styles.css
├── beranda.html
└── main.jsTahapan Pengerjaan #
- Bagian 1: Kerangka Dasbor dengan Grid
— design token warna dan spasi, lalu cangkang aplikasi
100dvhdengangrid-template-areasbeserta rencana gulir per area. - Bagian 2: Sidebar dan Navigasi
— sidebar gelap, tautan menu dengan status aktif
is-aktifdanaria-current, serta persiapan tombol sakelar; berkas HTML pertama kali muncul. - Bagian 3: Kartu Ringkasan
— deretan kartu adaptif, tipografi angka
tabular-nums, dan panah tren lewat::before. - Bagian 4: Tabel Transaksi — tabel semantik dalam area gulir, kepala kolom menempel, jumlah rata kanan, dan lencana status berkontras baik.
- Bagian 5: Dasbor Responsif
— media query keruntuhan ke bilah atas, JavaScript pembuka sidebar dengan
aria-expanded, utilitas fokus, dan penghormatanprefers-reduced-motion.
Ikuti urutannya, karena setiap bagian membangun di atas snapshot bagian sebelumnya.
Berkas Sumber Lengkap #
Tiga berkas berikut adalah kondisi akhir proyek, identik dengan snapshot Bagian 5. Gunakan tombol unduh pada header tiap code block untuk menyimpannya, lalu susun folder sesuai struktur di atas. Snapshot halaman ditampilkan dengan label beranda.html; saat dipraktikkan, simpan isinya sebagai index.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>/* ============================================================
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); }
/* ---------- 7. Tabel transaksi ---------- */
.seksi-transaksi {
background-color: var(--warna-permukaan); border-radius: var(--radius); overflow: hidden;
border: 1px solid var(--warna-garis); box-shadow: var(--bayangan-1);
}
.seksi-kepala {
display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
gap: var(--jarak-3); padding: var(--jarak-4) var(--jarak-5);
border-bottom: 1px solid var(--warna-garis);
}
.seksi-judul { font-size: 17px; font-weight: 800; }
.tautan-lain { font-size: 13.5px; font-weight: 600; color: var(--warna-utama); text-decoration: none; }
.tautan-lain:hover { text-decoration: underline; }
.tabel-gulir { max-height: 380px; overflow-y: auto; }
.tabel-transaksi { width: 100%; border-collapse: collapse; font-size: 14px; }
.tabel-transaksi th, .tabel-transaksi td {
padding: 12px var(--jarak-5); text-align: left;
border-bottom: 1px solid var(--warna-garis);
}
.tabel-transaksi thead th {
position: sticky; top: 0; z-index: 1;
background-color: var(--warna-utama-lembut); color: var(--warna-utama-tinta);
font-size: 12.5px; letter-spacing: 0.05em; text-transform: uppercase;
}
.tabel-transaksi tbody tr:nth-child(even) { background-color: #f8fafc; }
.tabel-transaksi tbody tr:last-child td { border-bottom: none; }
.kolom-tanggal { white-space: nowrap; color: var(--warna-teks-lembut); }
.kolom-kategori { color: var(--warna-teks-lembut); }
.kolom-jumlah { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sel-masuk { color: var(--warna-masuk); }
.sel-keluar { color: var(--warna-keluar); }
/* ---------- 8. Lencana status ---------- */
.lencana-status { display: inline-block; padding: 3px 12px; border-radius: var(--radius-penuh); font-size: 12px; font-weight: 700; }
.lencana-status--masuk { background-color: var(--warna-masuk-latar); color: var(--warna-masuk); }
.lencana-status--keluar { background-color: var(--warna-keluar-latar); color: var(--warna-keluar); }
/* ---------- 9. Responsif ---------- */
@media (max-width: 860px) {
.cangkang-app {
grid-template-areas: "kepala" "utama";
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.kepala-app { padding: var(--jarak-3) var(--jarak-4); }
.tombol-samping {
display: inline-flex; align-items: center; gap: var(--jarak-2); padding: 8px 14px;
border: 1px solid var(--warna-garis); border-radius: var(--radius-kecil);
background-color: var(--warna-permukaan); font-size: 14px; font-weight: 700; cursor: pointer;
}
.kepala-periode { display: none; }
.sidebar {
position: fixed; top: 0; bottom: 0; left: 0; z-index: 30; width: min(280px, 80vw);
transform: translateX(-100%);
transition: transform var(--durasi) var(--pelunakan), box-shadow var(--durasi) var(--pelunakan);
}
.sidebar.is-terbuka { transform: translateX(0); box-shadow: var(--bayangan-2); }
.tirai-samping {
position: fixed; inset: 0; z-index: 20; border: none; padding: 0; cursor: pointer;
background-color: rgba(15, 23, 42, 0.45);
opacity: 0; visibility: hidden; pointer-events: none;
transition: opacity var(--durasi) var(--pelunakan), visibility var(--durasi) var(--pelunakan);
}
.tirai-samping.is-tampak { opacity: 1; visibility: visible; pointer-events: auto; }
.isi-utama { padding: var(--jarak-4); }
.tabel-transaksi th, .tabel-transaksi td { padding: 10px var(--jarak-3); }
.kolom-kategori { display: none; }
}
/* ---------- 10. Fokus dan gerak ---------- */
:focus-visible { outline: 2px solid var(--warna-fokus); outline-offset: 2px; }
.menu-tautan:focus-visible { outline-color: #5eead4; }
@media (prefers-reduced-motion: reduce) {
.menu-tautan, .sidebar, .tirai-samping { transition: none; }
}/* ============================================================
main.js — interaksi Dashboard Keuangan.
1) Status aktif menu: klik tautan menu memindahkan class
is-aktif beserta atribut aria-current ke tautan itu.
2) Pada layar kecil, tombol menu membuka/menutup sidebar
dan menyelaraskan aria-expanded; tirai gelap menutupnya.
Tidak ada penyimpanan di sisi browser.
============================================================ */
(function () {
"use strict";
var sidebar = document.getElementById("sidebar-nav");
var tombolMenu = document.getElementById("tombol-samping");
var tirai = document.getElementById("tirai-samping");
var daftarTautan = document.querySelectorAll(".menu-tautan");
/* Buka atau tutup sidebar lalu selaraskan status ARIA. */
function aturSidebar(terbuka) {
sidebar.classList.toggle("is-terbuka", terbuka);
tirai.classList.toggle("is-tampak", terbuka);
tombolMenu.setAttribute("aria-expanded", terbuka ? "true" : "false");
}
/* Pindahkan tanda menu aktif ke tautan yang dipilih. */
function pilihMenu(tautanDipilih) {
daftarTautan.forEach(function (tautan) {
var aktif = tautan === tautanDipilih;
tautan.classList.toggle("is-aktif", aktif);
if (aktif) {
tautan.setAttribute("aria-current", "page");
} else {
tautan.removeAttribute("aria-current");
}
});
aturSidebar(false);
}
tombolMenu.addEventListener("click", function () {
aturSidebar(!sidebar.classList.contains("is-terbuka"));
});
tirai.addEventListener("click", function () {
aturSidebar(false);
});
daftarTautan.forEach(function (tautan) {
tautan.addEventListener("click", function (event) {
event.preventDefault(); /* tautan berupa "#" dalam latihan */
pilihMenu(tautan);
});
});
})();Menyiapkan design token dasbor dan merakit cangkang aplikasi setinggi 100dvh dengan grid-template-areas, lengkap dengan rencana gulir per area agar sidebar tetap terlihat.
Buka → # Bagian 2: Sidebar dan NavigasiMengisi kolom samping dengan sidebar gelap berisi brand dan menu, menandai tautan aktif dengan class is-aktif dan aria-current, serta menyiapkan tombol sakelar untuk layar kecil.
Buka → # Bagian 3: Kartu RingkasanMembangun tiga kartu ringkasan Saldo, Pemasukan, dan Pengeluaran: grid adaptif tanpa media query, tipografi angka tabular-nums, serta panah tren yang digambar lewat ::before.
Buka → # Bagian 4: Tabel TransaksiMenata tabel transaksi semantik di dalam area gulir: kepala kolom menempel dengan position sticky, jumlah rata kanan bertipografi tabular, dan lencana status Masuk/Keluar berkontras baik.
Buka → # Bagian 5: Dasbor ResponsifMeruntuhkan dasbor menjadi bilah atas di layar kecil: sidebar geser dengan tirai, JavaScript pembuka menu yang menjaga aria-expanded, cincin fokus, dan penghormatan prefers-reduced-motion.
Buka →