Textarea
AtomLabel + textarea + hint + error anatomy. Vertical resizing is enabled via resize-y and the parts are linked through aria-describedby.
<%- include('modules/ui/Textarea', { id: 'msg', label: 'Message', placeholder: 'Enter your message…' }) %>
<%- include('modules/ui/Textarea', { id: 'desc', label: 'Description', required: true }) %>
Max 500 characters
<%- include('modules/ui/Textarea', { id: 'bio', label: 'Bio', hint: 'Max 500 characters', placeholder: 'Tell us about yourself' }) %>
This field is required
<%- include('modules/ui/Textarea', { id: 'notes', label: 'Notes', error: 'This field is required' }) %>
<%- include('modules/ui/Textarea', { id: 'ro', label: 'Read-only notes', disabled: true, value: 'This field cannot be edited.' }) %>
<%- include('modules/ui/Textarea', { id: 'fixed', label: 'Fixed height', resize: 'none', rows: 4 }) %>
<%- include('modules/ui/Textarea', { id: 'long', label: 'Long-form content', rows: 6 }) %>
<%
var _id = locals.id || 'textarea-' + Math.random().toString(36).substr(2, 9);
var _rows = locals.rows || 4;
var _dis = !!locals.disabled;
var _req = !!locals.required;
var _val = (locals.value !== undefined && locals.value !== null) ? String(locals.value) : '';
var _hintId = (locals.hint && !locals.error) ? (_id + '-hint') : '';
var _errorId = locals.error ? (_id + '-error') : '';
var _describedBy = [_hintId, _errorId].filter(function (x) { return !!x; }).join(' ');
var baseClass = 'block w-full rounded-md border px-3 py-2 text-sm transition-colors resize-y '
+ 'text-text-primary placeholder:text-text-disabled '
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:border-border-focus '
+ 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-sunken ';
baseClass += locals.error
? 'border-error ring-1 ring-error bg-error-subtle'
: 'border-border bg-surface-base';
%>
<% if (_hintId) { %><%= locals.hint %>
<% } %>
<% if (_errorId) { %><%= locals.error %>
<% } %>