kodingup.camp

Beranda/HTML, CSS & SCSS Mastery

Variabel & Styling Dinamis dengan Sass

Modul 9 dari 115 menit bacaDynamic Styling Variables

Isi modul

    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:

    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

    1. Masih memakai @import. Sass akan menampilkan peringatan deprecation, dan suatu saat fitur itu dihapus. Cara benar: @use untuk memuat, @forward untuk meneruskan.
    2. Lupa namespace, menulis $warna-utama padahal memuat dengan @use "tokens". Sass akan error "Undefined variable". Cara benar: tokens.$warna-utama, atau beri alias.
    3. Mengonfigurasi variabel tanpa !default. with (...) akan error karena variabel itu tidak bisa dikonfigurasi. Cara benar: tandai variabel yang boleh diubah dengan !default.
    4. Nesting terlalu dalam, misalnya .halaman .konten .kartu .judul span. Cara benar: maksimal tiga level, manfaatkan &__ BEM untuk tetap datar.
    5. Mengedit file .css hasil compile. Perubahan akan tertimpa saat compile berikutnya. Cara benar: hanya edit file .scss; folder dist adalah hasil, bukan sumber.

    Latihan

    1. Mudah, Buat _warna.scss berisi $utama, $sekunder, dan $latar, lalu pakai di main.scss dengan @use dan namespace w.
    2. Sedang, Tulis ulang CSS ini dengan nesting dan &: .menu {}, .menu__item {}, .menu__item--aktif {}, .menu__item:hover {}, .menu a {}.
    3. Menantang, Buat modul _tema.scss dengan map $tema berisi tiga warna dan variabel $radius: 8px !default;. Dari main.scss, konfigurasi $radius menjadi 16px dan 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

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.