@bootcn-vue/forms
v0.6.2
Published
Accessible, customizable form components built with Bootstrap 5 and Vue 3
Maintainers
Readme
@bootcn-vue/forms
Accessible, customizable form components and primitives built with Bootstrap 5 and Vue 3.
📚 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 inputDirect Installation
npm install @bootcn-vue/forms @bootcn-vue/core bootstrap reka-uiImport 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 messagedisabled?: boolean- Disable staterequired?: 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 properforattribute - 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 bindingtype?: string- Input type (default:text)placeholder?: stringdisabled?: booleanreadonly?: booleanrequired?: booleanautocomplete?: stringclass?: 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 valuemax?: number- Maximum value- All standard input props
Complete Field Components
For ready-to-use form fields with labels, validation, and help text:
- @bootcn-vue/field-text - Text input field
- @bootcn-vue/field-password - Password field with toggle
- FieldSSN - Social Security Number field (included in this package)
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
forandidattributes - ✅ 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 (cnfunction,cva)@bootcn-vue/tooltip- Tooltip component for labels (requires Bootstrap tooltip styles)reka-ui- Accessible primitivesmaska- Input masking library@fortawesome/vue-fontawesome- Icons
Peer Dependencies
vue^3.5.0bootstrap^5.3.0
Links
- GitHub Repository
- Documentation
- Report Issues
- Input Label Guide - Detailed guide on using InputLabel
Related Packages
- @bootcn-vue/cli - CLI for adding components
- @bootcn-vue/core - Core utilities
- @bootcn-vue/field-text - Text input field
- @bootcn-vue/field-password - Password field
- @bootcn-vue/buttons - Button components
License
MIT © Shashank Shandilya
