vlossom
v2.2.3
Published
Vlossom UI components for Vue
Readme
Features
- Various Color Schemes — Easily style any component with a single prop
- Layered Style Sets — Global, named, or per-instance style overrides via CSS variables
- Overlay System — Modal, Toast, Alert, Confirm, Prompt as plugins with full app context inheritance
- Declarative Responsive — Breakpoint objects instead of media queries
- Async Validation — Sync and async rules with built-in defaults per component
- Dark / Light Theme — One-line global toggle, auto-persisted
Requirements
- Vue 3.5+
Installation
npm install vlossom
# or
pnpm add vlossom
# or
yarn add vlossom[!TIP] Vlossom supports per-component tree shaking. See VLOSSOM_USAGE_GUIDE.md.
Setup
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { createVlossom, VlossomComponents } from 'vlossom';
import 'vlossom/styles';
const app = createApp(App);
app.use(
createVlossom({
components: VlossomComponents,
}),
);
app.mount('#app');Usage
<template>
<vs-button primary>Hello Vlossom</vs-button>
</template>
<script setup lang="ts">
// Components are globally registered via createVlossom — no import needed
</script>Deep into
Basic Components
Mix buttons, inputs, and modals with reactive bindings, props, and slots:
<template>
<vs-button primary>Click me</vs-button>
<vs-input v-model="name" label="Name" color-scheme="blue" />
<vs-modal v-model="isOpen">
<template #header>Title</template>
Modal content here
</vs-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const name = ref('');
const isOpen = ref(false);
</script>Declarative Responsive Layout
Use breakpoint objects instead of media queries — no CSS required:
<template>
<vs-grid column-gap="16px" row-gap="16px">
<vs-responsive
v-for="item in items"
:key="item.id"
:grid="{ xs: 12, sm: 6, md: 4, lg: 3, xl: 2 }"
>
<div>{{ item.name }}</div>
</vs-responsive>
</vs-grid>
</template>Form Validation (Sync & Async)
Pass sync and async rules in the same array — Vlossom handles both seamlessly:
<template>
<vs-input v-model="email" label="Email" :rules="[required, uniqueEmail]" :max="50" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const email = ref('');
const required = (v: string) => (v ? '' : 'Email is required');
const uniqueEmail = async (v: string) => {
const res = await fetch(`/api/check-email?email=${v}`);
const { exists } = await res.json();
return exists ? 'Already in use' : '';
};
</script>Overlay Plugins (Toast · Alert · Confirm · Prompt · Modal)
Call overlays from any composable — no template wiring needed:
<script setup lang="ts">
import { useVlossom } from 'vlossom';
import EditForm from './EditForm.vue';
const $vs = useVlossom();
function notify() {
$vs.toast.success('Saved successfully');
}
async function alertSaved() {
await $vs.alert.open('Saved successfully');
}
async function remove() {
const ok = await $vs.confirm.open('Delete this item?', {
okText: 'Delete',
cancelText: 'Cancel',
colorScheme: 'red',
});
if (ok) {
$vs.toast.info('Item deleted');
}
}
async function askReason() {
const reason = await $vs.prompt.open('Why are you deleting this item?', {
input: { placeholder: 'Reason' },
okText: 'Submit',
cancelText: 'Skip',
});
if (reason) {
$vs.toast.info(`Reason: ${reason}`);
}
}
function openEditor() {
$vs.modal.open(EditForm, {
size: 'md',
callbacks: {
onSave: (data: unknown) => {
$vs.toast.success('Updated');
$vs.modal.close();
},
},
});
}
</script>Components
Layout
| Component | Description | | ------------------------------------------------------- | -------------------------------------------- | | vs-container | Responsive content container | | vs-grid | Grid layout system | | vs-layout | Page layout with header, footer, and content | | vs-page | Full page wrapper | | vs-responsive | Responsive breakpoint wrapper | | vs-block | Block-level section wrapper | | vs-header | Page header | | vs-footer | Page footer |
Actions
| Component | Description | | ----------------------------------------------- | -------------- | | vs-button | Button control |
Form
| Component | Description | | ------------------------------------------------------------- | ------------------------------- | | vs-input | Text input field | | vs-textarea | Multi-line text input | | vs-date-picker | Date and time input | | vs-checkbox | Checkbox and checkbox set | | vs-radio | Radio button and radio set | | vs-select | Dropdown select | | vs-switch | Toggle switch | | vs-toggle | Toggle button | | vs-file-drop | Drag and drop file upload | | vs-search-input | Search input with suggestions | | vs-form | Form wrapper with validation | | vs-input-wrapper | Input label and message wrapper |
Display
| Component | Description | | ----------------------------------------------------------- | ---------------------------- | | vs-accordion | Collapsible content panel | | vs-avatar | User avatar | | vs-bar | Horizontal bar | | vs-chip | Compact label chip | | vs-divider | Visual divider line | | vs-expandable | Expandable section | | vs-grouped-list | Grouped list of items | | vs-image | Image with fallback | | vs-label-value | Label-value pair display | | vs-skeleton | Loading skeleton placeholder | | vs-table | Data table | | vs-text-wrap | Text with overflow handling |
Feedback
| Component | Description | | --------------------------------------------------- | --------------------- | | vs-loading | Loading spinner | | vs-message | Inline message banner | | vs-modal | Modal dialog | | vs-drawer | Slide-in drawer panel | | vs-dimmed | Dimmed overlay | | vs-progress | Progress bar | | vs-toast | Toast notification |
Navigation
| Component | Description | | ------------------------------------------------------- | ------------------------- | | vs-tabs | Tab navigation | | vs-pagination | Page pagination | | vs-steps | Step indicator | | vs-index-view | Index-based view switcher |
Utility
| Component | Description | | --------------------------------------------------------------- | ---------------------------- | | vs-floating | Floating positioned element | | vs-focus-trap | Focus trap for accessibility | | vs-inner-scroll | Inner scrollable area | | vs-render | Conditional renderer | | vs-visible-render | Visibility-based renderer | | vs-theme-button | Dark/light theme toggle | | vs-tooltip | Tooltip popup |
Vlossom Options
Configure the app-wide theme, radius, and named style sets in createVlossom, then adjust individual components with props when needed:
app.use(
createVlossom({
components: VlossomComponents,
theme: 'dark',
radiusRatio: 0.5,
styleSet: {
brand: {
VsButton: {
backgroundColor: '#1a1a1a',
fontColor: '#fff',
},
},
},
}),
);themesets the initial global theme. Use<vs-theme-button />to toggle it at runtime.radiusRatiocontrols the global border radius ratio from0to1.styleSetdefines reusable named style overrides for components.messagesoverrides the built-in texts Vlossom renders. See Messages.color-schemeapplies a built-in color to a component instance.- Available colors:
red·orange·brown·amber·yellow·lime·green·emerald·teal·cyan·sky·blue·indigo·violet·purple·fuchsia·pink·rose·gray·none
- Available colors:
Messages
Every text Vlossom renders on its own — validation messages, empty states, overlay button labels, aria labels — comes
from a single message table. Override any subset in createVlossom to localize or reword them:
app.use(
createVlossom({
components: VlossomComponents,
messages: {
VS_VALIDATION_REQUIRED: '필수 항목입니다',
VS_VALIDATION_MAX_LENGTH: ({ value }) => `최대 ${value}자까지 입력할 수 있어요`,
VS_TABLE_NO_DATA: '내역이 없어요',
},
}),
);Line Tiers
Borders and dividers are drawn with two line variables, so lines carry a visual hierarchy instead of all reading at the same weight. Each color scheme and theme defines both.
| Variable | Used for |
| --------------------- | ------------------------------------------------------------------------------------------ |
| --vs-cs-border | Default. Everything at rest: borders, dividers and form controls. |
| --vs-cs-border-strong | Lines that need to be noticed: focus, hover, active, and separators that delimit a region. |
Both tiers come straight from the palette, and the resting tier is always exactly one palette step weaker than the strong one, in every scheme and theme:
.my-scope {
--vs-cs-border: #c8c8c8;
--vs-cs-border-strong: #a3a3a3;
}