294 lines
10 KiB
Markdown
294 lines
10 KiB
Markdown
|
|
# RFC-003: Canonical Select Component
|
|||
|
|
|
|||
|
|
**Status:** 🟡 Draft — Under review
|
|||
|
|
**Tier:** 1 (Canonical)
|
|||
|
|
**Inherits From:** — (root component)
|
|||
|
|
**Author:** AI Agent
|
|||
|
|
**Date:** 2026-07-02
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. COGNITIVE PURPOSE
|
|||
|
|
|
|||
|
|
Select exists to allow users to choose exactly one option from a predefined list. It reduces cognitive load by eliminating free-form input when the valid options are known in advance.
|
|||
|
|
|
|||
|
|
**Design Goal:** The user can see available options, make a selection, and confirm their choice without typing or guessing.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. WHEN TO USE
|
|||
|
|
|
|||
|
|
- Choosing from 5–20 predefined options
|
|||
|
|
- Selecting a single value (country, status, category)
|
|||
|
|
- When options have clear labels
|
|||
|
|
- When free-form input would cause errors
|
|||
|
|
|
|||
|
|
## 3. WHEN NOT TO USE
|
|||
|
|
|
|||
|
|
- Fewer than 4 options (use Radio Group or Segmented Control)
|
|||
|
|
- More than 50 options (use Searchable Select or Autocomplete)
|
|||
|
|
- Multiple selections (use Multi-Select or Checkbox Group)
|
|||
|
|
- Boolean choice (use Switch or Checkbox)
|
|||
|
|
- Free-form entry (use Input)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. USER EXPECTATION
|
|||
|
|
|
|||
|
|
- Clicking the field reveals all options
|
|||
|
|
- Selected option is clearly indicated
|
|||
|
|
- Current selection is visible when closed
|
|||
|
|
- Options are searchable if list is long
|
|||
|
|
- Keyboard can navigate options
|
|||
|
|
- Disabled options are visually distinct
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. INTERACTION CONTRACT
|
|||
|
|
|
|||
|
|
| Event | Response |
|
|||
|
|
|-------|----------|
|
|||
|
|
| Click | Open dropdown, show options |
|
|||
|
|
| Click option | Select option, close dropdown |
|
|||
|
|
| Click outside | Close dropdown, keep selection |
|
|||
|
|
| Tab | Move focus to next field, close dropdown |
|
|||
|
|
| Arrow Down | Open dropdown or move to next option |
|
|||
|
|
| Arrow Up | Move to previous option |
|
|||
|
|
| Enter | Select focused option, close dropdown |
|
|||
|
|
| Escape | Close dropdown, revert to previous selection |
|
|||
|
|
| Type letter | Jump to first option starting with that letter |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. FAILURE BEHAVIOUR
|
|||
|
|
|
|||
|
|
| Scenario | Response |
|
|||
|
|
|----------|----------|
|
|||
|
|
| No option selected | Show placeholder, allow form submission if optional |
|
|||
|
|
| Required but empty | Show error on blur or submit |
|
|||
|
|
| Option disabled | Prevent selection, show disabled state |
|
|||
|
|
| Options load asynchronously | Show loading state, then populate |
|
|||
|
|
| Options fail to load | Show error, allow retry |
|
|||
|
|
| User types non-matching letter | No action, or show "no results" |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. ACCESSIBILITY CONTRACT
|
|||
|
|
|
|||
|
|
- **Label:** Every Select MUST have associated `<label>` or `aria-label`
|
|||
|
|
- **Role:** MUST use `role="combobox"` or native `<select>`
|
|||
|
|
- **Expanded:** MUST communicate open/closed state with `aria-expanded`
|
|||
|
|
- **Selected:** MUST communicate selected option with `aria-selected`
|
|||
|
|
- **Focus:** Focus MUST move to dropdown when opened, return to trigger when closed
|
|||
|
|
- **Screen reader:** MUST announce number of options, current selection, and position
|
|||
|
|
- **Keyboard:** MUST support all interaction contract keyboard events
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. AI CONTRACT
|
|||
|
|
|
|||
|
|
| Scenario | AI Choice |
|
|||
|
|
|----------|-----------|
|
|||
|
|
| 5–20 predefined options, single choice | Select |
|
|||
|
|
| < 4 options, single choice | Radio Group |
|
|||
|
|
| > 50 options | Searchable Select / Autocomplete |
|
|||
|
|
| Multiple selections | Multi-Select / Checkbox Group |
|
|||
|
|
| Boolean toggle | Switch / Checkbox |
|
|||
|
|
| Free-form entry | Input |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. TOKEN DEPENDENCIES
|
|||
|
|
|
|||
|
|
| Token | Semantic Reference | Usage |
|
|||
|
|
|-------|-------------------|-------|
|
|||
|
|
| `select-bg` | `surface-primary` | Background |
|
|||
|
|
| `select-border` | `border-default` | Default border |
|
|||
|
|
| `select-border-focus` | `brand-500` | Focus border |
|
|||
|
|
| `select-border-error` | `semantic-red-500` | Error border |
|
|||
|
|
| `select-text` | `text-primary` | Selected text |
|
|||
|
|
| `select-placeholder` | `text-muted` | Placeholder text |
|
|||
|
|
| `select-padding` | `space-3` | Internal padding |
|
|||
|
|
| `select-radius` | `radius-md` | Border radius |
|
|||
|
|
| `select-height-sm` | `space-8` | Small height (32px) |
|
|||
|
|
| `select-height-md` | `space-10` | Medium height (40px) |
|
|||
|
|
| `select-height-lg` | `space-12` | Large height (48px) |
|
|||
|
|
| `select-dropdown-bg` | `surface-primary` | Dropdown background |
|
|||
|
|
| `select-dropdown-border` | `border-default` | Dropdown border |
|
|||
|
|
| `select-dropdown-shadow` | `shadow-md` | Dropdown elevation |
|
|||
|
|
| `select-option-hover` | `surface-secondary` | Option hover background |
|
|||
|
|
| `select-option-selected` | `brand-50` | Selected option background |
|
|||
|
|
| `select-option-disabled` | `surface-secondary` | Disabled option background |
|
|||
|
|
| `select-error-bg` | `semantic-red-50` | Error background tint |
|
|||
|
|
| `select-disabled-bg` | `surface-secondary` | Disabled background |
|
|||
|
|
| `select-disabled-text` | `text-muted` | Disabled text |
|
|||
|
|
| `select-chevron` | `text-muted` | Chevron icon color |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. LAYOUT
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────┐
|
|||
|
|
│ Label (optional) │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ ┌─────────────────────────────────┐ │
|
|||
|
|
│ │ Selected option ▼ │ │
|
|||
|
|
│ └─────────────────────────────────┘ │
|
|||
|
|
│ ┌─────────────────────────────────┐ │
|
|||
|
|
│ │ Option 1 │ │
|
|||
|
|
│ │ Option 2 (selected) │ │
|
|||
|
|
│ │ Option 3 │ │
|
|||
|
|
│ │ Option 4 (disabled) │ │
|
|||
|
|
│ └─────────────────────────────────┘ │
|
|||
|
|
│ Helper text / Error message │
|
|||
|
|
└─────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **Label:** Above select, left-aligned, `text-sm`, `font-medium`
|
|||
|
|
- **Trigger:** Full width, padding `space-3`, chevron icon right-aligned
|
|||
|
|
- **Dropdown:** Full width of trigger, max-height 240px, scrollable
|
|||
|
|
- **Option:** Padding `space-2 space-3`, full width
|
|||
|
|
- **Helper/Error:** Below select, `text-xs`
|
|||
|
|
- **Gap:** `space-1` between label and select, `space-1` between select and helper
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. SIZES
|
|||
|
|
|
|||
|
|
| Size | Height | Font Size | Padding | Usage |
|
|||
|
|
|------|--------|-----------|---------|-------|
|
|||
|
|
| Small | 32px | 14px | `space-2` | Dense forms, tables |
|
|||
|
|
| Medium | 40px | 16px | `space-3` | Default, most forms |
|
|||
|
|
| Large | 48px | 18px | `space-4` | Mobile, touch-first |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. VARIANTS
|
|||
|
|
|
|||
|
|
| Variant | Visual | Usage |
|
|||
|
|
|---------|--------|-------|
|
|||
|
|
| Default | Border, background | Standard form select |
|
|||
|
|
| Ghost | No border, underline only | Minimal UI, filters |
|
|||
|
|
| Filled | Solid background, no border | High-contrast forms |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 13. STATES
|
|||
|
|
|
|||
|
|
| State | Visual | Motion |
|
|||
|
|
|-------|--------|--------|
|
|||
|
|
| Default | `select-border`, `select-bg` | — |
|
|||
|
|
| Hover | Border darkens | `duration-instant` |
|
|||
|
|
| Focus | `select-border-focus`, focus ring | `duration-instant` |
|
|||
|
|
| Open | Dropdown visible, chevron rotates | `duration-transition` |
|
|||
|
|
| Option hover | `select-option-hover` | `duration-instant` |
|
|||
|
|
| Option selected | `select-option-selected` | `duration-interaction` |
|
|||
|
|
| Option disabled | `select-option-disabled`, muted text | — |
|
|||
|
|
| Disabled | `select-disabled-bg`, `select-disabled-text` | — |
|
|||
|
|
| Error | `select-border-error`, `select-error-bg` | `duration-interaction` |
|
|||
|
|
| Loading | Spinner in trigger | `duration-attention` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 14. KEYBOARD
|
|||
|
|
|
|||
|
|
| Key | Action |
|
|||
|
|
|-----|--------|
|
|||
|
|
| `Tab` | Move focus to next field, close dropdown |
|
|||
|
|
| `Shift+Tab` | Move focus to previous field |
|
|||
|
|
| `Enter` / `Space` | Open dropdown or select focused option |
|
|||
|
|
| `Arrow Down` | Open dropdown or move to next option |
|
|||
|
|
| `Arrow Up` | Move to previous option |
|
|||
|
|
| `Escape` | Close dropdown, revert selection |
|
|||
|
|
| `Home` | Move to first option |
|
|||
|
|
| `End` | Move to last option |
|
|||
|
|
| Letter key | Jump to first option starting with that letter |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 15. TOUCH
|
|||
|
|
|
|||
|
|
- Touch target: Minimum 40px height (Medium size)
|
|||
|
|
- Touch target for options: Minimum 44px height
|
|||
|
|
- Active state: Brief background tint on option
|
|||
|
|
- Scroll: Native scroll within dropdown
|
|||
|
|
- No gesture support (no swipe to dismiss)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 16. MOTION
|
|||
|
|
|
|||
|
|
| Transition | Animation | Duration | Easing |
|
|||
|
|
|-----------|-----------|----------|--------|
|
|||
|
|
| Dropdown open | Fade + slide down | `duration-transition` | `ease-out` |
|
|||
|
|
| Dropdown close | Fade + slide up | `duration-transition` | `ease-in` |
|
|||
|
|
| Option hover | Background color change | `duration-instant` | `ease-out` |
|
|||
|
|
| Option select | Background highlight | `duration-interaction` | `ease-out` |
|
|||
|
|
| Chevron rotate | Rotate 180° | `duration-transition` | `ease-out` |
|
|||
|
|
| Error appear | Border + background tint | `duration-interaction` | `ease-out` |
|
|||
|
|
|
|||
|
|
**Reduced Motion:** Dropdown appears/disappears instantly (`duration-instant`). No slide animation.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 17. ANTI-PATTERNS
|
|||
|
|
|
|||
|
|
- ❌ **Select with < 4 options:** Use Radio Group instead
|
|||
|
|
- ❌ **Select with > 50 options:** Use Searchable Select or Autocomplete
|
|||
|
|
- ❌ **No placeholder or label:** User cannot infer purpose
|
|||
|
|
- ❌ **Disabled options without visual distinction:** User thinks they can select them
|
|||
|
|
- ❌ **Dropdown wider than trigger:** Breaks visual alignment
|
|||
|
|
- ❌ **Dropdown clipped by container:** Use portal/overlay to escape overflow:hidden
|
|||
|
|
- ❌ **No keyboard navigation:** Accessibility failure
|
|||
|
|
- ❌ **Changing options while open:** Causes focus loss and confusion
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 18. RELATED COMPONENTS
|
|||
|
|
|
|||
|
|
- **Input** — Free-form text entry
|
|||
|
|
- **Radio Group** — Few options, all visible
|
|||
|
|
- **Checkbox Group** — Multiple selections
|
|||
|
|
- **Multi-Select** — Multiple selections from dropdown
|
|||
|
|
- **Autocomplete** — Searchable selection with free-form input
|
|||
|
|
- **Search** — Input with suggestions
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 19. DDR REFERENCES
|
|||
|
|
|
|||
|
|
- **DDR-001:** Device Adaptive over Desktop First (affects sizing)
|
|||
|
|
- **DDR-002:** Information-level typography (affects label and option text)
|
|||
|
|
- **DDR-003:** Semantic color system (affects states)
|
|||
|
|
- **DDR-004:** Motion System v1 intention-based tokens (affects dropdown animation)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 20. ACCEPTANCE CRITERIA
|
|||
|
|
|
|||
|
|
- [ ] All 12 Definition of Done criteria met
|
|||
|
|
- [ ] Works with screen reader (NVDA, VoiceOver, JAWS)
|
|||
|
|
- [ ] Keyboard navigation works (all keys in interaction contract)
|
|||
|
|
- [ ] Touch target minimum 40px on trigger, 44px on options
|
|||
|
|
- [ ] Dropdown does not clip inside overflow:hidden containers
|
|||
|
|
- [ ] Error state visible without color alone
|
|||
|
|
- [ ] Disabled options clearly distinct from enabled
|
|||
|
|
- [ ] Focus management correct (into dropdown, back to trigger)
|
|||
|
|
- [ ] Reduced motion respected
|
|||
|
|
- [ ] Chevron icon rotates when opening/closing
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ÄNDRINGSHISTORIA
|
|||
|
|
|
|||
|
|
| Version | Datum | Beskrivning |
|
|||
|
|
|---------|-------|-------------|
|
|||
|
|
| 1.0 | 2026-07-02 | Initial RFC-003 following RFC-001 structure |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## STATUS
|
|||
|
|
|
|||
|
|
**DRAFT — Awaiting review**
|