Styling
@saastro/forms provides multiple ways to customize the appearance of your form fields. You can apply CSS classes to different parts of each field, control sizes, visibility, and dark mode. For grid layout configuration (columns, gap, breakpoints), see the Layout System page.
classNames() Method
The classNames() method allows you to add CSS classes to specific parts of a field:
.addField('email', (f) =>
f.type('email')
.label('Email')
.required()
.classNames({
wrapper: 'bg-gray-50 p-4 rounded-lg',
input: 'border-2 border-blue-500 focus:border-blue-700',
label: 'text-lg font-bold text-blue-900',
error: 'text-red-600 font-medium',
helper: 'text-gray-500 italic',
})
)
Available Class Targets
| Target | Description | Applied To |
|---|---|---|
wrapper | Container element | <div> wrapping the entire field |
input | Input control | <input>, <textarea>, <select>, etc. |
label | Field label | <label> element |
error | Error message | Error text below the field |
helper | Helper text | Help text below the field |
JSON Configuration
{
"type": "email",
"label": "Email",
"wrapper_className": "bg-gray-50 p-4 rounded-lg",
"input_className": "border-2 border-blue-500",
"label_className": "text-lg font-bold",
"error_className": "text-red-600 font-medium",
"helper_className": "text-gray-500 italic",
"schema": { "required": true, "format": "email" }
}
Field Order
Control the display order of fields (useful for responsive reordering):
.addField('sidebar', (f) =>
f.type('text')
.label('Sidebar')
.required()
.order({ default: 2, lg: 1 }) // Second on mobile, first on desktop
)
Field Size
Set the size of input fields:
.addField('search', (f) =>
f.type('text')
.label('Search')
.required()
.size('sm') // 'sm' | 'md' | 'lg'
)
| Size | Description |
|---|---|
sm | Small - compact inputs |
md | Medium - default size |
lg | Large - prominent inputs |
Options Layout (Groups)
For fields with multiple options (checkbox-group, radio, switch-group), control the options grid:
{
"type": "checkbox-group",
"label": "Interests",
"optionsClassName": "grid grid-cols-2 md:grid-cols-3 gap-4",
"options": [...]
}
Hiding Labels
Hide the label while keeping it accessible for screen readers:
.addField('search', (f) =>
f.type('text')
.label('Search') // Still needed for accessibility
.hideLabel() // Visually hidden
.placeholder('Search...')
.required()
)
Conditional Visibility
Hide fields responsively by breakpoint:
.addField('mobileOnly', (f) =>
f.type('text')
.label('Mobile Field')
.required()
.hidden({ default: 'visible', lg: 'hidden' }) // Hidden on desktop
)
.addField('desktopOnly', (f) =>
f.type('text')
.label('Desktop Field')
.required()
.hidden({ default: 'hidden', lg: 'visible' }) // Hidden on mobile
)
Complete Example
const config = FormBuilder.create('styled-form')
.columns(12)
.gap(6)
.addField('name', (f) =>
f
.type('text')
.label('Full Name')
.placeholder('John Doe')
.required()
.size('lg')
.columns({ default: 12, md: 6 })
.classNames({
wrapper: 'bg-white shadow-sm rounded-lg p-4',
input: 'text-lg',
label: 'text-primary font-semibold',
}),
)
.addField('email', (f) =>
f
.type('email')
.label('Email Address')
.placeholder('john@example.com')
.required()
.email()
.columns({ default: 12, md: 6 })
.classNames({
wrapper: 'bg-white shadow-sm rounded-lg p-4',
error: 'text-destructive font-medium',
}),
)
.addField('bio', (f) =>
f
.type('textarea')
.label('Bio')
.placeholder('Tell us about yourself...')
.rows(4)
.optional()
.columns({ default: 12 })
.classNames({
wrapper: 'bg-muted/50 rounded-lg p-4',
input: 'min-h-[120px]',
helper: 'text-xs text-muted-foreground',
})
.helperText('Optional - maximum 500 characters'),
)
.addStep('main', ['name', 'email', 'bio'])
.build();
CSS Variables
The form components use CSS variables that you can override:
:root {
--input-height: 2.5rem;
--input-padding: 0.75rem;
--label-font-size: 0.875rem;
--error-font-size: 0.75rem;
}
Tailwind CSS Integration
All class names work with Tailwind CSS out of the box. Use any Tailwind utility classes:
.classNames({
wrapper: 'space-y-2 transition-all duration-200',
input: 'focus:ring-2 focus:ring-primary/50 hover:border-primary',
label: 'tracking-wide uppercase text-xs',
})
Dark Mode
Classes work with Tailwind’s dark mode:
.classNames({
wrapper: 'bg-white dark:bg-gray-800',
input: 'border-gray-300 dark:border-gray-600',
label: 'text-gray-900 dark:text-gray-100',
})