Web Development

PostCSS: CSS Transformation

Tutorial lengkap PostCSS dari nol — plugin system, autoprefixer, nesting, custom selectors, custom properties, dan workflow modern CSS dengan contoh kode praktis

1. Pengenalan PostCSS

PostCSS adalah tool untuk mentransformasi CSS menggunakan JavaScript. PostCSS bukanlah preprocessor seperti Sass atau Less — ia adalah platform yang memungkinkan Anda membangun pipeline transformasi CSS melalui plugin. Setiap plugin menangani satu transformasi spesifik, dan Anda bisa menggabungkan ratusan plugin sesuai kebutuhan.

PostCSS saat ini digunakan oleh perusahaan besar seperti Google, Facebook, Wikipedia, dan GitHub. Bahkan Tailwind CSS dan Autoprefixer dibangun di atas PostCSS!

PostCSS vs CSS Preprocessor

Aspek PostCSS Sass/SCSS Less
PendekatanPlugin-based (modular)All-in-one (monolithic)All-in-one (monolithic)
FiturTerinstall sesuai kebutuhanSemua fitur bawaanSemua fitur bawaan
Nesting✅ (via plugin)✅ Built-in✅ Built-in
Variables✅ CSS Custom Properties✅ $variables✅ @variables
Future CSS✅ (via postcss-preset-env)❌ Manual❌ Manual
Ukuran output🟢 Minimal (sesuai plugin)🟡 Standar🟡 Standar
Ekosistem2000+ pluginsTerbatasTerbatas

Mengapa PostCSS Penting?

Diagram: PostCSS Plugin Pipeline
┌──────────────────────────────────────────────────────────┐
│                  POSTCSS PIPELINE                         │
│                                                          │
│  Input CSS                                               │
│  ┌──────────────┐                                        │
│  │ .card {      │                                        │
│  │   color: red;│                                        │
│  │   & h2 {}   │                                        │
│  │ }            │                                        │
│  └──────┬───────┘                                        │
│         │                                                │
│  ┌──────▼──────────────────────────────────────────┐     │
│  │              PostCSS Parser                      │     │
│  │  Mengubah CSS → AST (Abstract Syntax Tree)      │     │
│  └──────┬──────────────────────────────────────────┘     │
│         │                                                │
│  ┌──────▼──────────────────────────────────────────┐     │
│  │              Plugin Pipeline                     │     │
│  │                                                  │     │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────┐  │     │
│  │  │ postcss- │→ │ postcss- │→ │ postcss-     │  │     │
│  │  │ nesting  │  │ preset-  │  │ autoprefixer │  │     │
│  │  │          │  │ env      │  │              │  │     │
│  │  └──────────┘  └──────────┘  └──────────────┘  │     │
│  └──────┬──────────────────────────────────────────┘     │
│         │                                                │
│  ┌──────▼──────────────────────────────────────────┐     │
│  │              PostCSS Generator                   │     │
│  │  Mengubah AST → CSS string                      │     │
│  └──────┬──────────────────────────────────────────┘     │
│         │                                                │
│  Output CSS                                              │
│  ┌──────────────┐                                        │
│  │ .card {      │                                        │
│  │   color: red;│                                        │
│  │ }            │                                        │
│  │ .card h2 {}  │                                        │
│  │ .card {      │                                        │
│  │   -webkit-...│                                        │
│  │ }            │                                        │
│  └──────────────┘                                        │
└──────────────────────────────────────────────────────────┘

2. Instalasi dan Setup

Instalasi PostCSS

Bash
# Instal PostCSS dan CLI
npm install postcss postcss-cli --save-dev

# Instal beberapa plugin populer
npm install autoprefixer postcss-nesting postcss-preset-env --save-dev

# Atau dengan pnpm
pnpm add -D postcss postcss-cli autoprefixer postcss-nesting

# Verifikasi instalasi
npx postcss --version
# Output: 8.x.x

Konfigurasi PostCSS

postcss.config.js
// postcss.config.js — konfigurasi utama PostCSS
// File ini otomatis terdeteksi oleh Vite, Webpack, dan bundler lainnya

export default {
  plugins: {
    // postcss-preset-env: include banyak plugin modern CSS sekaligus
    'postcss-preset-env': {
      stage: 3,                    // Hanya fitur stage 3+ (stabil)
      features: {
        'nesting-rules': true,     // CSS Nesting
        'custom-properties': true, // CSS Variables
        'custom-selectors': true,  // Custom selectors
        'color-mix': true,         // color-mix() function
      },
      autoprefixer: {
        grid: true                 // Autoprefix untuk CSS Grid
      },
      browsers: [
        '> 1%',
        'last 2 versions',
        'not dead',
        'not ie 11'
      ]
    },

    // Atau gunakan plugin individual:
    // 'postcss-nesting': {},
    // autoprefixer: {},
  }
}

Konfigurasi Alternatif (CJS)

postcss.config.cjs
// postcss.config.cjs — untuk project yang menggunakan CommonJS
module.exports = {
  plugins: [
    require('autoprefixer'),
    require('postcss-nesting'),
    require('postcss-preset-env')({
      stage: 3,
      browsers: ['> 1%', 'last 2 versions']
    })
  ]
}

CLI Usage

Bash
# Compile satu file
npx postcss input.css -o output.css

# Compile dengan plugin spesifik
npx postcss input.css -o output.css --use autoprefixer

# Watch mode — compile otomatis saat file berubah
npx postcss input.css -o output.css --watch

# Minify output
npx postcss input.css -o output.css --no-map --minify

# Compile dengan config file
npx postcss src/**/*.css --dir dist/ --config postcss.config.js
💡 Tips

Jika Anda menggunakan Vite, Next.js, atau Nuxt, PostCSS sudah terintegrasi secara otomatis. Cukup buat file postcss.config.js di root proyek dan plugin akan langsung aktif — tidak perlu konfigurasi bundler tambahan!

3. Cara Kerja PostCSS

PostCSS bekerja dalam tiga tahap: parsing (CSS → AST), transformasi (AST dimanipulasi oleh plugins), dan generation (AST → CSS output).

AST (Abstract Syntax Tree)

JavaScript — Memanipulasi AST
// PostCSS mengubah CSS menjadi AST yang bisa dimanipulasi
import postcss from 'postcss';

const css = `
.card {
  color: red;
  font-size: 16px;
}

.card:hover {
  color: blue;
}
`;

// Parse CSS ke AST
const root = postcss.parse(css);

// Iterasi semua aturan
root.walkRules((rule) => {
  console.log('Selector:', rule.selector);
  // Output:
  // Selector: .card
  // Selector: .card:hover

  // Iterasi semua declaration dalam rule
  rule.walkDecls((decl) => {
    console.log(`  ${decl.prop}: ${decl.value}`);
    // Output:
    //   color: red
    //   font-size: 16px
  });
});

// Menambahkan declaration baru
root.walkRules('.card', (rule) => {
  rule.append({ prop: 'padding', value: '1rem' });
  rule.append({ prop: 'border-radius', value: '8px' });
});

// Generate CSS dari AST
const result = root.toString();
console.log(result);
// .card {
//   color: red;
//   font-size: 16px;
//   padding: 1rem;
//   border-radius: 8px;
// }

Node Types dalam PostCSS AST

Node Type Contoh CSS Deskripsi
RootSeluruh file CSSRoot node dari AST
AtRule@media ...At-rule (media query, import, dll.)
Rule.card { }Selector + blok deklarasi
Declarationcolor: redProperty + value
Comment/* comment */Komentar CSS

4. Autoprefixer

Autoprefixer adalah plugin PostCSS yang paling populer. Ia secara otomatis menambahkan vendor prefix (-webkit-, -moz-, -ms-) berdasarkan data dari Can I Use dan target browser yang Anda tentukan.

Cara Kerja Autoprefixer

CSS — Sebelum Autoprefixer
/* Input CSS (yang Anda tulis) */
.container {
  display: flex;
  gap: 1rem;
}

.card {
  transform: rotate(45deg);
  transition: all 0.3s ease;
}

.gradient {
  background: linear-gradient(to right, #ff0000, #0000ff);
}

.placeholder::placeholder {
  color: #999;
}

.scrollbar {
  scrollbar-width: thin;
}
CSS — Setelah Autoprefixer
/* Output CSS (setelah Autoprefixer memproses) */
.container {
  display: flex;
  gap: 1rem;
}

.card {
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
  -webkit-transition: all 0.3s ease;
  transition: all 0.3s ease;
}

.gradient {
  background: -webkit-linear-gradient(left, #ff0000, #0000ff);
  background: linear-gradient(to right, #ff0000, #0000ff);
}

.placeholder::-webkit-input-placeholder {
  color: #999;
}
.placeholder::placeholder {
  color: #999;
}

.scrollbar {
  scrollbar-width: thin;
}
/* Prefix hanya ditambahkan jika target browser membutuhkannya */

Konfigurasi Autoprefixer

postcss.config.js — Autoprefixer Config
export default {
  plugins: {
    autoprefixer: {
      // Method 1: Browserslist (paling umum)
      overrideBrowserslist: [
        '> 1%',           // Browser dengan market share > 1%
        'last 2 versions', // 2 versi terakhir setiap browser
        'not dead',        // Tidak termasuk browser yang sudah discontinued
        'not ie 11'        // Tidak termasuk IE 11
      ],

      // Method 2: Gunakan .browserslistrc file
      // (tidak perlu overrideBrowserslist di sini)

      // Grid autoplacement untuk IE
      grid: 'autoplace',

      // Jangan hapus prefix yang sudah ada
      remove: true
    }
  }
}

Browserslist Configuration

.browserslistrc
# .browserslistrc — konfigurasi target browser
# Digunakan oleh Autoprefixer, Babel, ESLint, dll.

# Production (default)
> 1%
last 2 versions
not dead
not op_mini all

# Development — browser modern saja
[development]
last 1 chrome version
last 1 firefox version
last 1 safari version

# Modern browsers — hanya yang support ES modules
[modern]
supports es6-module
not dead

# Contoh query lainnya:
# chrome >= 80          → Chrome 80+
# ios_saf >= 14         → Safari iOS 14+
# firefox_esr           → Firefox ESR
# samsung >= 13         → Samsung Internet 13+
# covers > 80% in ID    → Browser populer di Indonesia

5. CSS Nesting

CSS Nesting memungkinkan Anda menulis selector yang bersarang (nested) seperti di Sass, tapi langsung menggunakan CSS. Browser modern (Chrome 120+, Firefox 117+, Safari 17.2+) sudah mendukung CSS nesting native, tapi PostCSS memungkinkan Anda menggunakan fitur ini di semua browser.

PostCSS Nesting

CSS — PostCSS Nesting
/* Input: CSS dengan nesting */

/* Nesting dengan & (parent selector) */
.card {
  padding: 1.5rem;
  border-radius: 12px;
  background: var(--card-bg);

  & .card-title {
    font-size: 1.25rem;
    font-weight: 700;
    margin-bottom: 0.5rem;
  }

  & .card-content {
    color: var(--text-secondary);
    line-height: 1.6;
  }

  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
  }

  &:first-child {
    margin-top: 0;
  }

  /* Nested media query */
  @media (max-width: 768px) {
    padding: 1rem;

    & .card-title {
      font-size: 1rem;
    }
  }
}

/* Complex nesting */
.navbar {
  display: flex;
  align-items: center;
  padding: 0 1rem;

  & .brand {
    font-size: 1.5rem;
    font-weight: 800;

    & img {
      height: 40px;
    }

    &:hover {
      opacity: 0.8;
    }
  }

  & .nav-links {
    display: flex;
    gap: 1rem;
    list-style: none;

    & li a {
      text-decoration: none;
      padding: 0.5rem 1rem;
      border-radius: 8px;

      &.active {
        background: var(--primary-color);
        color: white;
      }

      &:hover {
        background: var(--hover-bg);
      }
    }
  }
}
CSS — Output (setelah PostCSS)
/* Output: CSS tanpa nesting (flat selectors) */

.card {
  padding: 1.5rem;
  border-radius: 12px;
  background: var(--card-bg);
}

.card .card-title {
  font-size: 1.25rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
}

.card .card-content {
  color: var(--text-secondary);
  line-height: 1.6;
}

.card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transform: translateY(-2px);
}

.card:first-child {
  margin-top: 0;
}

@media (max-width: 768px) {
  .card {
    padding: 1rem;
  }

  .card .card-title {
    font-size: 1rem;
  }
}

.navbar {
  display: flex;
  align-items: center;
  padding: 0 1rem;
}

.navbar .brand {
  font-size: 1.5rem;
  font-weight: 800;
}

.navbar .brand img {
  height: 40px;
}

.navbar .brand:hover {
  opacity: 0.8;
}

.navbar .nav-links {
  display: flex;
  gap: 1rem;
  list-style: none;
}

.navbar .nav-links li a {
  text-decoration: none;
  padding: 0.5rem 1rem;
  border-radius: 8px;
}

.navbar .nav-links li a.active {
  background: var(--primary-color);
  color: white;
}

.navbar .nav-links li a:hover {
  background: var(--hover-bg);
}
⚠️ PostCSS Nesting vs postcss-nested

Ada dua plugin nesting yang populer: postcss-nesting (mengikuti spesifikasi CSS Nesting resmi W3C) dan postcss-nested (meniru perilaku Sass). Gunakan postcss-nesting untuk kompatibilitas dengan native CSS nesting di masa depan. Perbedaan utama: postcss-nesting membutuhkan & di depan setiap nested selector.

6. Custom Selectors & Properties

Custom Selectors

CSS — Custom Selectors
/* Definisikan custom selector sekali, gunakan di mana saja */
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
@custom-selector :--text-input input[type="text"], input[type="email"], input[type="password"];
@custom-selector :--btn :any-link, button;

/* Penggunaan */
:--heading {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  line-height: 1.2;
}

:--text-input {
  padding: 0.75rem 1rem;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  font-size: 1rem;
}

:--btn {
  padding: 0.75rem 1.5rem;
  border-radius: 8px;
  background: var(--primary-color);
  color: white;
  font-weight: 600;
  cursor: pointer;
}

/* Output (setelah PostCSS): */
/* h1, h2, h3, h4, h5, h6 { ... } */
/* input[type="text"], input[type="email"], input[type="password"] { ... } */
/* :any-link, button { ... } */

CSS Custom Properties (Variables)

CSS — Custom Properties
/* Definisikan CSS Custom Properties di :root */
:root {
  /* Colors */
  --color-primary: #3b82f6;
  --color-primary-dark: #2563eb;
  --color-primary-light: #93c5fd;
  --color-surface: #1a1a2e;
  --color-text: #e2e8f0;
  --color-text-secondary: #94a3b8;

  /* Spacing scale */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 2rem;
  --space-xl: 4rem;

  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  /* Border radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
  --radius-full: 9999px;
}

/* Dark theme override */
[data-theme="dark"] {
  --color-surface: #0f172a;
  --color-text: #f1f5f9;
  --color-text-secondary: #94a3b8;
}

/* Menggunakan variables */
.button {
  background: var(--color-primary);
  color: white;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  transition: background 0.2s;
}

.button:hover {
  background: var(--color-primary-dark);
}

Menggunakan Custom Properties di JavaScript

JavaScript
// Membaca CSS Custom Property dari JavaScript
const root = document.documentElement;
const primaryColor = getComputedStyle(root)
  .getPropertyValue('--color-primary');
console.log(primaryColor); // #3b82f6

// Mengubah CSS Custom Property dari JavaScript
root.style.setProperty('--color-primary', '#ef4444');

// Contoh: theme switcher
function setTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme);
  localStorage.setItem('theme', theme);
}

// Toggle dark/light mode
function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  setTheme(current === 'dark' ? 'light' : 'dark');
}

7. postcss-preset-env

postcss-preset-env memungkinkan Anda menggunakan fitur CSS modern (yang belum didukung semua browser) dengan cara menuliskan CSS standar, dan PostCSS akan mentranspilasinya agar kompatibel dengan browser lama. Ini seperti Babel, tapi untuk CSS!

Fitur yang Didukung

postcss.config.js
export default {
  plugins: {
    'postcss-preset-env': {
      // Stage: 0 (experimental) sampai 4 (standar resmi)
      // Stage 3 = fitur yang sangat mungkin jadi standar
      stage: 3,

      // Enable/disable fitur spesifik
      features: {
        'nesting-rules': true,
        'custom-media-queries': true,
        'custom-selectors': true,
        'color-mix': true,
        'is-pseudo-class': true,
        'focus-visible-pseudo-class': true,
        'gap-properties': true,
        'clamp': true
      },

      // Target browser
      browsers: [
        '> 1%',
        'last 2 versions',
        'not dead'
      ],

      // Inject polyfill (untuk fitur yang butuh JS)
      insertBefore: {},
      insertAfter: {}
    }
  }
}

Contoh Modern CSS dengan preset-env

CSS — Modern Features
/* 1. Custom Media Queries */
@custom-media --mobile (max-width: 768px);
@custom-media --tablet (max-width: 1024px);
@custom-media --desktop (min-width: 1025px);

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 1rem;

  @media (--mobile) {
    padding: 0.5rem;
  }
}

/* 2. :is() pseudo-class */
.card :is(h1, h2, h3) {
  font-weight: 800;
}

/* 3. :focus-visible */
button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* 4. color-mix() */
.button-primary {
  background: color-mix(in srgb, var(--color-primary) 80%, black);
}

.button-hover {
  background: color-mix(in srgb, var(--color-primary) 90%, white);
}

/* 5. clamp() untuk responsive typography */
.heading-1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

.heading-2 {
  font-size: clamp(1.25rem, 3vw, 2rem);
}

/* 6. Logical Properties */
.card {
  margin-inline: auto;
  padding-block: 2rem;
  padding-inline: 1.5rem;
  border-inline-start: 4px solid var(--color-primary);
}

8. Plugin Populer

Ekosistem PostCSS memiliki lebih dari 2000 plugin. Berikut beberapa yang paling berguna untuk workflow sehari-hari:

Plugin untuk Development

Plugin Fungsi Install
autoprefixerVendor prefix otomatisnpm i autoprefixer
postcss-nestingCSS Nesting (W3C spec)npm i postcss-nesting
postcss-preset-envModern CSS → compatible CSSnpm i postcss-preset-env
postcss-import@import inlining (seperti Sass)npm i postcss-import
postcss-mixinsMixins (seperti Sass)npm i postcss-mixins
postcss-extend@extend (seperti Sass)npm i postcss-extend

Plugin untuk Production

Plugin Fungsi Install
cssnanoCSS minification & optimizationnpm i cssnano
postcss-sort-media-queriesUrutkan media queriesnpm i postcss-sort-media-queries
postcss-combine-duplicated-selectorsGabungkan selector duplikatnpm i postcss-combine-duplicated-selectors
postcss-purgecssHapus CSS yang tidak digunakannpm i postcss-purgecss
postcss-cssoMinifikasi dengan CSO algorithmnpm i postcss-csso
postcss-urlRewrite URL dalam CSSnpm i postcss-url

cssnano — CSS Minification

postcss.config.js — Production Config
export default {
  plugins: {
    'postcss-preset-env': { stage: 3 },
    autoprefixer: {},
    cssnano: {
      // Preset: default, advanced, atau custom
      preset: ['advanced', {
        // Opsi advanced
        discardComments: {
          removeAll: true    // Hapus semua komentar
        },
        reduceIdents: true,  // Minify keyframe names
        zindex: true,        // Optimize z-index values
        mergeRules: true,    // Gabungkan rules yang sama
        discardDuplicates: true  // Hapus duplikat
      }]
    }
  }
}

postcss-import — File Splitting

CSS — @import dengan postcss-import
/* src/main.css */

/* Import utilities */
@import './utils/variables.css';
@import './utils/mixins.css';
@import './utils/animations.css';

/* Import base styles */
@import './base/reset.css';
@import './base/typography.css';

/* Import components */
@import './components/button.css';
@import './components/card.css';
@import './components/navbar.css';
@import './components/modal.css';

/* Import layouts */
@import './layouts/grid.css';
@import './layouts/container.css';

/* postcss-import akan menggabungkan semua file ini */
/* menjadi satu file CSS output */

9. Integrasi dengan Vite & Framework

Vite

Vite mendukung PostCSS secara native. Cukup buat file postcss.config.js di root proyek dan Vite akan otomatis menggunakannya.

vite.config.js
// vite.config.js — PostCSS sudah terintegrasi
// Tapi Anda bisa override dari sini jika diperlukan
import { defineConfig } from 'vite';

export default defineConfig({
  css: {
    postcss: {
      plugins: [
        // Override postcss config langsung di sini
        // Atau biarkan postcss.config.js yang menangani
      ]
    },
    // Preprocessor options (untuk Sass/SCSS)
    preprocessorOptions: {
      scss: {
        additionalData: `@import "~/assets/variables.scss";`
      }
    }
  }
});

Tailwind CSS + PostCSS

postcss.config.js — Tailwind CSS
// postcss.config.js — Tailwind CSS + PostCSS
export default {
  plugins: {
    // Tailwind CSS adalah PostCSS plugin!
    tailwindcss: {},

    // Autoprefixer — setelah Tailwind
    autoprefixer: {},

    // cssnano — hanya di production
    ...(process.env.NODE_ENV === 'production' ? {
      cssnano: {
        preset: ['default', {
          discardComments: { removeAll: true }
        }]
      }
    } : {})
  }
}

Webpack Integration

webpack.config.js
// webpack.config.js — PostCSS dengan postcss-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',     // Inject CSS ke DOM
          'css-loader',       // Resolve @import dan url()
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  'autoprefixer',
                  'postcss-nesting',
                  ['postcss-preset-env', { stage: 3 }]
                ]
              }
            }
          }
        ]
      }
    ]
  }
};

10. Membuat Custom Plugin

Salah satu kekuatan terbesar PostCSS adalah kemudahan membuat plugin sendiri. Plugin PostCSS adalah fungsi JavaScript yang menerima AST dan memanipulasinya.

Plugin Sederhana

postcss-plugin-rem-to-px.js
// Plugin untuk mengkonversi rem ke px (fallback)
// postcss-plugin-rem-to-px.js

const plugin = {
  postcssPlugin: 'postcss-rem-to-px',

  Declaration(decl) {
    // Cari semua nilai yang mengandung rem
    if (decl.value.includes('rem')) {
      // Konversi rem ke px (base: 16px)
      const remMatch = decl.value.match(/([\d.]+)rem/g);

      if (remMatch) {
        let newValue = decl.value;

        remMatch.forEach(match => {
          const remValue = parseFloat(match);
          const pxValue = remValue * 16;
          newValue = newValue.replace(match, `${pxValue}px /* ${match} */`);
        });

        // Tambahkan declaration baru sebelum yang lama
        decl.cloneBefore({
          prop: decl.prop,
          value: newValue
        });
      }
    }
  }
};

plugin.postcss = true;
export default plugin;

// Penggunaan di postcss.config.js:
// import remToPx from './postcss-plugin-rem-to-px.js';
// plugins: { './postcss-plugin-rem-to-px.js': {} }

Plugin yang Lebih Kompleks

postcss-plugin-theme-variants.js
// Plugin untuk generate theme variants secara otomatis
// postcss-plugin-theme-variants.js

const plugin = {
  postcssPlugin: 'postcss-theme-variants',

  // Plugin initialization
  Once(root, { result }) {
    const themeDecls = [];

    // Cari semua rule dengan @theme-variant
    root.walkAtRules('theme-variant', atRule => {
      const themeName = atRule.params;
      const themeRule = atRule.clone();

      // Buat selector baru dengan data-theme attribute
      themeRule.selector = `[data-theme="${themeName}"] ${atRule.parent.selector}`;

      // Tambahkan ke root
      themeDecls.push(themeRule);
      atRule.remove();
    });

    // Tambahkan semua theme variants di akhir file
    if (themeDecls.length > 0) {
      const wrapper = postcss.rule({
        selector: ''
      });

      themeDecls.forEach(rule => {
        root.append(rule);
      });
    }
  }
};

plugin.postcss = true;
export default plugin;

// Penggunaan:
// .button {
//   background: blue;
//
//   @theme-variant dark {
//     background: white;
//     color: black;
//   }
//
//   @theme-variant high-contrast {
//     background: black;
//     color: yellow;
//   }
// }

// Output:
// .button { background: blue; }
// [data-theme="dark"] .button { background: white; color: black; }
// [data-theme="high-contrast"] .button { background: black; color: yellow; }
💡 Tips

Saat membuat plugin PostCSS, gunakan postcss.plugin() API atau object format dengan postcssPlugin property. Object format (PostCSS 8+) lebih disarankan karena mendukung parallel execution dan tree-shaking yang lebih baik.

11. Quiz Pemahaman

Uji pemahaman Anda tentang PostCSS dengan quiz interaktif berikut!

1. Apa itu PostCSS?

2. Apa fungsi utama Autoprefixer?

3. Bagaimana cara PostCSS memproses CSS?

4. Plugin apa yang menggabungkan banyak fitur CSS modern sekaligus?

5. Di mana file konfigurasi PostCSS biasanya disimpan?

📝 Ringkasan

Dalam tutorial ini, Anda telah mempelajari:

  • Pengenalan PostCSS dan perbedaannya dengan Sass/Less
  • Instalasi, setup, dan konfigurasi PostCSS
  • Cara kerja PostCSS: parsing, transformasi, generation (AST)
  • Autoprefixer untuk vendor prefix otomatis
  • CSS Nesting dengan postcss-nesting
  • Custom Selectors dan CSS Custom Properties
  • postcss-preset-env untuk CSS modern
  • Plugin populer: cssnano, postcss-import, postcss-mixins
  • Integrasi dengan Vite, Tailwind, dan Webpack
  • Membuat custom PostCSS plugin