Halaman profil yang terasa profesional biasanya tidak dibangun dari banyak komponen, melainkan dari sedikit keputusan yang dijaga konsisten. Dua keputusan paling menentukan adalah palet warna dan skala tipografi: keduanya menyentuh hampir setiap elemen di layar. Pada bagian pertama ini kita menyiapkan file styles/profil.css, mendefinisikan token warna di :root, lalu membangun skala tipografi untuk nama, peran, bio, judul subbagian, dan tautan kontak. Kartu, avatar, dan layout baru menyusul pada bagian berikutnya — fondasi dulu, bangunan kemudian.
Tujuan Bagian dan Prasyarat #
Di akhir bagian ini, halaman kita sudah menampilkan hierarki teks yang jelas pada latar abu-abu lembut, tanpa kartu dan tanpa gambar. Target spesifiknya:
- Enam token warna didefinisikan sekali di
:rootdan dikonsumsi lewatvar(). - Skala tipografi empat tingkat: 36px (nama), 20px (judul subbagian), 18px (peran), 16px (teks dasar).
line-height: 1.5dideklarasikan dibodydan diwarisi seluruh teks.
Konsep yang dipakai sudah dibahas pada chapter Font Family , Font Size dan Weight , Line Height , Color Syntax , dan Custom Properties . Buka kembali chapter tersebut bila ada yang terasa asing.
Menyiapkan Struktur dan Markup #
Buat folder proyek halaman-profil/ berisi index.html dan subfolder styles/. Markup awal sengaja sederhana: satu wadah .halaman dan lima elemen teks.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Budi Santoso — Profil</title>
<link rel="stylesheet" href="styles/profil.css">
</head>
<body>
<div class="halaman">
<h1 class="profil-nama">Budi Santoso</h1>
<p class="profil-peran">Frontend Web Developer</p>
<p class="profil-bio">
Saya membangun antarmuka web yang cepat, rapi, dan mudah
dipakai. Saat ini saya sedang memperdalam CSS modern.
</p>
<section class="profil-bagian">
<h2 class="profil-bagian-judul">Keahlian</h2>
</section>
<section class="profil-bagian">
<h2 class="profil-bagian-judul">Kontak</h2>
<a class="tautan-kontak" href="mailto:[email protected]">[email protected]</a>
</section>
</div>
</body>
</html>Menulis Token Warna dan Skala Tipografi #
Sekarang tulis potongan pertama styles/profil.css. Kelompok token diletakkan paling atas agar mudah ditemukan, kemudian aturan dasar, lalu kelas per elemen.
:root {
--warna-teks: #0f172a;
--warna-teks-lembut: #475569;
--warna-aksen: #0284c7;
--warna-latar: #f1f5f9;
--warna-kartu: #ffffff;
--warna-garis: #e2e8f0;
}
* {
margin: 0;
}
body {
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
color: var(--warna-teks);
background-color: var(--warna-latar);
}
.halaman {
max-width: 760px;
margin: 0 auto;
padding: 48px 16px;
}
.profil-nama {
font-size: 36px;
font-weight: 700;
line-height: 1.2;
color: var(--warna-teks);
}
.profil-peran {
font-size: 18px;
font-weight: 500;
color: var(--warna-aksen);
}
.profil-bio {
margin-top: 16px;
color: var(--warna-teks-lembut);
}
.profil-bagian-judul {
font-size: 20px;
font-weight: 600;
letter-spacing: 0.5px;
color: var(--warna-teks);
}
.tautan-kontak {
color: var(--warna-aksen);
font-weight: 500;
text-decoration: underline;
}Hasil di Browser:
Judul "Budi Santoso" tampil 36px dengan bobot 700 dan
line-height 43.2px. Peran berwarna rgb(2, 132, 199) pada
ukuran 18px. Bio mewarisi line-height 24px dari body dan
berwarna rgb(71, 85, 105). Latar halaman rgb(241, 245, 249).Bedah Aturan Utama #
Mari kita urai aturan-aturan kunci di atas:
- Blok
:root: enam custom properties menyimpan warna dalam satu tempat. Token--warna-kartudan--warna-garisbelum dipakai pada bagian ini; keduanya menunggu kartu pada Bagian 2. - Aturan
* { margin: 0 }: reset minimal yang menghapus margin bawaanh1,h2, danp. Dari sini setiap jarak di halaman adalah keputusan kita, bukan bawaan browser. - Aturan
body:font-family: system-ui, sans-serifmemakai font antarmuka sistem;line-height: 1.5pada font 16px dihitung browser menjadi 24px;colordanbackground-colormenarik token dari:root. Karenacolordanline-heightbersifat inherited, seluruh teks mewarisinya tanpa deklarasi tambahan. - Aturan
.halaman:max-width: 760pxmembatasi lebar baris teks, danmargin: 0 automendorong wadah ke tengah viewport. - Aturan
.profil-nama: ukuran terbesar 36px, bobot 700, denganline-height: 1.2yang dihitung menjadi 43.2px — rapat untuk judul, nyaman untuk teks isi. - Aturan
.profil-perandan.profil-bagian-judul: keduanya memakai bobot menengah (500 dan 600) agar tidak bersaing dengan nama.letter-spacing: 0.5pxmemberi ruang kecil pada judul subbagian. - Aturan
.tautan-kontak:text-decoration: underlinemenjaga konvensi tautan, sementara warna aksen menandai interaktivitas.
Uji Bagian Ini #
Halaman uji bagian ini tersedia di /tests/css/proyek/halaman-profil/typography-dan-warna.html
. Halaman tersebut memuat salinan CSS di atas beserta elemen probe beratribut data-expected; ringkasannya juga tersimpan sebagai berkas tests/typography-dan-warna.spec.js yang dapat diunduh dari bagian Berkas Sumber. Asersi yang diverifikasi:
| Elemen | Properti | Diharapkan |
|---|---|---|
body | color | rgb(15, 23, 42) |
body | background-color | rgb(241, 245, 249) |
body | line-height | 24px |
.profil-nama | font-size | 36px |
.profil-nama | line-height | 43.2px |
.profil-peran | color | rgb(2, 132, 199) |
.profil-bagian-judul | letter-spacing | 0.5px |
.tautan-kontak | text-decoration-line | underline |
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
styles/profil.css
Unduh berkas/* =========================================================
Proyek: Halaman Profil — styles/profil.css
File utuh proyek yang berkembang mengikuti tiga bagian:
Bagian 1 (Typography dan Warna), Bagian 2 (Box Model dan
Spacing), Bagian 3 (Responsive Layout). Komentar "Bagian N"
menandai kelompok aturan yang dibahas pada setiap bagian,
dan komentar kecil mencatat perubahan antarbagian.
========================================================= */
/* Bagian 1: Typography dan Warna -------------------------- */
:root {
--warna-teks: #0f172a;
--warna-teks-lembut: #475569;
--warna-aksen: #0284c7;
--warna-latar: #f1f5f9;
--warna-kartu: #ffffff;
--warna-garis: #e2e8f0;
}
* {
margin: 0;
box-sizing: border-box; /* ditambahkan pada Bagian 2 */
}
body {
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
color: var(--warna-teks);
background-color: var(--warna-latar);
}
.halaman {
max-width: 760px;
margin: 0 auto;
padding: 32px 16px; /* Bagian 3: padding dasar diturunkan dari 48px 16px */
}
.profil-nama {
/* Bagian 3: nilai tetap 36px diganti clamp() agar fluid */
font-size: clamp(30px, 24px + 1vw, 40px);
font-weight: 700;
line-height: 1.2;
color: var(--warna-teks);
}
.profil-peran {
font-size: 18px;
font-weight: 500;
color: var(--warna-aksen);
}
.profil-bio {
color: var(--warna-teks-lembut);
}
.profil-bagian-judul {
font-size: 20px;
font-weight: 600;
letter-spacing: 0.5px;
color: var(--warna-teks);
}
.tautan-kontak {
color: var(--warna-aksen);
font-weight: 500;
text-decoration: underline;
}
.tautan-kontak:hover {
text-decoration-thickness: 2px;
}
/* Bagian 2: Box Model dan Spacing ------------------------- */
.kartu-profil {
width: min(100%, 300px); /* Bagian 3: mulanya 300px tetap */
margin: 0 auto;
padding: 24px;
background-color: var(--warna-kartu);
border: 1px solid var(--warna-garis);
border-radius: 12px;
box-shadow: 0 4px 12px rgb(15 23 42 / 8%);
}
.profil-kepala {
display: flex;
flex-direction: column; /* Bagian 3: kembali ke baris pada breakpoint */
align-items: center;
gap: 16px;
margin-bottom: 16px;
text-align: center;
}
.avatar {
flex: none; /* jaga ukuran: avatar tidak ikut menyusut */
width: 96px;
height: 96px;
border-radius: 50%;
border: 2px solid var(--warna-garis);
}
.profil-bagian {
margin-top: 24px;
}
.daftar-keahlian {
display: flex;
flex-wrap: wrap;
gap: 8px;
list-style: none;
}
.keahlian {
padding: 8px 12px;
background-color: var(--warna-latar);
border-radius: 8px;
font-size: 14px;
color: var(--warna-teks-lembut);
}
/* Bagian 3: Responsive Layout ----------------------------- */
@media (min-width: 640px) {
.halaman {
padding: 64px 32px;
}
.profil-kepala {
flex-direction: row;
text-align: left;
margin-bottom: 24px;
}
}
tests/box-model-dan-spacing.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Halaman Profil
// Bagian 2: Box Model dan Spacing
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji:
// /tests/css/proyek/halaman-profil/box-model-dan-spacing.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// Kartu profil selebar 300px berkat box-sizing: border-box,
// padding dan border tidak menambah lebar totalnya.
{ elemen: ".kartu-profil", properti: "width", diharapkan: "300px" },
// Padding 24px di semua sisi memberi ruang lega bagi konten.
{ elemen: ".kartu-profil", properti: "padding", diharapkan: "24px" },
// Border tipis 1px dengan warna token --warna-garis (#e2e8f0).
{ elemen: ".kartu-profil", properti: "border-top-width", diharapkan: "1px" },
{ elemen: ".kartu-profil", properti: "border-top-color", diharapkan: "rgb(226, 232, 240)" },
// Sudut kartu membulat 12px, latar kartu putih.
{ elemen: ".kartu-profil", properti: "border-radius", diharapkan: "12px" },
{ elemen: ".kartu-profil", properti: "background-color", diharapkan: "rgb(255, 255, 255)" },
// .profil-kepala adalah flex container dengan jarak antaritem 16px.
{ elemen: ".profil-kepala", properti: "display", diharapkan: "flex" },
{ elemen: ".profil-kepala", properti: "gap", diharapkan: "16px" },
// Ritme vertikal: kepala kartu diberi margin bawah 16px.
{ elemen: ".profil-kepala", properti: "margin-bottom", diharapkan: "16px" },
// Avatar berukuran 96px tetap (flex: none agar tidak menyusut
// di dalam flex container) dengan sudut penuh (lingkaran).
{ elemen: ".avatar", properti: "width", diharapkan: "96px" },
{ elemen: ".avatar", properti: "height", diharapkan: "96px" },
{ elemen: ".avatar", properti: "border-radius", diharapkan: "50%" },
// Setiap subbagian kartu berjarak 24px dari blok di atasnya.
{ elemen: ".profil-bagian", properti: "margin-top", diharapkan: "24px" },
// Daftar keahlian adalah flex yang meliputi baris dengan gap 8px.
{ elemen: ".daftar-keahlian", properti: "display", diharapkan: "flex" },
{ elemen: ".daftar-keahlian", properti: "flex-wrap", diharapkan: "wrap" },
{ elemen: ".daftar-keahlian", properti: "gap", diharapkan: "8px" },
// Chip keahlian berpadding 8px vertikal dan 12px horizontal.
{ elemen: ".keahlian", properti: "padding-top", diharapkan: "8px" },
{ elemen: ".keahlian", properti: "padding-left", diharapkan: "12px" },
// Chip membulat 8px dengan teks 14px.
{ elemen: ".keahlian", properti: "border-radius", diharapkan: "8px" },
{ elemen: ".keahlian", properti: "font-size", diharapkan: "14px" },
];
tests/responsive-layout.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Halaman Profil
// Bagian 3: Responsive Layout
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji (viewport 1280x720, sehingga breakpoint
// min-width: 640px berada pada keadaan aktif):
// /tests/css/proyek/halaman-profil/responsive-layout.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// Wadah halaman dibatasi 760px dan, pada layar >= 640px,
// berpadding 64px vertikal serta 32px horizontal.
{ elemen: ".halaman", properti: "max-width", diharapkan: "760px" },
{ elemen: ".halaman", properti: "padding-top", diharapkan: "64px" },
// Kartu tetap 300px dan terpusat: margin auto terhitung browser
// menjadi 198px pada konten selebar 696px (760 - 2 x 32).
{ elemen: ".kartu-profil", properti: "width", diharapkan: "300px" },
{ elemen: ".kartu-profil", properti: "margin-left", diharapkan: "198px" },
// Judul fluid: clamp(30px, 24px + 1vw, 40px) pada viewport 1280px
// menghasilkan 24 + 12.8 = 36.8px, masih di dalam rentang.
{ elemen: ".profil-nama", properti: "font-size", diharapkan: "36.8px" },
// Pada layar >= 640px kepala profil kembali tersusun horizontal.
{ elemen: ".profil-kepala", properti: "flex-direction", diharapkan: "row" },
{ elemen: ".profil-kepala", properti: "gap", diharapkan: "16px" },
// Jarak kepala ke bio diperbesar menjadi 24px pada breakpoint.
{ elemen: ".profil-kepala", properti: "margin-bottom", diharapkan: "24px" },
// Daftar keahlian tetap berjajar dengan gap 8px.
{ elemen: ".daftar-keahlian", properti: "flex-direction", diharapkan: "row" },
{ elemen: ".daftar-keahlian", properti: "gap", diharapkan: "8px" },
];
tests/typography-dan-warna.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Halaman Profil
// Bagian 1: Typography dan Warna
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji:
// /tests/css/proyek/halaman-profil/typography-dan-warna.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// body membawa warna teks utama dari token --warna-teks (#0f172a
// dikonversi browser menjadi rgb(15, 23, 42)).
{ elemen: "body", properti: "color", diharapkan: "rgb(15, 23, 42)" },
// Latar halaman memakai token --warna-latar (#f1f5f9).
{ elemen: "body", properti: "background-color", diharapkan: "rgb(241, 245, 249)" },
// Ukuran teks dasar 16px dengan line-height 1.5 (16 x 1.5 = 24px).
{ elemen: "body", properti: "font-size", diharapkan: "16px" },
{ elemen: "body", properti: "line-height", diharapkan: "24px" },
// .profil-nama berada di puncak skala tipografi: 36px, tebal 700.
{ elemen: ".profil-nama", properti: "font-size", diharapkan: "36px" },
{ elemen: ".profil-nama", properti: "font-weight", diharapkan: "700" },
// line-height 1.2 pada font 36px dihitung browser menjadi 43.2px.
{ elemen: ".profil-nama", properti: "line-height", diharapkan: "43.2px" },
// .profil-peran memakai warna aksen (#0284c7) dan bobot medium.
{ elemen: ".profil-peran", properti: "color", diharapkan: "rgb(2, 132, 199)" },
{ elemen: ".profil-peran", properti: "font-size", diharapkan: "18px" },
{ elemen: ".profil-peran", properti: "font-weight", diharapkan: "500" },
// .profil-bio memakai warna teks lembut (#475569).
{ elemen: ".profil-bio", properti: "color", diharapkan: "rgb(71, 85, 105)" },
// Judul bagian 20px, bobot 600, dengan letter-spacing 0.5px.
{ elemen: ".profil-bagian-judul", properti: "font-size", diharapkan: "20px" },
{ elemen: ".profil-bagian-judul", properti: "font-weight", diharapkan: "600" },
{ elemen: ".profil-bagian-judul", properti: "letter-spacing", diharapkan: "0.5px" },
// Tautan kontak berwarna aksen dan digarisbawahi.
{ elemen: ".tautan-kontak", properti: "color", diharapkan: "rgb(2, 132, 199)" },
{ elemen: ".tautan-kontak", properti: "text-decoration-line", diharapkan: "underline" },
];