Merilis design system di ui.pangaea.id
ui.pangaea.id adalah katalog design systemⓘ Pangaea — tempat setiap komponen UI kami hidup sebagai halaman yang bisa Anda telusuri, kendalikan lewat kontrol, dan salin cara pakainya. Artikel ini menceritakan bagaimana kami mengeluarkannya dari satu app, mengemasnya jadi paket ber-versi, merilisnya ke sebuah registry, lalu men-deploy katalognya ke subdomain sendiri — dan mengotomatiskan rilis tiap komponen dengan Changesetsⓘ.

Kenapa kami mengeluarkan UI-nya
Semua tampilan pangaea.id lahir dari satu sistem rv-*: token warna, tipografi, dan komponen React —
tapi semuanya terkurung di dalam satu app. Tak ada surface lain yang bisa memakainya tanpa
menyalin-tempel. Tujuannya jadi jelas: keluarkan sistem itu jadi primitif yang berdiri sendiri,
ber-versi, dan bisa dikonsumsi — supaya pangaea.id dan surface Pangaea berikutnya menarik dari satu
sumber yang sama.
Katalognya sendiri punya tugas: satu tempat untuk menelusuri tiap komponen, menguji tiap state-nya (tekan tombolnya, buka modalnya), dan menyalin cara pakainya yang persis. Panel kanan memunculkan satu kontrol per prop, jadi Anda bisa memutar sebuah komponen ke bentuk yang Anda mau, lalu menyalin snippet yang sudah jadi.
Satu repo, banyak paket — keputusannya
Ada tiga cara mengemas sekumpulan komponen. Kami memilih yang ketiga, dan alasannya penting.
- Semua komponen dalam satu
@labspangaea/ui. Perbaiki satu tombol → semua orang kebagian versi baru, walau tak ada yang berubah untuk mereka. Consumer menarik seluruh sistem hanya untuk memakai satu kartu.
- Tiap komponen jadi repo GitHub sendiri (polyrepo). Ber-versi rapi, tapi 37 repo untuk di-clone, di-CI, dan dijaga sinkron. Mengubah satu token warna berarti pull request di banyak tempat.
- Satu monorepoⓘ berisi satu paket ber-versi per komponen —
@labspangaea/ds-button,@labspangaea/ds-card, dan seterusnya. Satu tempat untuk dibaca dan di-CI, tapi tiap komponen naik versi sendiri-sendiri. Ini bentuk yang dipakai Radix, MUI, dan Chakra.
Perekat yang membuatnya bekerja ada di setiap package.json: React dipasang sebagai
peer dependencyⓘ (bukan dependency biasa), jadi consumer tak
pernah menarik salinan React kedua; tiap paket mengirim CSS-nya sendiri yang sudah dikompilasi
dari --rv-* token; dan semua paket duduk dalam satu npm workspaceⓘ
sehingga saling terhubung secara lokal saat pengembangan. Hasilnya: @labspangaea/ds-button bisa
dirilis ke 0.1.2 sementara 36 paket lain tetap di 0.1.0.
Cara katalognya di-deploy ke ui.pangaea.id
Katalog itu situs statis (dibangun oleh Astro). Ia dikompilasi ke folder dist/, lalu di-deploy ke
Cloudflare Pagesⓘ — pipeline yang persis sama seperti pangaea.id sendiri. Bagian yang khas hanya
dua: membuat project Pages-nya, lalu memasang subdomain-nya.
Langkah 1 — buat project Pages (sekali)
wrangler pages deploy tak otomatis membuat project; ia error Project not found. Buat sekali dulu,
dari terminal:
npx wrangler login # OAuth di browser, sekali saja
npx wrangler pages project create pangaea-ui --production-branch main
Namanya, pangaea-ui, harus sama persis dengan --project-name di workflow deploy. Setelah itu tiap
push ke main membangun katalog dan menjalankan wrangler pages deploy apps/gallery/dist --project-name=pangaea-ui — deploy live dalam hitungan detik.
Langkah 2 — arahkan subdomain lewat CNAME
Situs sudah live di pangaea-ui.pages.dev. Untuk memberinya alamat cantik ui.pangaea.id, tambahkan
custom domain ke project Pages-nya, lalu satu CNAMEⓘ di DNS:
ui.pangaea.id CNAME pangaea-ui.pages.dev
Karena DNS pangaea.id sudah ada di Cloudflare, menambahkan custom domain di
Workers & Pages → pangaea-ui → Custom domains → ui.pangaea.id membuat record CNAME itu otomatis
dan menyediakan sertifikat TLS-nya. Buka ui.pangaea.id, dan katalognya muncul di root.
Merilis satu komponen — cara kerja Changesets
Ini bagian intinya. Bagaimana perubahan pada satu tombol berubah jadi versi 0.1.2 yang terpublikasi,
tanpa Anda pernah mengetik nomor versi dengan tangan.
Sebuah changeset adalah satu berkas .changeset/*.md kecil yang Anda commit bersama perubahan
Anda. Isinya cuma dua hal: paket mana yang naik dan seberapa besar (patch/minor/major), plus satu
baris changelog.
---
"@labspangaea/ds-button": patch
---
Tambah prop `loading` ke Button.
Dari situ, tiga perintah melakukan sisanya — dan Anda hampir selalu hanya menjalankan yang pertama:
changeset— membuat berkas.mddi atas (memilih paket + ukuran + ringkasan).changeset version— menerapkan semua changeset: menaikkanversiondipackage.json, menulisCHANGELOG.md, lalu menghapus berkas changeset yang sudah dipakai.changeset publish— mempublikasikan paket mana pun yang versinya belum ada di registry, dan memasang git tag-nya.
Ukuran naiknya versi mengikuti semantic versioningⓘ: patch
(0.1.0 → 0.1.1) untuk perbaikan, minor (0.1.0 → 0.2.0) untuk fitur baru yang kompatibel,
major (0.1.0 → 1.0.0) untuk perubahan yang memutus kompatibilitas. Publikasinya menuju
GitHub Packagesⓘ di bawah scopeⓘ
@labspangaea — cocok dengan pemilik repo, karena itulah syarat GitHub Packages.
Pipeline rilisnya, ujung ke ujung
Ketika sebuah changeset mendarat di main, satu CI/CDⓘ run melakukan
seluruh rantai ini sendiri:
Dari 2 PR jadi 1
Rilis pertama kali butuh dua merge manual. Kami memperbaikinya jadi satu. Bedanya ada di siapa yang men-merge Version PR — Anda, atau bot-nya.
- PR Anda (kode + changeset) → Anda merge.
- Version PR (naikkan versi + changelog) → Anda merge lagi. Langkah manual kedua ini yang merepotkan.
- PR Anda (kode + changeset) → Anda merge.
- Version PR → bot yang membuka dan men-merge-nya, lalu mempublikasikan — semua dalam satu run. Anda tak menyentuh PR kedua.
Triknya: merge yang dilakukan GITHUB_TOKEN bawaan tidak memicu run workflow baru (GitHub
memblokirnya agar tak jadi loop). Jadi alih-alih menunggu merge memicu publish, workflow-nya
men-merge Version PR lalu mem-publish di run yang sama. Tak perlu Personal Access Token, tak perlu
merge kedua. Satu-satunya langkah manual yang tersisa di seluruh rilis adalah menulis changeset-nya.
Berikutnya — memindahkan www.pangaea.id ke design system
Katalog di ui.pangaea.id adalah rumah paket-paketnya; langkah berikutnya adalah membuat
www.pangaea.id mengonsumsinya. apps/labs akan menambahkan paket @labspangaea/ds-* yang
dipakainya, meng-import-nya saat build, dan vite-react-ssg mem-prerender-nya — jadi HTML tetap
lengkap untuk crawler, CSP tetap bersih, dan deploy tetap statis (model yang aman untuk SEO; tak ada
fetch saat runtime).
revamp.css lalu digantikan bertahap oleh CSS dari @labspangaea/ds-tokens plus komponen-komponennya.
Karena nama --rv-* token dipertahankan, migrasinya mekanis: satu komponen ditukar dalam satu waktu,
diverifikasi, lalu lanjut ke berikutnya. Sistem yang tadinya terkurung di satu app kini jadi lapisan
UI bersama untuk seluruh surface Pangaea — dengan www yang pertama.
Cerita di balik keputusannya — runtime module federation yang nyaris kami bangun tapi tidak — ada di build diary: Federasi yang tak kami bangun.
Pertanyaan umum
Kenapa satu paket per komponen, bukan satu paket besar?
Supaya tiap komponen naik versi sendiri-sendiri. Dengan satu paket besar, memperbaiki satu tombol memaksa versi baru untuk semua orang — walau tak ada yang berubah bagi mereka, dan consumer harus menarik seluruh sistem hanya untuk memakai satu kartu. Satu paket ber-versi per komponen (bentuk Radix/MUI/Chakra) berarti @labspangaea/ds-button bisa naik ke 0.1.2 sementara 36 paket lain tetap di 0.1.0. Semuanya tetap dalam satu monorepo, jadi masih satu tempat untuk dibaca dan di-CI.
Kenapa GitHub Packages, bukan npm publik?
Karena paket-paketnya hidup di repo yang sama dengan pemiliknya (labspangaea), dan GitHub Packages memetakan scope npm ke akun GitHub bernama sama — jadi @labspangaea/* terbit langsung di bawah akun yang memiliki repo, tanpa registry terpisah untuk dikelola. Consumer menambahkan satu baris .npmrc untuk menunjuk scope @labspangaea ke GitHub Packages.
Apakah katalognya butuh JavaScript untuk berjalan?
Katalognya adalah situs statis (Astro) dengan satu island React untuk kontrol interaktif. Halamannya di-prerender jadi HTML, lalu island-nya menghidupkan kontrol dan kanvas live. Komponen yang dipublikasikan sendiri mengirim CSS biasa dari --rv-* token, jadi consumer non-Tailwind pun bisa memakainya tanpa runtime tambahan.
Bagaimana saya merilis update sebuah komponen?
Jalankan npm run changeset, pilih paket dan ukuran naiknya (patch/minor/major) plus satu baris changelog, lalu commit dan push berkas itu ke main. CI membuka Version PR, meng-auto-merge-nya, dan mempublikasikan hanya paket yang berubah — satu run, tanpa merge manual. Satu-satunya langkah manual adalah menulis changeset-nya.
Sources