Home / Snippets / UI Components /
Textarea
Styled textarea with focus ring, character count hint, and auto-resize — dark-mode ready, pure CSS.
Quick implementation
/* HTML:
<div class="field">
<label for="msg">Your message</label>
<textarea class="css-textarea" id="msg" placeholder="Write something…"></textarea>
<span class="textarea-hint">0 / 500 characters</span>
</div> */
.field {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.field label {
font-size: 0.875rem;
font-weight: 600;
color: var(--text);
}
.css-textarea {
width: 100%;
min-height: 8rem;
padding: 0.75rem 1rem;
background: var(--bg);
border: 1.5px solid oklch(0.35 0.02 260);
border-radius: 0.5rem;
color: var(--text);
font-family: inherit;
font-size: 0.95rem;
line-height: 1.6;
resize: vertical;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
box-sizing: border-box;
}
.css-textarea::placeholder {
color: var(--muted);
}
.css-textarea:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px oklch(0.72 0.19 265 / 0.2);
}
.textarea-hint {
font-size: 0.8rem;
color: var(--muted);
text-align: right;
}
@media (prefers-reduced-motion: reduce) {
.css-textarea {
transition: none;
}
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building accessible form components.
Goal: A styled textarea with a visible focus ring, a muted character-count hint below it, and vertical resize support — pure CSS, no JavaScript required for the visual states.
Technical constraints:
- Remove the default outline on focus and replace it with border-color + box-shadow glow using oklch() alpha.
- Use oklch() for all color values, never hex or rgba().
- The border must be 1.5px solid in the resting state and transition to the accent color on :focus.
- The ::placeholder color must use the muted design token.
- Wrap all transitions in @media (prefers-reduced-motion: reduce).
- The component must work correctly in a dark-mode-only design system (prefers-color-scheme: dark).
Framework variant (pick one):
A) Vanilla HTML + CSS — label, textarea, and hint span wrapped in a .field container.
B) React component — accept id, label, placeholder, maxLength, and value/onChange props; render the character count dynamically.
Edge cases to handle:
- Invalid/error state: red border and glow using oklch() red hue.
- Disabled state: reduced opacity, cursor: not-allowed, no focus ring.
- resize: none variant for fixed-height contexts such as chat inputs.
- Very long placeholder text must wrap gracefully inside the textarea bounds.
Return HTML + CSS (or React + CSS).
Why this matters in 2026
Default browser textarea styling is inconsistent across platforms and almost always looks out of place in a custom design system. A well-styled textarea — with a clear focus ring, restrained resize handle, and a muted character hint — signals to users that the interface is polished and accessible. In dark-mode-only designs, the challenge is compounding: default form controls use light-mode system colors that can appear as blinding white boxes against dark backgrounds. Replacing them with oklch()-based border and shadow values keeps everything perceptually consistent.
The logic
The resting border uses a low-lightness oklch() value to remain visible against the dark background without screaming for attention. On :focus, both the border-color and a semi-transparent box-shadow ring update simultaneously, giving users two distinct visual cues about the active field. The transition on both properties is wrapped in a prefers-reduced-motion query so the state change still happens — just instantly — for users who prefer no motion. The resize: vertical property retains browser-native height control while preventing the textarea from breaking horizontal layouts.
Accessibility & performance
The <label> element must be explicitly associated with the textarea via matching for and id attributes — never rely on placeholder text as a label, because it disappears when the user starts typing. The character-count hint should be linked to the textarea with aria-describedby so screen readers announce it alongside the field description. All visual transitions use border-color and box-shadow, both compositor-friendly properties that do not trigger layout recalculation. The resize: vertical constraint is purely cosmetic and does not affect accessibility.