Package Exports
- cd-form-library
- cd-form-library/dist/browser.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (cd-form-library) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
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 > Head Code):
<script src="https://unpkg.com/cd-form-library@latest/dist/browser.js"></script>
Then add data-cd-form="true"
to your form element:
<form data-cd-form="true">
<!-- Your form content -->
</form>
Features
- Input Formatting: Automatic date, time, and percentage formatting with masks
- Country Code Dropdowns: Auto-populated, searchable country code selects with multiple display formats
- 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
<!-- 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
<!-- 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
<!-- Automatic % symbol -->
<input type="text" data-input="percent" placeholder="Enter percentage">
Country Code Dropdowns
Auto-populate select dropdowns with international country calling codes in multiple formats:
<!-- Basic country code (just the code) -->
<select data-input="country-code" name="country-code"></select>
<!-- Result: +1, +44, +33, etc. -->
<!-- With ISO country code -->
<select data-input="country-code:iso" name="country-code"></select>
<!-- Result: +1 (US), +44 (GB), +33 (FR), etc. -->
<!-- With flag emoji -->
<select data-input="country-code:flag" name="country-code"></select>
<!-- Result: 🇺🇸 +1, 🇬🇧 +44, 🇫🇷 +33, etc. -->
<!-- With country name -->
<select data-input="country-code:name" name="country-code"></select>
<!-- Result: +1 United States, +44 United Kingdom, etc. -->
<!-- With flag and ISO (recommended) -->
<select data-input="country-code:flag-iso" name="country-code"></select>
<!-- Result: 🇺🇸 +1 (US), 🇬🇧 +44 (GB), etc. -->
<!-- With flag and full name -->
<select data-input="country-code:flag-name" name="country-code"></select>
<!-- Result: 🇺🇸 +1 United States, 🇬🇧 +44 United Kingdom, etc. -->
Searchable/Editable Dropdown with Finsweet:
For a more elegant searchable dropdown experience, use Finsweet Combo Box. The library automatically detects and works with Finsweet combo boxes:
<div fs-combobox-element="dropdown">
<div class="dropdown-toggle">
<input fs-combobox-element="text-input" type="text" placeholder="Search country code..." />
<button fs-combobox-element="clear">Clear</button>
</div>
<div class="dropdown-list">
<select
data-input="country-code:flag-iso"
fs-combobox-element="select"
name="country-code">
<!-- Auto-populated by CD Form Library -->
</select>
<div fs-combobox-element="empty">No matching countries found</div>
</div>
</div>
Features:
- United States appears first (optimized for US-based users)
- Type to search by code, ISO, or country name
- Editable text input for custom entries
- Works seamlessly with
data-cd-field-key
auto-fill
Dynamic Form Visibility
Show or hide form sections based on radio button or select field values.
Basic Setup
<!-- 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
<!-- 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
<!-- 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
becomesmember_name-1
,member_name-2
, etc.member_email
becomesmember_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
<!-- 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
<!-- 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:
<!-- 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
<!-- 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 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
- Install TryFormly on your Webflow site following their documentation
- Add CD Form Library script after TryFormly:
<!-- 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>
- Enable both systems on your form:
<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
<!-- 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
<!-- 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
<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:
<!-- 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):
<!-- 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:
<!-- 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:
// 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:
/* 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:
// 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