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 |
|---|---|---|---|
| Pendekatan | Plugin-based (modular) | All-in-one (monolithic) | All-in-one (monolithic) |
| Fitur | Terinstall sesuai kebutuhan | Semua fitur bawaan | Semua 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 |
| Ekosistem | 2000+ plugins | Terbatas | Terbatas |
Mengapa PostCSS Penting?
┌──────────────────────────────────────────────────────────┐
│ 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
# 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 — 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 — 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
# 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
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)
// 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 |
|---|---|---|
Root | Seluruh file CSS | Root node dari AST |
AtRule | @media ... | At-rule (media query, import, dll.) |
Rule | .card { } | Selector + blok deklarasi |
Declaration | color: red | Property + 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
/* 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;
}
/* 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
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 — 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
/* 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);
}
}
}
}
/* 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);
}
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
/* 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)
/* 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
// 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
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
/* 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 |
|---|---|---|
autoprefixer | Vendor prefix otomatis | npm i autoprefixer |
postcss-nesting | CSS Nesting (W3C spec) | npm i postcss-nesting |
postcss-preset-env | Modern CSS → compatible CSS | npm i postcss-preset-env |
postcss-import | @import inlining (seperti Sass) | npm i postcss-import |
postcss-mixins | Mixins (seperti Sass) | npm i postcss-mixins |
postcss-extend | @extend (seperti Sass) | npm i postcss-extend |
Plugin untuk Production
| Plugin | Fungsi | Install |
|---|---|---|
cssnano | CSS minification & optimization | npm i cssnano |
postcss-sort-media-queries | Urutkan media queries | npm i postcss-sort-media-queries |
postcss-combine-duplicated-selectors | Gabungkan selector duplikat | npm i postcss-combine-duplicated-selectors |
postcss-purgecss | Hapus CSS yang tidak digunakan | npm i postcss-purgecss |
postcss-csso | Minifikasi dengan CSO algorithm | npm i postcss-csso |
postcss-url | Rewrite URL dalam CSS | npm i postcss-url |
cssnano — CSS Minification
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
/* 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 — 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
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 — 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
// 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
// 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; }
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?
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