Text Bars & Inputs (Desktop)
Labels, not just placeholders
A placeholder is not a label. Placeholder text disappears the moment the user starts typing, so if it was the only identifier for the field, the user loses context mid-entry — a real problem in a longer form or when returning to a field. Every input needs a persistent label above or beside it; use the placeholder for a short example of the expected format ("e.g. sakura@otoyin.app"), not for the field's name.
Sizing and states
Match the control-sizing scale from desktop-ui-buttons-controls (sm/md/lg), and cover the same state set:
| State |
Treatment |
| Default |
Neutral border, surface-base or slightly recessed background |
| Focus |
Clear, distinct border/ring color (usually the accent color) — this is one of the most-seen states in any form-heavy app, don't skip it |
| Filled (has content, not focused) |
Same as default — don't add a separate "filled" style that fights the focus state |
| Disabled |
Reduced opacity, cursor: not-allowed, background flattened so it doesn't look editable |
| Error |
Danger-colored border + an inline error message directly below the field (see desktop-ui-feedback-messaging) — never color alone |
| Success/valid (only where confirmation genuinely helps, e.g. a username-availability check) |
Subtle success-colored border or a small checkmark, not a full color flood |
Search bars specifically
- Icon on the left (magnifying glass, from your chosen icon set — never Lucide, see
desktop-ui-icons), clear/×-button on the right that only appears once there's input.
- If the app has a global search/command-palette pattern, keep the visual language consistent between the inline search bar and the palette — they're the same mental action to the user.
- Debounce live-search input (typically 150–300ms after the user stops typing) rather than firing a query on every keystroke — this matters for both perceived responsiveness and for not hammering a local search index on modest hardware.
- Show a keyboard shortcut hint (e.g. a small
⌘K/Ctrl+K badge) inside or beside the field if one exists — desktop users rely on this far more than mobile users ever will.
Textareas
- Auto-grow to fit content up to a sane max-height, then scroll internally — don't force a fixed tiny box for potentially long text, and don't let it grow to consume the whole window either.
- Show a character/line count only when there's an actual limit that matters (e.g. a title field) — don't add one decoratively.
Combo boxes / selects
- If the list is short (under ~8 options) and all options benefit from being visible at once, consider a segmented control or radio group instead (see
desktop-ui-buttons-controls) — a dropdown hides information a visible set wouldn't.
- If the list is long or searchable, a combo box with type-to-filter is the right call — but it needs its own focus/keyboard-navigation handling (arrow keys to move, Enter to select, Escape to close) since desktop users expect this to work.
Validation timing
- Validate on blur (when the user leaves the field) for most fields, not on every keystroke — validating while someone is mid-type flashes errors for a value they haven't finished entering yet, which reads as broken rather than helpful.
- Exception: fields with an immediate, calculable constraint the user benefits from seeing live (a password-strength meter, a character counter near its limit) — those can update live, but the error styling/messaging should still wait until the field is complete or blurred.
- Re-validate immediately on submit, and scroll/focus to the first invalid field if there's more than one.
Review format
| Before |
After |
Why |
| Field identified only by its placeholder text |
Persistent label above the field, placeholder used for an example value |
Placeholder disappears on focus, leaving no identifier while the user is typing |
| Live search fires a query on every keystroke |
200ms debounce after typing stops |
Reduces redundant work and feels less jittery, especially on modest hardware |
| Error shown in red border only |
Red border + specific inline error text below the field |
Color alone doesn't explain what's wrong or meet accessibility contrast expectations for all users |
1---2name: desktop-ui-text-inputs3description: Use whenever designing or reviewing text fields, search bars, textareas, combo boxes/selects, or form validation in a desktop app. Depends on desktop-ui-foundations. Trigger on "text input," "search bar," "form field," "validation," or "this input looks off."4---56# Text Bars & Inputs (Desktop)78## Labels, not just placeholders910**A placeholder is not a label.** Placeholder text disappears the moment the user starts typing, so if it was the only identifier for the field, the user loses context mid-entry — a real problem in a longer form or when returning to a field. Every input needs a persistent label above or beside it; use the placeholder for a short example of the expected format ("e.g. sakura@otoyin.app"), not for the field's name.1112## Sizing and states1314Match the control-sizing scale from `desktop-ui-buttons-controls` (sm/md/lg), and cover the same state set:1516| State | Treatment |17|---|---|18| Default | Neutral border, `surface-base` or slightly recessed background |19| Focus | Clear, distinct border/ring color (usually the accent color) — this is one of the most-seen states in any form-heavy app, don't skip it |20| Filled (has content, not focused) | Same as default — don't add a separate "filled" style that fights the focus state |21| Disabled | Reduced opacity, `cursor: not-allowed`, background flattened so it doesn't look editable |22| Error | Danger-colored border + an inline error message directly below the field (see `desktop-ui-feedback-messaging`) — never color alone |23| Success/valid (only where confirmation genuinely helps, e.g. a username-availability check) | Subtle success-colored border or a small checkmark, not a full color flood |2425## Search bars specifically2627- Icon on the left (magnifying glass, from your chosen icon set — never Lucide, see `desktop-ui-icons`), clear/×-button on the right that only appears once there's input.28- If the app has a global search/command-palette pattern, keep the visual language consistent between the inline search bar and the palette — they're the same mental action to the user.29- Debounce live-search input (typically 150–300ms after the user stops typing) rather than firing a query on every keystroke — this matters for both perceived responsiveness and for not hammering a local search index on modest hardware.30- Show a keyboard shortcut hint (e.g. a small `⌘K`/`Ctrl+K` badge) inside or beside the field if one exists — desktop users rely on this far more than mobile users ever will.3132## Textareas3334- Auto-grow to fit content up to a sane max-height, then scroll internally — don't force a fixed tiny box for potentially long text, and don't let it grow to consume the whole window either.35- Show a character/line count only when there's an actual limit that matters (e.g. a title field) — don't add one decoratively.3637## Combo boxes / selects3839- If the list is short (under ~8 options) and all options benefit from being visible at once, consider a segmented control or radio group instead (see `desktop-ui-buttons-controls`) — a dropdown hides information a visible set wouldn't.40- If the list is long or searchable, a combo box with type-to-filter is the right call — but it needs its own focus/keyboard-navigation handling (arrow keys to move, Enter to select, Escape to close) since desktop users expect this to work.4142## Validation timing4344- Validate on blur (when the user leaves the field) for most fields, not on every keystroke — validating while someone is mid-type flashes errors for a value they haven't finished entering yet, which reads as broken rather than helpful.45- Exception: fields with an immediate, calculable constraint the user benefits from seeing live (a password-strength meter, a character counter near its limit) — those can update live, but the *error* styling/messaging should still wait until the field is complete or blurred.46- Re-validate immediately on submit, and scroll/focus to the first invalid field if there's more than one.4748## Review format4950| Before | After | Why |51|---|---|---|52| Field identified only by its placeholder text | Persistent label above the field, placeholder used for an example value | Placeholder disappears on focus, leaving no identifier while the user is typing |53| Live search fires a query on every keystroke | 200ms debounce after typing stops | Reduces redundant work and feels less jittery, especially on modest hardware |54| Error shown in red border only | Red border + specific inline error text below the field | Color alone doesn't explain what's wrong or meet accessibility contrast expectations for all users |