Summary
The "Add Member" field on the Teams page is a plain text input with no guidance. Users must know the exact username. Add autocomplete that shows matching users as you type.
Design
Data Source
- Fetch full user list from `GET /api/v1/users` once on input focus (lazy fetch, then cache)
- Client-side substring filtering — no per-keystroke API calls
- Refresh cache on each focus event to pick up new users
UI Pattern
Matching
- Case-insensitive substring match (`username.toLowerCase().includes(query)`)
- Highlight matched portion
Selection
- Click to select
- Arrow keys to navigate + Enter to select
- Escape closes dropdown
- Typing exact username + Enter still works (existing behavior preserved)
Already-Members
- Show existing team members dimmed (0.45 opacity, italic "already member" suffix)
- Non-selectable (`pointer-events: none`)
Empty State
- "No users found" in muted italic text
Accessibility
- `role="combobox"` on input, `role="listbox"` on dropdown, `role="option"` on items
- `aria-expanded`, `aria-activedescendant`, `aria-autocomplete="list"`
- Keyboard navigation fully supported
CSS Specs
```css
.autocomplete-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.autocomplete-item {
padding: 8px 12px;
color: #e2e8f0;
cursor: pointer;
font-size: 0.9rem;
border-bottom: 1px solid #334155;
}
.autocomplete-item:hover,
.autocomplete-item.selected {
background: #334155;
}
.autocomplete-match {
color: #3498db;
font-weight: 600;
}
.autocomplete-item.already-member {
opacity: 0.45;
cursor: default;
pointer-events: none;
}
.autocomplete-item.already-member::after {
content: " (already member)";
color: #64748b;
font-style: italic;
font-size: 0.8rem;
}
.autocomplete-empty {
padding: 12px;
color: #64748b;
text-align: center;
font-style: italic;
}
```
Implementation
JavaScript (`web/js/app.js`)
- ~100 lines, self-contained function
- Event listeners: `focus` (fetch/show), `input` (filter), `keydown` (arrows/enter/escape), `click` (select), `blur` with delay (close)
- Dropdown markup created dynamically
- Selected username populates the existing input field — existing form submission unchanged
CSS (`web/css/style.css`)
- ~30 lines for the dropdown, items, highlights, states
HTML (`web/index.html`)
- Add `position: relative` wrapper around the member input
- No other HTML changes needed (dropdown is dynamic)
Tests
- Functional test: add a valid user via autocomplete, verify member appears
- Functional test: attempt to add already-member, verify rejection
- Verify the input still works without autocomplete (direct typing + enter)
Docs
- Update any teams documentation to mention autocomplete behavior
Summary
The "Add Member" field on the Teams page is a plain text input with no guidance. Users must know the exact username. Add autocomplete that shows matching users as you type.
Design
Data Source
UI Pattern
Matching
Selection
Already-Members
Empty State
Accessibility
CSS Specs
```css
.autocomplete-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.autocomplete-item {
padding: 8px 12px;
color: #e2e8f0;
cursor: pointer;
font-size: 0.9rem;
border-bottom: 1px solid #334155;
}
.autocomplete-item:hover,
.autocomplete-item.selected {
background: #334155;
}
.autocomplete-match {
color: #3498db;
font-weight: 600;
}
.autocomplete-item.already-member {
opacity: 0.45;
cursor: default;
pointer-events: none;
}
.autocomplete-item.already-member::after {
content: " (already member)";
color: #64748b;
font-style: italic;
font-size: 0.8rem;
}
.autocomplete-empty {
padding: 12px;
color: #64748b;
text-align: center;
font-style: italic;
}
```
Implementation
JavaScript (`web/js/app.js`)
CSS (`web/css/style.css`)
HTML (`web/index.html`)
Tests
Docs