A Vue 3 UI library with a real, first-class Vue Vapor build, plain CSS by default, animation-agnostic when you want more. One component per primitive, no compound-component sprawl, fully native to Vue's own APIs.
Docs & full component reference → vael-ui.dev
Vue Vapor compiles templates straight to fine-grained reactive DOM updates instead of the classic virtual DOM (render → VNodes → diff → patch) — smaller runtime, faster updates. vael-ui ships both builds from the same package: every component is written once as a normal VDOM SFC, then compiled a second time through Vapor's compiler into a separate entry point.
import { Button } from 'vael-ui' // classic VDOM build
import { Button } from 'vael-ui/vapor' // compiled Vapor build, same props/APIplayground/vapor is a live showcase of the Vapor build with nothing else in the tree, every
mounted component there is genuinely Vapor, verified via Vue's own __vapor flag in
packages/vapor-ui/tests.
pnpm add vael-ui// main.ts
import 'vael-ui/style.css' // import before your own global/Tailwind CSS<script setup lang="ts">
import { Button, ConfigProvider } from 'vael-ui'
</script>
<template>
<ConfigProvider :theme="{ primary: '#6366f1', radius: '12px' }">
<Button @click="save">Save</Button>
</ConfigProvider>
</template>Dark mode is CSS-only, the library responds to <html data-theme="dark"> and, absent that,
prefers-color-scheme. useColorScheme() handles the state part (persistence, applying the
attribute, live OS-preference updates) if you want a toggle:
import { useColorScheme } from 'vael-ui'
const { mode, setMode } = useColorScheme({
persist: {
get: () => localStorage.getItem('theme'),
set: (m) => localStorage.setItem('theme', m ?? ''),
},
})Full theming/i18n/ConfigProvider setup: Global Setup guide.
v-tooltip and v-scroll-mask need global registration unless imported directly as
vTooltip/vScrollMask inside the same <script setup> that uses them:
import { createApp } from 'vue'
import { vTooltip, vScrollMask } from 'vael-ui'
const app = createApp(App)
app.directive('tooltip', vTooltip)
app.directive('scroll-mask', vScrollMask)v-tooltip also needs a single <TooltipHost /> mounted once anywhere in your app (a shared
singleton every tooltip target renders through). Skipping either step is what causes a
Failed to resolve directive warning, or a tooltip that silently never shows.
Vapor apps import the same names from vael-ui/vapor instead. Full options and the
Vapor-specific directive shape: Animation Integration guide.
packages/
ui/ the library — publishes "vael-ui" + "vael-ui/vapor" from the same dist/
the only place any component is hand-authored
vapor-ui/ Vapor build/test tooling: compiles the generated tree into
packages/ui/dist/vapor, plus Vapor browser tests
scripts/ generate-vapor.mjs — copies each listed component into
vapor-ui/src/generated, marks it `vapor`, rewrites imports
playground/
vdom/ every primitive in CSS / motion-v / imperative flavors
vapor-interop/ a VDOM + Vapor mixed app
vapor/ standalone Vapor build showcase (vael-ui/vapor only)
docs/ the vael-ui.dev site — generated component reference + guides
| Script | What it does |
|---|---|
pnpm build |
Builds the library (packages/ui/dist) and the Vapor variant (dist/vapor) |
pnpm typecheck |
Builds, then vue-tsc over every workspace package |
pnpm test |
Browser tests for the library (vitest + Playwright, real Chromium) |
pnpm test:vapor / test:vapor-interop |
Vapor build and interop verification |
pnpm play / play:vapor / play:vapor-interop |
Dev servers for each playground |
pnpm lint / pnpm format |
oxlint + eslint / oxfmt write |
pnpm changeset |
Describe a pending change for the next release |
The library depends on vael-ui's built dist/, not src/ — pnpm play alone silently
serves a stale build the moment you edit library source. Run pnpm --filter vael-ui dev
(tsdown --watch) alongside it, or rebuild after each change.
pnpm install- Make your change, add tests where it makes sense
pnpm typecheck && pnpm testbefore opening a PR- Commits follow Conventional Commits — enforced by a commit-msg hook
- If your change affects the published
vael-uipackage, runpnpm changeset
Releases are automated: merging to main opens or updates a "Version Packages" PR from any
pending changesets; merging that PR publishes to npm.
MIT