npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fastkit/vui

v1.3.2

Published

A simple, extensible UI kit for Vue applications.

Readme

@fastkit/vui

🌐 English | 日本語

A simple and extensible UI component library for Vue.js 3 applications. Focused on full TypeScript support, color theme system, accessibility, and tight integration with @fastkit/vue-form-control.

Features

  • 45+ UI Components: Comprehensive component set including buttons, forms, navigation, data display
  • Integrated Ecosystem: 20+ @fastkit packages with unified API
  • Full TypeScript Support: Type-safe props, events, and slot definitions
  • Composition API Design: Full adoption of Vue 3 modern patterns
  • Color Theme System: Flexible theming with CSS Variables foundation
  • Form Integration: Seamless integration with @fastkit/vue-form-control
  • Accessibility: WAI-ARIA compliant, keyboard navigation support
  • Responsive Design: Mobile-first grid system
  • Internationalization: Multi-language font settings, RTL language support
  • Programmatic UI: Imperative operations for dialogs, notifications, etc.

Installation

npm install @fastkit/vui
# or
pnpm add @fastkit/vui

Basic Usage

Plugin Setup

import { createApp } from 'vue';
import { VuiPlugin } from '@fastkit/vui';
import { createRouter } from 'vue-router';

const app = createApp(App);
const router = createRouter(/* routes */);

// Install VUI plugin
app.use(VuiPlugin, {
  router,
  colorScheme: {
    // Color theme settings
    primary: '#1976d2',
    secondary: '#424242'
  },
  uiSettings: {
    primaryScope: 'primary',
    buttonDefault: {
      color: 'primary',
      variant: 'contained'
    }
  }
});

Application Setup

<template>
  <VApp>
    <!-- Application content -->
    <router-view />
  </VApp>
</template>

<script setup lang="ts">
import { VApp } from '@fastkit/vui';
</script>

Component List

Layout & Structure

  • VApp - Application root container
  • VGrid (VGridContainer, VGridItem) - Responsive grid system
  • VPaper - Material Design-style paper container
  • VCard (VCardContent, VCardActions) - Card layout
  • VToolbar (VToolbarTitle, VToolbarMenu, VToolbarEdge) - Toolbar

Navigation

  • VNavigation, VNavigationItem - Side navigation
  • VBreadcrumbs - Breadcrumb navigation
  • VTabs, VTab - Tab interface
  • VPagination - Pagination

Form Controls

  • VButton, VButtonGroup - Button and button group
  • VTextField - Text input field
  • VTextarea - Multi-line text input
  • VNumberField - Number input field
  • VSelect - Select dropdown
  • VCheckbox, VCheckboxGroup - Checkbox
  • VRadio, VRadioGroup - Radio button
  • VSwitch, VSwitchGroup - Switch toggle
  • VOption, VOptionGroup - Option element

Data Display

  • VDataTable - Data table
  • VListTile - List item
  • VAvatar - User avatar
  • VChip - Chip/tag element
  • VIcon - Icon display

Feedback & Interaction

  • VDialog - Modal dialog
  • VSnackbar - Notification snackbar
  • VTooltip - Tooltip
  • VMenu - Context menu
  • VSheetModal - Sheet modal

Others

  • VSkeltonLoader - Skeleton loading
  • VBusyImage - Lazy loading image
  • VHero - Hero section
  • VContentSwitcher - Content switcher

Usage Examples

Button Components

<template>
  <div>
    <!-- Basic button -->
    <VButton @click="handleClick">Click</VButton>

    <!-- Primary button -->
    <VButton color="primary" variant="contained">
      Save
    </VButton>

    <!-- Button with icon -->
    <VButton
      startIcon="mdi-search"
      color="primary"
      variant="outlined"
      :loading="searching"
      @click="handleSearch"
    >
      Search
    </VButton>

    <!-- Button group -->
    <VButtonGroup>
      <VButton>Left</VButton>
      <VButton>Center</VButton>
      <VButton>Right</VButton>
    </VButtonGroup>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { VButton, VButtonGroup } from '@fastkit/vui';

const searching = ref(false);

const handleClick = () => {
  console.log('Button clicked');
};

const handleSearch = async () => {
  searching.value = true;
  try {
    // Search processing
    await performSearch();
  } finally {
    searching.value = false;
  }
};
</script>

Form Components

<template>
  <VCard>
    <VCardContent>
      <h2>User Registration</h2>

      <!-- Text input -->
      <VTextField
        v-model="form.name.value"
        label="Name"
        required
        :rules="[required(), minLength(2)]"
        :invalid="form.name.invalid"
        :error-message="form.name.errorMessage"
      />

      <!-- Email input -->
      <VTextField
        v-model="form.email.value"
        label="Email Address"
        type="email"
        required
        :rules="[required(), email()]"
        :invalid="form.email.invalid"
        :error-message="form.email.errorMessage"
      />

      <!-- Select -->
      <VSelect
        v-model="form.category.value"
        label="Category"
        :items="categories"
        placeholder="Select category"
        required
      />

      <!-- Checkbox -->
      <VCheckbox
        v-model="form.agreement.value"
        :rules="[required()]"
      >
        I agree to the terms of use
      </VCheckbox>
    </VCardContent>

    <VCardActions>
      <VButton
        color="primary"
        variant="contained"
        :disabled="form.invalid"
        @click="handleSubmit"
      >
        Register
      </VButton>
      <VButton variant="text" @click="handleCancel">
        Cancel
      </VButton>
    </VCardActions>
  </VCard>
</template>

<script setup lang="ts">
import {
  VCard, VCardContent, VCardActions,
  VTextField, VSelect, VCheckbox, VButton
} from '@fastkit/vui';
import { useForm } from '@fastkit/vue-form-control';
import { required, email, minLength } from '@fastkit/rules';

const categories = [
  { value: 'personal', label: 'Personal' },
  { value: 'business', label: 'Business' },
  { value: 'education', label: 'Education' }
];

const form = useForm({
  name: {
    value: '',
    rules: [required(), minLength(2)]
  },
  email: {
    value: '',
    rules: [required(), email()]
  },
  category: {
    value: '',
    rules: [required()]
  },
  agreement: {
    value: false,
    rules: [required()]
  }
}, {
  onSubmit: async (values) => {
    console.log('Form submitted:', values);
    await api.register(values);
  }
});

const handleSubmit = () => {
  form.submit();
};

const handleCancel = () => {
  form.reset();
};
</script>

Data Table

<template>
  <VDataTable
    :items="users"
    :headers="headers"
    :loading="loading"
    item-key="id"
    selectable
    @select="handleSelect"
  >
    <!-- Custom column -->
    <template #item.status="{ item }">
      <VChip
        :color="item.status === 'active' ? 'success' : 'warning'"
        size="sm"
      >
        {{ item.status === 'active' ? 'Active' : 'Inactive' }}
      </VChip>
    </template>

    <template #item.actions="{ item }">
      <VButton size="sm" variant="text" @click="editUser(item)">
        Edit
      </VButton>
      <VButton
        size="sm"
        variant="text"
        color="error"
        @click="deleteUser(item)"
      >
        Delete
      </VButton>
    </template>
  </VDataTable>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { VDataTable, VChip, VButton } from '@fastkit/vui';

const loading = ref(false);
const users = ref([
  { id: 1, name: 'John Tanaka', email: '[email protected]', status: 'active' },
  { id: 2, name: 'Hanako Sato', email: '[email protected]', status: 'inactive' }
]);

const headers = [
  { key: 'name', title: 'Name', sortable: true },
  { key: 'email', title: 'Email', sortable: true },
  { key: 'status', title: 'Status' },
  { key: 'actions', title: 'Actions', width: 120 }
];

const handleSelect = (selectedItems: any[]) => {
  console.log('Selected items:', selectedItems);
};

const editUser = (user: any) => {
  console.log('Edit:', user);
};

const deleteUser = (user: any) => {
  console.log('Delete:', user);
};
</script>

Layout System

<template>
  <VApp>
    <!-- Toolbar -->
    <VToolbar color="primary" variant="flat">
      <VToolbarTitle>My App</VToolbarTitle>
      <VToolbarMenu>
        <VButton variant="text" color="on-primary">
          Menu
        </VButton>
      </VToolbarMenu>
    </VToolbar>

    <!-- Main content -->
    <VGridContainer>
      <VGridItem cols="12" md="3">
        <!-- Side navigation -->
        <VNavigation>
          <VNavigationItem to="/dashboard" icon="mdi-dashboard">
            Dashboard
          </VNavigationItem>
          <VNavigationItem to="/users" icon="mdi-account-group">
            Users
          </VNavigationItem>
          <VNavigationItem to="/settings" icon="mdi-cog">
            Settings
          </VNavigationItem>
        </VNavigation>
      </VGridItem>

      <VGridItem cols="12" md="9">
        <!-- Page content -->
        <VPaper class="pa-4">
          <router-view />
        </VPaper>
      </VGridItem>
    </VGridContainer>
  </VApp>
</template>

<script setup lang="ts">
import {
  VApp, VToolbar, VToolbarTitle, VToolbarMenu,
  VGridContainer, VGridItem, VNavigation, VNavigationItem,
  VPaper, VButton
} from '@fastkit/vui';
</script>

Programmatic UI Operations

You can control UI elements from JavaScript using VUI services.

import { useVui } from '@fastkit/vui';

const vui = useVui();

// Alert dialog
await vui.alert('Processing completed');

// Confirmation dialog
const confirmed = await vui.confirm({
  title: 'Confirmation',
  message: 'Do you want to execute this operation?',
  okText: 'Execute',
  cancelText: 'Cancel'
});

if (confirmed) {
  // Process when confirmed
}

// Prompt dialog
const result = await vui.prompt({
  title: 'Enter name',
  message: 'Please enter a new name',
  defaultValue: 'Default value'
});

// Form prompt
const formResult = await vui.formPrompt(
  {
    state: { name: '', description: '' },
    title: 'Create New'
  },
  (state) => (
    <>
      <VTextField
        label="Name"
        v-model={state.name}
        required
      />
      <VTextarea
        label="Description"
        v-model={state.description}
        rows={3}
      />
    </>
  )
);

// Snackbar notification
vui.snackbar.show({
  message: 'Saved',
  color: 'success',
  timeout: 3000
});

// Custom dialog
const dialog = await vui.dialog.show({
  component: MyCustomDialog,
  props: {
    data: someData
  }
});

Theme Customization

CSS Variables

:root {
  /* Color palette */
  --color-primary: #1976d2;
  --color-secondary: #424242;
  --color-success: #4caf50;
  --color-warning: #ff9800;
  --color-error: #f44336;

  /* Size settings */
  --control-field-rem-sm: 0.875rem;
  --control-field-rem-md: 1rem;
  --control-field-rem-lg: 1.125rem;

  /* Spacing */
  --root-spacing: 8px;

  /* Font */
  --typo-base-font: Roboto, 'Noto Sans JP', sans-serif;

  /* Shadow */
  --shadow-1: 0px 2px 1px -1px rgba(0, 0, 0, 0.2);
  --shadow-4: 0px 2px 4px -1px rgba(0, 0, 0, 0.2);

  /* Transition */
  --transition-primary: cubic-bezier(0.25, 0.8, 0.5, 1);
}

Custom Color Scheme

// Define custom colors
app.use(VuiPlugin, {
  colorScheme: {
    primary: {
      main: '#1976d2',
      light: '#42a5f5',
      dark: '#1565c0',
      contrast: '#ffffff'
    },
    secondary: {
      main: '#dc004e',
      light: '#ff5983',
      dark: '#9a0036',
      contrast: '#ffffff'
    }
  }
});

Dark Theme Support

<template>
  <VApp :theme="currentTheme">
    <VButton @click="toggleTheme">
      {{ currentTheme === 'dark' ? 'Light' : 'Dark' }} Theme
    </VButton>
    <!-- App content -->
  </VApp>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useColorScheme } from '@fastkit/vui';

const { theme: currentTheme, toggle: toggleTheme } = useColorScheme();
</script>

Advanced Usage Examples

Custom Component Creation

<template>
  <VCard class="user-profile">
    <VCardContent>
      <div class="user-profile__header">
        <VAvatar :src="user.avatar" size="lg" />
        <div class="user-profile__info">
          <h3>{{ user.name }}</h3>
          <p>{{ user.role }}</p>
        </div>
      </div>

      <VTabs v-model="activeTab">
        <VTab value="profile">Profile</VTab>
        <VTab value="settings">Settings</VTab>
        <VTab value="activity">Activity</VTab>
      </VTabs>

      <VContentSwitcher :value="activeTab">
        <template #profile>
          <UserProfileTab :user="user" />
        </template>
        <template #settings>
          <UserSettingsTab :user="user" />
        </template>
        <template #activity>
          <UserActivityTab :user="user" />
        </template>
      </VContentSwitcher>
    </VCardContent>
  </VCard>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import {
  VCard, VCardContent, VAvatar, VTabs, VTab, VContentSwitcher
} from '@fastkit/vui';

interface User {
  name: string;
  role: string;
  avatar: string;
}

const props = defineProps<{
  user: User;
}>();

const activeTab = ref('profile');
</script>

<style scoped>
.user-profile__header {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.user-profile__info h3 {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 500;
}

.user-profile__info p {
  margin: 0.25rem 0 0;
  color: var(--color-text-secondary);
}
</style>

Complex Form Wizard

<template>
  <VCard class="form-wizard">
    <VCardContent>
      <VToolbar variant="flat" class="mb-4">
        <VToolbarTitle>Registration Wizard</VToolbarTitle>
        <VToolbarEdge>
          Step {{ currentStep + 1 }} / {{ steps.length }}
        </VToolbarEdge>
      </VToolbar>

      <!-- Step indicator -->
      <div class="step-indicator">
        <div
          v-for="(step, index) in steps"
          :key="step.name"
          class="step-indicator__item"
          :class="{
            'step-indicator__item--active': index === currentStep,
            'step-indicator__item--completed': index < currentStep
          }"
        >
          <VIcon
            :name="index < currentStep ? 'mdi-check' : step.icon"
            size="sm"
          />
          <span>{{ step.title }}</span>
        </div>
      </div>

      <!-- Step content -->
      <VContentSwitcher :value="currentStep">
        <template #0>
          <PersonalInfoStep v-model="formData.personal" />
        </template>
        <template #1>
          <ContactInfoStep v-model="formData.contact" />
        </template>
        <template #2>
          <PreferencesStep v-model="formData.preferences" />
        </template>
        <template #3>
          <ConfirmationStep :data="formData" />
        </template>
      </VContentSwitcher>
    </VCardContent>

    <VCardActions>
      <VButton
        variant="text"
        :disabled="currentStep === 0"
        @click="previousStep"
      >
        Back
      </VButton>

      <div class="flex-grow" />

      <VButton
        v-if="currentStep < steps.length - 1"
        color="primary"
        variant="contained"
        :disabled="!canProceed"
        @click="nextStep"
      >
        Next
      </VButton>

      <VButton
        v-else
        color="primary"
        variant="contained"
        :loading="submitting"
        @click="submitForm"
      >
        Complete
      </VButton>
    </VCardActions>
  </VCard>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import {
  VCard, VCardContent, VCardActions,
  VToolbar, VToolbarTitle, VToolbarEdge,
  VContentSwitcher, VButton, VIcon
} from '@fastkit/vui';

const currentStep = ref(0);
const submitting = ref(false);

const steps = [
  { name: 'personal', title: 'Personal Info', icon: 'mdi-account' },
  { name: 'contact', title: 'Contact Info', icon: 'mdi-email' },
  { name: 'preferences', title: 'Preferences', icon: 'mdi-cog' },
  { name: 'confirm', title: 'Confirmation', icon: 'mdi-check-circle' }
];

const formData = ref({
  personal: { name: '', birthday: '' },
  contact: { email: '', phone: '' },
  preferences: { newsletter: false, theme: 'light' }
});

const canProceed = computed(() => {
  // Validation for each step
  switch (currentStep.value) {
    case 0:
      return formData.value.personal.name && formData.value.personal.birthday;
    case 1:
      return formData.value.contact.email;
    case 2:
      return true;
    default:
      return false;
  }
});

const nextStep = () => {
  if (currentStep.value < steps.length - 1) {
    currentStep.value++;
  }
};

const previousStep = () => {
  if (currentStep.value > 0) {
    currentStep.value--;
  }
};

const submitForm = async () => {
  submitting.value = true;
  try {
    await api.submitRegistration(formData.value);
    // Success processing
  } finally {
    submitting.value = false;
  }
};
</script>

<style scoped>
.step-indicator {
  display: flex;
  justify-content: space-between;
  margin-bottom: 2rem;
}

.step-indicator__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  opacity: 0.6;
  transition: opacity 0.2s;
}

.step-indicator__item--active,
.step-indicator__item--completed {
  opacity: 1;
}

.flex-grow {
  flex-grow: 1;
}
</style>

API

Composables

useVui()

Access VUI service instance.

const vui = useVui();

// Dialog operations
vui.alert(message)
vui.confirm(options)
vui.prompt(options)
vui.formPrompt(state, renderer)

// Notifications
vui.snackbar.show(options)
vui.snackbar.hide()

// Navigation
vui.router.push(location)
vui.location.assign(url)

useControl()

Provides common functionality for control elements.

const control = useControl(props, options);

// Properties
control.size        // 'sm' | 'md' | 'lg'
control.classes     // Computed CSS classes
control.isDisabled  // Disabled state

useColorScheme()

Provides color theme control.

const { theme, toggle, setTheme } = useColorScheme();

theme.value         // Current theme
toggle()            // Toggle theme
setTheme('dark')    // Set specific theme

Plugin Options

interface VuiPluginOptions {
  router: Router;
  colorScheme?: VueColorSchemePluginSettings;
  uiSettings?: {
    primaryScope: ScopeName;
    buttonDefault: {
      color: ScopeName;
      variant: ColorVariant;
    };
    dialogOk?: {
      color?: ScopeName;
      variant?: ColorVariant;
    };
  };
  icons?: {
    menuDown: IconName;
    navigationExpand: RawIconProp;
    // Other icon settings
  };
  stack?: VueStackPluginOptions;
  form?: VueFormServiceOptions;
}

Accessibility

Keyboard Navigation

  • Tab/Shift+Tab: Focus movement
  • Enter/Space: Button/checkbox operation
  • Arrow Keys: Radio button/tab/menu navigation
  • Escape: Close dialog/menu
  • Home/End: Move to beginning/end of list/table

ARIA Support

<!-- Automatically applied ARIA attributes -->
<button aria-disabled="true" aria-label="Save button">
<input aria-invalid="true" aria-describedby="error-message">
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">

Screen Reader Support

  • Voice reading of form errors
  • State change notifications
  • Proper placement of landmark elements

Dependencies

{
  "dependencies": {
    "@fastkit/vue-form-control": "Form functionality",
    "@fastkit/vue-color-scheme": "Color theme",
    "@fastkit/vue-stack": "Stack management",
    "@fastkit/vue-action": "Action functionality",
    "@fastkit/vue-app-layout": "Layout",
    "@fastkit/rules": "Validation",
    "@fastkit/helpers": "Utilities"
  },
  "peerDependencies": {
    "vue": "^3.5.0",
    "vue-router": "^4.0.0"
  }
}

Documentation

For detailed documentation, please visit here.

License

MIT