CSS-IN-JS FRONTEND REACT STYLING WEB-DEVELOPMENT PERFORMANCE DEVELOPER-EXPERIENCE BEST-PRACTICES UI-UX JAVASCRIPT TYPESCRIPT

Memilih Strategi CSS-in-JS yang Tepat: Dari Runtime hingga Compile-Time

⏱️ 7 menit baca
👨‍💻

Memilih Strategi CSS-in-JS yang Tepat: Dari Runtime hingga Compile-Time

1. Pendahuluan

Pernahkah Anda merasa pusing mengatur file CSS di proyek web yang semakin besar? Konflik antar class name, style yang sulit dilacak asalnya, atau perubahan kecil yang entah kenapa merusak tampilan di bagian lain aplikasi? Jika ya, Anda tidak sendiri. Masalah-masalah ini adalah “sakit kepala” klasik dalam pengembangan frontend yang skalanya terus bertambah.

Di sinilah CSS-in-JS masuk sebagai solusi. Konsep ini, yang populer di ekosistem React (namun bisa juga digunakan di framework lain), pada dasarnya memungkinkan kita menulis CSS langsung di dalam file JavaScript (atau TypeScript) komponen kita. Ini bukan sekadar tren, melainkan sebuah pendekatan yang fundamental dalam memecahkan masalah modularitas, scoping, dan dynamic styling yang sering ditemui dalam aplikasi web modern.

Artikel ini akan membawa Anda menyelami dunia CSS-in-JS, memahami mengapa ia muncul, manfaatnya, serta perbedaan mendasar antara dua filosofi utamanya: runtime dan compile-time. Kita akan membedah beberapa library populer seperti Styled Components, Emotion, dan Vanilla Extract, lengkap dengan contoh konkret, untuk membantu Anda memilih strategi yang paling tepat untuk proyek Anda. Siapkah Anda mengucapkan selamat tinggal pada “specificity wars” dan menyambut era styling yang lebih terstruktur dan menyenangkan? Mari kita mulai!

2. Mengapa CSS-in-JS? Masalah yang Dipecahkan

Sebelum kita membahas “bagaimana”, mari kita pahami dulu “mengapa”. Mengapa developer beralih ke CSS-in-JS ketika ada CSS biasa, Sass, atau BEM? Jawabannya terletak pada masalah-masalah yang sering muncul di aplikasi web skala besar:

📌 Masalah CSS Klasik:

  1. Global Scope & Konflik Nama: Class name CSS bersifat global. Ini berarti button yang Anda definisikan di ComponentA.css bisa menimpa button di ComponentB.css. Hasilnya? “Specificity wars” yang membuat debugging menjadi mimpi buruk.
  2. Manajemen Dependensi & Dead Code: Sulit mengetahui style mana yang benar-benar digunakan oleh komponen tertentu. Seringkali kita ragu menghapus style karena takut merusak bagian lain, yang berujung pada penumpukan dead code dan ukuran bundle yang membengkak.
  3. Dynamic Styling yang Rumit: Mengubah style berdasarkan state komponen (misalnya, tombol aktif/non-aktif) seringkali memerlukan manipulasi class name secara manual di JavaScript, yang bisa menjadi verbose dan rawan error.
  4. Konsistensi Tema: Menerapkan theming atau design system secara konsisten di seluruh aplikasi bisa menjadi tantangan besar dengan CSS tradisional.

Solusi dari CSS-in-JS:

CSS-in-JS mengatasi masalah ini dengan beberapa cara:

💡 Contoh Sederhana Perbandingan:

CSS Tradisional (dengan BEM):

// Button.module.css
.button {
  padding: 10px 20px;
  border-radius: 4px;
  background-color: blue;
  color: white;
}

.button--secondary {
  background-color: gray;
}
// Button.jsx
import styles from './Button.module.css';

function Button({ children, variant = 'primary' }) {
  const className = variant === 'secondary' ? styles['button--secondary'] : styles.button;
  return <button className={className}>{children}</button>;
}

Dengan CSS-in-JS (misal: Styled Components):

// Button.jsx
import styled from 'styled-components';

const StyledButton = styled.button`
  padding: 10px 20px;
  border-radius: 4px;
  background-color: ${(props) => (props.variant === 'secondary' ? 'gray' : 'blue')};
  color: white;
`;

function Button({ children, variant = 'primary' }) {
  return <StyledButton variant={variant}>{children}</StyledButton>;
}

Perhatikan bagaimana logika styling dinamis menjadi lebih ringkas dan terintegrasi langsung dengan komponen. Ini adalah salah satu kekuatan utama CSS-in-JS!

3. Gaya Runtime CSS-in-JS: Styled Components & Emotion

Pendekatan runtime CSS-in-JS adalah yang paling umum dan mungkin yang pertama kali Anda temui. Dengan pendekatan ini, style Anda ditulis dalam JavaScript, dan kemudian saat aplikasi berjalan (runtime), library CSS-in-JS akan mengambil style tersebut, meng-generate class name unik, dan menyuntikkannya ke dalam <style> tag di <head> dokumen HTML Anda.

Styled Components

Styled Components adalah pelopor dan salah satu library CSS-in-JS paling populer. Ini memperkenalkan konsep “visual primitives” di mana Anda membuat komponen React yang sudah memiliki style-nya sendiri.

// Button.jsx
import styled from 'styled-components';

// Membuat komponen button dengan style bawaan
const StyledButton = styled.button`
  background-color: ${props => props.primary ? '#61dafb' : 'white'};
  color: ${props => props.primary ? 'white' : '#61dafb'};
  font-size: 1em;
  padding: 0.25em 1em;
  border: 2px solid #61dafb;
  border-radius: 3px;
  cursor: pointer;

  &:hover {
    opacity: 0.8;
  }
`;

// Komponen lain yang menggunakan StyledButton
const Container = styled.div`
  text-align: center;
  margin-top: 20px;
`;

function App() {
  return (
    <Container>
      <StyledButton>Normal Button</StyledButton>
      <StyledButton primary>Primary Button</StyledButton>
    </Container>
  );
}

export default App;

Fitur Utama Styled Components:

Emotion

Emotion adalah alternatif populer untuk Styled Components, dikenal karena fleksibilitas dan performanya yang sedikit lebih baik dalam beberapa benchmark. Emotion menawarkan API yang lebih fleksibel, memungkinkan Anda menulis style sebagai object atau tagged template literals.

// Button.jsx
import { css } from '@emotion/react';
import styled from '@emotion/styled';

// Menggunakan `css` prop untuk gaya
const buttonStyles = css`
  background-color: hotpink;
  color: white;
  padding: 10px 20px;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  &:hover {
    background-color: palevioletred;
  }
`;

function MyButton({ children }) {
  return <button css={buttonStyles}>{children}</button>;
}

// Menggunakan `styled` API yang mirip Styled Components
const AnotherButton = styled.button`
  background-color: ${props => props.primary ? 'mediumseagreen' : 'lightgray'};
  color: ${props => props.primary ? 'white' : 'black'};
  padding: 12px 24px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
`;

function App() {
  return (
    <div>
      <MyButton>Emotion Button</MyButton>
      <AnotherButton primary>Primary Emotion Button</AnotherButton>
    </div>
  );
}

export default App;

Fitur Utama Emotion:

⚠️ Trade-off Runtime CSS-in-JS:

4. Gaya Compile-Time CSS-in-JS: Vanilla Extract & Linaria

Berbeda dengan pendekatan runtime, compile-time CSS-in-JS (sering disebut juga “Zero-Runtime CSS-in-JS”) bertujuan untuk mengekstrak semua style Anda ke file CSS statis terpisah selama proses build (kompilasi). Hasilnya adalah CSS murni yang dapat diunduh dan di-parse oleh browser tanpa perlu JavaScript tambahan. Ini menggabungkan manfaat CSS-in-JS (seperti scoping dan dynamic props di sisi JavaScript) dengan performa CSS tradisional.

Vanilla Extract

Vanilla Extract adalah library “zero-runtime” dari tim di balik Braid Design System (Shopify). Ini memungkinkan Anda menulis style menggunakan TypeScript/JavaScript, tetapi semua style tersebut diekstrak menjadi file .css terpisah saat build time.

// styles.css.ts (perhatikan ekstensi .css.ts)
import { style, globalStyle, keyframes } from '@vanilla-extract/css';
import { createVar } from '@vanilla-extract/css';

export const primaryColor = createVar(); // Mendefinisikan variabel CSS

export const rotate = keyframes({
  '0%': { transform: 'rotate(0deg)' },
  '