Installation
Setup
Add to a Vue-Lynx project
Install the @vyui/core package
pnpm add @vyui/core
yarn add @vyui/core
bun add @vyui/core
deno add npm:@vyui/core
npm install @vyui/core
Use a primitive
Primitives are unstyled. Compose them with your own classes, tokens, or design system.
<script setup>
import { SliderRoot, SliderTrack, SliderRange, SliderThumb } from '@vyui/core'
import { ref } from 'vue'
const value = ref(50)
</script>
<template>
<SliderRoot v-model="value" :max="100">
<SliderTrack>
<SliderRange />
</SliderTrack>
<SliderThumb />
</SliderRoot>
</template>
Add styled components with @vyui/kit
Install both packages
pnpm add @vyui/core @vyui/kit
yarn add @vyui/core @vyui/kit
bun add @vyui/core @vyui/kit
deno add npm:@vyui/core npm:@vyui/kit
npm install @vyui/core @vyui/kit
@vyui/kit depends on @vyui/core, but it does not re-export the full core surface. Import styled Vy* components from kit, and import raw primitives or utilities directly from core unless the kit docs call out a specific Vy* convenience alias.
Configure Tailwind
@vyui/kit ships its styling as a Tailwind preset. Add it alongside the Lynx preset, and feed the kit ui-* state markers into the Lynx preset's uiVariants plugin — the class-based replacements for Lynx-incompatible data-[state] selectors. Without this step components render unstyled.
import type { Config } from 'tailwindcss'
import { createLynxPreset } from '@lynx-js/tailwind-preset'
import { createVyuiPreset, VYUI_UI_STATES } from '@vyui/kit/tailwind'
const lynxPreset = createLynxPreset({
lynxUIPlugins: {
uiVariants: {
prefixes: defaults => ({ ...defaults, ui: [...defaults.ui, ...VYUI_UI_STATES] }),
},
},
})
export default {
// Scan the kit + core component sources so their utility classes aren't purged.
content: [
'./src/**/*.{vue,js,ts}',
'./node_modules/@vyui/kit/dist/**/*.js',
'./node_modules/@vyui/core/dist/**/*.js',
],
presets: [lynxPreset, createVyuiPreset()],
} satisfies Config
Import the theme stylesheet
@vyui/kit/style.css defines the default design tokens using Tailwind theme() calls, so pull it in through your Tailwind pipeline with a CSS @import — not a plain JS import. Put any --ui-color-* overrides after it.
@tailwind base;
@tailwind utilities;
/* @vyui/kit default theme tokens — overrides go below this line. */
@import '@vyui/kit/style.css';
Register Vy UI
Vy UI needs its theme config provided once at your app entry. On Vue-Lynx — the primary target — do this with provideVyUI and import components by name where you use them, so the bundler only pulls what you reference. Kit's defaults use lucide icons, so register that set up front, and install the Intl polyfill for Lynx's PrimJS engine.
import { createApp } from 'vue-lynx'
import { installIntlPolyfill, registerIconSet } from '@vyui/core'
import { provideVyUI } from '@vyui/kit'
import lucide from '@iconify-json/lucide/icons.json'
import App from './App.vue'
import './index.css'
installIntlPolyfill()
registerIconSet('lucide', lucide)
const app = createApp(App)
provideVyUI(app)
app.mount()
Then import components where you use them:
<script setup>
import { VyButton } from '@vyui/kit'
</script>
<template>
<VyButton>Tap me</VyButton>
</template>
app.use(VyUI), which registers every Vy* component globally so <VyButton> works without an import — convenient, but it pulls the entire component set into your bundle. Vue-Lynx's createApp has no app.component, so app.use(VyUI) degrades to theme-only there (with a dev warning) — provideVyUI + named imports is the native path. Register just a subset globally with app.use(VyUI, { components: { VyButton } }).Runtime modes
@vyui/kit runs on any Vue-compatible runtime, but only some expose app.component. Pick the entry that matches yours:
| Runtime | Entry | Components |
|---|---|---|
| Vue-Lynx / minimal (native path) | provideVyUI(app, config) | Local imports (import { VyButton } from '@vyui/kit') |
| Full Vue (web) | app.use(VyUI, config) | Registered globally |
| Build (Tailwind) | createVyuiPreset(config) | — (generates the class surface) |
Vue-Lynx's createApp has no app.component, so app.use(VyUI) degrades to theme-only there (with a dev warning) — provideVyUI is the blessed native path. Runtime config selects from the classes Tailwind already emitted; it never creates styling.
Single-source config with defineVyuiConfig
The color set is meaningful to both planes: the Tailwind preset must generate the classes, and the runtime must select the same set. Author it once with defineVyuiConfig (from the light @vyui/kit/config entry — safe to import in a Tailwind config; it never pulls component code) and feed the result to both:
import { defineVyuiConfig } from '@vyui/kit/config'
export default defineVyuiConfig({
theme: {
primary: 'orange',
gray: 'stone',
colors: ['primary', 'secondary', 'success', 'info', 'warning', 'error'],
},
components: {
button: { slots: { base: 'rounded-xl' } },
},
})
import vyuiConfig from './vyui.config'
import { createVyuiPreset } from '@vyui/kit/tailwind'
export default {
presets: [lynxPreset, createVyuiPreset(vyuiConfig)],
}
import vyuiConfig from './vyui.config'
import { provideVyUI } from '@vyui/kit' // or: app.use(VyUI, vyuiConfig) on web
provideVyUI(app, vyuiConfig)
Mount the overlay & toast hosts
VyModal, VyDrawer, VyPopover and friends render into a portal host, and VyToast needs a toast provider — the plugin does not wire these up for you. Mount both once near the root:
<script setup>
import { OverlayRoot, ToastProvider } from '@vyui/core'
</script>
<template>
<ToastProvider>
<!-- your app -->
</ToastProvider>
<!-- Portal host for overlays; keep it a sibling at the app root. -->
<OverlayRoot />
</template>
Use a Vy* component
<script setup>
import { VyButton, VySlider } from '@vyui/kit'
import { ref } from 'vue'
const value = ref(50)
</script>
<template>
<VySlider v-model="value" :max="100" />
<VyButton variant="solid" color="primary">Save</VyButton>
</template>
Options
Kit ships sensible defaults — you only need to reach for these when you want to override theme tokens, swap a primary color, or change a component's default variant.
theme
Pass component overrides under ui.<component> to deep-merge them into the default Tailwind Variants theme. Every Vy* component reads from this — no rebuild required. Like Nuxt UI, everything lives under the single ui namespace.
createApp(App).use(VyUI, {
ui: {
button: {
defaultVariants: { color: 'primary' },
},
},
})
colors
Override the semantic color slots (primary, secondary, success, info, warning, error, neutral) by redefining the --ui-color-{semantic}-{shade} CSS variables in your own stylesheet.
:root {
--ui-color-primary-500: #6366f1;
--ui-color-primary-600: #4f46e5;
}
Adding a custom color
The configurable color set (primary, secondary, success, info, warning, error — neutral is always appended) is extensible. Declare the extra color once in your shared config so the build (which generates the classes) and the runtime (which selects them) can't drift:
import { defineVyuiConfig } from '@vyui/kit/config'
import { COLORS } from '@vyui/kit/tailwind'
export default defineVyuiConfig({
theme: { colors: [...COLORS, 'tertiary'] },
})
Pass that same object to createVyuiPreset(vyuiConfig) and provideVyUI(app, vyuiConfig) as shown above. createVyuiPreset dev-warns if you list a color it can't back with a --ui-color-* var, so a "class resolves to nothing" mistake surfaces at build time. To also type-check tertiary on every component's color prop:
declare module '@vyui/kit' {
interface VyuiColorRegistry { tertiary: true }
}
Then add the --ui-color-tertiary-{50..950} CSS-var block (the .d.ts + CSS can be generated with node node_modules/@vyui/kit/scripts/gen-colors.mjs --colors tertiary=indigo). See the @vyui/kit README for the full flow.