Overview
The Forms component provides a comprehensive styling system for all form elements. It includes modern form controls, validation states, floating labels, inline forms, responsive layouts, and accessibility features. The component maintains consistency across all input types while providing flexibility for different use cases.
📝 Complete Form Controls
Styling for all input types, select, textarea, checkboxes, radios, and file inputs.
✅ Validation States
Built-in validation styling with success, error states, and feedback messages.
🏷️ Floating Labels
Modern floating label inputs for improved user experience and space efficiency.
📐 Flexible Layouts
Inline forms, aiab-grid layouts, responsive designs, and form grouping options.
♿ Full Accessibility
Proper focus management, ARIA attributes, and screen reader support.
🎨 Customizable Styling
CSS variables for easy theming and consistent design across all form elements.
Basic Form Elements
Text Inputs
<div class="aiab-form-group">
<label for="text-input">Text Input</label>
<input type="text" id="text-input" class="aiab-form-control" placeholder="Enter text here">
</div>
<div class="aiab-form-group">
<label for="email-input">Email Input</label>
<input type="email" id="email-input" class="aiab-form-control" placeholder="user@example.com">
</div>
<div class="aiab-form-group">
<label for="password-input">Password Input</label>
<input type="password" id="password-input" class="aiab-form-control" placeholder="Enter password">
</div>
Select and Textarea
<div class="aiab-form-group">
<label for="select-input">Select Dropdown</label>
<select id="select-input" class="aiab-form-control">
<option value="">Choose an option</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
</div>
<div class="aiab-form-group">
<label for="textarea-input">Textarea</label>
<textarea id="textarea-input" class="aiab-form-control" rows="4" placeholder="Enter your message..."></textarea>
</div>
Checkboxes and Radio Buttons
Checkboxes
Radio Buttons
Inline Checkboxes and Radios
<!-- Standard Checkboxes -->
<div class="aiab-form-check">
<input type="checkbox" id="check1" class="aiab-form-check-input">
<label for="check1" class="aiab-form-check-label">Default checkbox</label>
</div>
<!-- Radio Buttons -->
<div class="aiab-form-check">
<input type="radio" id="radio1" name="radioGroup" class="aiab-form-check-input">
<label for="radio1" class="aiab-form-check-label">First radio</label>
</div>
<!-- Inline Checkboxes -->
<div class="aiab-form-check aiab-form-check-inline">
<input type="checkbox" id="inline-check1" class="aiab-form-check-input">
<label for="inline-check1" class="aiab-form-check-label">Inline 1</label>
</div>
Form Sizes
<!-- Large Input -->
<input type="text" class="aiab-form-control aiab-form-control-lg" placeholder="Large input">
<!-- Default Input -->
<input type="text" class="aiab-form-control" placeholder="Default input">
<!-- Small Input -->
<input type="text" class="aiab-form-control aiab-form-control-sm" placeholder="Small input">
Floating Labels
<div class="aiab-form-floating">
<input type="text" id="floating-text" class="aiab-form-control" placeholder="Name">
<label for="floating-text">Full Name</label>
</div>
<div class="aiab-form-floating">
<input type="email" id="floating-email" class="aiab-form-control" placeholder="name@example.com">
<label for="floating-email">Email address</label>
</div>
<div class="aiab-form-floating">
<select id="floating-select" class="aiab-form-control">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
</select>
<label for="floating-select">Works with selects</label>
</div>
Form Validation
<!-- Valid Input -->
<div class="aiab-form-group">
<label for="valid-input">Valid Input</label>
<input type="text" id="valid-input" class="aiab-form-control aiab-is-valid" value="This looks good!">
<div class="valid-feedback">Great! This field is valid.</div>
</div>
<!-- Invalid Input -->
<div class="aiab-form-group">
<label for="invalid-input">Invalid Input</label>
<input type="text" id="invalid-input" class="aiab-form-control aiab-is-invalid">
<div class="invalid-feedback">Please provide a valid input.</div>
</div>
<!-- Help Text -->
<div class="aiab-form-group">
<label for="help-input">Input with Help Text</label>
<input type="text" id="help-input" class="aiab-form-control">
<div class="aiab-form-text">This is some helpful text.</div>
</div>
Form Layouts
Grid Layout
<!-- Two Column Grid -->
<div class="aiab-form-grid-2">
<div class="aiab-form-group">
<label for="first-name">First Name</label>
<input type="text" id="first-name" class="aiab-form-control">
</div>
<div class="aiab-form-group">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" class="aiab-form-control">
</div>
</div>
<!-- Three Column Grid -->
<div class="aiab-form-grid-3">
<div class="aiab-form-group">
<label for="city">City</label>
<input type="text" id="city" class="aiab-form-control">
</div>
<div class="aiab-form-group">
<label for="state">State</label>
<select id="state" class="aiab-form-control">...</select>
</div>
<div class="aiab-form-group">
<label for="zip">Zip</label>
<input type="text" id="zip" class="aiab-form-control">
</div>
</div>
Inline Form
<form class="aiab-form-inline">
<div class="aiab-form-group">
<label for="inline-name">Name</label>
<input type="text" id="inline-name" class="aiab-form-control" placeholder="Jane Doe">
</div>
<div class="aiab-form-group">
<label for="inline-email">Email</label>
<input type="email" id="inline-email" class="aiab-form-control" placeholder="jane@example.com">
</div>
<button type="submit" class="aiab-btn aiab-btn--primary">Sign up</button>
</form>
Special Input Types
<!-- Range Input -->
<div class="aiab-form-group">
<label for="range-input">Range Input</label>
<input type="range" id="range-input" class="aiab-form-control" min="0" max="100" value="50">
</div>
<!-- File Input -->
<div class="aiab-form-group">
<label for="file-input">File Input</label>
<input type="file" id="file-input" class="aiab-form-control">
</div>
<!-- Search Form -->
<div class="aiab-search-form">
<input type="search" class="aiab-form-control" placeholder="Search...">
<button type="submit">🔍</button>
</div>
Complete Form Example
<form id="contact-form" novalidate>
<fieldset>
<legend>Contact Information</legend>
<div class="aiab-form-grid-2">
<div class="aiab-form-group">
<label for="first-name">First Name *</label>
<input type="text" id="first-name" class="aiab-form-control" required>
<div class="invalid-feedback">Please enter your first name.</div>
</div>
<div class="aiab-form-group">
<label for="last-name">Last Name *</label>
<input type="text" id="last-name" class="aiab-form-control" required>
<div class="invalid-feedback">Please enter your last name.</div>
</div>
</div>
<div class="aiab-form-group">
<label for="email">Email Address *</label>
<input type="email" id="email" class="aiab-form-control" required>
<div class="invalid-feedback">Please enter a valid email address.</div>
</div>
<div class="aiab-form-group">
<button type="submit" class="aiab-btn aiab-btn--primary">Send Message</button>
</div>
</fieldset>
</form>
CSS Classes Reference
Form Control Classes
| Class | Description |
|---|---|
.aiab-form-control |
Base form control styling |
.aiab-form-control-sm |
Small form control |
.aiab-form-control-lg |
Large form control |
.aiab-form-group |
Form field grouping aiab-container |
.aiab-form-text / .aiab-help-text |
Helper text for form fields |
Layout Classes
| Class | Description |
|---|---|
.aiab-form-inline |
Inline form layout |
.aiab-form-grid |
Auto-fit aiab-grid layout |
.aiab-form-grid-2 |
Two-column aiab-grid layout |
.aiab-form-grid-3 |
Three-column aiab-grid layout |
.aiab-form-row |
Flex aiab-row for form fields |
Checkbox/Radio Classes
| Class | Description |
|---|---|
.aiab-form-check |
Checkbox/radio wrapper |
.aiab-form-check-input |
Checkbox/radio input |
.aiab-form-check-label |
Checkbox/radio label |
.aiab-form-check-inline |
Inline checkbox/radio |
Floating Label Classes
| Class | Description |
|---|---|
.aiab-form-floating |
Floating label aiab-container |
Validation Classes
| Class | Description |
|---|---|
.aiab-is-valid |
Valid form control state |
.aiab-is-invalid |
Invalid form control state |
.valid-feedback |
Valid state feedback message |
.invalid-feedback |
Invalid state feedback message |
.was-validated |
Form validation trigger class |
Special Input Classes
| Class | Description |
|---|---|
.aiab-search-form |
Search input with button |
JavaScript Integration
// Form validation utility
class FormValidator {
constructor(form) {
this.form = form;
this.init();
}
init() {
this.form.addEventListener('submit', (e) => this.handleSubmit(e));
this.form.addEventListener('input', (e) => this.handleInput(e));
this.form.addEventListener('change', (e) => this.handleChange(e));
}
handleSubmit(e) {
e.preventDefault();
if (this.validateForm()) {
this.onValidSubmit();
} else {
this.onInvalidSubmit();
}
}
handleInput(e) {
const field = e.target;
if (field.classList.contains('aiab-is-invalid') || field.classList.contains('aiab-is-valid')) {
this.validateField(field);
}
}
handleChange(e) {
this.validateField(e.target);
}
validateForm() {
const fields = this.form.querySelectorAll('input, select, textarea');
let isValid = true;
fields.forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
this.form.classList.add('was-validated');
return isValid;
}
validateField(field) {
const isRequired = field.hasAttribute('required');
const isEmpty = !field.value.trim();
const isValidType = this.validateFieldType(field);
let isValid = true;
if (isRequired && isEmpty) {
isValid = false;
} else if (!isEmpty && !isValidType) {
isValid = false;
}
this.updateFieldState(field, isValid);
return isValid;
}
validateFieldType(field) {
const type = field.type;
const value = field.value.trim();
if (!value) return true; // Empty values are handled by required check
aiab-switch (type) {
case 'email':
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
case 'url':
try {
new URL(value);
return true;
} catch {
return false;
}
case 'tel':
return /^[\+]?[1-9][\d]{0,15}$/.test(value.replace(/[-\s\(\)]/g, ''));
default:
return field.checkValidity();
}
}
updateFieldState(field, isValid) {
field.classList.remove('aiab-is-valid', 'aiab-is-invalid');
field.classList.add(isValid ? 'aiab-is-valid' : 'aiab-is-invalid');
// Update feedback messages
const feedback = field.parentNode.querySelector(
isValid ? '.valid-feedback' : '.invalid-feedback'
);
if (feedback) {
feedback.style.display = 'block';
}
// Hide opposite feedback
const oppositeFeedback = field.parentNode.querySelector(
isValid ? '.invalid-feedback' : '.valid-feedback'
);
if (oppositeFeedback) {
oppositeFeedback.style.display = 'none';
}
}
onValidSubmit() {
// Handle successful form submission
console.log('Form is valid and ready to submit');
// Show success message
this.showMessage('Form submitted successfully!', 'success');
// You can add AJAX submission here
// this.submitFormData();
}
onInvalidSubmit() {
// Handle invalid form submission
console.log('Form has validation errors');
// Focus first invalid field
const firstInvalid = this.form.querySelector('.aiab-is-invalid');
if (firstInvalid) {
firstInvalid.focus();
}
this.showMessage('Please fix the errors below.', 'error');
}
showMessage(text, type) {
// Create and show aiab-alert message
const alert = document.createElement('div');
alert.className = `aiab-alert aiab-alert--${type}`;
alert.textContent = text;
this.form.insertBefore(alert, this.form.firstChild);
// Remove after 5 seconds
setTimeout(() => alert.remove(), 5000);
}
reset() {
this.form.reset();
this.form.classList.remove('was-validated');
// Clear all validation states
const fields = this.form.querySelectorAll('.aiab-is-valid, .aiab-is-invalid');
fields.forEach(field => {
field.classList.remove('aiab-is-valid', 'aiab-is-invalid');
});
}
}
// Auto-initialize forms with validation
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form[data-validate]');
forms.forEach(form => new FormValidator(form));
});
// Range input value display
function updateRangeValue(input, displayElement) {
displayElement.textContent = input.value;
}
// Search form enhancement
function enhanceSearchForms() {
const searchForms = document.querySelectorAll('.aiab-search-form');
searchForms.forEach(form => {
const input = form.querySelector('input[type="search"]');
const button = form.querySelector('button');
if (input && button) {
// Submit on button click
button.addEventListener('click', (e) => {
e.preventDefault();
performSearch(input.value);
});
// Submit on Enter key
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
performSearch(input.value);
}
});
}
});
}
function performSearch(query) {
console.log('Searching for:', query);
// Implement your search logic here
}
// Initialize enhancements
document.addEventListener('DOMContentLoaded', () => {
enhanceSearchForms();
// Initialize range value displays
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
const display = document.getElementById('range-value');
if (display) {
input.addEventListener('input', () => updateRangeValue(input, display));
}
});
});
Best Practices
Form Structure
- Semantic markup: Use proper form elements, fieldsets, and legends for screen readers
- Label association: Always associate labels with inputs using for/id attributes
- Required indicators: Clearly mark required fields with * or "required" text
- Logical grouping: Group related fields using fieldsets and form groups
User Experience
- Clear feedback: Provide immediate validation feedback as users type
- Error prevention: Use appropriate input types and constraints to prevent errors
- Progressive enhancement: Ensure forms work without JavaScript
- Mobile optimization: Use appropriate input types for mobile keyboards
Accessibility
- Focus management: Provide clear focus indicators and logical tab order
- Error handling: Announce validation errors to screen readers
- Sufficient contrast: Ensure form elements meet WCAG contrast requirements
- Touch targets: Make form controls large enough for touch interaction
Performance
- Debounced validation: Avoid excessive validation on every keystroke
- Lazy loading: Load complex form components only when needed
- Efficient selectors: Use efficient CSS selectors for form styling
- Minimal JavaScript: Use native HTML5 validation when possible