Setiap kali menemukan website dengan UI yang rapi dan konsisten, respons pertama saya sebagai web developer biasanya sama:
F12. Inspect Element.
Mulai dari mengecek warna, font-size, border-radius, spacing, shadow, sampai detail kecil lain yang membuat sebuah interface terasa konsisten.
Kadang saya hanya ingin mengetahui warna yang digunakan sebuah tombol. Tapi tanpa terasa, saya sudah scrolling satu halaman penuh untuk mencoba memahami visual language di balik desain tersebut.
Kalau hanya satu atau dua komponen, proses ini masih cukup mudah.
Masalahnya mulai terasa ketika tujuannya adalah memahami design system dari sebuah website secara keseluruhan.
Warna apa yang sebenarnya digunakan sebagai primary token?
Bagaimana typography scale-nya?
Apakah spacing benar-benar konsisten di berbagai halaman?
Apakah button, card, input, dan badge mengikuti pola visual yang sama?
Dan bagian yang paling melelahkan: setelah semua informasi tersebut dikumpulkan, dokumentasinya masih harus dibuat secara manual.
Dari situ muncul satu pertanyaan sederhana:
Kenapa tidak membuat tool yang bisa melakukan reverse engineer design system sebuah website secara otomatis?
Bukan sekadar mengambil warna, tetapi mencoba membaca pola visual, mengelompokkannya menjadi design token, mendeteksi komponen UI, kemudian mengubah semuanya menjadi dokumentasi yang benar-benar bisa digunakan.
Dari eksperimen akhir pekan tersebut, lahirlah Design System Reverse Engineer — sebuah Chrome Extension berbasis Manifest V3, React 19, TypeScript, Vite, Tailwind CSS, dan Shadow DOM.
Di artikel ini saya ingin membahas bagaimana extension ini dibangun, alasan di balik beberapa keputusan arsitekturnya, serta beberapa gotchas teknis yang membuat proses pengembangannya jauh lebih menarik dari yang awalnya dibayangkan.
Masalah yang Mau Diselesaikan
Ketika melakukan audit atau riset design system sebuah website, workflow-nya biasanya cukup repetitif:
- Inspect tombol untuk mengetahui
background-color,border-radius,padding, danbox-shadow. - Inspect heading dan paragraph untuk mengetahui font family, font-size, weight, dan line-height.
- Mengumpulkan warna yang digunakan pada surface, border, icon, dan text.
- Mencatat semuanya ke Notepad, spreadsheet, atau dokumen terpisah.
- Menyusun ulang semua informasi tersebut menjadi design token.
Kemudian proses yang sama harus diulang lagi ketika berpindah ke halaman lain.
Padahal sebagian besar informasi yang dibutuhkan sebenarnya sudah tersedia di browser.
Karena itu, targetnya cukup sederhana: membuat sebuah tool yang bisa mengambil sebanyak mungkin informasi visual yang relevan dari sebuah halaman web, kemudian mengubahnya menjadi dokumentasi design system yang lebih terstruktur.
Extension ini berfokus pada lima area utama:
- Color System — HEX/RGB dan indikasi peran visual seperti Primary, Surface, Text, Border, dan Accent.
- Typography — font family, weight, size, line-height, serta pengelompokan seperti Display, Heading, Body, Caption, Label, dan Navigation.
- Design Tokens — spacing, border-radius, shadow, dan ukuran umum yang digunakan di halaman.
- UI Components — deteksi berbasis heuristik untuk pola seperti Button, Card, Input, Badge, Navbar, Modal, dan komponen umum lainnya.
- Documentation — WYSIWYG editor, Markdown export, CSS variables, dan implementation snippets.
Jadi workflow-nya berubah dari:
Inspect → Copy → Paste
menjadi:
Scan → Analyze → Structure → Document
Arsitektur Extension
Membangun Chrome Extension menggunakan React ternyata sedikit berbeda dibandingkan membangun web application biasa.
Stack yang digunakan dalam eksperimen ini adalah:
- Chrome Extension Manifest V3
- React 19
- TypeScript
- Vite
- Tailwind CSS
- Shadow DOM
- Chrome Extension APIs
Secara high-level, extension ini terdiri dari beberapa bagian:
- Content Script — berjalan pada halaman target dan melakukan proses scanning.
- React UI — menangani interface extension, termasuk off-canvas panel.
- Service Worker — menangani proses background dan komunikasi antar bagian extension.
- Analyzer — mengolah informasi DOM dan CSS menjadi design token serta component metadata.
- Markdown Generator — mengubah hasil analisis menjadi dokumentasi terstruktur.
- Export Layer — menghasilkan CSS variables dan implementation snippets.
Salah satu tantangan pertama ternyata bukan analyzer-nya.
Masalahnya justru bagaimana memasukkan React UI ke website milik orang lain tanpa membuat CSS dari kedua sisi saling mengganggu.
1. Shadow DOM untuk CSS Isolation
Ini menjadi salah satu keputusan arsitektur yang paling penting.
Bayangkan extension dijalankan pada website yang memiliki CSS seperti:
button {
all: unset;
}
* {
box-sizing: border-box;
}
Atau sebaliknya, extension menggunakan Tailwind dan stylesheet-nya secara tidak sengaja memengaruhi halaman target.
Jika UI extension langsung di-inject ke DOM biasa, kemungkinan terjadinya CSS conflict cukup besar.
Solusinya adalah menggunakan Shadow DOM.
Extension membuat host element khusus:
const shadowHost = document.createElement('div');
shadowHost.id = 'chrome-ds-re-root';
shadowHost.style.position = 'fixed';
shadowHost.style.zIndex = '2147483647';
shadowHost.style.top = '0';
shadowHost.style.right = '0';
const shadowRoot = shadowHost.attachShadow({
mode: 'open'
});
Stylesheet extension kemudian dimasukkan langsung ke dalam Shadow Root:
const styleEl = document.createElement('style');
styleEl.textContent = stylesText;
shadowRoot.appendChild(styleEl);
Setelah itu React di-render di dalam environment tersebut:
const appContainer = document.createElement('div');
shadowRoot.appendChild(appContainer);
const reactRoot = createRoot(appContainer);
reactRoot.render(<App />);
Dengan pendekatan ini, UI extension memiliki boundary styling sendiri.
CSS dari website target tidak secara langsung masuk ke component tree extension, dan stylesheet extension juga tidak bebas memengaruhi DOM utama website.
Untuk extension yang harus berjalan di berbagai website, isolation seperti ini menjadi salah satu bagian terpenting dari arsitekturnya.
2. Tragedi Vite Code Splitting
Ini mungkin salah satu bagian yang paling bikin pusing selama proses development.
Build pertama terlihat baik-baik saja.
Tapi ketika dijalankan di Chrome, muncul beberapa runtime error yang cukup aneh.
Bahkan beberapa output CSS terlihat seperti terpotong:
7uto;
margin-t0;
Awalnya saya mengira masalahnya ada di Tailwind atau proses injection stylesheet.
Ternyata masalahnya berkaitan dengan struktur bundle yang dihasilkan.
Vite menggunakan Rollup untuk proses bundling dan dapat melakukan code splitting menjadi beberapa chunk.
Untuk web application biasa, pendekatan ini justru sangat berguna karena browser dapat memuat bagian kode sesuai kebutuhan.
Namun content script memiliki deployment model yang berbeda. Dalam implementasi ini, content script perlu tersedia sebagai bundle standalone yang predictable.
Karena itu konfigurasi Rollup diubah menjadi:
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
cssCodeSplit: false,
rollupOptions: {
input: {
content: resolve(
import.meta.dirname,
'src/content/content.ts'
),
'service-worker': resolve(
import.meta.dirname,
'src/background/service-worker.ts'
),
},
output: {
entryFileNames: '[name].js',
manualChunks: undefined,
},
},
},
});
Hasilnya adalah content.js sebagai standalone bundle yang membawa dependency yang dibutuhkan oleh content script.
Ukuran bundle akhirnya sekitar 680 KB.
Memang lebih besar dibandingkan bundle yang menggunakan aggressive code splitting, tetapi untuk use case ini trade-off tersebut cukup masuk akal.
Pelajaran yang didapat cukup sederhana:
Strategi bundling yang optimal untuk web application belum tentu optimal untuk browser extension.
Dalam kasus ini, predictable deployment lebih penting dibandingkan mengoptimalkan setiap kilobyte dari content script.
3. Multi-Page Crawling Mode
Setelah single-page scanner berhasil berjalan, muncul masalah berikutnya.
Satu halaman belum tentu cukup untuk memahami design system sebuah website.
Homepage mungkin menggunakan satu shade warna biru, sementara halaman pricing menggunakan warna yang sedikit berbeda.
Sebuah card bisa menggunakan 12px radius di satu halaman dan 8px di halaman lain.
Typography juga bisa terlihat konsisten sampai kita membuka bagian website yang berbeda.
Jika hanya melakukan scanning pada satu URL, pattern lokal bisa dengan mudah dianggap sebagai global design decision.
Karena itu extension ini memiliki Multi-Page Crawling Mode.
Workflow-nya kurang lebih seperti ini:
- Menemukan internal links dalam domain yang sama.
- Mengumpulkan URL yang relevan.
- Memproses halaman-halaman tersebut secara asynchronous.
- Menyimpan hasil analisis dari setiap halaman.
- Menggabungkan semuanya menjadi Master Design System Specification.
Setelah data digabungkan, extension dapat melihat frekuensi penggunaan token dan komponen.
Misalnya sebuah warna muncul di hampir seluruh halaman.
Atau komponen seperti Navbar dan Footer muncul secara konsisten di berbagai halaman.
Data tersebut kemudian dapat digunakan untuk menghasilkan Global Consistency Score.
Color Consistency
██████████████████░░ 89%
Typography Consistency
████████████████░░░░ 81%
Spacing Consistency
██████████████░░░░░░ 72%
Component Consistency
█████████████████░░░ 85%
Score tersebut bukan dimaksudkan untuk menentukan apakah sebuah website memiliki design system yang "bagus" atau "buruk".
Score ini lebih tepat dianggap sebagai indikator konsistensi pattern visual yang berhasil dideteksi di antara halaman yang dianalisis.
Tujuannya bukan memberikan judgment.
Tujuannya adalah membuat pattern yang sebelumnya tersembunyi menjadi lebih mudah dilihat.
Dari Raw CSS ke Design Token
Di sinilah project ini mulai menjadi lebih menarik.
Browser bisa memberikan value mentah seperti:
rgb(37, 99, 235)
atau:
16px
atau:
border-radius: 8px;
Tetapi design system membutuhkan konteks.
Mengetahui bahwa sebuah warna adalah:
#2563EB
tentu berguna.
Namun mengetahui bahwa warna tersebut terutama digunakan pada button, link, dan active state jauh lebih berguna.
Karena itu analyzer mencoba menghubungkan raw values dengan konteks penggunaannya:
Primary
#2563EB
Usage:
Button, Link, Active State
Frequency:
47%
Hal yang sama berlaku untuk typography.
Daripada hanya menghasilkan:
font-size: 16px
font-weight: 400
analyzer dapat mencoba mengelompokkannya berdasarkan penggunaan:
Body
16px / 24px
400
Heading
32px / 40px
700
Proses ini menggunakan informasi DOM, computed styles, karakteristik element, dan sejumlah heuristik.
Dan justru di sinilah salah satu bagian tersulit dari project ini.
Reverse engineering design system bukan hanya masalah membaca CSS.
Kita juga harus mencoba memahami konteks bagaimana CSS tersebut digunakan.
Kenapa Markdown?
Ide awalnya sebenarnya cukup sederhana: hasil analisis diekspor menjadi JSON.
Untuk machine-readable data, JSON memang sangat nyaman.
Tapi untuk dokumentasi yang dibaca manusia, hasilnya tidak terlalu menyenangkan.
Tujuan akhirnya adalah menghasilkan sesuatu yang bisa dibaca, diedit, dibagikan, dan digunakan sebagai starting point untuk implementation.
Karena itu output akhirnya dibuat dalam bentuk Markdown.
Contohnya:
# Design System
## Colors
### Primary
- Value: #2563EB
- Usage: Button, Link, Active State
## Typography
### Heading
- Font: Inter
- Size: 32px
- Weight: 700
- Line Height: 40px
## Spacing
- 4px
- 8px
- 12px
- 16px
- 24px
- 32px
Extension ini juga memiliki WYSIWYG editor, karena dokumentasi yang dihasilkan secara otomatis hampir selalu membutuhkan sedikit human refinement.
Jadi workflow-nya menjadi:
Generate → Review → Edit → Export
Data token yang sama juga dapat diubah menjadi CSS variables:
:root {
--color-primary: #2563eb;
--color-surface: #ffffff;
--color-text: #111827;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
}
Dengan begitu, hasil reverse engineering tidak berhenti sebagai dokumentasi.
Hasilnya bisa menjadi starting point untuk implementasi project baru.
Lesson Learned
Ide awal project ini sebenarnya terdengar sederhana:
Scan DOM → Extract CSS → Generate Markdown.
Ternyata di balik workflow tersebut ada cukup banyak problem menarik.
Project ini akhirnya menjadi eksplorasi tentang:
- Bagaimana Manifest V3 bekerja sebagai platform browser extension modern.
- Bagaimana mengisolasi React UI menggunakan Shadow DOM.
- Bagaimana Vite dan Rollup bekerja di luar deployment model SPA tradisional.
- Bagaimana content script berinteraksi dengan website yang tidak kita kontrol.
- Bagaimana mengubah data
computedStylemenjadi design token yang lebih bermakna. - Dan yang paling penting, bagaimana melakukan reverse engineering sebuah design system dari rendered output-nya, bukan dari design file aslinya.
Bagian terakhir mungkin justru menjadi konsep paling menarik dari project ini.
Sebuah website mungkin tidak memiliki public design system.
Mungkin tidak ada Figma file yang bisa diakses.
Bahkan mungkin tidak ada dokumentasi design system sama sekali.
Tapi browser tetap memperlihatkan jejak dari design system tersebut melalui rendered interface.
Colors.
Typography.
Spacing.
Radius.
Shadows.
Component patterns.
Tantangannya adalah mengubah semua jejak tersebut menjadi sesuatu yang benar-benar berguna.
What's Next?
Versi sekarang masih jauh dari sempurna.
Ada beberapa hal yang masih ingin dieksplorasi:
- Semantic color detection yang lebih akurat.
- Pattern recognition yang lebih baik untuk component yang kompleks.
- Responsive behavior analysis.
- Breakpoint detection.
- Dark/light theme comparison.
- Figma Variables export.
- Design token JSON export.
- Design system comparison antar website.
- Dan mungkin yang paling menarik: AI-assisted design system analysis.
Rule-based analysis memiliki batas.
Mendeteksi bahwa sebuah warna muncul 200 kali relatif mudah.
Menentukan bahwa warna tersebut kemungkinan besar adalah brand primary berdasarkan di mana dan bagaimana warna tersebut digunakan jauh lebih menarik.
Di situlah AI mulai bisa memainkan peran.
Daripada hanya bertanya:
"Warna apa saja yang digunakan website ini?"
generasi berikutnya dari tool seperti ini mungkin bisa bertanya:
"Design decision apa yang direpresentasikan oleh value-value tersebut?"
Dan jika itu berhasil, project ini bisa berkembang dari sekadar CSS extraction tool menjadi sesuatu yang lebih dekat dengan design system intelligence tool.
Source Code
Source code project ini sudah dirapikan dan tersedia di GitHub:
GitHub: github.com/luftinur/chrome-designmd
Kalau ingin mencoba atau melakukan eksperimen sendiri, repository tersebut bisa di-clone dan extension dapat di-build secara lokal mengikuti setup yang tersedia di project.
Kalau ada ide fitur lain yang bisa membuat proses reverse engineering design system menjadi lebih berguna, feel free untuk membagikannya.
Karena pada akhirnya, project ini dimulai dari satu masalah yang sangat sederhana:
Capek harus Inspect Element satu per satu.
