webcake-ui-kit
v1.0.30
Published
UI Kit for Vue 2 && 3 - Pure Options API
Readme
webcake-ui-kit
The Vue UI kit that doesn't make you choose.
One library. Two Vue versions. Zero build step. Ship the same components to Vue 2.7 and Vue 3 — from a single source, with one import.
📖 Live Docs & Storybook → ui.webcake.io
Browse every component, prop, slot, and variant — running live in the browser.
👋 Welcome
Migrating from Vue 2 to Vue 3 is painful enough — your UI library shouldn't make it worse.
Most Vue component libraries force you to pick a side. Choose Vue 2 → you're stuck. Choose Vue 3 → you have to rewrite the app first. Either way, you carry the cost.
webcake-ui-kit refuses that tradeoff. Every component is hand-authored under strict dual-compatibility rules so the same import compiles, renders, and behaves identically on Vue 2.7 and Vue 3.4+ — from a codebase you can grep, fork, and theme as if it were your own.
import { WkButton, WkDialog, WkInput } from 'webcake-ui-kit'
// ✅ Vue 2.7 — works
// ✅ Vue 3.x — works
// ✅ Same API. Same styles. Same behavior. One source of truth.No -vue2 package. No -vue3 package. No build artifact gymnastics. Just .vue files, the way the framework intended.
⚡ Quick start
Get a button on screen in under 60 seconds.
1. Install
npm install webcake-ui-kitpnpm add webcake-ui-kityarn add webcake-ui-kitPeer dependency:
vue ^2.6.0 || ^3.0.0. That's it.
2. Import the styles once
import 'webcake-ui-kit/styles'3. Register components
Vue 3
Vue 2.7
// main.js
import { createApp } from 'vue'
import { WkButton, WkDialog, WkInput } from 'webcake-ui-kit'
import 'webcake-ui-kit/styles'
import App from './App.vue'
const app = createApp(App)
app.component('WkButton', WkButton)
app.component('WkDialog', WkDialog)
app.component('WkInput', WkInput)
app.mount('#app')// main.js
import Vue from 'vue'
import { WkButton, WkDialog, WkInput } from 'webcake-ui-kit'
import 'webcake-ui-kit/styles'
import App from './App.vue'
Vue.component('WkButton', WkButton)
Vue.component('WkDialog', WkDialog)
Vue.component('WkInput', WkInput)
new Vue({ render: h => h(App) }).$mount('#app')4. Use it
<template>
<div>
<WkButton variant="primary" @click="open = true">Open dialog</WkButton>
<WkDialog v-model="open" title="Hello from webcake 👋">
<WkInput v-model="name" placeholder="Your name" />
</WkDialog>
</div>
</template>
<script>
export default {
data: () => ({ open: false, name: '' })
}
</script>🎉 That's it. The same .vue file works on both runtimes — no #ifdef, no wrapper, no shim.
✨ Why teams pick webcake
🎯 True dual-compat
One source. Vue 2.7 and Vue 3.x. Tested on both runtimes in CI — every PR, every component.
📦 Ships raw SFC
No pre-compiled artifacts. Your bundler compiles .vue files once, alongside your app. Zero version lock.
🎨 Token-driven theming
Override --color-primary and the whole kit rebrands. No SASS, no providers, no JS theme objects.
♿ Accessible by default
Semantic HTML, :focus-visible rings, ARIA where it matters. Not retrofitted — designed in.
🪶 Tree-shakable
Named exports only. Import Button, leave the rest behind. No Vue.use(KitchenSink).
📖 Storybook included
Every component, every variant, every prop — browsable before you npm install.
🧩 Components
All exports are prefixed with Wk to avoid global name collisions and to be greppable across consumer apps.
| Forms | Overlays | Layout | Display | | :------------------------ | :------------ | :------------------ | :----------- | | WkButton | WkDialog | WkAccordion | WkBadge | | WkButtonGroup | WkAlertDialog | WkAccordionItem | WkTag | | WkInput | WkTooltip* | WkTabs | WkDivider | | WkCheckbox | | WkBreadcrumb | WkSpinner | | WkCheckboxGroup | | WkPagination | WkTypography | | WkRadio / WkRadioGroup | | WkSidebarItem | | | WkSelect / WkSelectOption | | WkSidebarGroupLabel | | | WkSwitch / WkSwitchGroup | | | | | WkToggle / WkToggleGroup | | | | | WkSlider | | | | | WkRichCheckboxGroup | | | | | WkRichSwitchGroup | | | |
* coming soon · more on the way
All components follow the same conventions: validated props, declared emits, BEM-style hooks (ui-button--primary), and v-model where it makes sense.
👉 Want to see them in action? Browse the live Storybook →
🎨 Theming
Every component reads from CSS custom properties. Drop them on :root and the whole kit rebrands:
:root {
--color-primary: #ff6b9d;
--color-primary-hover: #ff4d8a;
--radius-md: 12px;
--font-sans: 'Inter', system-ui, sans-serif;
}Dark mode? Already wired — every color token has a .dark companion. Toggle a class on <html> and you're done.
.dark {
--color-bg: #0b0b0f;
--color-fg: #f4f4f5;
/* …everything else cascades */
}No SASS variables. No JS theme provider. No runtime patching. Just CSS, the way the platform meant it.
🛡️ The dual-compat guarantee
Every component compiles cleanly on both runtimes — enforced by a strict authoring contract:
- ✅ Pure Options API — no
<script setup>, nosetup(), no Composition API imports - ✅ Single root template (Vue 2 has no fragments)
- ✅
emitsalways declared (Vue 3 needs it for$attrsseparation) - ✅ No Vue-3-only features (
<Teleport>,<Suspense>, multiv-model) - ✅ No Vue-2-only features (filters,
.native,$listeners)
And every commit runs through four parallel build lanes:
| | Runtime | Bundler | What it catches |
| :-: | :------ | :---------------------------------- | :------------------------------------ |
| 🟢 | Vue 3.4 | Vite + @vitejs/plugin-vue | Modern compile errors, fragment leaks |
| 🟣 | Vue 2.7 | webpack 4 + vue-template-compiler | Legacy compile errors, banned APIs |
| 🟡 | Vue 3.2 | Storybook 6.5 | Story-side regressions |
| 🧪 | Both | Vitest × 2 lanes | Runtime + behavior parity |
If a PR doesn't build on either side, it doesn't ship. Period.
🏗️ Why ship raw SFC?
Most Vue libraries publish pre-compiled .js artifacts targeting one Vue runtime. That's how the ecosystem ended up fragmented into -vue2 and -vue3 packages.
webcake-ui-kit publishes the source .vue files directly. Your app's bundler — Vite, webpack, whatever you use — compiles them against your Vue version. One source, two outputs, zero version lock-in.
The tradeoff: you need a bundler that handles
.vue. (You already do.)
This is the entire reason the kit exists. Everything else — the components, the tokens, the Storybook — is downstream of that decision.
🚦 Compatibility
| | Version | Status |
| :----------------- | :-------------------------------------- | :----------: |
| Vue 2 | 2.7.x | ✅ supported |
| Vue 3 | 3.0+ (tested on 3.4) | ✅ supported |
| Bundlers | Vite, webpack 4/5, Rollup, esbuild | ✅ |
| Node (consumer) | any version your bundler supports | ✅ |
| Node (contributor) | 14.x (Vue 2 sandbox uses webpack 4) | ⚠️ pinned |
🧪 Local development
npm run preview # Vue 2 + Vue 3 dev servers side by side (HMR on both)
npm run test:build # Compile-check everything (Vue 2, Vue 3, Storybook) — ~16s
npm test # Unit suite on both runtimes, in parallelVisit localhost:8001 (Vue 3) and localhost:8080 (Vue 2) side-by-side to spot dual-compat regressions in real time.
Full contributor workflow — including the 10-file checklist for adding a component — lives in CLAUDE.md.
Environment: Node 14 · Python ≤ 3.10 (the Vue 2 playground uses webpack 4 with native deps).
🌐 Resources
| | | | :---------------------: | :------------------------------------------------------------------------------- | | 🌍 Docs & Storybook | ui.webcake.io | | 📦 npm | npmjs.com/package/webcake-ui-kit | | 🐙 GitHub | pancake-vn/webcake-ui-kit | | 🐛 Issues | Report a bug | | 💬 Discussions | Ask a question |
🤝 Contributing
PRs welcome — especially new components, accessibility fixes, and Vue 2/3 parity bug reports.
Before opening one:
- Read CLAUDE.md — it documents the dual-compat rules and the 10-file checklist for adding a component.
- Run
npm run test:build && npm test— both must pass on Vue 2 and Vue 3 lanes. - Add a Storybook story and a unit spec. New components without both are not accepted.
📜 License
MIT © Webcake Team — use it, fork it, ship it.
