# Searchable select combobox UX for B2B project pickers (Voxpopme portal) Market Research Report

**Generated on:** 2026-10-14 16:16:27.010657  
**Industry:** Searchable select combobox UX for B2B project pickers (Voxpopme portal)  
**Geography:** Global  
**Details:** # Research prompt: searchable select / combobox UX for a project picker

## Context

Voxpopme is a B2B video research platform. Customers upload video and audio files into a web portal and assign them to a project. The engineering team has built a shared searchable select component (a combobox) and is using it first in the upload flow's "Add to an existing project" field. A UX designer is reviewing it before it rolls out across the product, and the same component will later be reused for other pickers (for example, choosing a report inside an AI research assistant).

Current behaviour:

- Clicking into the field opens a list of the user's most recent projects straight away. Each row shows the project name and its numeric project ID. The project the user is currently in is listed first.
- Typing filters the list. The matching part of each project name is shown in bold.
- The list loads 50 projects at a time. Scrolling to the end loads the next 50.
- A footer reads "Showing 50 of 482. Type to narrow the list."
- Keyboard support: Up and Down arrows to move, Enter to select, Escape to close. There are hover and selected states.
- Messages: "No matches found", "Loading...", "Loading more..." and a load error state.
- Long project names are truncated so the ID stays visible.
- Hint text below the field reads "Select from the list or start typing to filter".
- A second variant of the component stays closed until the user types ("type first") and needs its own hint text.
- Typing a project ID does not yet match projects; that is a planned backend change.

Known concerns:

- The input border is about 1.4:1 contrast against the page background. WCAG 2.1 SC 1.4.11 asks for 3:1 for the parts of a control that identify it.
- The brand style guide's focus style is a soft glow at about 1.3:1. The component uses a 2px solid blue outline instead.

Users are market researchers and insight managers, often working across tens or hundreds of projects. Some users have thousands of projects. They use desktop browsers in the main and some use screen readers or keyboard only.

I want research that helps me decide whether this component is right, what to change before it ships, and what rules to set for every future use of it.

## Research sections

### 1. Combobox and searchable select patterns

1. What are the current best-practice patterns for a combobox that shows options on focus versus one that only opens on typing? When does each suit?
2. How do leading design systems (for example GOV.UK, USWDS, Atlassian, Shopify Polaris, Material, Carbon, Radix, Ant) handle "show recent items on click, filter as you type"? Where do they differ and why?
3. Is "most recent first, current item pinned to the top" a well-evidenced default order? What alternatives exist (alphabetical, most used, grouped)?
4. Should the list open on click, on focus, or both? What are the trade-offs for mouse, keyboard and touch?
5. Are there documented usability studies on how many options should be visible before users prefer to type rather than scroll?

### 2. Filtering, matching and highlighting

1. What matching rules do users expect: starts-with, contains, fuzzy, token-based? Which performs best in usability testing for lists of named items?
2. Is bolding the matched substring helpful, and how should it behave with multiple matches, case differences or accented characters?
3. How should the component handle whitespace, punctuation and partial numeric input ahead of ID matching being available?
4. What are the best debounce and minimum-character conventions for server-side filtering, and what do users tolerate?
5. How should "no matches" be worded and what should it offer next (clear the search, create a new item, check spelling)?

### 3. Pagination, infinite scroll and large data sets

1. Is infinite scroll inside a dropdown a sound pattern, or is "type to narrow" plus a hard cap better? What does the evidence say?
2. What page size conventions exist for searchable selects backed by an API, and why 50 rather than 20 or 100?
3. How should a count footer such as "Showing 50 of 482" be worded and announced to assistive technology?
4. How should loading, loading more and load error states look and behave, including retry?
5. What happens for users with thousands of items: does the pattern still hold, or should grouping, favourites or recents be introduced?

### 4. Keyboard, focus and screen reader behaviour

1. What does the WAI-ARIA Authoring Practices combobox pattern require for roles, states and keyboard interaction, and where do real implementations commonly get it wrong?
2. How should arrow keys, Home, End, Page Up and Page Down, Tab, Enter and Escape behave, including when the list is paginated?
3. What should be announced when the list opens, when results change on typing, when more items load, and when there are no matches?
4. How should the "type first" closed variant expose the fact that pressing Down Arrow opens the full list?
5. How should the selected and hovered states differ visually and semantically (aria-selected, aria-activedescendant)?

### 5. Visual design and contrast

1. What does WCAG 2.1 and 2.2 require for input borders, focus indicators (SC 2.4.11 and 2.4.13) and selected states in a combobox?
2. Where a brand style guide's focus glow fails contrast, what are proven ways to keep brand feel while meeting 3:1 (for example dual-ring, offset outline, inner plus outer ring)?
3. How do design systems handle a global token change for input borders without breaking existing screens? What rollout approaches work?
4. What are best practices for truncating long option labels while keeping a secondary identifier (an ID) visible, including tooltips and full-text on hover or focus?
5. Should IDs be shown at all to end users, and if so, how should they be styled relative to the name?

### 6. Hint text, labels and microcopy

1. What are evidence-based guidelines for hint text on comboboxes? Should hint text describe behaviour, or should the behaviour be self-evident?
2. Compare wording for the open-on-click variant and the type-first variant. Propose three options for each, in plain English that reads naturally in both UK and US English.
3. Should hint text sit above the field, below it, or as placeholder, and what are the accessibility consequences of each?
4. How should placeholder text, hint text and the empty-state message relate so they do not repeat each other?
5. What tone and length of microcopy do enterprise B2B tools use for these states, with examples?

### 7. Mobile, touch and responsive behaviour

1. How should a searchable select behave on small screens: inline dropdown, bottom sheet, or full-screen picker?
2. How does infinite scroll inside a dropdown behave with touch scrolling and the on-screen keyboard?
3. What touch target sizes and spacing are needed for option rows (WCAG 2.5.8)?

### 8. Component API and reuse across a design system

1. What props and variants do mature design systems expose for searchable selects (async loading, grouping, create-new, multi-select, clearable, disabled, read-only)?
2. What should be fixed at the design-system level versus configurable per use, so that reuse in a different context (for example picking a report in an AI assistant) stays consistent?
3. How should the component document its states in Storybook so designers and developers review the same thing?
4. What testing approach (unit, visual regression, automated accessibility checks, manual screen reader testing) catches the most issues for this type of component?

### 9. Error handling and edge cases

1. What should happen if the API fails on first load, on load more, or on filter? How should each be worded and what recovery should be offered?
2. How should the component handle a previously selected project that no longer exists, has been archived, or the user has lost access to?
3. How should it behave if the user types, pauses, and the result set changes under their cursor?
4. What happens with duplicate project names, very short names, emoji or non-Latin characters?
5. How should the component behave when the user has no projects at all?

### 10. Usability testing plan

1. What tasks and measures would best test this picker with 5 to 8 research professionals?
2. What are the most likely failure points to watch for, based on published studies of comboboxes and autocompletes?
3. How should the open-on-click variant be compared against the type-first variant, and what sample size gives a usable signal?
4. What specific screen reader and keyboard-only test scripts should be run?

### 11. Competitive and comparable products

1. How do comparable B2B tools (research platforms, analytics tools, project management tools, cloud consoles with many resources) handle picking one item from hundreds or thousands?
2. What patterns do they use for recents, favourites, grouping and search across large lists?
3. What do users complain about in public reviews or forums about these pickers?

### 12. Risks, trade-offs and things to challenge

1. What assumptions in the current design are most likely to be wrong?
2. Where could "show recent projects on click" cause harm, such as privacy, mis-selection, or slow performance?
3. What would a sceptical accessibility auditor flag first?
4. What should be cut from scope to ship a simpler, better version?

## Output instructions

- Give specific, implementable recommendations with exact values (pixel sizes, contrast ratios, debounce times, page sizes, ARIA attributes, wording), not ranges.
- Cite sources for every claim that rests on research, a standard or a design system. Prefer primary sources: W3C, WCAG, WAI-ARIA APG, Nielsen Norman Group, Baymard, GOV.UK Design System research, published usability studies.
- Flag trade-offs clearly and say which option you recommend and why.
- Mark anything surprising, contested, or where the evidence is thin.
- Finish with a prioritised list of changes to make before shipping, changes to make later, and rules to adopt for every future use of the component.
- Write in plain English. Do not use em dashes.

---

# A Safer, Faster Project Picker for Voxpopme

## Executive Summary

**Recommendation: keep the searchable select, but do not roll out the shared component unchanged.** The pattern suits a known-item selection task with hundreds of projects. The important changes are selection safety, contrast, accessible asynchronous behavior and honest search instructions, rather than a different visual control.

- **Appropriate Core Pattern**: USWDS recommends a combobox when there are more than **15 choices**, but also reports unresolved assistive-technology usability concerns -> keep the pattern and make manual accessibility testing a release gate. [1]
- **Contrast Is A Shipping Blocker**: the supplied **1.4:1 border** and **1.3:1 glow** are below **3:1** where they provide necessary control or state identification -> fix the border and retain a measured solid focus indicator. [2]
- **Explicit Selection Prevents Wrong Uploads**: APG permits editable comboboxes that restrict values to allowed options -> submit a committed project ID, never the typed search string or merely highlighted row. [5]
- **Recency Is Useful, Not Universally Best**: Carbon recommends alphabetical options, while split-menu research supports prioritizing frequently selected items -> keep a clearly identified current project and stable personal recents, but test them against known-name retrieval. [24][46]
- **Search Needs A Written Contract**: USWDS combines contains matching with starts-with results first; Material UI defaults to case- and accent-insensitive matching -> adopt predictable name matching and disclose that ID search is not available yet. [1][20]
- **Separate Fetching From Visibility**: Adobe documents asynchronous infinite loading, while Baymard cautions against long, scrollable ecommerce suggestion lists -> retain **50 items per API page**, but show **6 rows**, not 50 rows at once. These are recommended product defaults, not established optima. [11][3]
- **Use A Concrete Timing Default**: Algolia recommends **200 ms debounce** and a loading threshold equal to debounce plus **300 ms** -> start with **200 ms debounce** and show a spinner after **500 ms**, then measure the actual experience. [26]
- **Small Tests Find Problems, Not Statistical Winners**: NN/g distinguishes qualitative five-user testing from quantitative studies needing at least **20 users** -> compare both variants with **8 professionals**, counterbalance their order, and report observed problems rather than significance. [27]

**Scope:** global UX and component-market landscape, including government, enterprise design systems, research platforms, analytics products and cloud tools. This is not a revenue or market-share estimate. The supplied Voxpopme behavior is the starting point, not an independently audited implementation. Values labelled **recommended defaults** below are implementable specifications to test, not claims that research establishes a universal optimum.


## 1. Choose Browse-First Without Opening On Every Tab Focus

### Trigger and selection model

Use an **editable, single-select, restricted-value combobox with manual commitment**. APG explicitly allows typing to filter a discrete set of allowed values; its free-text example is not a complete project-assignment specification. [5][6]

**Recommended trigger policy:** keep opening on deliberate click or tap. Also open on Down Arrow, Alt + Down Arrow, or typing **1 meaningful character**. Do not open merely because Tab lands on the field. Do not automatically highlight the first result after typing, and never commit on blur. This favors an intentional upload destination choice while preserving keyboard browsing.

Open-on-focus is a valid alternative, not an accessibility requirement. APG allows focus-triggered, button-triggered and character-triggered expansion. Adobe exposes `focus`, `input` and `manual` trigger modes; Material UI defaults `openOnFocus` to false. [5][11][14] Opening on every focus improves immediate discoverability but adds announcements and potentially a request during keyboard form traversal. Click-only without Down Arrow support excludes keyboard users. Touch requires a deliberate activation, with the mobile presentation described in section 7.

### What the major systems actually provide

| System | Documented interaction | Large-list and ordering position | Implication for Voxpopme |
|---|---|---|---|
| GOV.UK accessible-autocomplete | `showAllValues=false`; true shows all values on click; `minLength=0`; `autoselect=false` | Standalone component with minimal maintenance support; not a prescribed recents system. [19] | Useful pattern reference, not a drop-in guarantee |
| USWDS | Contains filtering; starts-with results precede other contains matches | Recommends combobox above 15 choices; explicitly notes assistive-technology concerns. [1] | Strong search convention, but test the actual build |
| Atlassian | Searchable single/multi selection; `loadOptions` handles remote data | Supports grouped options and clearable selection. [13] | Mature configuration, not evidence for current-project pinning |
| Shopify Polaris React | Autocomplete wraps Combobox and Listbox; examples filter labels as text changes | Sections, loading and end-of-list loading callbacks. [12] | Useful merchant/B2B precedent; identify this specifically as the React documentation |
| Material UI | Load-on-open and search-as-you-type; independently controlled input and selected value | Configurable matching, virtualization and infinite-loading examples. [20] | This is MUI's implementation, not a universal Material Design rule |
| Carbon | Click anywhere in the field to open; type to refine | Recommends alphabetical options; truncation with accessible full-text disclosure. [24] | Explicit counterexample to recency as the universal default |
| Radix Primitives Select | Button-triggered select with typeahead | Supports groups; documented Select is not an editable search-input combobox. [15] | Do not put an input inside Select and assume combobox semantics follow |
| Ant Design | Search is configurable; remote-search example includes debounce and callback ordering | Grouping, custom search sorting and virtual scrolling. [16] | Powerful enterprise precedent with configuration and accessibility responsibilities |

The convergence is searchable selection and configurable data handling, not a common recency algorithm. Library capability should not be mistaken for demonstrated usability superiority.

### Ordering and visible-option evidence

**Recommended initial order:** current project first, labelled "Current project", then personal recents. Define recent as the user's last successful project selections, freeze the order while the popup is open, and deduplicate by ID. A current project is not automatically a selected project. Once a query exists, include the current project only if it matches.

**Case study:** split-menu research prioritizes frequently selected items, not necessarily recently viewed items. It finds faster selection in its studied menus; NN/g separately explains why alphabetical order suits known-name tasks. [46][43] This supports testing a later "Current project / Recent / All projects A-Z" structure, not claiming the present ordering is proven best. Do not alphabetize only the first fetched page and imply the whole collection is sorted.

**Evidence is thin:** Baymard observes problems above about 10 desktop ecommerce suggestions, but this does not establish a B2B threshold at which users prefer typing over scrolling. [3] Start with **6 visible rows** and test recognition, typing and scrolling separately.


## 2. Make Name Matching Predictable Before Adding ID Search

### Recommended matching contract

| Rule | Exact recommended behavior | Why choose it |
|---|---|---|
| Whitespace | Trim query ends; treat whitespace-only input as empty; collapse repeated query spaces | Prevent accidental empty searches |
| Case and accents | Case-insensitive and locale-aware accent-equivalent matching; preserve the original displayed name | Align with common implementation conventions, without rewriting project labels |
| Tokens | Every query token must match some part of the name; token order need not equal name order | Supports remembered fragments of long research titles |
| Ranking | Exact full-name match, then full-name prefix, then all-token word prefixes, then other contains matches; stable name/ID tie-break | Make stronger literal matches easier to find |
| Punctuation | Treat ordinary separators such as spaces, hyphens and slashes consistently in the search index; preserve significant characters in names such as `C++` | Avoid destructive blanket punctuation removal |
| Fuzzy matching | Off for the first release; later offer a separately labelled "Similar names" group only after no literal matches | Avoid presenting approximate titles as if they were exact |
| Numeric query now | Search numeric text in project names only; do not parse the query as an ID | A year in a name can legitimately match |
| Numeric query later | Exact full-ID match ranks first, followed by ID-prefix matches and name matches; preserve the entered identifier as text | Make explicit identifier lookup predictable |

USWDS documents contains matching and starts-with ranking; Material UI defaults to ignoring case and accents and matching anywhere. These are conventions, not head-to-head evidence that one algorithm wins for Voxpopme. [1][20]

**Case study:** a medical autocomplete study compares a right-extension baseline with multi-prefix matching. Its **12 participants** enter **40 terms per condition**; multi-prefix requires significantly fewer keystrokes. The example "opt ner me" finds "optic nerve meningioma". [25] The mechanism is less exact recall of complete word sequences. It is relevant to long project titles, but a medical vocabulary is not evidence that fuzzy matching, or the proposed token-contains rule, is universally best for project selection.

### Highlighting without misleading users

Keep bold highlighting, using **font-weight 700**, for every non-overlapping literal matched span. Match ignoring case, but display the original case. Map normalized matches back to original grapheme boundaries so an accent, emoji or multi-character case mapping is not split. Render labels as text, escape any query used in a regular expression, and do not highlight an ID before ID matching exists.

**Contested transfer:** Baymard recommends emphasizing the predicted continuation rather than repeated typed characters in ecommerce query completion. [3] Voxpopme is selecting an existing entity, not composing a search query. Literal highlighting is a reasonable product hypothesis here, but test whether it helps users distinguish duplicate or similarly prefixed titles. Do not copy arrow-focused names into the input as committed values merely because ecommerce autocomplete does so. [3]

### Timing and no-match recovery

Use **200 ms trailing debounce**, **1 meaningful character** for typed searching, and **500 ms** before displaying the pending spinner. Empty-query browsing fetches immediately on deliberate opening. Algolia's guidance prefers 200 ms and warns that delays over 300 ms degrade its autocomplete experience. [26] These values do not guarantee acceptable backend latency.

Use: **"No projects match 'customer diary'. Try another name or clear your search."** Offer a **"Clear search"** button outside the listbox. For an unmatched numeric query before ID support, use: **"No project names match '1234'. Project ID search is not available yet."** Do not claim the project does not exist, silently approximate an ID, or make "Create project" the default recovery for a failed search.


## 3. Keep 50-Item Pages, Not A 50-Row Dropdown

Infinite loading is defensible for a resource picker, but it needs keyboard loading, stable focus and truthful counts. Adobe documents infinite scrolling in ComboBox; Polaris exposes end-of-list loading callbacks. [11][12] Neither establishes **50** as the optimal page size.

| Parameter | Recommended default | Status |
|---|---|---|
| API page size | **50 projects** | Retain the current engineering choice; measure payload and latency |
| Desktop option row | **44 CSS px minimum height** | Product default, larger than WCAG's AA minimum |
| Desktop list viewport | **264 CSS px**, excluding footer | Six standard-height rows; reduce to available viewport space |
| Prefetch threshold | When the last visible row is within **5 loaded options** of the end | Product default; prevents waiting at the boundary |
| Query debounce | **200 ms** | Matches documented Algolia convention. [26] |
| Delayed spinner | **500 ms after the last keystroke** | Derived from its debounce-plus-300-ms guidance. [26] |
| Result-update performance goal | **p95 at or below 500 ms from last keystroke to selectable results** | Proposed monitoring target, not an industry benchmark |

Fetching, rendering and visible rows are different quantities. Fifty loaded options can be reasonable while only six are visible. Twenty lowers a request's payload but needs more page requests; 100 does the opposite. No direct evidence found establishes which wins for this project population. Keep 50 until measurements show a reason to change.

### A relevant failure case

An Ant Design issue describes a **2,000-item** endpoint and the cost of loading everything. A later commenter reports that scroll-based pagination works, but keyboard navigation appends options and resets scrolling to the top. [34] The mechanism is not infinite loading itself; it is incorrect state preservation during append. Test the second page with Down Arrow, not only a mouse wheel.

Append by stable project ID, keep the active row mounted, preserve scroll position, and prevent concurrent load-more requests. At the last loaded option, Down Arrow requests more without wrapping to the first option. Keep the current active row while loading; another Down Arrow can enter newly loaded results. A hard cap must say **"First 50 matches. Type more to narrow your search"**, not imply the remaining matches are browsable. Prefer pagination if it passes these tests; use the explicit cap as a simpler fallback if it does not.

### Counts, loading and thousands of projects

Replace the footer with **"50 of 482 projects loaded. Type to narrow your search."** After filtering, use the filtered total, for example **"50 of 126 matching projects loaded."** Never reuse the account-wide total as the matching total. If the total is unknown, say **"50 projects loaded. More available."**

Use one polite status channel for results and waiting states; WCAG requires status information to be available without moving focus. [10] Announce completed changes, not every scroll event: "50 more projects loaded. 100 of 482 available." Loading messages are not selectable options. Initial loading, loading more and retry behavior must remain distinct.

For thousands of items, search remains primary. Add personal favorites or a short recent group if observation shows repeated retrieval, and an **"Browse all projects"** route for users who cannot remember a name. Do not add virtualization just because total records are large; first measure mounted-option cost and verify assistive-technology behavior. Baymard's objection to internal scrolling is important counterevidence, but comes from ecommerce suggestions rather than mandatory resource selection. [3]


## 4. Treat The Input, Popup And Committed ID Separately

The input needs a persistent visible label and `role="combobox"`, `aria-autocomplete="list"`, `aria-expanded`, and `aria-controls` pointing to the popup. The popup uses `role="listbox"`; project rows use `role="option"` and unique stable DOM IDs. While navigating, DOM focus remains on the input and `aria-activedescendant` identifies the active option. [5][6]

Connect help with `aria-describedby`. Set `aria-required="true"` when selection is required. For a validation failure, set `aria-invalid="true"` and associate its error text. Clear `aria-activedescendant` when the referenced option disappears or the popup closes. Scroll the active option into view; browsers do not do this automatically for active descendants. [6]

### Exact keyboard contract

| Key | Recommended behavior |
|---|---|
| Down Arrow | Open if closed; with an empty query browse the collection; move one option when open; request more at the loaded boundary |
| Up Arrow | Open and activate the last loaded option if closed; otherwise move one option upward; do not wrap |
| Alt + Down Arrow | Open without activating or committing an option |
| Enter | With an active option, commit its ID and label, close, and keep focus on the input; with the popup open but no active option, do not submit the upload form |
| Escape | Close and cancel the uncommitted search; restore the prior committed label, if any; do not clear a valid selection |
| Tab / Shift + Tab | Close, cancel uncommitted editing, restore the prior committed label if present, and move normally to the next/previous control; never commit a highlighted suggestion |
| Home / End | Preserve text-caret start/end behavior and return visual focus to editing; do not jump to the first/last project |
| Page Down / Page Up | When an option is active, move by **5 loaded rows**, keeping it visible; request the next page at its boundary; otherwise preserve browser behavior |
| Left / Right, Space, editing shortcuts | Preserve native text editing; Space enters a space, not a project selection |

Down, Enter, Escape and native editing follow APG conventions. Home/End have permitted alternatives; the five-row paging extension and no-wrap policy are Voxpopme recommendations, not APG requirements. Popup options and the indicator are outside the page Tab sequence. [5] Do not interpret optional Page-key behavior described for other popup types as mandatory for a listbox.

### Active, selected and hovered are not the same business event

APG's listbox-popup model says selection follows focus and allows one suggested value to be selected at a time. [5] In that implementation, `aria-selected="true"` represents the active suggestion, while **committedProjectId changes only on Enter or explicit pointer activation**. Libraries may distinguish focused and selected styling states, so inspect their implementation rather than adding a second competing selection model. [11]

Use a subtle hover background, a **2 px active-row outline**, and a checkmark plus **"Current selection"** text for the committed item. Mere hover must not commit or falsely set selection. Keep "Current project" distinct from "Current selection".

For the type-first variant, supply: **"Type a project name. Press Down Arrow to browse all projects."** A visible opening affordance must also exist. Recommended status messages are "50 projects available", "12 matching projects", "50 more projects loaded", and "No matching projects". Do not duplicate announcements already produced by the chosen library.

**Case study:** Adobe finds screen-reader differences despite following the pattern and adds targeted live announcements, avoiding duplicate announcements on other screen readers. [17] This makes real-device testing more valuable than an ARIA-attribute checklist alone.


## 5. Fix The Border Token And Keep A Measured Solid Focus Ring

The reported border is a likely AA failure **if it is necessary to identify the field**. WCAG does not require every decorative border to meet 3:1; it requires necessary control and state information to do so. [2] The supplied blue outline is preferable to an imperceptible glow, but its color and surrounding surfaces must be measured before claiming conformance.

| Criterion | Level and exact requirement | Component consequence |
|---|---|---|
| SC 1.4.11 Non-text Contrast | AA; necessary component/state information **3:1 against adjacent colors**, with specified exceptions. [2] | Measure the identifying border, active/selected indicators and focus treatment |
| SC 1.4.3 Contrast Minimum | AA; ordinary text **4.5:1**; large text **3:1**. [44] | Project names, IDs, hints and status text need text contrast |
| SC 2.4.7 Focus Visible | AA; visible keyboard focus. [4] | Do not replace the outline with an invisible glow |
| SC 2.4.11 Focus Not Obscured Minimum | WCAG 2.2 AA; focused component is not entirely hidden by author-created content. [7] | Check sticky headers, dialogs and popup positioning |
| SC 2.4.13 Focus Appearance | WCAG 2.2 AAA; qualifying area at least a **2 CSS px perimeter**, with **3:1 focused/unfocused contrast at the same pixels**. [8] | A useful design target, but not an AA requirement |

**Important correction:** SC 2.4.11 is not the rule requiring a two-pixel focus ring. SC 2.4.13 concerns area and state-change contrast, not just "blue against white".

### A concrete white-surface specification

Use a **1 px `#6B7280` border**, **2 px `#2563EB` focus outline**, and **2 px white offset/spacer** on `#FFFFFF`. Calculated sRGB contrast is **4.83:1** for the border and **5.17:1** for the blue against white. Use `#4B5563` for secondary text, calculated at **7.56:1** against white. These calculations support this specific surface combination, not every theme or surrounding background.

Keep the brand glow as a decorative layer outside the solid indicator. In forced-colors mode, use a visible system-color outline and do not rely on box-shadow. Ensure ancestors do not clip the outline. An inner dark border plus outer brand ring is an alternative; choose the offset solid ring for this release because it is easier to review consistently.

**Case study:** GOV.UK uses yellow and black together, including a thickened black border for focused form inputs. Atlassian defines a **2 px** focused-border token paired with its focused-color token. [23][29] Both preserve an identity while making focus a functional design decision, rather than treating the brand effect as sufficient by itself.

### Truncation, IDs and rollout

Use **16 px project names**, **14 px IDs**, **12 px horizontal row padding**, and a **12 px name/ID gap**. Let the name flex with `min-width: 0`; keep the ID non-shrinking. Preserve the full name in the accessible text, and show the full selected name in a wrapping summary below the field before upload. Keep IDs: duplicate names make a secondary discriminator valuable, but user testing should establish whether researchers actually recognize them. Google Cloud explicitly allows non-unique project names while IDs are unique. [35]

Carbon recommends single-line truncation and accessible tooltips. [24] A tooltip must work on active-row keyboard navigation as well as hover, remain hoverable and persistent where required, and be dismissible when it obscures content. Do not rely on HTML `title` alone. [18]

Introduce semantic tokens such as `border.input` and `focus.ring`, migrate the upload picker first, and inspect other controls before enabling the new token across the portal. Atlassian documents applying and testing tokens across an experience before release, with manual review of automated migration. [22] A staged feature-flag rollout is the recommended implementation, not a documented universal migration rule.


## 6. Use A Persistent Label And Different Hints For Each Variant

Keep the label **"Existing project"**. The upload-flow heading can explain the action; the field label should identify the required value. Make the searchable behavior visible through an opening indicator and immediate filtering, then use a short hint for the distinction users cannot infer.

GOV.UK recommends visible labels, short direct sentence-case wording, and a short hint relevant to most users. It explicitly rejects placeholders as substitutes for labels, hints or examples. Its example connects the hint using `aria-describedby`. [30]

### Six proposed hints

| Variant | Option | Exact wording |
|---|---|---|
| Open-on-click | **Recommended** | **Choose a project or type part of its name** |
| Open-on-click | Alternative 2 | Select a project from the list or search by name |
| Open-on-click | Alternative 3 | Browse recent projects or type to find another |
| Type-first | **Recommended** | **Type part of a project name to find it** |
| Type-first | Alternative 2 | Start typing to search your projects |
| Type-first | Alternative 3 | Search by project name, then choose a match |

All six avoid region-specific spelling and mouse-only instructions. The third browse-first option is appropriate only while the empty-query list genuinely represents recent projects. Do not change the noun to "report" without also adapting the accessible label and state messages in the later assistant picker.

For the type-first control, add an accessible instruction: **"Press Down Arrow to browse all projects. Use arrow keys to move and Enter to choose."** Put the equivalent instruction in a visible help treatment as well, not only in a screen-reader-only string. This exposes the empty-query browse escape hatch without falsely saying that typing is mandatory.

### Placement and division of responsibility

Place the visible label above the field, with **8 px** between label and control. Place the hint below with **8 px** separation and associate it through `aria-describedby`. These spacing values are recommended component tokens. Above-field hints are also valid when DOM order and association are clear; below-field placement preserves Voxpopme's current layout and gives a consistent error/help area.

Use **no placeholder by default**. A placeholder disappears during typing and should not carry an instruction users need later. [30] Before ID search ships, add the temporary note **"Search uses project names only; project ID search is not available yet"**, also described by the input. Remove that limitation note when the backend actually supports IDs.

| Surface | Its job | Exact example |
|---|---|---|
| Label | Identify the value | Existing project |
| Hint | Explain browse/search behavior | Choose a project or type part of its name |
| Placeholder | Optional, nonessential illustration | None |
| Pending state | Explain waiting | Loading projects... |
| No matches | Explain a completed unsuccessful search and recovery | No projects match 'customer diary'. Try another name or clear your search. |
| Empty account | Explain absence of eligible entities | You have no projects available for uploads. |
| Error | Explain failure, not absence | We couldn't load projects. Try again. |

The text should explain different situations, not repeat the same instruction three times. Keep error and empty-state recovery buttons separately actionable.

**Comparable enterprise tone:** Polaris exposes loading and empty-state configuration; GOV.UK's autocomplete exposes configurable no-results and short-query announcements. [12][19] Voxpopme should retain that brevity while replacing generic "options" with "projects". These examples show conventions, not a proven ideal word count.


## 7. Use A Mobile Tray That Fits The Visible Keyboard Viewport

Desktop use is primary, but a dropdown that passes desktop inspection can fail when a software keyboard covers its lower half. The question is available interaction space, not merely whether the CSS fits a narrow screen.

**Case study:** Adobe moves its small-screen ComboBox into a tray and places a search input inside it. During testing, the iOS keyboard covers the tray's bottom half and prevents reaching hidden items. Adobe uses `visualViewport.height` and its resize event to adapt to the usable space. [17]

The mechanism is a mismatch between layout assumptions and the actually visible viewport. The lesson for Voxpopme is to size the scrollable picker from available space after the keyboard opens, not a fixed fraction of a nominal screen height. Adobe's report concerns the browsers it tested; do not assume the historical Safari behavior describes every current browser unchanged.

### Exact responsive proposal

| Presentation | Recommended activation and layout | Trade-off |
|---|---|---|
| Desktop, width **640 CSS px or greater** | Nonmodal anchored popup; maximum **264 px** option viewport, reduced to space available; input retains DOM focus | Efficient in-place selection, but popup positioning and clipping need tests |
| Narrow screen, width **below 640 CSS px** | Deliberate tap or keyboard activation opens a modal bottom tray with label, search, results and Close | More usable scrolling area, but requires dialog focus management |
| Tray leaves less than **176 px** for results | Use a full-screen picker dialog sized to the visible viewport | Best space for four 44 px rows, but a greater context change |

The breakpoint and 176 px threshold are proposed implementation rules, not findings that those values are optimal. Ordinary Tab focus on the mobile trigger must not unexpectedly open a modal dialog. Give the trigger an accessible name that includes the current value; on activation, focus the tray's search input, and return focus to the trigger on close.

For a dialog presentation, use modal-dialog semantics and focus containment. For the mobile dialog, use a tested dialog implementation with focus inside it rather than reusing the original desktop input as its active-descendant controller. This is a presentation-specific implementation recommendation. Do not copy desktop attributes mechanically onto a separate mobile modal.

### Touch, scrolling and targets

Use **44 x 44 CSS px minimum hit areas** for the opening affordance, clear control and Close, plus **44 px minimum row height** with the whole row selectable. With full-height non-overlapping rows, use **0 px required inter-row gap**; separate action buttons with **8 px** as a product default.

WCAG 2.2 SC 2.5.8 is **24 x 24 CSS px at AA**, with spacing and other exceptions. Forty-four pixels is this report's stronger usability target, not the AA minimum. [9] Tiny visible icons may sit inside the larger target.

Keep search and Close outside the scrolling results. Contain overscroll within the tray, preserve its scroll position on append, and do not turn ordinary vertical scrolling into a row selection. Load-more needs the same keyboard and screen-reader path as desktop. Test with the keyboard open, device rotation, pinch zoom, long labels and both VoiceOver and TalkBack. A tray is recommended because it provides deliberate space for browsing; a cramped inline dropdown is acceptable only if those tasks remain reliably usable.


## 8. Standardize The Interaction Contract, Not Every Data Source

The shared component should own input mechanics, accessibility and request lifecycle. Each product use should supply its entities and eligibility rules. A later report picker in an AI assistant should not independently reinvent focus, loading or error handling.

### Recommended API boundary

| Capability | Mature-system precedent | Fixed in the shared component | Configurable per use |
|---|---|---|---|
| Identity and display | MUI separates input and selected value. [20] | Stable keys; query distinct from committed value | `getId`, `getLabel`, secondary identifier |
| Remote data | Atlassian `loadOptions`; Adobe abortable loading. [13][37] | Cancellation, stale-response rejection, state machine | `loadOptions({query,cursor,pageSize,signal,scope})` |
| Pagination | Adobe cursor/load-more handling. [37] | Append deduplication, scroll preservation, keyboard boundary | Server cursor and authorized filtered total |
| Trigger variant | Adobe `input`, `focus`, `manual`. [11] | Documented browse-first/type-first contracts | Which approved variant a use selects |
| Groups | Atlassian and Polaris support grouping. [13][12] | Accessible headings; groups are not options | Current, Recent, Favorites, owner or folder |
| Clearable | Atlassian `isClearable`. [13] | Accessible clear target and explicit reset event | `clearable`, consistent with required selection |
| Disabled | Adobe disabled input/options. [11] | No activation; correct semantics | Eligibility and reason text |
| Read-only | Adobe `isReadOnly`. [11] | Value can be read/copied; no editing or opening | Whether the surrounding workflow is locked |
| Create-new | Polaris supports a separate action prop. [12] | No nested interactive button inside an option | Optional external creation action and permission |
| Multi-select | Atlassian exposes separate multi behavior. [13] | Separate documented interaction model | Not enabled in the upload picker |

The actionable distinction is between approved configuration and unrestricted overrides. Teams may change nouns, grouping, data scope and creation permissions. They should not override ARIA roles, commit-on-blur behavior, contrast, loading announcements or keyboard semantics through a local styling or event prop.

For this release expose `valueId`, `onChange`, `inputValue`, `onInputChange`, `open`, `onOpenChange`, `loadOptions`, `selectedItem`, `required`, `disabled`, `readOnly`, `clearable`, `label`, `hint`, and a controlled state-message dictionary. Keep rendering customization constrained so an option cannot become an arbitrary interactive panel.

### Storybook as an executable contract

Create named stories for: closed empty; closed selected; pointer-open; keyboard-active; filtered; initial loading; loading more; all results loaded; no matches; no projects; first-load error; filter error; load-more error; retry success; duplicate names; long names; numeric query without ID support; archived selection; lost access; disabled; read-only; type-first; mobile tray; forced colors; and zoom/reflow.

Every story needs deterministic data and delay/error fixtures, an interaction sequence, and expected focus/announcement behavior. Include a second-page story with an active last-row option and a stale-response story where the older query returns last.

**Case study:** Storybook's accessibility testing audits the rendered DOM and labels some checks incomplete for manual confirmation. It is a first QA layer, not complete conformance. [28] Combine unit tests for matching/state transitions, browser interaction tests for keys and races, visual regression for every theme/state, axe checks for static violations, and manual screen-reader/voice testing. A green automated report cannot verify that a researcher uploaded into the intended project.


## 9. Recover From Failures Without Changing The Upload Destination

Model at least `closed`, `loadingInitial`, `ready`, `searching`, `loadingMore`, `emptyResults`, `emptyAccount`, and each error phase separately. "No matches" is a successful search with zero eligible results; an API failure is not an empty result set.

| Situation | Exact recommended message | Recovery and state preservation |
|---|---|---|
| First load fails | **We couldn't load projects. Try again.** | Retry the same scope; keep the field usable and any valid committed selection |
| Load more fails | **We couldn't load more projects. Try again.** | Keep loaded rows, active ID and scroll position; retry the failed cursor |
| Filter fails | **We couldn't search projects. Try again.** | Keep the query; retry it; do not offer stale results as if they match |
| Selected project deleted | **This project is no longer available. Choose another project.** | Invalidate the destination and require a replacement |
| Selected project archived | **This project is archived and cannot receive uploads. Choose another project.** | Apply only if Voxpopme's upload policy actually prohibits archived destinations |
| Access lost | **This project is no longer available to you. Choose another project.** | Remove inaccessible details from suggestions; invalidate the destination |
| No eligible projects | **You have no projects available for uploads.** | Offer Create project if permitted; otherwise explain how to request access |

Retry buttons belong in the field's status/error area, **outside the listbox**, and must be reachable by normal keyboard navigation. APG warns that listboxes do not provide an accessible structure for nested buttons, links or checkboxes. [36] Do not make an error row a fake selectable project.

### Stop races and accidental re-selection

Abort the old request on a new query, and also reject responses whose request token does not match the latest query and account scope. Reset the cursor on query change. Adobe's async-list model explicitly supports abort signals, loading/error states and cursor pagination. [37]

While a new query is pending, remove stale choices from interaction rather than letting an old result commit. Show the pending state after **500 ms**, with the input still editable. Do not auto-activate the first returned row. When appending, preserve the active ID; when replacement removes that ID, clear the active descendant and announce the updated result count.

For pointer interaction, capture the option's ID at pointer-down and accept pointer-up only for that same still-eligible item. Never resolve a click from a mutable row index. Avoid reordering an empty-query list underneath an open interaction. These are engineering safety recommendations addressing the supplied risk scenario, not observed Voxpopme failures.

### Handle identity and unusual labels deliberately

Use project IDs for keys, values and authorization checks. Display duplicate names with their ID; add an owner or workspace only when it is needed to disambiguate. Do not search only one fetched page. Preserve one-character names, emoji, accents and non-Latin strings; do not require two Latin letters to search. During IME composition, wait for `compositionend` before running the debounced search, and do not treat its Enter key as selection.

**Case study:** the GOV.UK audit finds Dragon dictation inserting text in a way the autocomplete does not expect, and a last-minute change breaking JAWS/NVDA behavior. [31] This reveals that keyboard events alone do not represent every input method.

Finally, revalidate project existence, upload eligibility and authorization on the server when the upload is submitted. Never silently substitute the current or next project. An edited but uncommitted query is not a valid destination; disable submission while that ambiguity remains, or cancel the edit and visibly restore the committed selection.


## 10. Test Eight Professionals, Including Retrieval And Recovery

Run a **60-minute formative session with 8 research professionals**. Recruit **2** who manage thousands of projects, **4** who manage hundreds and **2** with smaller collections. Include experienced keyboard-only and screen-reader users within those groups where possible; do not use inexperienced participants pretending to have an access need.

NN/g supports small qualitative studies but separates them from quantitative comparisons; its quantitative guidance says at least 20 participants, with more needed for tighter intervals. [27] Eight provides a useful problem-finding signal. It cannot establish a reliable population preference or prove a low error rate. A later powered comparison needs a baseline variance and meaningful target effect, not a generic magic sample size.

### Tasks and measures

| Task | What it tests | Record |
|---|---|---|
| Upload to the project currently open | Pinning and context versus committed selection | Correct final project ID; accidental acceptance |
| Find a recently used project without its full name | Recognition and usefulness of recents | Completion, scans, scrolls, search attempts |
| Find a nonrecent project using a middle-name fragment | Contains/token matching and scope | Time, query reformulations, missed matches |
| Choose between identical names with different IDs | Disambiguation and truncation | ID verification and wrong selection |
| Reach the second page using keyboard only | Pagination and focus continuity | Boundary behavior, announcements, lost focus |
| Recover from a failed search, then finish the upload | Error versus empty-state understanding | Retry discovery, retained query, correct upload |

Use different but equivalently structured projects in each variant to reduce learning. **Four participants start with browse-first and four with type-first**; each completes the six tasks in both variants. Ask neutral questions and do not teach the interaction before the first attempt. Include a numeric-ID expectation probe before the backend change, but do not count an explicitly unsupported capability as an unexplained usability failure.

Measure task completion without help, time to correct selection, wrong-project commitments, wrong-project uploads, query changes, zero-result searches, pages loaded, and recovery success. Collect a **7-point ease rating** after each task and ask which result they believed was selected. Treat preference as secondary to correct assignment.

### Specific accessibility scripts

1. **Keyboard:** Tab to the field; confirm visible focus without automatic selection. Down Arrow opens; arrows move; Enter chooses; Escape cancels; Tab leaves. Test Space, Home, End and editing shortcuts. At option 50, load and reach option 51 without a reset or wrap.
2. **Screen reader:** run **NVDA with Chrome**, **JAWS with Edge**, and **VoiceOver with Safari**. Hear label, hint, combobox and expansion state; type a query; verify one result-count update; navigate names plus IDs; select; reopen and cancel without altering the destination.
3. **States:** test initial loading, no matches, load-more failure, Retry, and a selected project becoming unavailable. Confirm messages arrive without moving focus and recovery controls are reachable.
4. **Mobile and other input:** VoiceOver on iOS Safari, TalkBack on Android Chrome, Dragon dictation on a supported desktop setup, **400% zoom**, and forced-colors mode.

**Observed failure backbone:** GOV.UK's testers uncover wrapped-label confusion under **12-times magnification**, dictation failures and screen-reader regressions. The Ant issue shows keyboard pagination resetting scroll. [31][34] Prioritize these tasks over a pleasant first-page mouse demo.

Recommended release gates are **zero wrong-project uploads**, **zero keyboard traps**, and **all six core tasks completed without moderator intervention by each participant**. These are strict formative gates, not proof that production error probability is zero.


## 11. Borrow Discovery Layers, Not Unverified Competitor Widgets

The comparable landscape favors several retrieval routes: names for known items, recents for interrupted work, favorites for repeated work, and hierarchy for items users cannot name. Those routes can coexist without turning every selector into a large navigation application.

| Product or family | Documented mechanism | Scope and limitation | Transfer to Voxpopme |
|---|---|---|---|
| Dovetail | Favorites, pinned content, project pages, folders and cross-type Quick Search. [33] | Research-content discovery, not evidence of its upload project combobox | Provide a broader browse/search route beyond the inline picker |
| Tableau Mobile | Home shows favorites, recents and collections; Explore navigates project hierarchy; search groups results by content type. [47] | Analytics-content discovery on mobile | Useful precedent for grouped report choices in the later assistant |
| Jira Core | Backend queried while typing; only matching results returned; whitespace-only queries ignored. [41] | Documented 8.21 release behavior, not a claim about every current Jira picker | Search the authorized collection, not just downloaded projects |
| GitHub Projects | Recently viewed/created projects, title text and combinable filters. [40] | Project discovery across ownership contexts, not upload assignment | Separate simple selection from advanced discovery |
| Google Cloud | Names need not be unique; IDs distinguish projects; picker can show an environment-related indicator. [35] | This source does not establish current recents or favorites tabs | Keep a secondary identifier and consider meaningful context tags |
| AWS Console | Search results and recently visited services can feed favorites; favorites are reorderable. [38] | Service/application navigation, not project assignment | Favorites can represent deliberate importance rather than accidental recency |

The useful market trend is layered discovery, not a proven universal widget. None of these sources provides comparative market share or a controlled Voxpopme-equivalent picker benchmark.

### Two mechanisms worth separating

**Jira scale case:** its release notes describe moving project search to backend queries as users type. [41] This removes the need to deliver every project before searching and makes the matching collection larger than the loaded page. For Voxpopme, the important requirement is full authorized-scope searching. The trade-off is dependency on network performance and correct cancellation; hiding all empty-query suggestions is not automatically necessary.

**Dovetail/Tableau discovery case:** Dovetail combines a project inventory with favorites and cross-type search; Tableau combines recents, collections and hierarchy. [33][47] These mechanisms support both known-name retrieval and contextual recognition. For a later AI-assistant report picker, group reports by a useful ownership/project context while retaining direct name search. For the upload flow, keep these richer tools one step away rather than inside every row.

### What public complaints actually support

The Ant Design pagination thread reports expensive all-at-once loading and later keyboard scroll resets. [34] A Jira community post describes a project missing from discovery and an inaccessible issue; a respondent suggests missing Browse Project permission, but no confirmed resolution appears. [32]

These are **anecdotes**, not prevalence estimates. They identify concrete risks: "not found" can mean permissions rather than matching failure, and mouse-successful pagination can remain keyboard-broken. The cited evidence does not support claims that users broadly dislike recents, that a particular competitor's picker is best, or that research-platform upload pickers all work alike.


## 12. Challenge Recency, Search Completeness And Apparent Selection

The biggest risk is not that the control looks unfamiliar. It is that it can appear to identify a destination more clearly than it actually does. Upload assignment has a stronger correctness requirement than ordinary navigation because the wrong project receives research material.

| Assumption to challenge | Potential harm | Recommended control | Evidence status |
|---|---|---|---|
| Current project belongs first in every result set | A nonmatching destination remains prominent during search | Pin only for empty queries; label context explicitly | Product risk hypothesis |
| Recent means relevant | An old but recently opened project displaces the intended one | Use personal selection recency; freeze order; test recognition tasks | Frequency-menu research is supportive but not recency-specific. [46] |
| Highlighted means selected | Enter or blur sends files to a suggestion the user did not commit | Keep committed ID separate; no autoselect or commit-on-blur | APG distinguishes exploratory navigation and preserving a prior choice. [5] |
| Displaying an ID means it is searchable | Numeric searches seem broken or imply nonexistent projects | Disclose name-only search until backend support exists | Supplied limitation; no demonstrated user prevalence |
| "Showing 50 of 482" explains scope | Users mistake downloaded results for visible rows or matching totals | Use loaded/matching wording and authorized totals | Recommended clarification |
| A good mouse demo means accessibility works | Keyboard pagination, voice input or announcements fail | Explicit boundary/input-method testing | Documented Ant and GOV.UK failures. [34][31] |
| An API failure equals no matches | Users abandon a valid project or create a duplicate | Phase-specific error and Retry | Recommended state distinction |
| A 2 px blue ring proves WCAG compliance | Ring color, clipping or state-change contrast still fails | Measure against actual surfaces and test obscuring | WCAG evaluates more than nominal thickness. [8][7] |

These controls address different failure mechanisms; a better search algorithm cannot fix a permission problem or an accidental commitment.

### Privacy and performance threat scenarios

Recent-project names may disclose confidential client or study context during screen sharing. A background recents request can also return projects outside the current account if cache keys omit scope. These are plausible threat scenarios, not reported Voxpopme incidents.

Scope requests and caches by user, account and permission context. Clear cached suggestions on account switch and logout. Do not include inaccessible project names in no-match explanations or total counts. Do not log raw queries or project titles by default; log event types, timings, query length, query kind and stable permitted identifiers instead. Keep a type-first option for sensitive workflows, but recognize that opening the browse list still exposes authorized names.

**Failure case:** the Jira forum user cannot find a project and receives a possible deletion/permission error. A respondent points to Browse Project permission, without a confirmed outcome. [32] This demonstrates why the UI should distinguish what it knows from what it cannot reveal. "No matches" should not become "This project does not exist".

### What a skeptical auditor will inspect first

Expect inspection of the necessary **3:1 control border**, focus visibility and clipping, role/state relationships, label/help associations, mounted active descendants, text contrast, keyboard exit, loading/no-match announcements, hover-only full labels, and usable touch targets. These concerns follow the contrast, combobox, status-message, tooltip and target-size requirements already discussed. [2][5][10][18][9]

Cut fuzzy matching, inline creation, multi-select, configurable per-screen keyboard behavior and premature virtualization from the first release. Do not cut explicit selection, accessible retry, request-order protection or complete search scope. If paginated keyboard loading cannot be made reliable, ship a clearly stated 50-result cap with narrowing instructions rather than a broken promise of browsing everything.


## Synthesis

The most important tension is between **recognition and recall**. NN/g explains that recognition benefits from more retrieval cues, while recall requires users to retrieve details from memory. [45] Recents and contextual IDs can provide recognition cues; a type-first interface demands that users remember enough of a name. But recognition becomes unreliable if unrelated recents dominate, results move, or visually active suggestions are mistaken for committed destinations.

| Strategy and relevant entities | Mechanism | Best scope | Main trade-off | Evidence strength |
|---|---|---|---|---|
| Browse-first; Voxpopme, Carbon, configurable GOV.UK/MUI | Show an initial choice set, then filter | Repeated work and uncertain name recall | Exposure of names and an extra data load | Documented patterns; Voxpopme ordering remains a hypothesis. [24][19][14] |
| Type-first/backend search; Jira, Adobe input trigger | Query narrows before results appear | Known-name retrieval and large remote collections | Blank-state discoverability and network dependency | First-party implementation evidence, not universal usability superiority. [41][11] |
| Alphabetical and literal matching; Carbon, USWDS, MUI | Stable scanning plus predictable string retrieval | Named resources with recognizable labels | Weak help when users cannot recall the name | Explicit guidance and filter conventions. [24][1][20] |
| Recents/favorites/hierarchy; GitHub, Dovetail, Tableau, AWS | Personal history, deliberate importance and contextual browsing | Returning to work and discovering unfamiliar content | More metadata and navigation complexity | Documented workflows; split-menu evidence concerns frequency, not these exact pickers. [40][33][47][38][46] |
| Configurable enterprise components; Atlassian, Polaris, Ant | Separate data-loading, grouping and rendering concerns | Reuse across several product contexts | Local overrides can break the shared contract | Mature documented APIs; not automatic accessibility assurance. [13][12][16] |
| Select primitive versus editable combobox; Radix | Button selection with typeahead | Simple noneditable selection | Not equivalent to a typed async search field | Documented distinction in capabilities. [15] |
| Mobile tray; Adobe | Allocate usable space around the software keyboard | Narrow-screen filtering and scrolling | Dialog focus management and context change | Detailed engineering/testing case. [17] |
| IDs and context; Google Cloud | Separate human names from stable identity | Duplicate names and operational certainty | Identifier clutter and an expectation of ID lookup | First-party identity documentation. [35] |

Across these approaches, the consistent requirement is stable identity, not identical presentation. A resource switcher can tolerate exploratory movement; an upload destination must make commitment explicit. A component library can expose many features while the product deliberately enables only a few.

There are three non-obvious divergences. First, ecommerce advice to avoid internal suggestion scrolling conflicts with enterprise libraries supporting infinite loading; the tasks differ, so test the resource-selection cost rather than importing either position wholesale. [3][11] Second, adaptive menu research supports frequent choices, while Carbon favors alphabetical options; combine a short familiar group with a stable searchable collection rather than continuously rearranging everything. [46][24] Third, adding visible IDs improves disambiguation but can worsen expectations while ID search is absent. The display/search contract needs to advance together.

**Decision-ready conclusion:** the current component is the right foundation, provided Voxpopme treats it as a reliable entity-selection system rather than an autocomplete text field. Ship a narrow single-select contract, measure retrieval and wrong-destination risk, and expand to the assistant's report picker only after the shared state, focus and authorization behavior has passed the same tests.


## Prioritised Shipping And Reuse Plan

### Before shipping

1. **P0: Fix identifying contrast.** Replace the reported low-contrast border; keep the solid focus outline. Adopt the proposed white-surface tokens, then test real surfaces, zoom, forced colors and clipping. Necessary control/state information needs 3:1. [2]
2. **P0: Make commitment unambiguous.** Store a committed project ID separately from input text and active suggestion. Require Enter or deliberate pointer selection; no auto-commit on blur. Revalidate the destination and permissions at upload submission.
3. **P0: Complete keyboard and screen-reader behavior.** Implement the documented roles/states, mounted active descendants, visible active-row scrolling, Escape cancellation and normal Tab exit. Test option 50 to 51 and every error state. [5][6][34]
4. **P0: Protect async state.** Abort obsolete searches, reject stale responses, reset cursors per query, preserve scroll/active ID on append, and provide phase-specific Retry outside the listbox.
5. **P0: Make search honest.** Search the full authorized collection by name; disclose missing ID lookup. Numeric text must not be silently interpreted as supported ID search.
6. **P1: Clarify labels and counts.** Use "Existing project", the selected variant's hint, "50 of 482 projects loaded", filtered totals after searching, and distinct no-match/no-project/error messages.
7. **P1: Set bounded defaults.** Keep 50 fetched; use 44 px rows, a 264 px desktop list viewport, 200 ms debounce and a 500 ms delayed spinner. Treat these as monitored product defaults.
8. **P1: Run the eight-person formative study and accessibility matrix.** Block release for wrong-project uploads, keyboard traps or failed core tasks. If keyboard pagination remains unreliable, use the explicitly disclosed 50-match cap instead.

### Later, in priority order

1. **Add ID search**, with exact-ID matches first, before describing IDs as searchable anywhere in the UI.
2. **Test Current / Recent / All A-Z grouping and favorites.** Start with **8 personal recent entries** in the proposed grouped variant; retain the simpler ordering unless it improves actual retrieval.
3. **Add a richer Browse all route** for people who cannot remember names, using owner/folder/account context where available.
4. **Optimize page size and rendering from measurements**, not total account size alone. Add virtualization only after profiling and assistive-technology testing.
5. **Consider labelled typo-tolerant suggestions and permission-aware creation** only if failed literal searches reveal a real need.
6. **Roll out semantic border/focus tokens across the portal** in reviewed stages, then migrate the assistant's report picker to the approved interaction contract.

### Rules for every future use

- **One identity, one commitment model:** stable entity keys; typing, hover and arrow focus never trigger business actions.
- **Two approved trigger variants:** browse-first and type-first, both with deliberate keyboard opening and matching instructions.
- **One accessibility contract:** labeled input, truthful expansion state, valid active descendants, usable contrast, status announcements and no nested interactive option content.
- **One async contract:** complete authorized search scope, cancellation, stale-response protection, cursor reset, stable append and accessible recovery.
- **One wording contract:** entity-specific labels; no placeholders as essential help; separate empty, unmatched and failed states. [30]
- **One test contract:** deterministic Storybook states, unit and browser tests, visual regression, automated checks and manual assistive-technology review before reuse. [28]
- **One measurement contract:** task success means the intended entity receives the completed operation. Track wrong-destination events, selection time, query reformulations, pages loaded, recovery success and latency by variant and collection size, without raw-title/query logging by default.

## References

1. *Combo box | U.S. Web Design System (USWDS)*. https://designsystem.digital.gov/components/combo-box/
2. *Understanding Success Criterion 1.4.11: Non-text Contrast | WAI | W3C*. https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html
3. *9 UX Best Practice Design Patterns for Autocomplete Suggestions (Only 19% Get Everything Right) – Baymard*. https://baymard.com/blog/autocomplete-design
4. *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/
5. * Combobox Pattern | APG | WAI | W3C*. https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
6. * Editable Combobox With List Autocomplete Example | APG | WAI | W3C*. https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/
7. *Understanding Success Criterion 2.4.11: Focus Not Obscured (Minimum) | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html
8. *Understanding Success Criterion 2.4.13: Focus Appearance | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html
9. *Understanding Success Criterion 2.5.8: Target Size (Minimum) | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
10. *Understanding Success Criterion 4.1.3: Status Messages | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html
11. *ComboBox – React Aria*. https://react-spectrum.adobe.com/react-aria/ComboBox.html
12. *Autocomplete — Shopify Polaris React*. https://polaris-react.shopify.com/components/selection-and-input/autocomplete
13. *Select - Examples - Components - Atlassian Design*. https://atlassian.design/components/select
14. *Autocomplete API - Material UI*. https://mui.com/material-ui/api/autocomplete/
15. *Select*. https://www.radix-ui.com/primitives/docs/components/select
16. *Select - Ant Design*. https://ant.design/components/select
17. *Creating an accessible autocomplete experience – React Spectrum Blog*. https://react-spectrum.adobe.com/blog/building-a-combobox.html
18. *Understanding Success Criterion 1.4.13: Content on Hover or Focus | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html
19. *GitHub - alphagov/accessible-autocomplete: An autocomplete component, built to be accessible. · GitHub*. https://github.com/alphagov/accessible-autocomplete
20. *React Autocomplete component - Material UI*. https://mui.com/material-ui/react-autocomplete
21. *Response Time Limits: Article by Jakob Nielsen - NN/G*. https://www.nngroup.com/articles/response-times-3-important-limits/
22. *Overview - Migrate to tokens - Atlassian Design*. https://atlassian.design/components/tokens/migration-guide
23. *Understanding focus state styles – GOV.UK Design System*. https://design-system.service.gov.uk/get-started/focus-states
24. *Dropdown | Carbon Design System*. https://www.carbondesignsystem.com/building-blocks/core/components/dropdown/guidelines
25. *Algorithmic and user study of an autocompletion algorithm on a large medical vocabulary - ScienceDirect*. https://www.sciencedirect.com/science/article/pii/S153204641100164X
26. *Debounce sources*. https://www.algolia.com/doc/ui-libraries/autocomplete/guides/debouncing-sources
27. *How Many Test Users in a Usability Study? - NN/G*. https://www.nngroup.com/articles/how-many-test-users/
28. *Accessibility tests | Storybook docs*. https://storybook.js.org/docs/writing-tests/accessibility-testing
29. *Overview - Border - Atlassian Design*. https://atlassian.design/foundations/border
30. *Text input – GOV.UK Design System*. https://design-system.service.gov.uk/components/text-input/
31. *What we learned from getting our autocomplete tested for accessibility – Accessibility in government*. https://accessibility.blog.gov.uk/2018/05/15/what-we-learned-from-getting-our-autocomplete-tested-for-accessibility/
32. [
	Question about browsing projects in Jira
](https://community.atlassian.com/forums/Jira-questions/Question-about-browsing-projects-in-Jira/qaq-p/2166391)
33. *Dovetail Usability FAQ: Navigation, Layout Changes, and Learning Curve*. https://dovetail.com/guides/dovetail-usability-faq
34. *Pagination in the <Select> component · Issue #12440 · ant-design/ant-design · GitHub*. https://github.com/ant-design/ant-design/issues/12440
35. *Create projects  |  Resource Manager  |  Google Cloud Documentation*. https://docs.cloud.google.com/resource-manager/docs/creating-managing-projects
36. * Listbox Pattern | APG | WAI | W3C*. https://www.w3.org/WAI/ARIA/apg/patterns/listbox/
37. *useAsyncList*. https://react-spectrum.adobe.com/react-stately/useAsyncList.html
38. *Adding favorites in the AWS Management Console - AWS Management Console*. https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/add-fave.html
39. *Accessible Rich Internet Applications (WAI-ARIA) 1.2*. https://www.w3.org/TR/wai-aria-1.2/#aria-setsize
40. *Finding your projects*. https://docs.github.com/en/enterprise-cloud@latest/issues/planning-and-tracking-with-projects/finding-your-projects
41. *Jira Core 8.21.x release notes | Atlassian Support | Atlassian Documentation*. https://confluence.atlassian.com/spaces/JIRACORE/pages/1097185145/Jira+Core+8.21.x+release+notes
42. *Workspaces in Power BI - Power BI | Microsoft Learn*. https://learn.microsoft.com/en-us/power-bi/collaborate-share/service-new-workspaces
43. *Alphabetical Sorting Must (Mostly) Die - NN/G*. https://www.nngroup.com/articles/alphabetical-sorting-must-mostly-die/
44. *Understanding Success Criterion 1.4.3: Contrast (Minimum) | WAI | W3C*. https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html
45. *Memory Recognition and Recall in User Interfaces - NN/G*. https://www.nngroup.com/articles/recognition-and-recall/
46. *Split menus: effectively using selection frequency to organize menus: ACM Transactions on Computer-Human Interaction: Vol 1, No 1*. https://dl.acm.org/doi/10.1145/174630.174632
47. *Explore Content on Tableau Mobile - Tableau*. https://help.tableau.com/current/mobile/mobile-user/en-us/tableau_mobile_explore.htm

