@vanduo-oss/vd3
| Install | |
|---|---|
npm install @vanduo-oss/vd3 |
|
| Latest Version: | 1.8.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 21, 2026 |
| Links: | GitHub · npm |
@vanduo-oss/vd3
Vanduo UI for Vue 3 — design system and component library.
Standalone Vanduo package: design tokens, CSS, and typed
Vd* components/composables. Sole peer: vue >=3.3. No pinia, no IIFE
runtime.
Status: 1.8.0 — complete layout primitive & UI atom prop parity, theme runtime preference hardening, Nunito font integration, and enhanced composables.
Install
pnpm add @vanduo-oss/vd3
Usage
Three 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 optional { themeDefaults, storagePrefix } —
themeDefaults overrides the generic baseline before the theme model first
reads it; storagePrefix remaps the six preference localStorage keys
(default "vanduo-") so independently loaded sites on the same origin can use separate storage keys (e.g.
app.use(VanduoVue, { themeDefaults: { PRIMARY_DARK: "blue" }, storagePrefix: "ts-school-" })).
<script setup lang="ts">
import { VdButton, VdCard } from "@vanduo-oss/vd3";
</script>
<template>
<VdCard>
<VdButton variant="primary">Save</VdButton>
</VdCard>
</template>
Components & composables
Everything is a named export from the package root — import only what you render; nothing registers globally.
- 63 components — 56
Vd*components plus 7 layout primitives (VdBox,VdCenter,VdCover,VdFrame,VdInline,VdStack,VdSwitcher). - 39 composable modules — the theme API (
setThemeDefaults,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.
Theming
The theme API (applyPreference and useThemePreference) 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 under a
configurable prefix (default vanduo-): vanduo-palette,
vanduo-primary-color, vanduo-neutral-color, vanduo-radius,
vanduo-theme-preference, vanduo-font-preference. Pass
storagePrefix: "app-" (or call setStoragePrefix) at bootstrap to isolate
namespaces; no automatic migration between prefixes. Apps sharing the same module
instance still share theme state, defaults, and configuration.
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 stylesheet without bundled icon fonts with
@vanduo-oss/vd3/css/core.
Filled primary surfaces use --vd-text-on-primary (hover:
--vd-text-on-primary-hover). Status fills use --vd-text-on-status.
Override those on <html> for a custom primary that the built-in hue matrix
does not cover. --vd-text-inverse is the dark-surface token, not on-fill
ink. Light .vd-btn-ink:hover stays white on black.
Primary RGB helpers remain comma-separated for rgba(var(--vd-color-primary-rgb), .25)
and follow built-in hue, palette and theme changes. For custom CSS colors prefer
color-mix(in srgb, var(--vd-color-primary) 25%, transparent); if overriding only
--vd-color-primary, also supply matching RGB channels for legacy RGB consumers.
The exported JSON is a flat resolved CSS-variable map, not a current DTCG interchange
schema. Format migration is outside this release's scope.
SSR
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. Theme preferences and toast
queues are module-wide: static SSR shells are supported, but request-specific
mutations are not isolated. A storage prefix does not provide per-app/request state.
Security
- Open-source scanning — this repo is monitored with Snyk (thanks for their support of open source).
- 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).
Exports
| Export | Contents |
|---|---|
@vanduo-oss/vd3 |
Components, composables, theme API, token data |
@vanduo-oss/vd3/highlight |
Optional highlightCode / highlight helpers |
@vanduo-oss/vd3/css |
Full stylesheet (dist/vd3.min.css) |
@vanduo-oss/vd3/css/core |
Full stylesheet without icon fonts (dist/vd3-core.min.css) |
@vanduo-oss/vd3/tokens.json |
Resolved flat CSS-variable data (dist/tokens.json) |
./css/core remains the full component stylesheet without icon fonts. A
true tokens-only CSS file and a core-only JS entry were evaluated and are
not shipped: adding them would overlap ./tokens.json / named JS imports
and would redefine what consumers already treat as /css/core.
Build pipeline
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 and optional highlighter (dist/{index,highlight}.{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.
Development
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:tokens
Then 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:coverage # Vitest + type tests; full source, ratcheted coverage
pnpm build # full chain including class coverage
pnpm test:skills # published links, API inventory, typed recipes (after build)
pnpm test:size # raw + gzip library artifact budgets
Node 24 LTS is the supported development and CI runtime; run nvm use to select
it for this repository from .nvmrc. The existing engines.node >=20.19.0
range remains a temporary consumer compatibility floor, tested on a best-effort
basis until the next major release; it is not the recommended development
runtime. Development also requires pnpm >= 10; packageManager pins pnpm
10.28.2. See CONTRIBUTING.md.
Documentation
- Component examples and guides
- Agent / LLM reference — SKILL.md
- Changelog — CHANGELOG.md
- Contributing — CONTRIBUTING.md
License
MIT © Vanduo
Temporary theme controls
At bootstrap, app.use(VanduoVue, { themePersistence: false }) disables the
shared theme singleton's automatic localStorage reads and writes. Controls still
share reactive state and apply preferences to the page. An app can explicitly
call loadPreference() and persistPreference() to own its saved choices.
The option defaults to true; call before creating theme consumers. Like
storagePrefix, it is module-global and does not isolate Vue apps or SSR requests.
Without the plugin, use setThemePersistence(false) at bootstrap.
Controlled customizer fans emit update:primary for previews/restores and
select:primary when a swatch is selected. Save only the latter when hover
previews must remain temporary.
Related Packages
Adkompas UI — design tokens, Vuetify theme preset, framework-agnostic CSS, and p...
nergous-ui-vue Design System — a zero-dependency Vue 3 component library: themea...
By Sages Elements components for Vue — styled by the paper-and-ink design tokens
Blessing UI — Themed Flat Interface. Flat by design, raised with Vue, blessed fo...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.8.0 | >=3.3.0 | MIT | |
| 1.7.6 | >=3.3.0 | MIT | |
| 1.7.5 | >=3.3.0 | MIT | |
| 1.7.4 | >=3.3.0 | MIT | |
| 1.7.3 | >=3.3.0 | MIT | |
| 1.7.2 | >=3.3.0 | MIT | |
| 1.7.1 | >=3.3.0 | MIT | |
| 1.7.0 | >=3.3.0 | MIT | |
| 1.6.0 | >=3.3.0 | MIT | |
| 1.5.0 | >=3.3.0 | MIT | |
| 1.4.0 | >=3.3.0 | MIT | |
| 1.3.0 | >=3.3.0 | MIT | |
| 1.2.3 | >=3.3.0 | MIT | |
| 1.2.2 | >=3.3.0 | MIT | |
| 1.2.1 | >=3.3.0 | MIT | |
| 1.2.0 | >=3.3.0 | MIT | |
| 1.1.0 | >=3.3.0 | MIT | |
| 1.0.0 | >=3.3.0 | MIT |
