Textarea
Multi-line text input components for property descriptions, notes, and messages with character counting.
Textarea Variants
Standard Textarea
Textarea with Character Count
0/200
Auto-resize Textarea
Textarea States
✓ Description meets all requirements
Minimum 50 characters required
Textarea Sizes
Real Estate Use Cases
Property Listing Form
Client Message Form
0/500
Include your preferred viewing times
Implementation Guide
HTML Structure
<!-- Standard Textarea -->
<div class="lyd-input-group">
<label class="lyd-input-label">Description</label>
<textarea class="lyd-textarea" placeholder="Enter text..." rows="4"></textarea>
</div>
<!-- Textarea with Character Count -->
<div class="lyd-input-group">
<label class="lyd-input-label">Message</label>
<div class="lyd-textarea-wrapper">
<textarea class="lyd-textarea" id="textarea" maxlength="200"></textarea>
<span class="lyd-character-count" id="count">0/200</span>
</div>
</div>
<!-- Textarea with Helper Text -->
<div class="lyd-input-group">
<label class="lyd-input-label">Notes</label>
<textarea class="lyd-textarea error" rows="3"></textarea>
<div class="lyd-textarea-helper error">This field is required</div>
</div>
JavaScript Features
// Character Counter
function setupCharacterCounter(textareaId, counterId) {
const textarea = document.getElementById(textareaId);
const counter = document.getElementById(counterId);
const maxLength = textarea.getAttribute('maxlength');
function updateCount() {
const current = textarea.value.length;
counter.textContent = `${current}/${maxLength}`;
// Update counter color
counter.classList.remove('warning', 'error');
if (current > maxLength * 0.9) {
counter.classList.add('error');
} else if (current > maxLength * 0.7) {
counter.classList.add('warning');
}
}
textarea.addEventListener('input', updateCount);
updateCount();
}
// Auto-resize
function setupAutoResize(textarea) {
function resize() {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
}
textarea.addEventListener('input', resize);
resize();
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Character counters
setupCharacterCounter('textarea-count', 'char-count');
setupCharacterCounter('message-area', 'message-count');
// Auto-resize
const autoResize = document.getElementById('auto-resize');
if (autoResize) {
setupAutoResize(autoResize);
}
});
React/Next.js Component
// components/Textarea.tsx
import { TextareaHTMLAttributes, forwardRef, useState, useEffect, useRef } from 'react';
interface TextareaProps extends TextareaHTMLAttributes {
label?: string;
helperText?: string;
error?: boolean;
success?: boolean;
showCount?: boolean;
autoResize?: boolean;
}
export const Textarea = forwardRef(({
label,
helperText,
error = false,
success = false,
showCount = false,
autoResize = false,
maxLength,
className = '',
onChange,
...props
}, ref) => {
const [count, setCount] = useState(0);
const textareaRef = useRef(null);
const handleChange = (e: React.ChangeEvent) => {
setCount(e.target.value.length);
if (autoResize && textareaRef.current) {
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px';
}
onChange?.(e);
};
useEffect(() => {
if (autoResize && textareaRef.current) {
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px';
}
}, [autoResize]);
const classes = `lyd-textarea ${error ? 'error' : ''} ${success ? 'success' : ''} ${autoResize ? 'auto-resize' : ''} ${className}`;
return (
<div className="lyd-input-group">
{label && <label className="lyd-input-label">{label}</label>}
{showCount && maxLength ? (
<div className="lyd-textarea-wrapper">
<textarea
ref={ref || textareaRef}
className={classes}
maxLength={maxLength}
onChange={handleChange}
{...props}
/>
<span className={`lyd-character-count ${count > maxLength * 0.9 ? 'error' : count > maxLength * 0.7 ? 'warning' : ''}`}>
{count}/{maxLength}
</span>
</div>
) : (
<textarea
ref={ref || textareaRef}
className={classes}
onChange={handleChange}
{...props}
/>
)}
{helperText && (
<div className={`lyd-textarea-helper ${error ? 'error' : ''} ${success ? 'success' : ''}`}>
{helperText}
</div>
)}
</div>
);
});
API Reference
| Class | Description |
|---|---|
.lyd-textarea |
Base textarea class |
.lyd-textarea-wrapper |
Container for textarea with character count |
.lyd-character-count |
Character counter display |
.lyd-textarea-helper |
Helper text below textarea |
.auto-resize |
Auto-resizing textarea |
.small |
Small size variant |
.large |
Large size variant |
.error |
Error state styling |
.success |
Success state styling |
Accessibility & Best Practices
Accessibility Features
- Use proper label associations
- Provide clear placeholder text
- Include aria-describedby for helper text
- Announce character count changes
- Ensure keyboard navigation
Best Practices
- Set appropriate default rows
- Allow vertical resizing only
- Provide character limits when needed
- Use helper text for guidance
- Consider auto-save for long content
- Validate content appropriately