va-responsive-components-library

A comprehensive Vue 3 component library built with modern development practices, providing reusable UI components, composables, and directives for building robust web applications.
1,232
Install
npm install va-responsive-components-library
Latest Version:0.0.3
Vue:3
Last Updated:May 2, 2026
Links: npm
Maintainer: weiyuedeng

VA Responsive Components Library

A comprehensive Vue 3 component library built with modern development practices, providing reusable UI components, composables, and directives for building robust web applications.

🚀 Features

  • 17 Production-Ready Components - Form controls, layout components, and utility components
  • 3 Powerful Composables - Reusable logic for common patterns
  • 2 Custom Directives - Loading states and UI enhancements
  • Vue 3 Composition API - Modern, performant, and type-safe
  • Fully Responsive UI & UX - Components automatically adapt to all screen sizes with optimized mobile-first design and touch-friendly interactions
  • Accessibility - ARIA attributes and keyboard navigation support
  • Customizable Themes - Multiple built-in color schemes

Newly released public components: FileUploader, DropdownMenu, Badge, Tab, FloatingActionButton, DynamicColorResponsiveButton, FoldableButton, Snackbar, Notification, Breadcrumb.

🆕 What's New

2026-05-01

  • Tab — responsive sizing props added. Tab can now adapt to any container width with fine-grained control:
    • width — total group width (Number → px, or any CSS length string)
    • tab_width — fixed per-tab width (e.g. EHR ez-bill main-tab 135px)
    • font_size, icon_size — independent label/icon scaling
    • gap, padding — per-tab spacing for compact / spacious presets
    • group_padding — horizontal padding on the group container itself (override outlined / filled default 0 24px, e.g. set 0 so tabs span the full configured width)
    • overflow_mode — 'scroll' (default, hidden horizontal scroll) or 'ellipsis' (tabs shrink, labels truncate in place)
    • Default: tabs keep their natural content width and the main row scrolls horizontally if content exceeds the container; labels never get aggressively cropped unless explicitly opted in.

2026-04-23

  • Public release of:
    • FileUploader — fully-controlled file uploader with drag-and-drop, a 7-state row status machine (default / uploading / pending-success / pending-failure / failed / hide / hidden), and parent-driven progress. Follows the Figma File Uploader design.

2026-04-22

  • Public release of:
    • Breadcrumb

2025-10-28

  • Public release of:
    • DropdownMenu
    • Badge
    • FloatingActionButton
    • DynamicColorResponsiveButton

Import options:

  • Global (plugin):
    • app.use(VaResponsiveComponentsLibrary) then use components directly in templates.
  • Named import:
    • import { DropdownMenu, Badge, FloatingActionButton, DynamicColorResponsiveButton, FoldableButton } from 'va-responsive-components-library'

📦 Installation

npm install va-responsive-components-library

or

npm install va-responsive-components-library@latest

🎯 Quick Start

1. Install and Setup

First, import and register the library in your main.js:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import VaResponsiveComponentsLibrary from 'va-responsive-components-library'
import 'va-responsive-components-library/style.css'

const app = createApp(App)
app.use(VaResponsiveComponentsLibrary)
app.mount('#app')

2. Use Components

<template>
  <div>
    <!-- Basic checkbox -->
    <Checkbox v-model="isChecked" label="Accept terms" />
    
    <!-- Phone input with country selection -->
    <AreaCodePhoneInput v-model="phoneData" />
    
    <!-- Dynamic color responsive button (Recommended) -->
    <DynamicColorResponsiveButton 
      display_name="Click me" 
      button_type="filled" 
      built_in_theme="primary"
      @click-button="handleClick"
    />

    <!-- Legacy button (Deprecated but still works; not maintained). New projects should migrate to DynamicColorResponsiveButton. -->
    <ResponsiveButton @click="handleClick">
      Click me
    </ResponsiveButton>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isChecked = ref(false)
const phoneData = ref({ area_code: '', phone_number: '' })

const handleClick = () => {
  console.log('Button clicked!')
}
</script>

📚 Components

Form Components

AreaCodePhoneInput

International phone number input with country selection and area code handling. Features automatic country flag display, smart focus management, and a clear button that appears on hover/focus.

Attributes
Attribute Description Type Default
v-model / modelValue binding value - object containing area_code (string) and phone_number (string) {area_code: string, phone_number: string} () => ({})
country_options custom country list with country_name and country_code properties (country_code is 2-byte uppercase, e.g. 'US') Array<{country_name: string, country_code: string}> () => []
enable_backup_country_options use built-in country list when no custom options provided boolean true
country_filterable enable country search/filtering in dropdown boolean true
disabled whether input is disabled boolean false
size size of the country selector and phone input 'small' | 'medium' | 'large' 'medium'
clearable show clear button on the phone input when it has a value boolean true
Events
Event Description Parameters
change triggers when the binding value changes {area_code: string, phone_number: string}
focus triggers when any part of the input gains focus {area_code: string, phone_number: string}
blur triggers when the entire input loses focus {area_code: string, phone_number: string}
clear triggers when clear button is clicked {area_code: string, phone_number: string}
Exposes
Method Description Type
focus focus the appropriate input (country selector if no country selected, phone input if country selected) () => void
blur blur both country selector and phone input () => void
clear clear both country selection and phone number () => void
alert show alert message below the input (message: string) => void
error show error message below the input (message: string) => void
removeAlertOrErrorEffect clear alert/error state and message () => void
<template>
  <AreaCodePhoneInput 
    v-model="phoneData" 
    :country_filterable="true"
    @change="handlePhoneChange"
  />
</template>

Navigation Components

Breadcrumb

Navigation breadcrumb component that displays a hierarchical path of items. Each breadcrumb item is clickable except for the current (last) item. Features separator chevrons between items and distinct styling for the current location.

Attributes
Attribute Description Type Default
stack breadcrumb items array - each item should have id and name properties Array<{id: string | number, name: string}> []
Events
Event Description Parameters
item-click triggers when a breadcrumb item is clicked (item: {id: string | number, name: string}, index: number)
Usage
<template>
  <Breadcrumb
    :stack="breadcrumbStack"
    @item-click="handleBreadcrumbClick"
  />
</template>

<script setup>
import { ref } from 'vue'

const breadcrumbStack = ref([
  { id: 'home', name: 'Home' },
  { id: 'products', name: 'Products' },
  { id: 'electronics', name: 'Electronics' },
  { id: 'current', name: 'Current Page' }
])

const handleBreadcrumbClick = (item, index) => {
  console.log(`Clicked: ${item.name} at index ${index}`)
  // Navigate to the clicked item
}
</script>
Theme/Behavior Notes

Visual Styling:

  • Non-current items use var(--IconGrey) text color and are clickable with pointer cursor
  • Current item (last item in stack) uses var(--VibrantDarkBlue) text color with underline decoration
  • Typography class: EHR_BodyM_14_Web (14px medium weight body text)
  • Separator chevrons between items with gray fill (#999999)

Interaction Rules:

  • Only non-current items emit item-click events
  • Current item is visually distinct but does not emit click events
  • Each item must have a unique id and a display name
Tab

Tab group component with 7 type variants: outlined, filled, underline, button, vertical, capsule, and oval. Supports optional icons, badge indicators (dot, number, 99+ overflow, text pill), an independent separated tab, and v-model selection.

Icons must be extracted to standalone SVG files. This component library never inlines <svg> markup in templates — every icon used in a tab.icon (or indie_tab.icon) field is a separate .svg asset that you import and pass as a string path. Don't paste raw SVG markup into the icon field. If you don't yet have the icon as a file, save the SVG to @/assets/icons/<name>.svg first, then import iconX from '@/assets/icons/<name>.svg' and reference iconX. This keeps the icon set auditable, lets the build cache & deduplicate assets, and is required for the auto dark-bg invert (filter: brightness(0) invert(1)) to work uniformly across variants. To bypass the inversion or render custom markup, use the #icon-{index} / #icon-indie slots — that is the only sanctioned place for inline SVG.

Icon color on dark backgrounds: for variants with a dark selected background (filled selected, button, capsule selected, vertical selected, and oval with color="vibrant-dark" selected), Tab automatically inverts the icon image to white via filter: brightness(0) invert(1). If you're passing a pre-colored or already-white icon, use the #icon-{index} slot to render your own markup and bypass this filter.

Default selection: Tab does not auto-select the first tab on mount. The selected tab is whichever one has value === modelValue; if modelValue is '' (the default) or doesn't match any tab.value, no tab is highlighted. Initialize your v-model ref to the desired tab's value (e.g. const selected = ref('tab1')) to pre-select on mount.

Badge clearing: badges are purely data-driven. Clicking a tab does not clear its red dot / content badge automatically — if you want "click to mark as read" behavior, mutate the tabs array (or the indie_tab object) yourself in the @change handler.

Attributes
Attribute Description Type Default
v-model / modelValue currently selected tab value. Must equal a tab.value for that tab to render as selected — no auto-selection on mount. Default '' means nothing is highlighted. string | number ''
tabs array of tab objects — each with value (required), label (required), icon? (string or false), badge? (true for dot, number for circle, string for pill, or object for full Badge props). Badges are data-driven: clicking a tab does not clear its badge; mutate the array yourself in @change if you want "click to mark as read". Array — (required)
type tab type variant 'outlined' | 'filled' | 'underline' | 'button' | 'vertical' | 'capsule' | 'oval' 'underline'
color color theme for capsule and oval types 'sky' | 'vibrant-dark' 'sky'
width total group width. Number → px, String accepts any CSS length ('50%', '32rem', '400px'). Omit to inherit parent (100%). string | number ''
tab_width fixed width applied to each tab. For horizontal types uses flex: 0 0 <w>; for vertical sets width / min-width directly (column flexbox would otherwise size height). Long labels ellipsis. Useful for grid-like layouts (e.g. EHR ez-bill main-tab 135px). string | number ''
font_size label font size. Number → px. When set, also relaxes line-height to 1.5 so labels don't clip when the font exceeds a per-type hard-coded line-height (e.g. underline's 20px). Leave empty to keep the variant's default line-height. string | number ''
icon_size icon box + SVG placeholder size. Number → px. Lets icons scale independently from font_size. string | number ''
gap gap between tabs inside the main group. Overrides per-variant default (outlined / filled default 8px). string | number ''
padding horizontal padding on each tab (padding-inline). Vertical padding from CSS is preserved. string | number ''
group_padding horizontal padding on the group container itself (padding-inline). outlined and filled types add 0 24px by default; set this to 0 so tabs span the full configured width. Number → px; string accepts any CSS length. string | number ''
overflow_mode how to handle content exceeding the group width: 'scroll' (default, hidden horizontal scroll) or 'ellipsis' (tabs shrink in place, labels truncate). 'scroll' | 'ellipsis' 'scroll'
indie_tab separated independent tab: { value, label, icon?, badge? }. Positioning: underline → right-aligned (margin-left: auto); vertical → pinned to bottom (margin-top: auto, requires sized parent); all other types render immediately after the main group. object null
Events
Event Description Parameters
update:modelValue syncs v-model when tab selection changes (value: string | number)
change emitted when a different tab is selected (value: string | number)
Slots
Name Description
#icon-{index} Custom icon content for tab at given index. Receives { tab, selected }
#icon-indie Custom icon for the indie tab
Usage

Each of the 7 type variants below has two example sets: "Dot badge" (boolean badge: true) and "Badge with content" (number / string / object).

Outlined (Large Page Tab)

The icon field on each tab accepts an imported SVG/PNG path (or false to hide the icon). Tab automatically inverts dark-background icons to white where appropriate.

<template>
  <!-- Dot badge -->
  <Tab v-model="selected" type="outlined" :tabs="primaryTabs" :indie_tab="primaryIndie" />

  <!-- Badge with content: dot, number, 99+, text pill -->
  <Tab v-model="selected2" type="outlined" :tabs="badgeTabs" :indie_tab="badgeIndie" />
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
// Icons — any SVG/PNG path; pass `false` on a tab to hide its icon
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconSP from '@/assets/icons/signature-programs.svg';
import iconPP from '@/assets/icons/practice-products.svg';
import iconPB from '@/assets/icons/product-bundles.svg';
import iconCF from '@/assets/icons/customized-fee.svg';
import iconEZ from '@/assets/icons/ez-bill.svg';
import iconDT from '@/assets/icons/document-template.svg';
import iconAU from '@/assets/icons/automation.svg';
import iconTM from '@/assets/icons/template-marketplace.svg';
import iconAR from '@/assets/icons/archive.svg';
import iconMS from '@/assets/icons/messages.svg';
import iconIN from '@/assets/icons/inbox.svg';

const selected = ref('tab1');
const selected2 = ref('tab1');

const primaryTabs = [
  { value: 'tab1', label: 'Option 1', icon: iconVP, badge: true },
  { value: 'tab2', label: 'Option 2', icon: iconSP, badge: true },
  { value: 'tab3', label: 'Option 3', icon: iconPP, badge: true },
  { value: 'tab4', label: 'Option 4', icon: iconPB, badge: true },
  { value: 'tab5', label: 'Option 5', icon: iconCF, badge: true },
];
const primaryIndie = { value: 'indie', label: 'Option 6', icon: iconEZ, badge: true };

const badgeTabs = [
  { value: 'tab1', label: 'Messages', icon: iconMS, badge: true },
  { value: 'tab2', label: 'Inbox',    icon: iconIN, badge: 3 },
  { value: 'tab3', label: 'Alerts',   icon: iconAU, badge: 12 },
  { value: 'tab4', label: 'Pending',  icon: iconTM, badge: 100 },
  { value: 'tab5', label: 'Spam',     icon: iconCF, badge: 'New' },
];
const badgeIndie = { value: 'tab6', label: 'Archive', icon: iconAR };
</script>

The Filled, Underline, Button, Vertical, Capsule, and Oval examples below are self-contained — each one imports only the icons it uses and defines its own tabs / indie_tab. Only the type (and optionally color, sizing props) changes per variant.

Filled (Large Page Tab)

Selected tab uses a dark-blue fill; icon images are auto-inverted to white on the selected tab.

<template>
  <!-- Dot badge -->
  <Tab v-model="selected" type="filled" :tabs="primaryTabs" :indie_tab="primaryIndie" />

  <!-- Badge with content (selected tab renders badge as white with dark text) -->
  <Tab v-model="selected2" type="filled" :tabs="badgeTabs" :indie_tab="badgeIndie" />
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconSP from '@/assets/icons/signature-programs.svg';
import iconPP from '@/assets/icons/practice-products.svg';
import iconPB from '@/assets/icons/product-bundles.svg';
import iconCF from '@/assets/icons/customized-fee.svg';
import iconEZ from '@/assets/icons/ez-bill.svg';
import iconDT from '@/assets/icons/document-template.svg';
import iconAU from '@/assets/icons/automation.svg';
import iconTM from '@/assets/icons/template-marketplace.svg';
import iconAR from '@/assets/icons/archive.svg';
import iconMS from '@/assets/icons/messages.svg';
import iconIN from '@/assets/icons/inbox.svg';

const selected = ref('tab1');
const selected2 = ref('tab1');

const primaryTabs = [
  { value: 'tab1', label: 'Option 1', icon: iconVP, badge: true },
  { value: 'tab2', label: 'Option 2', icon: iconSP, badge: true },
  { value: 'tab3', label: 'Option 3', icon: iconPP, badge: true },
  { value: 'tab4', label: 'Option 4', icon: iconPB, badge: true },
  { value: 'tab5', label: 'Option 5', icon: iconCF, badge: true },
];
const primaryIndie = { value: 'indie', label: 'Option 6', icon: iconEZ, badge: true };

const badgeTabs = [
  { value: 'tab1', label: 'Messages', icon: iconMS, badge: true },
  { value: 'tab2', label: 'Inbox',    icon: iconIN, badge: 3 },
  { value: 'tab3', label: 'Alerts',   icon: iconAU, badge: 12 },
  { value: 'tab4', label: 'Pending',  icon: iconTM, badge: 100 },
  { value: 'tab5', label: 'Spam',     icon: iconCF, badge: 'New' },
];
const badgeIndie = { value: 'tab6', label: 'Archive', icon: iconAR };
</script>

Underline (Medium Section Tab)

<template>
  <!-- Dot badge -->
  <Tab v-model="selected" type="underline" :tabs="primaryTabs" :indie_tab="primaryIndie" />

  <!-- Badge with content -->
  <Tab v-model="selected2" type="underline" :tabs="badgeTabs" :indie_tab="badgeIndie" />
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconSP from '@/assets/icons/signature-programs.svg';
import iconPP from '@/assets/icons/practice-products.svg';
import iconPB from '@/assets/icons/product-bundles.svg';
import iconCF from '@/assets/icons/customized-fee.svg';
import iconEZ from '@/assets/icons/ez-bill.svg';
import iconDT from '@/assets/icons/document-template.svg';
import iconAU from '@/assets/icons/automation.svg';
import iconTM from '@/assets/icons/template-marketplace.svg';
import iconAR from '@/assets/icons/archive.svg';

const selected = ref('tab1');
const selected2 = ref('tab1');

const primaryTabs = [
  { value: 'tab1', label: 'Option 1', icon: iconVP },
  { value: 'tab2', label: 'Option 2', icon: iconSP },
  { value: 'tab3', label: 'Option 3', icon: iconPP },
  { value: 'tab4', label: 'Option 4', icon: iconPB, badge: true },
  { value: 'tab5', label: 'Option 5', icon: iconCF, badge: true },
];
const primaryIndie = { value: 'indie', label: 'Option 6', icon: iconEZ, badge: true };

const badgeTabs = [
  { value: 'tab1', label: 'Messages', icon: iconEZ, badge: true },
  { value: 'tab2', label: 'Inbox',    icon: iconDT, badge: 3 },
  { value: 'tab3', label: 'Alerts',   icon: iconAU, badge: 12 },
  { value: 'tab4', label: 'Pending',  icon: iconTM, badge: 100 },
  { value: 'tab5', label: 'Spam',     icon: iconAR, badge: 'New' },
];
const badgeIndie = { value: 'tab6', label: 'Archive', icon: iconVP };
</script>

Button Tab

Full-width dark-blue button row; all tabs have a dark background, so icon images are always inverted to white. indie_tab is not typically used with this type.

<template>
  <!-- Dot badge -->
  <Tab v-model="selected" type="button" :tabs="primaryTabs" />

  <!-- Badge with content -->
  <Tab v-model="selected2" type="button" :tabs="badgeTabs" />
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconSP from '@/assets/icons/signature-programs.svg';
import iconPP from '@/assets/icons/practice-products.svg';
import iconPB from '@/assets/icons/product-bundles.svg';
import iconCF from '@/assets/icons/customized-fee.svg';
import iconEZ from '@/assets/icons/ez-bill.svg';
import iconDT from '@/assets/icons/document-template.svg';
import iconAU from '@/assets/icons/automation.svg';
import iconTM from '@/assets/icons/template-marketplace.svg';
import iconAR from '@/assets/icons/archive.svg';

const selected = ref('tab1');
const selected2 = ref('tab1');

const primaryTabs = [
  { value: 'tab1', label: 'Option 1', icon: iconVP, badge: true },
  { value: 'tab2', label: 'Option 2', icon: iconSP, badge: true },
  { value: 'tab3', label: 'Option 3', icon: iconPP, badge: true },
  { value: 'tab4', label: 'Option 4', icon: iconPB, badge: true },
  { value: 'tab5', label: 'Option 5', icon: iconCF, badge: true },
];

const badgeTabs = [
  { value: 'tab1', label: 'Messages', icon: iconEZ, badge: true },
  { value: 'tab2', label: 'Inbox',    icon: iconDT, badge: 3 },
  { value: 'tab3', label: 'Alerts',   icon: iconAU, badge: 12 },
  { value: 'tab4', label: 'Pending',  icon: iconTM, badge: 100 },
  { value: 'tab5', label: 'Spam',     icon: iconAR, badge: 'New' },
];
</script>

Vertical Menu Tab

Requires a sized parent container (the component fills 100% height). indie_tab is pinned to the bottom via margin-top: auto.

<template>
  <!-- Dot badge -->
  <div style="width: 320px; height: 900px; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff;">
    <Tab v-model="selected" type="vertical" :tabs="navTabs" :indie_tab="navIndie" />
  </div>

  <!-- Badge with content -->
  <div style="width: 320px; height: 900px; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff;">
    <Tab v-model="selected2" type="vertical" :tabs="navBadgeTabs" :indie_tab="navIndie" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconEZ from '@/assets/icons/ez-bill.svg';
import iconDT from '@/assets/icons/document-template.svg';
import iconAU from '@/assets/icons/automation.svg';
import iconTM from '@/assets/icons/template-marketplace.svg';
import iconAR from '@/assets/icons/archive.svg';
import iconCF from '@/assets/icons/customized-fee.svg';

const selected = ref('ez-bill');
const selected2 = ref('ez-bill');

const navTabs = [
  { value: 'ez-bill',              label: 'eZ-Bill',              icon: iconEZ, badge: true },
  { value: 'document-template',    label: 'Document Template',    icon: iconDT, badge: true },
  { value: 'automation',           label: 'Automation',           icon: iconAU },
  { value: 'template-marketplace', label: 'Template Marketplace', icon: iconTM },
  { value: 'archive',              label: 'Archive',              icon: iconAR, badge: true },
];
const navBadgeTabs = [
  { value: 'ez-bill',              label: 'eZ-Bill',              icon: iconEZ, badge: true },
  { value: 'document-template',    label: 'Document Template',    icon: iconDT, badge: 5 },
  { value: 'automation',           label: 'Automation',           icon: iconAU, badge: 12 },
  { value: 'template-marketplace', label: 'Template Marketplace', icon: iconTM, badge: 100 },
  { value: 'archive',              label: 'Archive',              icon: iconAR, badge: 'New' },
];
const navIndie = { value: 'customized-fee', label: 'Customized Fee', icon: iconCF };
</script>

Capsule Switch Tab (two color variants: sky default, vibrant-dark)

<template>
  <!-- Sky (default) — dot badge -->
  <div style="width: 540px;">
    <Tab v-model="selected" type="capsule" :tabs="switchTabs" />
  </div>

  <!-- Sky — badge with content -->
  <div style="width: 540px;">
    <Tab v-model="selected2" type="capsule" :tabs="switchBadgeTabs" />
  </div>

  <!-- Vibrant Dark — dot badge -->
  <div style="width: 540px;">
    <Tab v-model="selected3" type="capsule" color="vibrant-dark" :tabs="switchTabs" />
  </div>

  <!-- Vibrant Dark — badge with content -->
  <div style="width: 540px;">
    <Tab v-model="selected4" type="capsule" color="vibrant-dark" :tabs="switchBadgeTabs" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconPP from '@/assets/icons/practice-products.svg';

const selected = ref('vibrant');
const selected2 = ref('vibrant');
const selected3 = ref('vibrant');
const selected4 = ref('vibrant');

const switchTabs = [
  { value: 'vibrant',  label: 'Vibrant Products',  icon: iconVP, badge: true },
  { value: 'practice', label: 'Practice Products', icon: iconPP, badge: true },
];
const switchBadgeTabs = [
  { value: 'vibrant',  label: 'Vibrant Products',  icon: iconVP, badge: 3 },
  { value: 'practice', label: 'Practice Products', icon: iconPP, badge: 12 },
];
</script>

Oval Switch Tab (two color variants: sky default, vibrant-dark)

<template>
  <!-- Sky (default) — dot badge -->
  <div style="width: 580px;">
    <Tab v-model="selected" type="oval" :tabs="switchTabs" />
  </div>

  <!-- Sky — badge with content -->
  <div style="width: 580px;">
    <Tab v-model="selected2" type="oval" :tabs="switchBadgeTabs" />
  </div>

  <!-- Vibrant Dark — dot badge -->
  <div style="width: 580px;">
    <Tab v-model="selected3" type="oval" color="vibrant-dark" :tabs="switchTabs" />
  </div>

  <!-- Vibrant Dark — badge with content -->
  <div style="width: 580px;">
    <Tab v-model="selected4" type="oval" color="vibrant-dark" :tabs="switchBadgeTabs" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';
import iconVP from '@/assets/icons/vibrant-products.svg';
import iconPP from '@/assets/icons/practice-products.svg';

const selected = ref('vibrant');
const selected2 = ref('vibrant');
const selected3 = ref('vibrant');
const selected4 = ref('vibrant');

const switchTabs = [
  { value: 'vibrant',  label: 'Vibrant Products',  icon: iconVP, badge: true },
  { value: 'practice', label: 'Practice Products', icon: iconPP, badge: true },
];
const switchBadgeTabs = [
  { value: 'vibrant',  label: 'Vibrant Products',  icon: iconVP, badge: 3 },
  { value: 'practice', label: 'Practice Products', icon: iconPP, badge: 12 },
];
</script>

Click-to-clear-badge pattern (badges are data-driven, so you clear them yourself in @change)

<template>
  <Tab v-model="selected" type="underline" :tabs="tabs" @change="onChange" />
</template>

<script setup>
import { ref } from 'vue';
import { Tab } from 'va-responsive-components-library';

const selected = ref('tab1');
const tabs = ref([
  { value: 'tab1', label: 'Messages', badge: true },
  { value: 'tab2', label: 'Inbox', badge: 3 },
  { value: 'tab3', label: 'Alerts', badge: 12 },
]);

function onChange(value) {
  const t = tabs.value.find(x => x.value === value);
  if (t) t.badge = false;  // mark-as-read on click
}
</script>
Responsive Sizing

Tab adapts to any container by default (inherits parent width, scrolls horizontally when content overflows). For finer control, combine the responsive props below.

1. Total group width — width

<!-- Tab fills 380px regardless of parent. Number → px, or any CSS length string. -->
<Tab v-model="selected" type="filled" :width="380" :tabs="tabs" />
<Tab v-model="selected" type="oval"   width="50%"   :tabs="tabs" />
<Tab v-model="selected" type="outlined" width="32rem" :tabs="tabs" />

2. Fixed per-tab width + label font — tab_width + font_size (e.g. EHR ez-bill main-tab style)

<Tab
  v-model="selected"
  type="filled"
  :tab_width="135"
  :font_size="12"
  :tabs="tabs"
/>

3. Density presets — gap + padding + font_size + icon_size

<!-- Compact (sidebar / dense list) -->
<Tab
  v-model="selected"
  type="underline"
  :gap="2"
  :padding="6"
  :font_size="12"
  :icon_size="14"
  :tabs="tabs"
/>

<!-- Spacious (page header) -->
<Tab
  v-model="selected"
  type="underline"
  :gap="24"
  :padding="20"
  :font_size="16"
  :icon_size="24"
  :tabs="tabs"
/>

4. Overflow strategy — overflow_mode

<!-- Default: horizontal scroll (scrollbar hidden) — labels stay readable -->
<Tab v-model="selected" type="filled" :width="380" overflow_mode="scroll" :tabs="tabs" />

<!-- Or: ellipsis — tabs shrink in place, labels truncate where they sit -->
<Tab v-model="selected" type="filled" :width="380" overflow_mode="ellipsis" :tabs="tabs" />

All responsive props are independent — combine any of them. capsule always shares parent width evenly across its tabs because that is its design semantic.

ResponsiveButton

Responsive button component with multiple button types (filled, outlined, text), built-in themes (light, dark, error), and flexible sizing. Supports prefix/suffix slots, custom styling, and interaction effects.

Attributes
Attribute Description Type Default
display_name button text content String ''
size button size variant 'small' | 'medium' | 'large' 'small'
width_type button width behavior 'fill-whole' | 'fit-content' 'fit-content'
button_type visual style type 'filled' | 'outlined' | 'text' 'filled'
built_in_theme color theme (for text buttons, only error theme has effect; light/dark have no effect) 'light' | 'dark' | 'error' 'dark'
button_border_radius CSS border-radius value String '4px'
customized_class additional CSS class(es) String ''
button_id identifier for click event payload String ''
disabled disable button interaction Boolean false
Events
Event Description Parameters
click-button triggers when button is clicked (buttonId: String, displayName: String)
Slots
Name Description
default button text content (alternative to display_name)
prefix content before button text
suffix content after button text
<template>
  <ResponsiveButton
    display_name="Save Changes"
    size="medium"
    button_type="filled"
    built_in_theme="dark"
    width_type="fit-content"
    @click-button="handleSave"
  >
    <template #prefix>
      <svg viewBox="0 0 16 16"><path d="M8 2L6 6H2L5 9L4 14L8 11L12 14L11 9L14 6H10L8 2Z"/></svg>
    </template>
  </ResponsiveButton>
</template>

<script setup>
function handleSave(buttonId, displayName) {
  console.log('Button clicked:', buttonId, displayName)
}
</script>
Theme/Behavior Notes

Button Types:

  • filled: Solid background with theme color, white text
  • outlined: Transparent background with colored border, theme-colored text
  • text: No background or border, blue text by default. The error theme applies red color with appropriate hover/active states. light and dark themes have no visual effect.

Built-in Theme Colors:

  • dark: var(--VibrantDarkBlue) - primary dark blue
  • light: var(--New_Button_SkyBlue) - sky blue
  • error: var(--ErrorRed) - error red

Interaction States:

  • Hover: Filled buttons change to green hover color; outlined buttons darken; text buttons darken (or use error red hover if error theme)
  • Active: Filled buttons change to press blue color; outlined buttons show pressed state; text buttons show pressed background (or use error red pressed if error theme)
  • Disabled: Gray background/border with gray text, no cursor

Size Variants:

  • small: 4px 24px padding, 4px gap
  • medium: 8px 24px padding, 6px gap
  • large: 12px 32px padding, 8px gap
  • text buttons use 8px horizontal padding regardless of size

Deprecated Components

InputBox

Versatile input field with clearable icon, inside/outside label, styled themes, and validation helpers. The component dynamically adjusts border/background/label colors based on state (hover, focus, disabled, alert, error) and supports prefix/suffix slots.

Attributes
Attribute Description Type Default
v-model / modelValue bound input value string ''
label label text (optional) string ''
label_type where to render the label 'inside' | 'outside' 'outside'
theme_type visual styling theme 'filled' | 'outlined' 'outlined'
customized_theme_color focus/brand color used for caret color and focus border or underline; any valid CSS color or CSS var string ''
type native input type string 'text'
placeholder placeholder text string 'Enter'
clearable show a clear icon when focused/hovered and value is non-empty boolean false
required show a required asterisk on the label. Visual-only; does not enforce validation boolean false
disabled disable the input boolean false
autocomplete native autocomplete string 'off'

Behavior notes:

  • In filled theme, a bottom inset shadow emulates the underline. In outlined theme, an inset border is used. Colors are derived from:
    • Focus: customized_theme_color (or var(--Schemes-primary) fallback)
    • Hover: #17181C
    • Disabled: var(--disabled-color--) and var(--disabled-button-background-color--)
    • Alert/Error: var(--semantic-alert-color--) / var(--semantic-error-color--)
  • Inside label floats above the input content area and inherits a state color similar to the border color.
  • A message region appears below the field when either #supportingText slot has content or when alert/error is active with a message.
  • #supportingText is neutral and does not adopt alert/error colors; alert/error messages are colored separately.
  • Alert/Error messages are set via exposes: alert(message) / error(message); call removeAlertOrErrorEffect() to clear.
  • Disabled state down-weights message colors.
Events
Event Description Parameters
input fires on each keystroke (value: string)
change fires on Enter or blur (value: string)
focus input focused ()
blur input blurred ()
clear clear button clicked ()
Exposes
Method Description
focus programmatically focus the input
blur programmatically blur the input
alert show alert state with a message (yellow)
error show error state with a message (red)
removeAlertOrErrorEffect clear alert/error state
Slots
Name Description
prefix content rendered before the input (e.g., an icon)
suffix content rendered after the input (e.g., an icon)
supportingText optional helper text rendered below the field (e.g., character counter like 0/100). This is distinct from alert/error messages and uses a neutral color. When alert/error is active, their messages render alongside (with their own colors)

Tip: For prefix/suffix icons, prefer inline SVG that uses fill="currentColor". This way the icon color automatically follows the component state (hover/focus/disabled/alert/error). If you use <img> sources, they won't inherit color.

Usage
<template>
  <InputBox
    v-model="value"
    label="Email"
    label_type="inside"
    theme_type="outlined"
    customized_theme_color="var(--Schemes-primary)"
    type="email"
    placeholder="name@example.com"
    clearable
    autocomplete="email"
  >
    <template #prefix>
      
    </template>
  </InputBox>
</template>

<script setup>
import { ref } from 'vue'
const value = ref('')
</script>

#### SingleSelector
Dropdown selector with inside/outside label, themed styling, filtering, optional remote search with pagination, and a fully customizable per-option layout. Mirrors `InputBox` visual behavior (hover/focus/disabled/alert/error), supports prefix/suffix slots plus a neutral `supportingText` message region, a helper info line below the field, and an overridable background color.

##### Attributes

| Attribute | Description | Type | Default |
|-----------|-------------|------|---------|
| v-model / modelValue | binding value (selected option's `value`) | `string \| number \| boolean \| object \| array` | `''` |
| label | label text | `string` | `''` |
| label_type | label position — `'inside'` (inside the input box) or `'outside'` | `'inside' \| 'outside'` | `'outside'` |
| size | selector size | `'small' \| 'medium' \| 'large'` | `'small'` |
| theme_type | visual theme | `'filled' \| 'outlined'` | `'outlined'` |
| customized_theme_color | caret color and focus/brand color; any CSS color or CSS var | `string` | `''` |
| required | show red asterisk next to label | `boolean` | `false` |
| options | options array — each option supports `value`, `label`, `prefix?`, `suffix?`, `prefix_slot_raw_html_content?`, `suffix_slot_raw_html_content?`, `disabled?`, `is_selected?` | `Array<Option>` | `() => []` |
| filterable | enable client-side filter input | `boolean` | `false` |
| remote_search | emit query outward instead of local filtering | `boolean` | `false` |
| remote_search_pagination | (with `remote_search`) enable infinite-scroll pagination — emits `remote-search-load-more` | `boolean` | `false` |
| remote_search_has_more | parent-controlled flag: whether more pages are available | `boolean` | `false` |
| remote_search_load_more_loading | parent-controlled flag: whether the next page is being fetched; shows a "Loading more..." row | `boolean` | `false` |
| remote_search_scroll_threshold | px from bottom of the scroll area that triggers `remote-search-load-more` | `number` | `80` |
| disabled | disable selector | `boolean` | `false` |
| placeholder | placeholder when no value | `string` | `'Select'` |
| clearable | show clear icon when there is a value and focused/hovered | `boolean` | `false` |
| dropdown_max_height | max height of the dropdown scroll area (any CSS height) | `string` | `'400px'` |
| dropdown_width | override the dropdown width (any CSS width, e.g. `'300px'`); empty = match the trigger width | `string` | `''` |
| loading | loading state (mirrors dropdown's `is_loading`) | `boolean` | `false` |
| loading_text | loading text | `string` | `'Loading'` |
| no_data_text | empty-state text | `string` | `'No data'` |
| filter_only_among_options_value | filter against serialized `value` only | `boolean` | `false` |
| filter_only_among_options_label | filter against `label` only | `boolean` | `false` |
| helper_info | grey hint text displayed below the field | `string` | `''` |
| background_color_override | override the trigger's background color (ignored when `disabled`) | `string` | `''` |
| customizedOptionStyle | render each option via the `#option` scoped slot (slot props: `{ option, index, is_selected, disabled }`) instead of the default prefix/label/suffix layout | `boolean` | `false` |
| customizeOptionItem | render the entire option element via the `#option_item` scoped slot — no built-in `<button>` wrapper, click handler, or default row layout. Slot props: `{ option, index, is_selected, disabled, select }`. Call `select()` from your own element to trigger the normal selection. Takes precedence over `customizedOptionStyle`. | `boolean` | `false` |

Notes:

- `filter_only_among_options_value` and `filter_only_among_options_label` cannot both be `true`. If both are `true`, filtering uses `value`.
- Option `value` may be primitive or object; selection compares by `value` plus `label` for marking `is_selected` in lists.

##### Events

| Event | Description | Parameters |
|-------|-------------|------------|
| update:modelValue | syncs v-model when the selected value changes | `(value: any)` |
| change | triggers when the binding value changes | `(value: any)` |
| remote-search | emitted while typing when `remote_search=true` | `(query: string)` |
| remote-search-load-more | emitted when scrolling near the bottom with `remote_search_pagination=true` and `remote_search_has_more=true` | `(query: string)` |
| filter-change | emitted whenever the filter input text changes | `(query: string)` |
| visible-change | dropdown visibility changes | `(visible: boolean)` |
| focus | selector focused | `()` |
| blur | selector blurred | `()` |
| clear | clear button clicked | `()` |

##### Exposes

| Method | Description | Type |
|--------|-------------|------|
| focus | programmatically focus the selector | `() => void` |
| blur | programmatically blur the selector | `() => void` |
| alert | show alert state with a message (yellow) | `(msg: string) => void` |
| error | show error state with a message (red) | `(msg: string) => void` |
| removeAlertOrErrorEffect | clear alert/error state | `() => void` |
| setDropdownContentLoading | toggle the dropdown's internal loading spinner | `(val: boolean) => void` |

##### Slots

| Name | Slot props | Description |
|------|------------|-------------|
| prefix | — | content before the selected value (e.g. icon, flag) |
| suffix | — | replaces the default chevron; content after the selected value |
| supportingText | — | neutral helper text under the field; alert/error messages render alongside in their own colors |
| option | `{ option, index, is_selected, disabled }` | per-option custom content; only rendered when `customizedOptionStyle` is `true` |
| option_item | `{ option, index, is_selected, disabled, select }` | full per-option element (no built-in wrapper); only rendered when `customizeOptionItem` is `true`. Call `select()` to trigger the normal selection. |

```vue
<SingleSelector
  v-model="selected"
  :options="options"
  filterable
  clearable
  placeholder="Choose option"
  :customizedOptionStyle="true"
>
  <template #prefix>
    <Icon name="search" />
  </template>
  <template #option="{ option, is_selected }">
    <span :class="['my-option', { selected: is_selected }]">
      
      <span>{{ option.label }}</span>
      <small>{{ option.meta }}</small>
    </span>
  </template>
  <template #supportingText>
    Pick one option
  </template>
</SingleSelector>
DropdownMenu (dev-only)

Used internally by components to render options lists.

  • Props: width_type: 'fill-whole'|'fit-content', options (same schema as above), size: 'small'|'medium'|'large', with_box_shadow, is_loading, loading_text, no_data_text.
  • Events:
    • select-dropdown-option(value, label, in_dropdown_level)
    • add-new-shown-nested-dropdown(target_item_props, trigger)
    • remove-shown-nested-dropdown(target_item_props)
    • update-shown-nested-dropdown(target_item_props)
    • click-outside-dropdown-menu(event)
MultiSelector

Multi-select dropdown with selected values rendered as inline chips, built-in search, optional remote search with pagination, per-option action link, and a fully customizable per-option layout (checkbox kept on the left). The dropdown is split into an opaque outer box and an inner scroll element so rubber-band bounce never reveals content behind the dropdown.

Attributes
Attribute Description Type Default
v-model / modelValue array of selected values Array () => []
options options array — each option supports value, label, prefix_slot_raw_html_content?, action_label?, disabled? Array () => []
size selector size 'small' | 'medium' | 'large' 'medium'
placeholder placeholder in the search input when no chips are selected string 'Type to search'
disabled disable selector boolean false
empty_text text shown when no options match the search string 'No results'
remote_search disable internal keyword filtering; options are managed externally via @search boolean false
remote_search_pagination (with remote_search) enable infinite-scroll pagination — emits remote-search-load-more boolean false
remote_search_has_more parent-controlled flag: whether more pages are available boolean false
remote_search_load_more_loading parent-controlled flag: whether the next page is being fetched; shows a "Loading more..." row boolean false
remote_search_scroll_threshold px from bottom of the scroll area that triggers remote-search-load-more number 80
loading show a loading spinner in the dropdown instead of options boolean false
helper_info grey hint text displayed below the trigger string ''
dropdown_max_height max height of the dropdown scroll area (any CSS height, e.g. '400px', '50vh') string '400px'
customizedOptionStyle render each option's content (after the checkbox) via the #option scoped slot boolean false
customizeOptionItem render the entire option element via the #option_item scoped slot — no built-in <button> wrapper, no default checkbox or tint-box. Slot props: { option, index, is_selected, disabled, toggle }. Call toggle() from your own element to trigger the normal selection logic. Takes precedence over customizedOptionStyle. boolean false
Events
Event Description Parameters
update:modelValue syncs v-model when the selected values change (values: Array)
change emitted when the selected values change (values: Array)
visible-change dropdown visibility changes (visible: boolean)
focus search input focused ()
blur search input blurred ()
clear all chips cleared (value: null)
option-action an option's action link clicked (option: Option)
search emitted while typing when remote_search=true (query: string)
remote-search-load-more emitted when scrolling near the bottom with remote_search_pagination=true and remote_search_has_more=true (query: string)
Exposes
Method Description Type
focus programmatically focus the search input () => void
blur programmatically blur the search input () => void
clear remove all selected chips () => void
Slots
Name Slot props Description
option { option, index, is_selected, disabled } per-option custom content rendered to the right of the checkbox; only used when customizedOptionStyle is true
option_item { option, index, is_selected, disabled, toggle } full per-option element (no built-in wrapper or checkbox); only rendered when customizeOptionItem is true. Call toggle() to trigger the normal selection.
<MultiSelector
  v-model="selected"
  :options="options"
  size="medium"
  placeholder="Type to search"
  helper_info="Pick one or more"
  :customizedOptionStyle="true"
  @change="onChange"
  @option-action="onOptionAction"
>
  <template #option="{ option, is_selected }">
    <span :class="['opt', { selected: is_selected }]">
      <span class="opt-name">{{ option.label }}</span>
      <small class="opt-meta">{{ option.code }}</small>
    </span>
  </template>
</MultiSelector>
Theme/Behavior Notes
  • Chip layout: selected values render as Chip components inside the trigger; chip type follows size (small → 'regular', medium/large → 'inside-combo-selector').
  • Remote-search pagination: when remote_search, remote_search_pagination, and remote_search_has_more are all true, the component watches the dropdown scroll and emits remote-search-load-more once per threshold crossing. Set remote_search_load_more_loading to true while fetching to show the inline "Loading more..." row and suppress further emits.
  • Dropdown structure: the outer .multi-selector-dropdown is the opaque styled box (border, radius, white bg, isolation: isolate); the inner .multi-selector-dropdown-scroll handles overflow with overscroll-behavior: contain.
  • Option values must be JSON-serializable: selection equality, the selected-set membership check, toggleOption, and removeSelectedItem all compare via JSON.stringify(option.value). Values containing circular references, BigInt, functions, or symbols will throw or compare incorrectly. The v-for :key falls back to the option index for non-serializable values so rendering never crashes, but equality logic will still misbehave — prefer primitives or plain data objects for value.
TextInput

Single-line text input with small/medium/large sizes, clearable button, prefix/suffix slots, helper info, and an optional attached SingleSelector prefix (with forwarded #prefix and per-option #option slots). Supports alert/error states with a supporting message region.

Attributes
Attribute Description Type Default
v-model / modelValue bound value string | number ''
size input size (32 / 40 / 48 px) 'small' | 'medium' | 'large' 'medium'
type native input type string 'text'
placeholder placeholder text string 'Type to enter ...'
clearable show inline clear button when value is non-empty boolean true
disabled disable the input boolean false
autocomplete native autocomplete attribute string 'off'
helper_info grey hint text displayed below the input string ''
customized_theme_color caret color and focus/brand color; any CSS color or CSS var string ''
prefix_selector_options when non-empty, attaches a SingleSelector on the left — array of { value, label } Array () => []
v-model:prefix_selector_value / prefix_selector_value selected value of the prefix selector string | number | boolean | object null
prefix_selector_placeholder placeholder for the prefix selector string 'Select'
prefix_selector_dropdown_width forwarded to the inner SingleSelector's dropdown_width string ''
isDateOfBirth enable date of birth input mode with automatic formatting (placeholder is automatically set to dateFormat) boolean false
dateFormat format for date of birth input 'MM-DD-YYYY' | 'MM/DD/YYYY' 'MM-DD-YYYY'
Events
Event Description Parameters
update:modelValue emitted on every keystroke (value: string)
input emitted on every keystroke (value: string)
change emitted when Enter is pressed (value: string)
focus native input focused ()
blur native input blurred ()
clear inline clear button clicked ()
update:prefix_selector_value v-model update for the prefix selector (value: any)
prefix_selector_change prefix selector selection changed (value: any)
Exposes
Method Description Type
focus focus the input () => void
blur blur the input () => void
alert show alert message below the input (msg: string) => void
error show error message below the input (msg: string) => void
removeAlertOrErrorEffect clear alert/error state () => void
Slots
Name Slot props Description
prefix — content before the input field (icon/text)
suffix — content after the input field (icon/text)
supportingText — neutral helper text in the message region below the input; coexists with alert/error messages
prefix_selector_prefix — forwarded to the inner SingleSelector's #prefix slot (e.g. a flag or icon shown inside the prefix selector's selected value)
prefix_selector_option { option, index, is_selected, disabled } forwarded to the inner SingleSelector's #option scoped slot — when present, customizedOptionStyle is auto-enabled on the inner selector
<TextInput
  v-model="phone"
  size="large"
  placeholder="Phone number"
  v-model:prefix_selector_value="country"
  :prefix_selector_options="countryOptions"
  prefix_selector_dropdown_width="300px"
  helper_info="We will call this number"
>
  <template #prefix_selector_prefix>
    <span>{{ currentFlag }} +{{ currentCode }}</span>
  </template>
  <template #prefix_selector_option="{ option, is_selected }">
    <div :class="['opt', { selected: is_selected }]">
      <span>{{ option.flag }}</span>
      <span>{{ option.country }}</span>
      <span>+{{ option.code }}</span>
    </div>
  </template>
</TextInput>

Date of Birth Mode

When isDateOfBirth is true, the input automatically formats dates as the user types. The dateFormat prop controls the separator (- or /).

<!-- MM-DD-YYYY format -->
<TextInput
  v-model="birthDate"
  size="large"
  :isDateOfBirth="true"
  dateFormat="MM-DD-YYYY"
/>

<!-- MM/DD/YYYY format -->
<TextInput
  v-model="birthDate"
  size="large"
  :isDateOfBirth="true"
  dateFormat="MM/DD/YYYY"
/>

As the user types, the input automatically formats:

  • Typing 01152000 → 01-15-2000 (or 01/15/2000)
  • Backspace behavior respects separators
  • Placeholder shows the selected format
TextArea

Multi-line text input with a built-in toolbar for character count and a Clear button, fixed or stretch-to-fill height, and a helper info line. Visually matches TextInput (same border, hover tint, disabled state).

Attributes
Attribute Description Type Default
v-model / modelValue bound text string ''
placeholder placeholder text string 'Text Area'
rows initial visible row count (the textarea grows naturally beyond this) number 3
max_length when set, shows the character counter in the toolbar number 1000
show_char_count show the {count}/{max_length} characters counter boolean true
clearable show the Clear toolbar button when the textarea has a value boolean true
disabled disable the textarea boolean false
helper_info grey hint text displayed below the textarea string ''
height fixed textarea height — number (px), any CSS string (e.g. '8em'), or 'full' to stretch to the outer container number | string 84
Events
Event Description Parameters
update:modelValue emitted on every keystroke (value: string)
input emitted on every keystroke (value: string)
change native change event (value: string)
focus textarea focused ()
blur textarea blurred ()
clear Clear button clicked ()
Exposes
Method Description Type
focus focus the textarea () => void
blur blur the textarea () => void
alert show alert message (internal state) (msg: string) => void
error show error message (internal state) (msg: string) => void
removeAlertOrErrorEffect clear alert/error state () => void
<TextArea
  v-model="bio"
  placeholder="Tell us about yourself..."
  :rows="4"
  :max_length="500"
  helper_info="Max 500 characters"
  height="120"
/>
Label

Layout wrapper that places a label row above or beside any content with configurable gap and width. Supports vertical/horizontal layouts, required marker, optional icon slot, and a fully customizable label slot (like SingleSelector's customizedOptionStyle).

Attributes
Attribute Description Type Default
label label text string ''
required show (Required) marker in red boolean false
gap vertical layout only: gap between label row and content (number = px, or any CSS string) number | string 4
layout layout direction — 'vertical' (default) puts label above content; 'horizontal' puts label left with a 20 px gap 'vertical' | 'horizontal' 'vertical'
label_width horizontal layout only: fixed width of the label column (number = px, or any CSS string) number | string null
label_align horizontal layout only: vertical alignment of the label column 'top' | 'center' | 'bottom' 'top'
customizedLabelStyle when true, the label row's inner content is rendered via the #label scoped slot instead of the default icon + text + (Required) layout boolean false
Slots
Name Slot props Description
default — content placed below (vertical) or beside (horizontal) the label
icon — 24×24 icon displayed to the left of the label text (default layout only)
label { label, required, layout } fully replaces the default label row content; only rendered when customizedLabelStyle is true
<!-- Default layout -->
<Label label="Email" required>
  <TextInput v-model="email" />
</Label>

<!-- Horizontal with fixed width -->
<Label label="Billing Address" layout="horizontal" :label_width="160">
  <TextInput v-model="addr" />
</Label>

<!-- Fully customized label -->
<Label label="API Key" :customizedLabelStyle="true">
  <template #label="{ label, required }">
    <span class="custom-label">
      <strong>{{ label }}</strong>
      <em v-if="required">must fill</em>
      <span title="Secret">(?)</span>
    </span>
  </template>
  <TextInput v-model="key" />
</Label>
InfoContainer

Container that wraps any content and attaches an inline Notification below it for error/alert messages. When message is empty the wrapper is transparent (no border) and only the slot content is rendered.

Attributes
Attribute Description Type Default
message notification message; when empty/null the container is transparent and renders only the slot string ''
state notification state — maps to Notification theme (error → error, alert → warning) 'error' | 'alert' 'error'
action_label optional action link text shown on the right of the inline notification string ''
Events
Event Description Parameters
action emitted when the action link is clicked ()
Slots
Name Slot props Description
default — the content to wrap (typically one or more form fields)
<InfoContainer
  :message="formError"
  state="error"
  action_label="Retry"
  @action="retrySubmit"
>
  <Label label="Email" required>
    <TextInput v-model="email" />
  </Label>
  <Label label="Password" required>
    <TextInput v-model="pw" type="password" />
  </Label>
</InfoContainer>
Theme/Behavior Notes
  • State mapping: state="error" → red border + Notification theme error; state="alert" → amber border (CSS var --semantic-alert-color--) + Notification theme warning.
  • No message, no border: when message is falsy, state-none is applied and the border becomes transparent so the container is invisible in the layout.
Checkbox

Customizable checkbox with multiple themes, indeterminate state, and width control. Supports integration with GroupCheckbox for managing multiple related checkboxes.

Attributes
Attribute Description Type Default
v-model / modelValue binding value - true when checked, false when unchecked boolean false
v-model:indeterminate indeterminate state binding boolean false
isLabelClickable whether clicking the label toggles the checkbox boolean true
label checkbox label text string ''
width width behavior 'fit-content' | 'fill-whole' 'fit-content'
built_in_theme built-in color theme 'green' | 'primary-blue' | 'secondary-blue' | 'red' 'green'
customized_theme_color custom theme color (overrides built_in_theme) string (any valid CSS color value) ''
customized_class custom CSS class for complete styling override string ''
disabled whether checkbox is disabled boolean false
autofocus whether to auto focus on mount boolean false
Events
Event Description Parameters
update:modelValue syncs v-model when checked state changes (value: boolean)
update:indeterminate syncs v-model:indeterminate when indeterminate state changes (value: boolean)
change triggered when the checkbox is checked or unchecked (value: boolean)
Exposes
Method Description Type
focus programmatically focus the checkbox () => void
blur programmatically blur the checkbox () => void
Slots
Name Description
default custom label content (overrides label prop)
<template>
  <!-- Basic usage with default theme -->
  <Checkbox
    v-model="agreed"
    label="I agree to terms"
  />

  <!-- With custom theme color (overrides built_in_theme) -->
  <Checkbox
    v-model="agreed"
    label="I agree to terms"
    built_in_theme="primary-blue"
    customized_theme_color="#FF0000"
    @change="handleChange"
  />

  <!-- Using CSS variable for theme color -->
  <Checkbox
    v-model="agreed"
    label="I agree to terms"
    customized_theme_color="var(--Schemes-primary)"
    @change="handleChange"
  />

  <!-- With indeterminate state -->
  <Checkbox
    v-model="checked"
    v-model:indeterminate="indeterminate"
    label="Select all"
  />

  <!-- Full width with non-clickable label -->
  <Checkbox
    v-model="checked"
    label="Non-clickable label"
    :isLabelClickable="false"
    width="fill-whole"
  />

  <!-- Using slot for custom label content -->
  <Checkbox v-model="checked">
    <div>
      <strong>Bold Label</strong> with custom content
    </div>
  </Checkbox>
</template>

<script setup>
import { ref } from 'vue'

const agreed = ref(false)
const checked = ref(false)
const indeterminate = ref(false)

const handleChange = (value) => {
  console.log('Checkbox changed:', value)
}
</script>
Theme Behavior Notes

Theme Color Priority (highest to lowest):

  1. Disabled: Always uses var(--DisabledGrey_20) regardless of other theme settings
  2. customized_theme_color: When provided (non-empty), overrides built_in_theme
  3. built_in_theme: Falls back to built-in theme when no customized color is set
    • green: var(--New_Library_HoverGreen)
    • primary-blue: var(--VibrantDarkBlue)
    • secondary-blue: var(--New_Button_SkyBlue)
    • red: var(--ErrorRed)

Theme Color Applies To:

  • Checkbox border (all states: unchecked, checked, indeterminate)
  • Checked/indeterminate background color
  • Hover effect overlay (8% color mix of customized_theme_color)
  • Hover effect checked/indeterminate border color (50% color mix of customized_theme_color)
  • Active/hover border width changes

Indeterminate & Checked State Interaction:

  • When checked becomes true while indeterminate is true: indeterminate automatically becomes false
  • When indeterminate becomes true while checked is true: checked automatically becomes false
  • When both are true: checkbox displays as indeterminate
  • Visual representation: Indeterminate state takes precedence over checked state
GroupCheckbox

Container for managing multiple related checkboxes as a group. Automatically tracks which child checkboxes are checked and provides a reactive array of selected labels.

Attributes
Attribute Description Type Default
v-model / modelValue binding value - array of selected che

Related Packages

@vibrant-wellness/va-responsive-components-library

A comprehensive Vue 3 component library built with modern development practices,...

6,002
autoresponsive-vue

Auto Responsive Layout Library For Vue

4,650 141
sectum

A modern Vue 3 component library built with UnoCSS, featuring beautiful UI compo...

5,552
vue-not-visible

Vue directive for conditional rendering (like v-if) element on screen smaller th...

932 22

Version History

Version Released Vue License
0.0.3 ^3.5.13
0.0.2 ^3.5.13