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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bootcn-vue/forms

v0.6.2

Published

Accessible, customizable form components built with Bootstrap 5 and Vue 3

Readme

@bootcn-vue/forms

Accessible, customizable form components and primitives built with Bootstrap 5 and Vue 3.

npm version License: MIT

📚 Documentation

View Components & Examples - Interactive Storybook with live examples

Overview

This package provides a comprehensive set of form components and primitives for building accessible, type-safe forms in Vue 3. Components follow Bootstrap 5 conventions and use the RDS spacing system.

Architecture

  • Primitives - Low-level building blocks (InputRoot, InputLabel, InputField, InputError, InputHelp)
  • Specialized Components - Higher-level components built on primitives (InputPassword, InputMasked, InputNumericRange)
  • Field Components - Complete field components with labels, validation, and help text (see @bootcn-vue/field-text and @bootcn-vue/field-password)

Installation

Using CLI (Recommended)

# Initialize bootcn-vue (if not already done)
npx @bootcn-vue/cli@latest init

# Add form components (when available via CLI)
npx @bootcn-vue/cli@latest add input

Direct Installation

npm install @bootcn-vue/forms @bootcn-vue/core bootstrap reka-ui

Import Options

You can import all components or individual primitives:

// Import all components
import {
  InputRoot,
  InputLabel,
  InputField,
  InputError,
  InputHelp,
} from "@bootcn-vue/forms";

// Import individual primitives (includes context automatically)
import { InputLabel } from "@bootcn-vue/forms/InputLabel";
import { InputField } from "@bootcn-vue/forms/InputField";

Note: When importing individual primitives, the form context is automatically available, so components like InputLabel and InputField will work correctly within InputRoot.

Components

Form Primitives

Build custom form fields using composable primitives:

InputRoot

Container that provides form context to child components.

<script setup lang="ts">
import { InputRoot } from "@bootcn-vue/forms";
</script>

<template>
  <InputRoot id="email" :error="errorMessage" :disabled="false">
    <!-- Child components go here -->
  </InputRoot>
</template>

Props:

  • id: string - Unique ID for the input (required)
  • error?: string - Error message
  • disabled?: boolean - Disable state
  • required?: boolean - Required state

InputLabel

Accessible label with optional tooltip, optional badge, and customizable icons. Provides flexible labeling for form fields with semantic heading levels and visual sizing.

Basic Usage:

<script setup lang="ts">
import { InputLabel } from "@bootcn-vue/forms";
</script>

<template>
  <InputRoot id="email">
    <InputLabel>Email Address</InputLabel>
    <InputField type="email" />
  </InputRoot>
</template>

With Tooltip:

<InputRoot id="company">
  <InputLabel tooltip-message="Enter your company's legal name as it appears on official documents">
    Company Name
  </InputLabel>
  <InputField type="text" />
</InputRoot>

Optional Field:

<InputRoot id="middle-name">
  <InputLabel is-optional>
    Middle Name
  </InputLabel>
  <InputField type="text" />
</InputRoot>

Props:

| Prop | Type | Default | Description | | ----------------- | ---------------------------------------------- | ------------ | ----------------------------------------------- | | level | "h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6" | "h3" | Semantic heading level for the label | | size | "small" \| "medium" \| "large" | "small" | Visual size of the label text | | class | HTMLAttributes["class"] | undefined | Additional CSS classes | | tooltipMessage | string | undefined | Tooltip text. When provided, shows an info icon | | tooltipPosition | "top" \| "bottom" \| "left" \| "right" | "top" | Position of the tooltip relative to the icon | | isOptional | boolean | false | Shows an optional badge next to the label | | optionalText | string | "Optional" | Text displayed in the optional badge | | iconPosition | "before" \| "after" | "after" | Position of the tooltip icon relative to label |

Slots:

| Slot Name | Description | | ---------------- | ------------------------------------------------------ | | default | Label text content | | icon | Custom icon for tooltip trigger (replaces default SVG) | | optional-badge | Custom content for the optional indicator |

Advanced Examples:

Custom Tooltip Icon (FontAwesome):

<template>
  <InputRoot id="security-code">
    <InputLabel
      tooltip-message="This is sensitive information that we keep secure"
    >
      Security Code
      <template #icon>
        <FontAwesomeIcon
          :icon="['fas', 'shield-halved']"
          class="text-warning"
        />
      </template>
    </InputLabel>
    <InputField type="text" />
  </InputRoot>
</template>

<script setup lang="ts">
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { InputRoot, InputField, InputLabel } from "@bootcn-vue/forms";
</script>

Icon Position (Before Label):

<InputLabel
  tooltip-message="Important security information"
  icon-position="before"
>
  API Key
</InputLabel>

Custom Optional Badge:

<!-- Custom text -->
<InputLabel is-optional optional-text="Not Required">
  Nickname
</InputLabel>

<!-- Custom icon badge -->
<InputLabel is-optional>
  Display Name
  <template #optional-badge>
    <FontAwesomeIcon :icon="['fas', 'circle-question']" class="text-muted" size="sm" />
  </template>
</InputLabel>

Label Sizing:

<!-- Small (default) -->
<InputLabel level="h3" size="small">Small Label</InputLabel>

<!-- Medium -->
<InputLabel level="h3" size="medium">Medium Label</InputLabel>

<!-- Large -->
<InputLabel level="h2" size="large">Large Label</InputLabel>

Accessibility:

  • Uses semantic <label> element with proper for attribute
  • Tooltip icon button includes aria-label="More information"
  • Supports keyboard navigation for tooltip trigger
  • SVG icons have aria-hidden="true" to prevent screen reader announcement
  • Works seamlessly with InputRoot's context for ID management

📖 Detailed Guide: See INPUT_LABEL_GUIDE.md for comprehensive examples and advanced usage patterns.

InputField

The actual input element with Bootstrap styling.

<script setup lang="ts">
import { InputField } from "@bootcn-vue/forms";
</script>

<template>
  <InputField
    id="email"
    v-model="email"
    type="email"
    placeholder="[email protected]"
  />
</template>

Props:

  • id: string - Input ID (required)
  • modelValue?: string | number - v-model binding
  • type?: string - Input type (default: text)
  • placeholder?: string
  • disabled?: boolean
  • readonly?: boolean
  • required?: boolean
  • autocomplete?: string
  • class?: string

InputHelp

Help text displayed below the input.

<script setup lang="ts">
import { InputHelp } from "@bootcn-vue/forms";
</script>

<template>
  <InputHelp>We'll never share your email with anyone.</InputHelp>
</template>

InputError

Error message with alert styling.

<script setup lang="ts">
import { InputError } from "@bootcn-vue/forms";
</script>

<template>
  <InputError v-if="error">{{ error }}</InputError>
</template>

Specialized Input Components

InputPassword

Password input with show/hide toggle.

<script setup lang="ts">
import { InputPassword } from "@bootcn-vue/forms";
import { ref } from "vue";

const password = ref("");
</script>

<template>
  <InputPassword
    id="password"
    v-model="password"
    placeholder="Enter password"
  />
</template>

Features:

  • Toggle button to show/hide password
  • Maintains cursor position when toggling
  • Accessible with keyboard navigation
  • FontAwesome icons for toggle button

InputMasked

Input with custom masking using maska library.

<script setup lang="ts">
import { InputMasked } from "@bootcn-vue/forms";
import { ref } from "vue";

const phone = ref("");
</script>

<template>
  <InputMasked
    id="phone"
    v-model="phone"
    mask="(###) ###-####"
    placeholder="(555) 555-5555"
  />
</template>

Props:

  • mask: string | string[] - Mask pattern (e.g., ###-##-####)
  • All standard input props

Mask Patterns:

  • # - Number (0-9)
  • @ - Letter (a-z, A-Z)
  • * - Alphanumeric
  • ! - Escape character

InputNumericRange

Numeric input with min/max validation.

<script setup lang="ts">
import { InputNumericRange } from "@bootcn-vue/forms";
import { ref } from "vue";

const age = ref(25);
</script>

<template>
  <InputNumericRange
    id="age"
    v-model="age"
    :min="18"
    :max="120"
    placeholder="Enter your age"
  />
</template>

Props:

  • min?: number - Minimum value
  • max?: number - Maximum value
  • All standard input props

Complete Field Components

For ready-to-use form fields with labels, validation, and help text:

FieldSSN

Complete SSN field with masking and validation.

<script setup lang="ts">
import { FieldSSN } from "@bootcn-vue/forms";
import { ref } from "vue";

const ssn = ref("");
</script>

<template>
  <FieldSSN
    id="ssn"
    label="Social Security Number"
    v-model="ssn"
    required
    help-text="Format: XXX-XX-XXXX"
  />
</template>

Building Custom Fields

Use primitives to build your own field components:

<script setup lang="ts">
import {
  InputRoot,
  InputLabel,
  InputField,
  InputHelp,
  InputError,
} from "@bootcn-vue/forms";
import { ref, computed } from "vue";

const email = ref("");
const error = computed(() => {
  if (!email.value) return "Email is required";
  if (!email.value.includes("@")) return "Invalid email format";
  return "";
});
</script>

<template>
  <InputRoot id="email" :error="error" :required="true">
    <InputLabel for="email" level="h3" size="small" :required="true">
      Email Address
      <template #tooltip>We'll send confirmation here</template>
    </InputLabel>

    <InputField
      id="email"
      v-model="email"
      type="email"
      placeholder="[email protected]"
    />

    <InputHelp v-if="!error">
      We'll never share your email with anyone.
    </InputHelp>

    <InputError v-if="error">{{ error }}</InputError>
  </InputRoot>
</template>

Form Context

The InputRoot component provides context to all child components using Vue's provide/inject:

interface FormFieldContext {
  id: string;
  error?: string;
  disabled?: boolean;
  required?: boolean;
}

Child components automatically consume this context, eliminating prop drilling.

Accessibility

All components follow WCAG 2.1 guidelines:

  • ✅ Proper label associations with for and id attributes
  • ✅ ARIA attributes for error states (aria-invalid, aria-describedby)
  • ✅ Required indicators with aria-required
  • ✅ Keyboard navigation support
  • ✅ Screen reader announcements for errors
  • ✅ Focus management
  • ✅ High contrast support

TypeScript Support

Full TypeScript support with exported types:

import type { BaseFieldProps, FormFieldContext } from "@bootcn-vue/forms";

interface MyFieldProps extends BaseFieldProps {
  customProp?: string;
}

Styling

Components use Bootstrap 5 classes and the RDS spacing system:

<template>
  <!-- RDS spacing classes -->
  <div class="mb-space-sm">
    <InputField id="name" class="form-control" />
  </div>
</template>

Tooltip Styles Requirement

The InputLabel component uses tooltips from @bootcn-vue/tooltip. You must import Bootstrap's tooltip styles in your main SCSS file:

// Option 1: Import full Bootstrap (if you're using it)
@import "bootstrap/scss/bootstrap";
@import "@bootcn-vue/tooltip/src/tooltip.css";

// Option 2: Import only required Bootstrap modules for tooltips
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/root";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/tooltip";
@import "@bootcn-vue/tooltip/src/tooltip.css";

See the @bootcn-vue/tooltip documentation for more details.

Dependencies

  • @bootcn-vue/core - Core utilities (cn function, cva)
  • @bootcn-vue/tooltip - Tooltip component for labels (requires Bootstrap tooltip styles)
  • reka-ui - Accessible primitives
  • maska - Input masking library
  • @fortawesome/vue-fontawesome - Icons

Peer Dependencies

  • vue ^3.5.0
  • bootstrap ^5.3.0

Links

Related Packages

License

MIT © Shashank Shandilya