@kompas/ui
Standardizing frontend UI infrastructure across Kompas digital products
Stack: Vue.js, Tailwind CSS, Rollup, Storybook
The Problem Nobody Wanted to Talk About
At first glance this looked like a visual inconsistency problem. It wasn’t. It was organizational.
Every team at Kompas built their own buttons. Their own cards. Their own chips. Each product—kompas.id, bebas.kompas.id, the internal tools—had slightly different shades of blue, slightly different border radiuses, slightly different everything.
The designer team kept sending design files. The developers kept eyeballing hex codes. And every time someone needed a simple card component, they'd copy-paste from whatever project was open at the time.
Without a shared system, teams shipped quickly inside their own products but slowed each other down at the company level. UI drift increased maintenance overhead, design reviews became subjective debates, and onboarding new developers meant relearning the same patterns over and over.
What we actually needed was shared frontend infrastructure. Something teams could install once and stop thinking about.
Ownership
I owned the technical direction end to end: architecture, tooling, component APIs, and how releases were cut and consumed.
Adoption across teams was completely opt-in. There was no mandate from above. That meant the system had to earn adoption. No one was forced to use it. That constraint shaped every decision, from API simplicity to documentation quality.
Starting From Scratch
The first commit landed in July 2019. Pure boilerplate—the kind of optimistic Initial commit that doesn’t do much except signal intent.
The real work didn’t start until February 2020. I rebuilt the entire setup: Vue.js as the component framework, Rollup for bundling, and Tailwind CSS for styling.
I chose Rollup over webpack for a simple reason. For a library that other projects consume, Rollup produces cleaner output: better tree-shaking, native ES module support, and smaller bundles. Webpack is great for applications; Rollup is built for libraries.
Getting Tailwind to work properly with Storybook took longer than it should have. That css modules & postcss run on Storybook.. yay commit? That was after three days of config hell. PostCSS versions, purge settings, the whole mess.
The Components
Ten components formed the initial primitive set, enough to cover most UI needs without over-engineering the library:
kButton – The obvious starting point. Click handlers, variants, disabled states.
kCard – Surprisingly complex. Needed to work as a plain div, a link, a router-link, and a nuxt-link depending on props.
kImg – Aspect ratio preservation, lazy loading, srcset support. Critical for a media site.
kChip – Tags and labels. Closeable or not.
kAvatar – Profile pictures with fallback initials.
kBar – Top navigation bars.
kBreadcrumb – Navigation trails.
kProgress – Loading indicators with that shimmer effect.
kDialog – Modals. Everyone needs modals.
kAlert – Notifications and warnings.
Each component shipped with its own Storybook story and basic unit tests. The documentation lived on an internal Kompas UI development server, auto-deployed whenever we pushed to main.
Design Tokens
One day in February 2020 tells the story better than anything else: four separate commits in one day for colours, fonts, margins, and box shadows. That was the day we codified Kompas's visual language into a Tailwind config.
brand: {
1: '#00559A',
2: '#00447B'
}Those two blues. The newspaper blue. Now locked into code instead of floating in someone's design file.
That config file ended up being more valuable than most individual components. When the brand needed to update, we changed one file. Every product using the system got the update automatically.
The Lodash Decision
At some point the bundle size started creeping up enough to be noticeable. The culprit? Lodash. We were importing the entire library for maybe three utility functions.
The commit says it plainly: "bikin helper sendiri pengganti lodash" (made our own helpers to replace lodash). I wrote custom utilities for what we actually needed. Bundle size dropped immediately.
For a news site where readers might be on mid-range Android phones with spotty Indonesian mobile connections, every kilobyte matters. For us, performance wasn’t a “nice to have”. It was the baseline.
The Tailwind Decision
One commit message: "balikin ke tailwind sori hen bolak balik gini" (going back to tailwind, sorry for flip-flopping).
I'd briefly tried moving to CSS modules for better scoping. It worked, technically. But it meant giving up Tailwind's utility classes, and the team was already comfortable with them.
I chose developer experience over technical neatness. A design system nobody wants to use is a failed design system. Sometimes the right call isn’t the cleanest architecture, but the one the team will actually use.
What Shipped
The library published to npm as @kompas/ui. Install with npm i -D @kompas/ui, import the components, done.
By the time I moved on, the library powered:
kompas.id (the main subscription product)
Internal editorial tools
Marketing campaign pages
At least 117 commits spread across roughly two years. Ten components that gave Kompas a consistent visual language across products.
Impact
The copy-paste culture mostly disappeared. When a developer needed a button, they installed the package instead of hunting through old projects. UI decisions stopped being subjective debates and started being contract-driven.
Designers had a living reference. Instead of asking "did you use the right shade?", they could point developers to the internal development server and say "use kButton with variant primary." Design became prescriptive instead of advisory.
New projects no longer started from zero. The marketing team could launch campaign pages without waiting for someone to rebuild basic UI from scratch. The editorial tools team stopped reinventing cards and chips.
More importantly, it changed how teams worked together day to day. Frontend work shifted from reinventing primitives to assembling them. That reduced cognitive load for developers and decision fatigue for designers.
This move effectively cut UI development time for new projects in half.
For a subscription-based news business, consistency, performance, and speed aren’t aesthetic details. They show up directly in trust, conversion, and retention.
Lessons
I learned that components are the easy part. Alignment is the real work. The components are the easy part. The hard part is getting teams to adopt them without a mandate.
Bundle size behaves like a feature whether you treat it like one or not. Especially when your readers are spread across an archipelago with inconsistent connectivity.
The Tailwind config ended up being infrastructure, not configuration. The Tailwind config with Kompas's design tokens was the single source of truth that made everything else work.
Adoption followed developer experience, not architectural elegance. I chose Tailwind over CSS modules because the team would actually use it. Technical purity means nothing if nobody ships with your system.
Like what you see?
I'm open to freelance projects and full-time roles. If you need someone who obsesses over structure and ships clean code — let's talk.
:quality(70))