Kenapa ini penting
Bayangkan proyekmu sudah punya 40 file CSS. Klien minta warna merek diganti dari biru ke hijau, dan breakpoint tablet digeser. Dengan CSS murni dan nilai yang ditulis ulang di mana-mana, kamu harus mencari dan mengganti satu per satu sambil berharap tidak ada yang terlewat. Sass (Syntactically Awesome Style Sheets) adalah preprocessor: bahasa yang kamu tulis dengan fitur ekstra (variabel, nesting, modul, fungsi, loop), lalu di-compile menjadi CSS biasa yang dibaca browser. Browser tidak pernah melihat kode Sass kamu; ia hanya menerima hasil CSS-nya.
Sass punya dua sintaks: SCSS (.scss), yang mirip CSS dengan kurung kurawal dan titik koma, dan sintaks indentasi lama (.sass). Hampir semua proyek memakai SCSS, dan semua file CSS yang valid otomatis juga SCSS yang valid. Jadi kamu bisa mulai dari CSS yang sudah ada lalu menambah fitur Sass sedikit demi sedikit.
Konsep inti
Setup Sass
Implementasi resmi Sass saat ini adalah Dart Sass, yang dipasang lewat paket npm sass. Versi terbaru saat materi ini ditulis adalah 1.106.1 (dicek dengan npm view sass version). Implementasi lama seperti LibSass dan node-sass sudah tidak dikembangkan, jadi jangan dipakai untuk proyek baru.
# 1. Buat proyek dan pasang Sass sebagai dev dependency
mkdir kopi-sass && cd kopi-sass
npm init -y
npm install --save-dev sass
# 2. Cek versi yang terpasang
npx sass --version
# → 1.106.1 (atau lebih baru)
# 3. Compile sekali
npx sass src/scss/main.scss dist/css/main.css
# 4. Compile otomatis setiap file disimpan
npx sass --watch src/scss:dist/css
Tambahkan ke package.json supaya cukup mengetik npm run dev:
"scripts": {
"dev": "sass --watch src/scss:dist/css",
"build": "sass src/scss:dist/css --style=compressed --no-source-map"
}
Kalau nanti kamu memakai Vite (misalnya untuk React), cukup pasang sass atau sass-embedded sebagai dev dependency, lalu import file .scss langsung dari JavaScript; Vite yang akan meng-compile.
Variabel
Variabel Sass diawali $:
$warna-utama: #2563eb;
$radius: 12px;
.tombol { background: $warna-utama; border-radius: $radius; }
Variabel Sass hilang saat compile: hasil CSS-nya hanya berisi nilai #2563eb. Bandingkan dengan custom property CSS (--warna-utama) yang tetap hidup di browser dan bisa diubah saat runtime. Keduanya tidak saling menggantikan; di proyek modern mereka sering dipakai bersama: Sass untuk nilai saat build (breakpoint, perhitungan), custom property untuk nilai yang perlu berubah saat runtime (tema terang dan gelap).
Flag !default artinya "pakai nilai ini kecuali sudah diatur dari luar". Ini kunci untuk membuat modul yang bisa dikonfigurasi.
Scope: variabel yang dideklarasikan di dalam blok { } hanya berlaku di dalam blok itu.
Nesting
Nesting menulis selector bersarang mengikuti struktur HTML:
.kartu {
padding: 1rem;
&__judul { font-size: 1.25rem; } // → .kartu__judul
&--unggulan { border-color: gold; } // → .kartu--unggulan
&:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); } // → .kartu:hover
a { color: inherit; } // → .kartu a
}
Simbol & mewakili selector induk. Ini sangat cocok dengan BEM. Aturan emas: jangan nesting lebih dari tiga level, karena setiap level menambah specificity dan membuat CSS terikat ke struktur HTML.
Partial
Partial adalah file Sass yang namanya diawali garis bawah, misalnya _variabel.scss. Garis bawah memberi tahu Sass bahwa file ini hanya untuk dimuat oleh file lain, tidak di-compile menjadi CSS sendiri. Saat memuatnya, garis bawah dan ekstensi tidak perlu ditulis.
@use dan @forward (sistem modul)
Dulu file Sass dimuat dengan @import. Masalahnya, @import menaruh semua variabel di ruang global, file yang sama bisa termuat berkali-kali, dan sulit melacak asal sebuah variabel. Sejak Dart Sass 1.80.0, @import resmi deprecated dan direncanakan dihapus di Dart Sass 3.0. Penggantinya:
@use "path"memuat modul. Isinya diakses lewat namespace, yaitu nama file terakhir:@use "abstracts/tokens";lalutokens.$warna-utama. Setiap modul hanya dijalankan sekali, berapa kali pun di-@use.- Ganti namespace:
@use "abstracts/tokens" as t;lalut.$warna-utama. - Tanpa namespace:
@use "abstracts/tokens" as *;(pakai hemat, karena mengembalikan masalah "asal variabel tidak jelas"). - Konfigurasi:
@use "abstracts/tokens" with ($warna-utama: green);, hanya untuk variabel yang ditandai!default. @useharus ditulis di bagian paling atas file, sebelum aturan lain.@forward "path"meneruskan isi sebuah modul supaya bisa diakses oleh siapa pun yang memuat file ini. Biasanya dipakai di file_index.scsssebuah folder, sehingga satu@use "abstracts"sudah memberi akses ke semua isi folder itu.- Anggota (variabel, mixin, fungsi) yang namanya diawali
-atau_bersifat privat: tidak bisa diakses dari luar modulnya.
Sass juga menyediakan modul bawaan yang dimuat dengan @use "sass:...": sass:math (misalnya math.div untuk pembagian), sass:color, sass:map, sass:list, sass:string, dan sass:meta. Fungsi global lama seperti map-get() atau darken() juga sudah deprecated; pakai versi modul seperti map.get() dan color.adjust().
Map
Map adalah kumpulan pasangan kunci dan nilai, mirip object di JavaScript:
@use "sass:map";
$warna: (
"utama": #2563eb,
"sukses": #16a34a,
"bahaya": #dc2626,
);
.peringatan { color: map.get($warna, "bahaya"); } // → color: #dc2626
Fungsi penting di sass:map: map.get, map.has-key, map.merge, map.keys, map.values. Map menjadi sangat bertenaga ketika digabung dengan loop @each di modul berikutnya.
Contoh kode
Struktur folder:
src/scss/
├── abstracts/
│ ├── _index.scss
│ ├── _tokens.scss
│ └── _breakpoints.scss
├── components/
│ └── _kartu.scss
└── main.scss
src/scss/abstracts/_tokens.scss:
// Variabel dengan !default supaya bisa dikonfigurasi dari luar
$warna-utama: #2563eb !default;
$radius: 12px !default;
$font-dasar: "Plus Jakarta Sans", system-ui, sans-serif !default;
$warna: (
"utama": $warna-utama,
"sukses": #16a34a,
"bahaya": #dc2626,
"teks": #1e293b,
) !default;
$ruang: (
1: 0.25rem,
2: 0.5rem,
4: 1rem,
6: 1.5rem,
) !default;
// Privat: diawali tanda minus, tidak bisa dipakai di luar file ini
$-catatan-internal: "jangan dipakai di luar";
src/scss/abstracts/_breakpoints.scss:
$breakpoints: (
"sm": 40rem,
"md": 48rem,
"lg": 64rem,
) !default;
src/scss/abstracts/_index.scss:
// Satu pintu: siapa pun yang @use "abstracts" dapat akses ke keduanya
@forward "tokens";
@forward "breakpoints";
src/scss/components/_kartu.scss:
@use "sass:map";
@use "../abstracts" as a;
.kartu {
padding: map.get(a.$ruang, 6);
color: map.get(a.$warna, "teks");
border: 1px solid #e2e8f0;
border-radius: a.$radius;
&__judul {
margin: 0 0 map.get(a.$ruang, 2);
color: map.get(a.$warna, "utama");
}
&--bahaya {
border-color: map.get(a.$warna, "bahaya");
}
&:hover {
box-shadow: 0 8px 24px -8px rgb(0 0 0 / 0.2);
}
}
src/scss/main.scss:
// Konfigurasi modul tokens lewat abstracts sebelum file lain memakainya
@use "abstracts" with ($warna-utama: #7c3aed);
@use "components/kartu";
body {
margin: 0;
font-family: abstracts.$font-dasar;
}
Jalankan npx sass src/scss/main.scss dist/css/main.css --no-source-map. Hasilnya:
.kartu {
padding: 1.5rem;
color: #1e293b;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.kartu__judul {
margin: 0 0 0.5rem;
color: #7c3aed;
}
.kartu--bahaya {
border-color: #dc2626;
}
.kartu:hover {
box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.2);
}
body {
margin: 0;
font-family: "Plus Jakarta Sans", system-ui, sans-serif;
}
/* → Warna utama jadi ungu (#7c3aed) karena dikonfigurasi lewat with(...) di main.scss */
Perhatikan: konfigurasi with (...) di main.scss berhasil karena main.scss adalah file pertama yang memuat modul abstracts. Modul Sass hanya dijalankan sekali, jadi konfigurasinya harus diberikan pada pemuatan pertama.
Kesalahan umum
- Masih memakai
@import. Sass akan menampilkan peringatan deprecation, dan suatu saat fitur itu dihapus. Cara benar:@useuntuk memuat,@forwarduntuk meneruskan. - Lupa namespace, menulis
$warna-utamapadahal memuat dengan@use "tokens". Sass akan error "Undefined variable". Cara benar:tokens.$warna-utama, atau beri alias. - Mengonfigurasi variabel tanpa
!default.with (...)akan error karena variabel itu tidak bisa dikonfigurasi. Cara benar: tandai variabel yang boleh diubah dengan!default. - Nesting terlalu dalam, misalnya
.halaman .konten .kartu .judul span. Cara benar: maksimal tiga level, manfaatkan&__BEM untuk tetap datar. - Mengedit file
.csshasil compile. Perubahan akan tertimpa saat compile berikutnya. Cara benar: hanya edit file.scss; folderdistadalah hasil, bukan sumber.
Latihan
- Mudah, Buat
_warna.scssberisi$utama,$sekunder, dan$latar, lalu pakai dimain.scssdengan@usedan namespacew. - Sedang, Tulis ulang CSS ini dengan nesting dan
&:.menu {},.menu__item {},.menu__item--aktif {},.menu__item:hover {},.menu a {}. - Menantang, Buat modul
_tema.scssdengan map$temaberisi tiga warna dan variabel$radius: 8px !default;. Darimain.scss, konfigurasi$radiusmenjadi16pxdan cetak warna "aksen" dari map ke.badge.
Lihat solusi
// Soal 1: _warna.scss
$utama: #0ea5e9;
$sekunder: #f97316;
$latar: #f8fafc;
// main.scss
@use "warna" as w;
body { background: w.$latar; }
.tombol { background: w.$utama; }
.tombol--sekunder { background: w.$sekunder; }
// Soal 2
.menu {
display: flex;
&__item {
padding: 0.5rem 1rem;
&--aktif { font-weight: 700; } // → .menu__item--aktif
&:hover { background: #f1f5f9; } // → .menu__item:hover
}
a { color: inherit; } // → .menu a
}
// Soal 3: _tema.scss
$radius: 8px !default;
$tema: (
"utama": #2563eb,
"aksen": #f59e0b,
"netral": #64748b,
);
// main.scss
@use "sass:map";
@use "tema" with ($radius: 16px);
.badge {
border-radius: tema.$radius; // → 16px
background: map.get(tema.$tema, "aksen"); // → #f59e0b
}
Ringkasan
- Sass adalah preprocessor; pasang Dart Sass lewat
npm install --save-dev sass(versi 1.106.1 saat materi ini ditulis) dan compile dengannpx sass. - Variabel
$hilang saat compile; custom property CSS hidup di browser. Keduanya saling melengkapi. - Nesting dengan
&cocok untuk BEM; jangan lebih dari tiga level. - Partial diawali
_; muat dengan@use(pakai namespace) dan teruskan dengan@forward.@importsudah deprecated. - Map menyimpan pasangan kunci dan nilai; akses lewat modul bawaan
sass:map.