kodingup.camp

Beranda/HTML, CSS & SCSS Mastery

Review Ahli & Diskusi Teknis

Modul 11 dari 116 menit bacaExpert Review & Technical Discussion

Isi modul

    Kenapa ini penting

    Kamu sekarang menguasai alatnya. Pertanyaan berikutnya adalah pertanyaan yang biasa muncul di code review atau wawancara kerja: "Bagaimana kamu mengorganisasi stylesheet untuk proyek besar?" dan "Di tahun ini, masih perlu Sass atau cukup CSS native?" Jawaban yang matang tidak berupa "selalu pakai X", melainkan pertimbangan untung-rugi berdasarkan konteks. Modul ini membahas pola arsitektur 7-1, perbandingan jujur Sass dengan fitur CSS native (nesting dan custom properties), lalu studi kasus refactor dari stylesheet monolit menjadi struktur modular.

    Konsep inti

    Pola 7-1

    Pola 7-1 (dipopulerkan oleh panduan Sass Guidelines karya Kitty Giraudel) berarti 7 folder partial dan 1 file utama yang menyatukannya:

    Folder Isi Contoh file
    abstracts/ Hal yang tidak menghasilkan CSS sendiri: variabel, function, mixin, placeholder _tokens.scss, _mixins.scss, _functions.scss
    vendors/ CSS pihak ketiga _swiper.scss
    base/ Reset, tipografi dasar, gaya elemen HTML _reset.scss, _typography.scss
    layout/ Kerangka besar halaman _header.scss, _footer.scss, _grid.scss
    components/ Komponen kecil yang dipakai ulang _tombol.scss, _kartu.scss, _modal.scss
    pages/ Gaya khusus satu halaman _beranda.scss, _checkout.scss
    themes/ Variasi tema _gelap.scss

    File main.scss hanya berisi @use ke tiap folder, tanpa aturan gaya. Dengan sistem modul modern, setiap folder biasanya punya _index.scss yang meneruskan isinya dengan @forward (untuk abstracts) atau memuat file-filenya dengan @use (untuk folder yang menghasilkan CSS).

    Satu hal penting: dengan @use, setiap file yang butuh variabel atau mixin harus memuat abstracts sendiri. Ini berbeda dengan era @import di mana variabel "kebetulan" tersedia karena sudah diimpor lebih dulu. Awalnya terasa repot, tetapi justru membuat ketergantungan setiap file terlihat jelas.

    7-1 bukan aturan wajib. Proyek kecil cukup memakai 3 sampai 4 folder (abstracts, base, layout, components). Di aplikasi React atau Vue, gaya komponen sering diletakkan di samping komponennya (misalnya Tombol.module.scss), sementara abstracts dan base tetap terpusat.

    Sass vs CSS native: diskusi yang jujur

    CSS terus berkembang dan sekarang punya banyak fitur yang dulu hanya ada di Sass:

    Kebutuhan CSS native Sass
    Variabel Custom properties, hidup di runtime, ikut cascade, bisa diubah JS $variabel, hanya saat compile
    Nesting Didukung browser modern sejak 2023 Sudah lama ada, plus penggabungan string seperti &__judul
    Kalkulasi calc(), clamp(), min(), max() Matematika saat compile, math.div, unit conversion
    Modul / pemisahan file @import CSS (memicu request berantai tanpa bundler) @use/@forward, digabung jadi satu file saat compile
    Loop dan logika Tidak ada @each, @for, @if, @function
    Mixin Belum tersedia secara luas @mixin dengan argumen dan @content
    Manipulasi warna color-mix(), relative color syntax di browser modern sass:color
    Responsif berbasis wadah Container query Tidak relevan (Sass hanya menulis CSS)

    Ada satu perbedaan nesting yang sering menjebak: Sass bisa menggabungkan string selector seperti &__judul menjadi .kartu__judul. CSS native tidak bisa; di CSS native, &__judul tidak menghasilkan selector BEM. Jadi kalau proyekmu sangat bergantung pada pola BEM dengan &__, Sass masih unggul.

    Panduan keputusan:

    Yang perlu diingat: Sass menambah langkah build dan satu dependency. Itu biaya kecil, tetapi tetap biaya. Keputusan "pakai Sass atau tidak" sebaiknya diambil karena kebutuhan nyata, bukan karena kebiasaan.

    Checklist review stylesheet

    Saat me-review kode SCSS orang lain, periksa:

    1. Semua pemuatan memakai @use/@forward, tidak ada @import Sass.
    2. Nesting maksimal tiga level.
    3. Tidak ada nilai ajaib berulang; warna dan spacing berasal dari token.
    4. Media query lewat mixin breakpoint, bukan angka acak.
    5. Tidak ada @extend yang menyeberang ke dalam media query atau antar komponen.
    6. Loop hanya menghasilkan utility yang benar-benar dipakai.
    7. CSS hasil compile tidak di-commit sebagai sumber yang diedit manual.

    Contoh kode

    Studi kasus: kamu mewarisi style.scss satu file 400 baris dari proyek lama. Potongannya:

    // SEBELUM: style.scss (gaya lama, campur aduk)
    @import "variables";
    
    $blue: #2563eb;
    
    body { margin: 0; font-family: Arial; }
    
    .header {
      background: $blue;
      .nav {
        ul {
          li {
            a { color: white; &:hover { color: #ddd; } }
          }
        }
      }
    }
    
    .card { padding: 20px; border: 1px solid #ddd; }
    .card .title { color: $blue; font-size: 20px; }
    
    @media (min-width: 768px) { .card { padding: 30px; } }
    @media (min-width: 768px) { .header .nav ul { display: flex; } }
    
    .btn { background: $blue; color: white; padding: 10px 20px; }
    .btn-red { @extend .btn; background: red; }
    

    Masalah yang ditemukan saat review: @import usang, nesting lima level (.header .nav ul li a), warna diulang, breakpoint 768px ditulis manual berkali-kali, nama campuran (.card .title bukan BEM), @extend pada class nyata.

    Hasil refactor ke struktur 7-1 yang disederhanakan:

    src/scss/
    ├── abstracts/
    │   ├── _index.scss       # @forward tokens, mixins
    │   ├── _tokens.scss
    │   └── _mixins.scss
    ├── base/
    │   ├── _index.scss       # @use reset, typography
    │   ├── _reset.scss
    │   └── _typography.scss
    ├── layout/
    │   ├── _index.scss
    │   └── _header.scss
    ├── components/
    │   ├── _index.scss
    │   ├── _kartu.scss
    │   └── _tombol.scss
    └── main.scss
    

    abstracts/_tokens.scss:

    $warna: (
      "utama": #2563eb,
      "bahaya": #dc2626,
      "garis": #ddd,
      "teks-terang": #fff,
    ) !default;
    
    $breakpoints: ("md": 48rem, "lg": 64rem) !default;
    $font-dasar: system-ui, Arial, sans-serif !default;
    

    abstracts/_mixins.scss:

    @use "sass:map";
    @use "tokens";
    
    @mixin naik($nama) {
      @if not map.has-key(tokens.$breakpoints, $nama) {
        @error "Breakpoint '#{$nama}' tidak dikenal.";
      }
      @media (min-width: map.get(tokens.$breakpoints, $nama)) {
        @content;
      }
    }
    

    abstracts/_index.scss:

    @forward "tokens";
    @forward "mixins";
    

    base/_typography.scss (dimuat oleh base/_index.scss bersama _reset.scss):

    @use "../abstracts" as a;
    
    body {
      margin: 0;
      font-family: a.$font-dasar;
    }
    

    layout/_header.scss:

    @use "sass:map";
    @use "../abstracts" as a;
    
    .header {
      background: map.get(a.$warna, "utama");
    
      &__daftar {            // dulu: .header .nav ul
        margin: 0;
        padding: 0;
        list-style: none;
    
        @include a.naik(md) { display: flex; gap: 1rem; }
      }
    
      &__link {              // dulu: .header .nav ul li a
        color: map.get(a.$warna, "teks-terang");
    
        &:hover { opacity: 0.8; }
      }
    }
    

    components/_tombol.scss:

    @use "sass:map";
    @use "../abstracts" as a;
    
    // Mixin menggantikan @extend .btn
    @mixin -dasar-tombol($latar) {
      padding: 0.625rem 1.25rem;
      color: white;
      background: $latar;
      border: none;
    }
    
    .tombol {
      @include -dasar-tombol(map.get(a.$warna, "utama"));
    
      &--bahaya { background: map.get(a.$warna, "bahaya"); } // dulu: .btn-red
    }
    

    components/_kartu.scss:

    @use "sass:map";
    @use "../abstracts" as a;
    
    .kartu {
      padding: 1.25rem;
      border: 1px solid map.get(a.$warna, "garis");
    
      @include a.naik(md) { padding: 1.875rem; }
    
      &__judul {
        font-size: 1.25rem;
        color: map.get(a.$warna, "utama");
      }
    }
    

    components/_index.scss dan main.scss:

    // components/_index.scss
    @use "tombol";
    @use "kartu";
    
    // main.scss: hanya merangkai, tanpa aturan gaya
    @use "base";
    @use "layout";
    @use "components";
    
    npx sass src/scss/main.scss dist/css/main.css --style=compressed --no-source-map
    # → Satu file CSS, tanpa peringatan deprecation.
    #   Selector terdalam kini satu class (contoh .header__link) dibanding sebelumnya .header .nav ul li a.
    

    Langkah refactor yang aman di dunia nyata:

    1. Compile versi lama, simpan hasilnya sebagai before.css dan ambil screenshot halaman-halaman penting.
    2. Pindahkan token dulu (warna, breakpoint), tanpa mengubah selector.
    3. Pecah file per komponen, ganti @import dengan @use. Sass menyediakan alat migrasi resmi: npx sass-migrator module --migrate-deps src/scss/main.scss.
    4. Baru kemudian ratakan nesting dan ganti nama ke BEM, sambil memperbarui HTML.
    5. Bandingkan screenshot sebelum dan sesudah. Perbedaan visual berarti ada yang terlewat.

    Kesalahan umum

    1. Mengira variabel dari main.scss otomatis tersedia di partial lain. Dengan @use, tidak. Cara benar: setiap partial memuat abstracts yang dibutuhkannya sendiri.
    2. Menyalin pola &__judul dari Sass ke CSS native nesting. Di CSS native itu tidak menghasilkan .kartu__judul. Cara benar: tulis selector BEM lengkap di CSS native, atau tetap pakai Sass.
    3. Menerapkan 7-1 penuh untuk landing page satu halaman. Folder kosong hanya menambah kebingungan. Cara benar: mulai dari 3 sampai 4 folder, tambah sesuai kebutuhan.
    4. Refactor besar sekaligus tanpa pembanding. Cara benar: lakukan bertahap, simpan hasil compile dan screenshot sebelum mulai.
    5. Menghapus custom properties karena "sudah pakai Sass". Kamu kehilangan theming runtime. Cara benar: Sass menghasilkan custom properties dari map, keduanya bekerja sama.

    Latihan

    1. Mudah, Gambarkan struktur folder 7-1 untuk toko online kecil, lalu tentukan di folder mana file berikut diletakkan: _navbar.scss, _tombol.scss, _halaman-produk.scss, _tokens.scss, _reset.scss.
    2. Sedang, Tulis ulang kode SCSS berikut menjadi CSS native dengan nesting dan custom properties, tanpa Sass: $utama: #2563eb; .kartu { padding: 1rem; .judul { color: $utama; } &:hover { box-shadow: 0 2px 8px #0002; } }.
    3. Menantang, Buat file _tema.scss yang memakai @each untuk mengubah map Sass $warna menjadi custom properties di :root (misalnya --warna-utama), sehingga warna bisa ditimpa saat runtime oleh [data-theme="dark"].
    Lihat solusi
    # Soal 1
    scss/
    ├── abstracts/   _tokens.scss, _mixins.scss
    ├── vendors/     (kosong, isi kalau pakai library)
    ├── base/        _reset.scss, _typography.scss
    ├── layout/      _navbar.scss, _footer.scss
    ├── components/  _tombol.scss, _kartu-produk.scss
    ├── pages/       _halaman-produk.scss
    ├── themes/      _gelap.scss
    └── main.scss
    
    /* Soal 2: CSS native, tanpa compile */
    :root { --utama: #2563eb; }
    
    .kartu {
      padding: 1rem;
    
      .judul { color: var(--utama); }       /* → .kartu .judul */
      &:hover { box-shadow: 0 2px 8px #0002; } /* → .kartu:hover */
    }
    
    // Soal 3: _tema.scss
    $warna: (
      "utama": #2563eb,
      "latar": #ffffff,
      "teks": #0f172a,
    );
    
    :root {
      @each $nama, $nilai in $warna {
        --warna-#{$nama}: #{$nilai};
      }
    }
    
    [data-theme="dark"] {
      --warna-latar: #0f172a;
      --warna-teks: #f1f5f9;
    }
    
    body {
      color: var(--warna-teks);
      background: var(--warna-latar);
    }
    // → :root { --warna-utama: #2563eb; --warna-latar: #ffffff; --warna-teks: #0f172a; }
    //   Sass menyusun daftar dari data, browser yang mengganti nilainya saat tema berubah.
    

    Ringkasan

    Kuis pemahaman

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