Files
boc/docs/design/components/RFC-003-Select.md
T
Bernt 78436cecc2 docs: Foundation Freeze v1.0 + directory reorganization
- FOUNDATIONS-v1.0.md:
  - Freeze contract for Landvex Design System
  - Scope: Motion, AI Principles, Semantic Colors, Grid, Elevation, Typography, RFC Template, Definition of Done
  - Reference components: Button, Input, Select, Card (all ~83%)
  - Invariants: token rules, component rules, accessibility rules, AI rules
  - Change policy: no new foundation concepts without v2.0 RFC
  - Versioning: 1.0.x patches, 1.x.0 new components, 2.0.0 new foundations
  - Compatibility matrix for all foundations and components

- Directory reorganization:
  - docs/design/foundations/ → frozen foundation documents
  - docs/design/components/ → component RFCs
  - docs/design/README.md → navigation and structure

- Foundation documents moved:
  - TOKEN_PHILOSOPHY, SEMANTIC_COLOR_SYSTEM, GRID_ELEVATION
  - AI_DESIGN_PRINCIPLES, COMPONENT_TEMPLATE, RFC_DEFINITION_OF_DONE
  - DESIGN_ANTI_PATTERNS, COMPONENT_DECISION_TREE, GLOSSARY
  - BRAND_PALETTE, RELEASE_DEFINITION, SEMANTIC_COLOR_SYSTEM_REVIEW

- Component RFCs moved:
  - RFC-002-Input, RFC-003-Select, RFC-004-Card

Rationale: Clear separation between foundations (stable platform) and
components (built on top). Foundations v1.0 frozen — components can be
added freely within v1.x, but foundations require v2.0 RFC to change.
This prevents gradual erosion of the design system architecture.
2026-07-02 11:23:10 +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**