#+TITLE: Pattern catalog — "no empty input as meaningful" (third worked example) #+DATE: [2026-05-28 Thu] #+SOURCE: pearl issue-sources verify, follow-up to earlier UI-patterns handoff * Follow-up note A third worked example landed in pearl on 2026-05-28, shipped as commit =1288c2a=. Adding it here so the rulesets catalog discussion has three concrete examples to ground design questions in. * Pattern: "no empty input as meaningful" Whenever a prompt offers the user a choice between picking a value and picking "no value," the no-value option belongs in the candidate list, not behind empty input. ** Before The pearl ad-hoc filter builder had five sequential =completing-read= prompts (team, state, project, labels, assignee). Four of them relied on the "empty input means no constraint" convention, with a parenthetical hint in the label: #+begin_example Team (empty for any): _ State (empty for any): _ Project (empty for any): _ Labels (comma-separated, empty for none): _ #+end_example The user had to remember the convention. RET on an unselected prompt did something silently. The label hint was the only on-screen affordance, and it lived in the prompt rather than the candidate list, where the eye was actually looking. ** After #+begin_example Team: [ None. ] <-- first candidate, picked by default RET Engineering Design Marketing ... #+end_example Same logical behavior. The "no constraint" choice is now a candidate the user sees and picks, not a convention they have to recall. =require-match= is on, so the user picks from the list rather than free-typing. ** What changed in code Two helpers (one predicate, one list-prepender) and a defconst hold the sentinel string. Each =completing-read= wraps its candidate list with the prepender and treats the sentinel as the same logical opt-out empty input was before. Trivial back-compat: the predicate also returns true for nil and empty string, so any caller that still produces an empty answer (e.g. =completing-read-multiple= returning nothing) is handled. ** Why it matters for the catalog The principle is small but has a lot of surface area: every prompt across every project where "none" is a meaningful choice is a candidate. The cost is one helper per project (or one shared helper); the win is users stop having to know rules that aren't on screen. Worth thinking about as a catalog entry alongside the earlier two examples (one-prompt picker with typed prefix, magit-transient state-buttons). All three share the same underlying principle: *no hidden affordances; if it's a choice, it's in the list.* * What the catalog might need to capture for each pattern Surfacing this third example clarifies what a catalog entry probably needs to carry. Tentative shape: - One-sentence statement of the principle - The before / after the pattern replaced - The shape of the code change (small helpers, single source of truth) - Where the pattern applies (the surface area predicate) - Anti-pattern variants to avoid - A link or grep target for an in-the-wild example If that shape stabilizes, the catalog has a chance of staying scannable as it grows.