Vanduo UI for Vue 3 — design system and component library (the vd3 line).
The Vue3-only line of the Vanduo design system. Unlike
the previous three-package split (@vanduo-oss/core tokens +
@vanduo-oss/framework CSS/JS + @vanduo-oss/vue components), vd3 is fully
standalone: one package ships its own DTCG design tokens, CSS tree, and typed
Vd* components/composables. Sole peer dependency: vue >=3.3 — no pinia, and
no framework IIFE runtime (loadVanduoRuntime / window.Vanduo* are gone).
Status: 1.2.2 (native select chevron no longer tiles after a consumer
background shorthand + dark :focus; 1.2.1 added loading-spinner
visibility on outline/ghost, VdModal glass, spotlight/morph/stepper
polish; 1.2.0 added the opt-in .vd-btn-ring modifier).
pnpm add @vanduo-oss/vd3Three integration points — import the stylesheet, register the plugin, and render components:
// main.ts
import { createApp } from "vue";
import { VanduoVue } from "@vanduo-oss/vd3";
import "@vanduo-oss/vd3/css"; // full stylesheet (tokens + components + icons)
import App from "./App.vue";
createApp(App).use(VanduoVue).mount("#app");app.use(VanduoVue) accepts an optional { themeDefaults } to override the
generic baseline before the theme model first reads it (e.g.
app.use(VanduoVue, { themeDefaults: { PRIMARY_DARK: "blue" } })).
<script setup lang="ts">
import { VdButton, VdCard } from "@vanduo-oss/vd3";
</script>
<template>
<VdCard>
<VdButton variant="primary">Save</VdButton>
</VdCard>
</template>Everything is a named export from the package root — import only what you render; nothing registers globally.
- 52 components — 45
Vd*components plus 7 layout primitives (VdBox,VdCenter,VdCover,VdFrame,VdInline,VdStack,VdSwitcher). - ~35 composables — the theme layer (
useTheme,useThemeBridge, and theuseThemePreferencereactive singleton), plus form, overlay/dismissal, motion/scroll, and layout/interaction helpers. ThesanitizeHtmlwhitelist sanitizer is exported too.
The full per-group inventory and the theming contract live in the agent/LLM reference, SKILL.md.
The theme layer (useTheme) drives six data-* attributes on <html> —
data-palette, data-primary, data-neutral, data-radius, data-theme,
data-font — which the CSS resolves into --vd-* custom properties (e.g.
--vd-radius-scale). Preferences persist to six localStorage keys
(vanduo-palette, vanduo-primary-color, vanduo-neutral-color,
vanduo-radius, vanduo-theme-preference, vanduo-font-preference).
useThemePreference() is a module-scope reactive singleton (no pinia) that is
the single source of truth behind VdThemeSwitcher and VdThemeCustomizer;
its setters route through applyPreference + persistPreference. Override the
default palette/primary/etc. via app.use(VanduoVue, { themeDefaults }) or
setThemeDefaults(). Token data (DEFAULTS, PALETTE_OPTIONS, tokens, …) is
re-exported from the package root, or import raw JSON from
@vanduo-oss/vd3/tokens.json. Ship the token-only stylesheet with
@vanduo-oss/vd3/css/core.
The package is SSR / vite-ssg-safe: all browser access is client-guarded with
typeof window checks and onMounted / onScopeDispose lifecycle hooks, so
nothing touches window, document, localStorage, or matchMedia during
server render. useThemePreference seeds from defaults on the server and
hydrates from storage lazily on the first client call.
- Zero runtime dependencies beyond the
vue >=3.3peer — no pinia, no transitive runtime deps. - Hardened
.npmrc:ignore-scripts,minimum-release-age,save-exact,strict-peer-dependencies,trust-policy=no-downgrade,block-exotic-subdeps, and an explicitregistry. - MIT licensed (LICENSE); bundled third-party notices in THIRD-PARTY-LICENSES (Open Color, Phosphor Icons, and the adapted expanding-cards CSS — all MIT).
| Export | Contents |
|---|---|
@vanduo-oss/vd3 |
Components, composables, theme API, token data |
@vanduo-oss/vd3/css |
Full stylesheet (dist/vd3.min.css) |
@vanduo-oss/vd3/css/core |
Tokens-only stylesheet (dist/vd3-core.min.css) |
@vanduo-oss/vd3/tokens.json |
Resolved DTCG token data (dist/tokens.json) |
pnpm build runs the full chain, in order:
scripts/clean-dist.mjs— resetsdist/(the only step that cleans; vite runs withemptyOutDir: false).scripts/build-tokens.mjs— DTCG tokens (tokens/) → generated color partials (css/core/generated/, gitignored), the typed token-data module (src/theme/generated/tokens.data.ts, gitignored — inlined into the lib bundle) +dist/tokens.json. Zero-dependency and deterministic.scripts/build-css.mjs— bundlescss/vd3.csswith lightningcss intodist/vd3(.min).cssand the no-iconsdist/vd3-core(.min).css(+ source maps), and copiesfonts/and the Phosphor regular + fill icon weights intodist/.vite build— the library JS (dist/index.{js,cjs}).vue-tsc -p tsconfig.build.json— the.d.tsdeclarations.scripts/check-class-coverage.mjs— asserts everyvd-*class the components render has a selector indist/vd3.min.css(also standalone aspnpm check:classes).
pnpm build:tokens / pnpm build:css run steps 2–3 standalone;
pnpm gen:fib regenerates tokens/primitive/color.fib.tokens.json.
On a fresh clone, bootstrap the generated token-data module first — src/
imports src/theme/generated/tokens.data.ts (gitignored build output), so
lint/typecheck/test cannot pass until it exists:
pnpm install && pnpm build:tokensThen the usual gates:
pnpm lint # eslint
pnpm format:check # prettier (src, tests, scripts)
pnpm stylelint # authored css tree (generated partials excluded)
pnpm typecheck # vue-tsc --noEmit
pnpm test # vitest (jsdom) — token/DTCG/palette contracts + smoke
pnpm build # full chain (see Build pipeline)Requires Node >= 24 and pnpm >= 10 (packageManager: pnpm@10.28.2).
- Agent / LLM reference — SKILL.md
- Changelog — CHANGELOG.md
MIT © Vanduo