Files
boc/docs/design/RFC-003-Select.md
T
Bernt 303b79d4cd docs: Update Input/Select/Card to semantic aliases + raise maturity to 83%
- RFC-002 Input:
  - Token Dependencies updated to semantic aliases with primitives
  - All tokens now map through color.surface, color.border, color.text, color.status
  - No direct color references (e.g., semantic-red-500 → color.status.danger)
  - Helper/error text uses semantic tokens

- RFC-003 Select:
  - Token Dependencies updated to semantic aliases with primitives
  - Dropdown, option, and error states use semantic tokens
  - Selected option uses color.action.primary at 10% opacity
  - No direct color references

- RFC-004 Card:
  - Token Dependencies updated to semantic aliases with primitives
  - Surface, border, text hierarchy use semantic tokens
  - Shadow tokens mapped to elevation system
  - No direct color references

- COMPLIANCE_MATRIX.md:
  - Input: 33% → 83%
  - Select: 33% → 83%
  - Card: 33% → 83%
  - All now have Governance , Tokens , Motion , Accessibility , AI 

Rationale: Semantic model first. Components never reference primitives.
Token contracts (purpose, allowed, forbidden) ensure correct usage.
Dark mode implementable via token swap without component changes.
2026-07-02 11:04:00 +00:00

294 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 520 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 |
|----------|-----------|
| 520 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 Alias | Primitive | Usage |
|-------|---------------|-----------|-------|
| `select-bg` | `color.surface.default` | `gray-0` | Background |
| `select-border` | `color.border.default` | `gray-200` | Default border |
| `select-border-focus` | `color.border.focus` | `blue-500` | Focus border |
| `select-border-error` | `color.border.error` | `red-500` | Error border |
| `select-text` | `color.text.primary` | `gray-900` | Selected text |
| `select-placeholder` | `color.text.muted` | `gray-400` | Placeholder text |
| `select-padding` | `space-3` | `12px` | Internal padding |
| `select-radius` | `radius-md` | `8px` | Border radius |
| `select-height-sm` | `space-8` | `32px` | Small height |
| `select-height-md` | `space-10` | `40px` | Medium height |
| `select-height-lg` | `space-12` | `48px` | Large height |
| `select-dropdown-bg` | `color.surface.raised` | `gray-50` | Dropdown background |
| `select-dropdown-border` | `color.border.default` | `gray-200` | Dropdown border |
| `select-dropdown-shadow` | `shadow-md` | `0 4px 6px rgba(0,0,0,0.07)` | Dropdown elevation |
| `select-option-hover` | `color.surface.raised` | `gray-50` | Option hover background |
| `select-option-selected` | `color.action.primary` (at 10%) | `blue-50` | Selected option background |
| `select-option-disabled` | `color.surface.raised` | `gray-50` | Disabled option background |
| `select-error-bg` | `color.status.danger` (at 5%) | `red-50` | Error background tint |
| `select-disabled-bg` | `color.surface.raised` | `gray-50` | Disabled background |
| `select-disabled-text` | `color.text.muted` | `gray-400` | Disabled text |
| `select-chevron` | `color.text.muted` | `gray-400` | 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**