cd-form-library
Version:
Webflow-native form enhancement library using Custom Attributes
575 lines (425 loc) • 17.3 kB
Markdown
# CD Form Library
A Webflow-native form enhancement library that adds advanced functionality to forms using simple HTML data attributes. No complex JavaScript required - just add attributes to your HTML elements.
## Quick Start
Add this script tag to your Webflow site (in Project Settings > Custom Code > Footer Code):
```html
<script src="https://unpkg.com/cd-form-library@0.1.109/dist/browser.js"></script>
```
Then add `data-cd-form="true"` to your form element:
```html
<form data-cd-form="true">
<!-- Your form content -->
</form>
```
## Features
- **Input Formatting**: Automatic date, time, and percentage formatting with masks
- **Dynamic Form Visibility**: Show/hide form sections based on user selections
- **Dynamic Rows**: Add/remove repeatable form sections with automatic field naming
- **Summary Fields**: Real-time summary display of form data
- **Branch-Based Visibility**: Show different summary sections based on user choices
- **Tooltips**: Accessible, smart-positioning tooltips with keyboard navigation
## Input Formatting
Add input masks for dates, times, and percentages using the `data-input` attribute.
### Date Formatting
```html
<!-- MM/DD/YYYY format -->
<input type="text" data-input="date:mmddyyyy" placeholder="MM/DD/YYYY">
<!-- DD/MM/YYYY format -->
<input type="text" data-input="date:ddmmyyyy" placeholder="DD/MM/YYYY">
```
### Time Formatting
```html
<!-- Flexible single-digit hour format (easier for users) -->
<input type="text" data-input="time:h:mm am" placeholder="H:MM AM">
<input type="text" data-input="time:h:mm pm" placeholder="H:MM PM">
<!-- Traditional strict 2-digit hour format -->
<input type="text" data-input="time:hhmm am" placeholder="HH:MM AM">
<input type="text" data-input="time:hhmm pm" placeholder="HH:MM PM">
```
**User Experience:**
- `time:h:mm` - Users can type "1:30 PM" (single digit hours accepted)
- `time:hhmm` - Users must type "01:30 PM" (requires leading zero)
### Percentage Formatting
```html
<!-- Automatic % symbol -->
<input type="text" data-input="percent" placeholder="Enter percentage">
```
## Dynamic Form Visibility
Show or hide form sections based on radio button or select field values.
### Basic Setup
```html
<!-- Radio buttons -->
<input type="radio" name="business-type" value="llc"> LLC
<input type="radio" name="business-type" value="corporation"> Corporation
<!-- Section that shows only when "LLC" is selected -->
<div data-show-when="business-type=llc">
<h3>LLC Information</h3>
<!-- LLC-specific form fields -->
</div>
<!-- Section that shows only when "Corporation" is selected -->
<div data-show-when="business-type=corporation">
<h3>Corporation Information</h3>
<!-- Corporation-specific form fields -->
</div>
```
### Works With Any Form Element
```html
<!-- Select dropdown -->
<select name="state">
<option value="ca">California</option>
<option value="ny">New York</option>
</select>
<!-- Show only for California -->
<div data-show-when="state=ca">
<p>California-specific information</p>
</div>
```
## Dynamic Rows (Repeatable Sections)
Create forms where users can add/remove multiple rows of the same data.
### Basic Structure
```html
<!-- Container for repeatable rows -->
<div data-cd-repeat-group="team-members">
<!-- Template row (will be hidden and used for cloning) -->
<div data-cd-repeat-template data-cd-repeat-row="team-members" style="display: none;">
<input type="text" data-cd-repeat-name="member_name" placeholder="Name">
<input type="email" data-cd-repeat-name="member_email" placeholder="Email">
<button type="button" data-cd-repeat-remove="team-members">Remove</button>
</div>
<!-- First visible row -->
<div data-cd-repeat-row="team-members">
<input type="text" data-cd-repeat-name="member_name" placeholder="Name">
<input type="email" data-cd-repeat-name="member_email" placeholder="Email">
<button type="button" data-cd-repeat-remove="team-members">Remove</button>
</div>
</div>
<!-- Add button (can be placed anywhere) -->
<button type="button" data-cd-repeat-add="team-members">Add Team Member</button>
```
### Field Naming
The library automatically generates unique field names:
- `member_name` becomes `member_name-1`, `member_name-2`, etc.
- `member_email` becomes `member_email-1`, `member_email-2`, etc.
### Row Limits
- **Maximum**: 5 rows per group
- **Minimum**: 1 row (cannot remove the last row)
- Add button automatically disables at 5 rows
- Add button re-enables when rows are removed
### Alternative Button Patterns
```html
<!-- Generic add/remove buttons (will work with any group) -->
<button data-cd-add-row>Add Row</button>
<button data-cd-remove-row>Remove</button>
```
## Summary Fields
Display real-time summaries of form data anywhere in your form.
### Basic Summary Field
```html
<!-- Form input -->
<input type="text" name="company_name" placeholder="Company Name">
<!-- Summary display (updates automatically) -->
<span data-cd-input-field="company_name">Not specified</span>
```
### Summary Containers
Group related summary fields:
```html
<!-- Summary container for a specific dynamic row group -->
<div data-cd-summary-for="team-members">
<!-- Template for each row's summary -->
<div data-cd-summary-template style="display: none;">
<p>Name: <span data-cd-input-field="member_name-{i}">Not specified</span></p>
<p>Email: <span data-cd-input-field="member_email-{i}">Not specified</span></p>
</div>
</div>
```
The `{i}` placeholder gets replaced with row numbers (1, 2, 3, etc.).
### Radio Button Summaries
```html
<!-- Radio buttons -->
<input type="radio" name="business_type" value="LLC"> LLC
<input type="radio" name="business_type" value="Corporation"> Corporation
<!-- Summary -->
<p>Business Type: <span data-cd-input-field="business_type">Not selected</span></p>
```
## Multi-Step Forms & Logic Branching
For complex multi-step forms with conditional logic, we recommend using [TryFormly](https://tryformly.com) in combination with this library.
### TryFormly Integration
**TryFormly** provides:
- Multi-step form navigation
- Conditional logic and branching
- Form validation and progress tracking
- Step-by-step user experience
**CD Form Library** enhances TryFormly with:
- Advanced input formatting
- Dynamic repeatable sections
- Real-time summary fields
- Branch-aware summary visibility
### Setup with TryFormly
1. **Install TryFormly** on your Webflow site following their documentation
2. **Add CD Form Library** script after TryFormly:
```html
<!-- TryFormly script (install per their instructions) -->
<script src="https://cdn.tryformly.com/..."></script>
<!-- CD Form Library -->
<script src="https://unpkg.com/cd-form-library@0.1.109/dist/browser.js"></script>
```
3. **Enable both systems** on your form:
```html
<form data-cd-form="true" data-formly="true">
<!-- Your multi-step form content -->
</form>
```
### Branch-Based Summary Visibility
When using TryFormly, the library automatically detects step visibility changes and shows appropriate summary sections.
#### Step Wrappers with TryFormly
```html
<!-- TryFormly step with branch data -->
<div class="step_wrapper" data-cd-branch="business-type" data-answer="llc" style="display: none;">
<!-- LLC form content -->
<h3>LLC Information</h3>
<!-- Dynamic rows work within TryFormly steps -->
<div data-cd-repeat-group="llc-members">
<div data-cd-repeat-template data-cd-repeat-row="llc-members" style="display: none;">
<input type="text" data-cd-repeat-name="member_name" placeholder="Member Name">
<input type="text" data-cd-repeat-name="ownership" data-input="percent" placeholder="Ownership %">
<button type="button" data-cd-repeat-remove="llc-members">Remove</button>
</div>
<div data-cd-repeat-row="llc-members">
<input type="text" data-cd-repeat-name="member_name" placeholder="Member Name">
<input type="text" data-cd-repeat-name="ownership" data-input="percent" placeholder="Ownership %">
<button type="button" data-cd-repeat-remove="llc-members">Remove</button>
</div>
</div>
<button type="button" data-cd-repeat-add="llc-members">Add Member</button>
</div>
<div class="step_wrapper" data-cd-branch="business-type" data-answer="corporation" style="display: none;">
<!-- Corporation form content -->
<h3>Corporation Information</h3>
<input type="text" name="incorporation_date" data-input="date:mmddyyyy" placeholder="Incorporation Date">
</div>
```
#### Branch-Specific Summaries
```html
<!-- Summary step - shows different content based on user's path -->
<div class="step_wrapper" data-step="summary">
<h2>Form Summary</h2>
<!-- Shows only when LLC branch was selected -->
<div data-cd-summary-branch="business-type:llc" style="display: none;">
<h3>LLC Summary</h3>
<p>Business Type: LLC</p>
<div data-cd-summary-for="llc-members">
<h4>LLC Members:</h4>
<div data-cd-summary-template style="display: none;">
<p><span data-cd-input-field="member_name-{i}">Not specified</span> - <span data-cd-input-field="ownership-{i}">Not specified</span></p>
</div>
</div>
</div>
<!-- Shows only when Corporation branch was selected -->
<div data-cd-summary-branch="business-type:corporation" style="display: none;">
<h3>Corporation Summary</h3>
<p>Business Type: Corporation</p>
<p>Incorporation Date: <span data-cd-input-field="incorporation_date">Not specified</span></p>
</div>
</div>
```
### Best Practices with TryFormly
**Step Organization:**
- Use TryFormly for navigation and validation
- Use CD Form Library for enhanced field functionality
- Place dynamic rows within appropriate TryFormly steps
- Add summary steps at the end with branch-specific content
**Data Persistence:**
- TryFormly handles step navigation and data persistence
- CD Form Library maintains summary synchronization across steps
- Both systems work together seamlessly
**Validation:**
- TryFormly handles form validation and step progression
- CD Form Library provides input formatting and masks
- Both systems respect each other's validation states
## Complete Example
```html
<form data-cd-form="true">
<!-- Business Type Selection -->
<h3>What type of business do you have?</h3>
<input type="radio" name="business_type" value="llc"> LLC
<input type="radio" name="business_type" value="corporation"> Corporation
<!-- LLC-specific section -->
<div data-show-when="business_type=llc">
<h3>LLC Members</h3>
<div data-cd-repeat-group="llc-members">
<!-- Template row -->
<div data-cd-repeat-template data-cd-repeat-row="llc-members" style="display: none;">
<input type="text" data-cd-repeat-name="member_name" placeholder="Member Name">
<input type="text" data-cd-repeat-name="ownership" data-input="percent" placeholder="Ownership %">
<button type="button" data-cd-repeat-remove="llc-members">Remove</button>
</div>
<!-- First row -->
<div data-cd-repeat-row="llc-members">
<input type="text" data-cd-repeat-name="member_name" placeholder="Member Name">
<input type="text" data-cd-repeat-name="ownership" data-input="percent" placeholder="Ownership %">
<button type="button" data-cd-repeat-remove="llc-members">Remove</button>
</div>
</div>
<button type="button" data-cd-repeat-add="llc-members">Add Member</button>
</div>
<!-- Corporation-specific section -->
<div data-show-when="business_type=corporation">
<h3>Corporation Details</h3>
<input type="text" name="incorporation_date" data-input="date:mmddyyyy" placeholder="Incorporation Date">
</div>
<!-- Summary Section -->
<div class="summary">
<h3>Form Summary</h3>
<p>Business Type: <span data-cd-input-field="business_type">Not selected</span></p>
<!-- LLC Summary -->
<div data-cd-summary-for="llc-members">
<h4>LLC Members:</h4>
<div data-cd-summary-template style="display: none;">
<p><span data-cd-input-field="member_name-{i}">Not specified</span> - <span data-cd-input-field="ownership-{i}">Not specified</span></p>
</div>
</div>
<!-- Corporation Summary -->
<p>Incorporation Date: <span data-cd-input-field="incorporation_date">Not specified</span></p>
</div>
</form>
```
## Tooltips
Add accessible, smart-positioning tooltips to your forms with automatic viewport detection and keyboard navigation.
### Standard Mode (Relume Pattern)
When the tooltip component and trigger are together:
```html
<!-- Standard tooltip - trigger and panel are siblings in component -->
<div class="tooltip_component">
<!-- Trigger element -->
<span class="tooltip_element-wrapper">
What does this mean?
</span>
<!-- Tooltip panel (hidden by default) -->
<div class="tooltip_tooltip-wrapper" style="display: none;">
<div class="tooltip_pointer is-top"></div>
<p>This is the tooltip content that explains the field.</p>
</div>
</div>
```
### Split Mode (Inline Labels)
When you want the trigger in a label but the tooltip component elsewhere (useful for inline help text):
```html
<!-- Group container (optional, helps organize related tooltips) -->
<div data-tt-group>
<!-- Label with inline tooltip trigger -->
<label>
Company Name
<span class="tooltip_element-wrapper" data-tt="company-help">ℹ️</span>
</label>
<input type="text" name="company_name">
<!-- Tooltip component placed elsewhere, matched by data-tt -->
<div class="tooltip_component" data-tt-for="company-help">
<div class="tooltip_tooltip-wrapper" style="display: none;">
<div class="tooltip_pointer is-top"></div>
<p>Enter your company's legal name as it appears on official documents.</p>
</div>
</div>
</div>
```
### Tooltip Positioning
Control tooltip position with pointer classes:
```html
<!-- Top position (default) -->
<div class="tooltip_pointer is-top"></div>
<!-- Bottom position -->
<div class="tooltip_pointer is-bottom"></div>
<!-- Left position -->
<div class="tooltip_pointer is-left"></div>
<!-- Right position -->
<div class="tooltip_pointer is-right"></div>
```
The library automatically flips the tooltip to the opposite side if there's not enough viewport space.
### Keyboard Accessibility
Tooltips are fully keyboard accessible:
- **Tab**: Focus on tooltip trigger
- **Enter/Space** (when focused): Show tooltip
- **Escape**: Hide tooltip
- **Tab away**: Hide tooltip
### CSS Classes Required
Your CSS should include these classes (typically from Relume components):
- `.tooltip_component` - Main container
- `.tooltip_element-wrapper` - Trigger element
- `.tooltip_tooltip-wrapper` - Tooltip panel
- `.tooltip_pointer` - Arrow pointer with position classes (`is-top`, `is-bottom`, `is-left`, `is-right`)
### Dynamic Rows Compatibility
Tooltips automatically reinitialize when new dynamic rows are added, so they work seamlessly with repeatable sections.
## Events
The library dispatches custom events that you can listen for:
```javascript
// When a row is added
document.addEventListener('cd:row:added', (event) => {
console.log('Row added to group:', event.detail.groupName);
console.log('Row index:', event.detail.rowIndex);
});
// When a row is removed
document.addEventListener('cd:row:removed', (event) => {
console.log('Row removed from group:', event.detail.groupName);
});
// When input formatting is applied
document.addEventListener('cd:inputformat:bound', (event) => {
console.log('Input formatting applied to:', event.target);
});
// When input format validation fails
document.addEventListener('cd:inputformat:invalid', (event) => {
console.log('Invalid input format:', event.target);
});
// When form wrapper visibility changes
document.addEventListener('form-wrapper-visibility:shown', (event) => {
console.log('Section shown:', event.target);
});
document.addEventListener('form-wrapper-visibility:hidden', (event) => {
console.log('Section hidden:', event.target);
});
```
## Webflow Integration Tips
### CSS Classes
Add these CSS classes to your Webflow elements for better styling:
```css
/* Hide remove buttons on first rows if desired */
.dynamic-row:first-child [data-cd-remove-row],
.dynamic-row:first-child [data-cd-repeat-remove] {
display: none;
}
/* Style for rows being added */
.visible-row {
opacity: 1;
transition: opacity 0.3s ease;
}
/* Style for disabled add buttons */
[data-cd-repeat-add][style*="pointer-events: none"] {
opacity: 0.5;
cursor: not-allowed;
}
```
### Form Submission
When the form is submitted, all field names will include their row suffixes:
```
member_name-1=John Doe
member_email-1=john@example.com
member_name-2=Jane Smith
member_email-2=jane@example.com
ownership-1=50%
ownership-2=50%
```
### Debugging
Enable debug mode to see detailed console logs:
```javascript
// Via URL parameter
// Add ?cd-debug=true to your page URL
// Via localStorage (persists across page loads)
localStorage.setItem('cd-debug', 'true');
```
## Version Information
Current version: 0.1.109
## Browser Support
- Chrome 80+
- Firefox 75+
- Safari 13+
- Edge 80+
## License
MIT License