- Introduced a new Switch component for toggling options within forms, enhancing user interactivity. - Updated formkit-custom.js to include the Switch component with appropriate props. - Enhanced formkit-theme.js to define styles for the Switch component, ensuring consistent theming. - Added CSS styles for the Switch component to improve visual presentation and user experience. - Updated FormBuilderCanvas and FormBuilderComponents to support the new Switch component in the form builder interface. - Enhanced documentation to include details about the new Switch component and its usage within forms.
15 KiB
Form Builder Grid System - Complete Reference
Overview
The form builder uses an intelligent 12-column CSS Grid system that automatically manages component placement and provides intuitive width selection through visual previews and smart recommendations. This system replaces technical percentages with user-friendly names and includes automatic gap-filling optimization.
Grid Architecture
CSS Grid Foundation
.grid-container {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-flow: row dense; /* Enables automatic gap filling */
column-gap: 16px;
row-gap: 16px;
width: 100%;
}
Component Grid Placement
.form-component {
grid-column: span 12; /* Default to full width */
width: 100% !important; /* Force width within grid cell */
transition: all 0.2s ease;
}
Width Options System
Available Width Options
Name | Percentage | Grid Columns | Grid Span | Use Cases |
---|---|---|---|---|
Narrow | 25% | 3 of 12 | span 3 |
Small inputs like age, zip codes, short codes |
Small | 33% | 4 of 12 | span 4 |
Short text fields, city names, grouped inputs |
Medium | 50% | 6 of 12 | span 6 |
Names, phone numbers, paired inputs |
Wide | 75% | 9 of 12 | span 9 |
Addresses, URLs, prominent fields |
Full | 100% | 12 of 12 | span 12 |
Text areas, headings, single-column layouts |
Width Selection Interface
Visual Grid Preview
Each width option displays a mini 12-column grid preview:
<div class="grid-container-mini">
<div
v-for="i in 12"
:key="i"
class="grid-cell"
:class="{
'active': i <= option.gridColumns,
'inactive': i > option.gridColumns
}"
></div>
</div>
Current Selection Feedback
The interface shows:
- Current width name (e.g., "Medium")
- Grid columns used (e.g., "6 of 12 columns")
- Visual representation of current selection
- Percentage equivalent for technical reference
Smart Recommendation System
Field Type Recommendations
The system automatically suggests optimal widths based on component type:
Narrow Fields (25% - 3 columns)
Recommended for: Small, precise inputs
number
: Age, quantity, countsdate
: Birth dates, deadlinestime
: Appointment timescolor
: Color picker inputsotp
: Verification codes
const narrowRecommended = ['number', 'date', 'time', 'color', 'otp']
Small Fields (33% - 4 columns)
Recommended for: Short text inputs
text
: Names, titles, short answers
const smallRecommended = ['text']
Medium Fields (50% - 6 columns)
Recommended for: Standard form inputs
email
: Email addressestel
: Phone numberspassword
: Password fieldsmask
: Formatted inputs (SSN, phone)select
: Dropdown selectionsdatetime-local
: Combined date/time
const mediumRecommended = ['email', 'tel', 'password', 'mask', 'select', 'datetime-local']
Wide Fields (75% - 9 columns)
Recommended for: Longer inputs requiring more space
url
: Website addressesfile
: File upload fieldsdropzone
: Drag & drop upload areas
const wideRecommended = ['url', 'file', 'dropzone']
Full Width Fields (100% - 12 columns)
Recommended for: Content and multi-option components
textarea
: Multi-line text areasheading
: Section headingsparagraph
: Descriptive textcheckbox
: Checkbox groupsradio
: Radio button groupsrange
: Range slidersswitch
: Toggle switchesbutton
: Action buttonsinfo-display
: Information displays
const fullRecommended = [
'textarea', 'heading', 'paragraph', 'checkbox',
'radio', 'range', 'switch', 'button', 'info-display'
]
Visual Recommendation Indicators
- Green Ring: Recommended options have green highlighting
- Selection State: Current choice highlighted in blue
- Combined State: Selected + recommended shows green background with checkmark
- Recommendation Badge: "Recommended" text appears next to optimal choices
Smart Grid Placement Algorithm
Optimal Placement Logic
The system automatically finds the best position for new components:
findOptimalGridPlacement() {
if (this.formComponents.length === 0) {
// First component - full width
return {
gridColumn: 'span 12',
rowIndex: 0,
width: '100%'
}
}
// Group components by their implicit rows
const rows = this.analyzeGridRows()
// Find row with remaining space
const rowWithSpace = rows.find(row => row.remainingSpace > 0)
if (rowWithSpace) {
// Use remaining space in existing row
const remainingColumns = rowWithSpace.remainingSpace
const widthPercent = this.columnsToPercentage(remainingColumns)
return {
gridColumn: `span ${remainingColumns}`,
rowIndex: rows.indexOf(rowWithSpace),
width: `${widthPercent}%`
}
} else {
// Create new row
return {
gridColumn: 'span 12',
rowIndex: rows.length,
width: '100%'
}
}
}
Row Analysis
analyzeGridRows() {
const rows = []
let currentRowSpace = 12
this.formComponents.forEach(component => {
const spanMatch = component.props.gridColumn?.match(/span\s+(\d+)/) || []
const columnSpan = parseInt(spanMatch[1]) || 12
if (columnSpan <= currentRowSpace) {
// Add to current row
currentRowSpace -= columnSpan
} else {
// Start new row
rows.push({ remainingSpace: currentRowSpace })
currentRowSpace = 12 - columnSpan
}
})
return rows
}
Grid Layout Optimization
Automatic Gap Filling
The grid-auto-flow: row dense
CSS property enables automatic gap filling:
- Components automatically fill available spaces
- No manual positioning required
- Optimal use of screen real estate
Layout Optimization Algorithm
optimizeGridLayout() {
const rows = this.groupComponentsByRows()
rows.forEach(row => {
if (row.remainingSpace > 0) {
this.distributeRemainingSpace(row)
}
})
}
distributeRemainingSpace(row) {
if (row.components.length === 1 && row.remainingSpace > 0) {
// Expand single component to fill row
const comp = row.components[0]
this.updateComponentSize(comp.component, 12)
} else if (row.components.length > 1) {
// Distribute space proportionally
const extraSpanPerComponent = Math.floor(row.remainingSpace / row.components.length)
let remainingExtraSpan = row.remainingSpace % row.components.length
row.components.forEach(comp => {
let extraSpan = extraSpanPerComponent
if (remainingExtraSpan > 0) {
extraSpan += 1
remainingExtraSpan--
}
const newSpan = comp.span + extraSpan
this.updateComponentSize(comp.component, newSpan)
})
}
}
Collapsible Panel Integration
Quick Width Selector
The collapsible panel includes a compact width selector:
<div class="width-selector-compact">
<button
v-for="option in compactWidthOptions"
:key="option.value"
@click="updateQuickSetting('width', option.value, option.gridColumns)"
class="width-btn"
:class="{ 'active': getComponentWidthPercent() === option.value }"
>
{{ option.name }}
</button>
</div>
Compact Options
const compactWidthOptions = [
{ name: 'S', value: 25, gridColumns: 3, description: 'Small (25%)' },
{ name: 'M', value: 50, gridColumns: 6, description: 'Medium (50%)' },
{ name: 'L', value: 75, gridColumns: 9, description: 'Large (75%)' },
{ name: 'XL', value: 100, gridColumns: 12, description: 'Extra Large (100%)' }
]
Grid Component Implementation
Component Props Structure
interface ComponentProps {
width: string // e.g., "50%"
gridColumn: string // e.g., "span 6"
label?: string
name?: string
// ... other props
}
Width Calculation Functions
// Convert percentage to grid columns
const percentageToGridColumns = (percentage) => {
const mapping = {
25: 3, // 3/12 = 25%
33: 4, // 4/12 = 33.33%
50: 6, // 6/12 = 50%
66: 8, // 8/12 = 66.67%
75: 9, // 9/12 = 75%
100: 12 // 12/12 = 100%
}
return mapping[percentage] || Math.round((percentage / 100) * 12)
}
// Convert grid columns to percentage
const gridColumnsToPercentage = (columns) => {
const mapping = {
3: 25, // 3/12 = 25%
4: 33, // 4/12 = 33.33%
6: 50, // 6/12 = 50%
8: 66, // 8/12 = 66.67%
9: 75, // 9/12 = 75%
12: 100 // 12/12 = 100%
}
return mapping[columns] || Math.round((columns / 12) * 100)
}
// Set component width with grid sync
const setComponentWidth = (percentage, gridColumns) => {
component.props.width = `${percentage}%`
component.props.gridColumn = `span ${gridColumns}`
}
Responsive Behavior
Mobile Optimization
@media (max-width: 768px) {
.grid-container {
grid-template-columns: 1fr; /* Single column on mobile */
column-gap: 0;
}
.form-component {
grid-column: span 1 !important; /* Force single column */
}
}
Tablet Optimization
@media (min-width: 769px) and (max-width: 1024px) {
.grid-container {
grid-template-columns: repeat(8, 1fr); /* 8 columns on tablet */
}
.form-component[style*="span 12"] {
grid-column: span 8 !important; /* Adjust full-width components */
}
}
Resize Handle System
Interactive Resizing
<div v-if="selectedComponentId === component.id && resizeMode" class="resize-handles">
<div
class="resize-handle resize-handle-right"
@mousedown="startResize($event, component)"
></div>
</div>
Resize Logic with Snap-to-Grid
const handleResize = (event) => {
const deltaX = event.clientX - initialX.value
const container = document.querySelector('.grid-container')
const containerWidth = container.offsetWidth
const deltaPercentage = (deltaX / containerWidth) * 100
let newWidth = initialWidth.value + deltaPercentage
newWidth = Math.max(25, Math.min(100, newWidth))
// Snap to standard widths
const standardWidths = [25, 33, 50, 66, 75, 100]
for (const std of standardWidths) {
if (Math.abs(newWidth - std) < 5) {
newWidth = std
break
}
}
// Convert to grid columns and update
const gridColumns = percentageToGridColumns(newWidth)
updateComponentSize(component, newWidth, gridColumns)
}
CSS Styling Reference
Grid Container Styles
.grid-container {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-flow: row dense;
column-gap: 16px;
row-gap: 16px;
width: 100%;
padding: 0;
box-sizing: border-box;
}
Width Selector Modal Styles
.width-option {
@apply border border-gray-200 rounded-lg p-4 transition-all duration-200 hover:border-blue-300 hover:bg-blue-50 cursor-pointer flex items-center space-x-4;
}
.width-option.selected {
@apply border-blue-500 bg-blue-50 ring-2 ring-blue-200;
}
.width-option.recommended {
@apply ring-2 ring-green-200 border-green-300;
}
.width-option.selected.recommended {
@apply border-green-500 bg-green-50 ring-2 ring-green-200;
}
Grid Preview Styles
.grid-container-mini {
@apply grid grid-cols-12 gap-1 w-32;
}
.grid-cell {
@apply h-2 rounded-sm transition-colors duration-200;
}
.grid-cell.active {
@apply bg-blue-500;
}
.grid-cell.inactive {
@apply bg-gray-200;
}
Quick Selector Styles
.width-selector-compact {
@apply flex space-x-1;
}
.width-btn {
@apply flex-1 px-2 py-1.5 text-xs font-medium text-center rounded-md border border-gray-200 bg-white hover:border-blue-300 hover:bg-blue-50 transition-colors duration-200;
}
.width-btn.active {
@apply border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-200;
}
Best Practices
Grid Layout Design
- Start Simple: Begin with recommended widths
- Mix Widths: Combine different widths for visual interest
- Logical Grouping: Use consistent widths for related fields
- Visual Balance: Avoid too many full-width fields in sequence
- Mobile-First: Consider how layouts adapt on smaller screens
Performance Considerations
- Grid Optimization: Use the automatic optimization feature
- Component Limits: Keep forms under 50 components for best performance
- Responsive Testing: Test across different screen sizes
- Browser Support: Ensure CSS Grid polyfills for older browsers
User Experience Guidelines
- Intuitive Widths: Trust the recommendation system
- Visual Feedback: Use the grid preview for precision
- Consistent Patterns: Establish width patterns across forms
- Progressive Enhancement: Start with basics, add complexity gradually
Troubleshooting
Common Issues
Grid Layout Not Working
- Check CSS Grid browser support
- Verify
grid-template-columns
is applied - Ensure components have valid
gridColumn
values
Components Overlapping
- Check for invalid span values (must be 1-12)
- Verify total spans in row don't exceed 12
- Use grid optimization to fix layouts
Responsive Issues
- Test media query breakpoints
- Verify mobile-specific grid rules
- Check component width calculations
Performance Problems
- Reduce number of components
- Use virtual scrolling for large forms
- Optimize grid calculations
Debugging Tools
// Debug grid layout
const debugGridLayout = () => {
const rows = groupComponentsByRows()
console.table(rows.map(row => ({
components: row.components.length,
remainingSpace: row.remainingSpace,
totalSpan: 12 - row.remainingSpace
})))
}
// Validate component grid properties
const validateGridProps = (component) => {
const span = component.props.gridColumn?.match(/span\s+(\d+)/)?.[1]
const width = parseInt(component.props.width)
console.log({
id: component.id,
span: parseInt(span),
width: width,
valid: span >= 1 && span <= 12 && width > 0 && width <= 100
})
}
Migration Guide
Upgrading from Percentage-Only System
- Update Component Props:
// Old system
component.props.width = "50%"
// New system
component.props.width = "50%"
component.props.gridColumn = "span 6"
- Migrate Existing Forms:
const migrateFormToGrid = (form) => {
return {
...form,
components: form.components.map(component => ({
...component,
props: {
...component.props,
gridColumn: component.props.gridColumn ||
`span ${percentageToGridColumns(parseInt(component.props.width) || 100)}`
}
}))
}
}
- Update CSS:
/* Remove old percentage-based widths */
.form-component {
/* width: var(--component-width); // Remove this */
grid-column: var(--grid-column); /* Add this */
}
This grid system documentation reflects the complete implementation including visual selection, smart recommendations, automatic optimization, and responsive behavior.
Last updated: December 2024