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.
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
# RFC-002: Canonical Input Component
|
||||
|
||||
**Status:** 🟡 Draft — Under review
|
||||
**Tier:** 1 (Canonical)
|
||||
**Inherits From:** — (root component)
|
||||
**Author:** AI Agent
|
||||
**Date:** 2026-07-02
|
||||
|
||||
---
|
||||
|
||||
## 1. COGNITIVE PURPOSE
|
||||
|
||||
Input exists to capture free-form text or numeric data from the user. It is the primary interface for data entry in forms, filters, and search.
|
||||
|
||||
**Design Goal:** The user knows what to enter, where to enter it, and whether the entry is valid — without reading documentation.
|
||||
|
||||
---
|
||||
|
||||
## 2. WHEN TO USE
|
||||
|
||||
- Free-form text entry (name, address, description)
|
||||
- Numeric entry (quantity, price, coordinates)
|
||||
- Password entry
|
||||
- Search queries
|
||||
- Filter values
|
||||
|
||||
## 3. WHEN NOT TO USE
|
||||
|
||||
- Selection from predefined options (use Select)
|
||||
- Boolean toggle (use Switch or Checkbox)
|
||||
- Date/time selection (use Date Picker)
|
||||
- File upload (use File Input)
|
||||
- Rich text (use Rich Text Editor)
|
||||
|
||||
---
|
||||
|
||||
## 4. USER EXPECTATION
|
||||
|
||||
- Input field is identifiable as editable
|
||||
- Placeholder text hints at expected format
|
||||
- Focus state indicates where typing will appear
|
||||
- Validation errors are shown immediately or on blur
|
||||
- Required fields are marked
|
||||
- Disabled fields are visually distinct
|
||||
|
||||
---
|
||||
|
||||
## 5. INTERACTION CONTRACT
|
||||
|
||||
| Event | Response |
|
||||
|-------|----------|
|
||||
| Click | Focus input, place cursor |
|
||||
| Type | Character appears at cursor position |
|
||||
| Tab | Move focus to next field |
|
||||
| Shift+Tab | Move focus to previous field |
|
||||
| Blur | Validate (if configured), show error if invalid |
|
||||
| Enter | Submit form (if single-line), new line (if multi-line) |
|
||||
| Escape | Clear input (if configured), or cancel |
|
||||
|
||||
---
|
||||
|
||||
## 6. FAILURE BEHAVIOUR
|
||||
|
||||
| Scenario | Response |
|
||||
|----------|----------|
|
||||
| Invalid input on blur | Show error message below input, red border |
|
||||
| Invalid input on submit | Show error, scroll to first error, focus it |
|
||||
| Network validation fails | Show error, allow retry |
|
||||
| Input exceeds max length | Prevent further input, show character count |
|
||||
| Input is required but empty | Show error on blur or submit |
|
||||
| Input is disabled | Ignore all interactions, show disabled state |
|
||||
|
||||
---
|
||||
|
||||
## 7. ACCESSIBILITY CONTRACT
|
||||
|
||||
- **Label:** Every input MUST have an associated `<label>` or `aria-label`
|
||||
- **Focus:** Focus ring MUST be visible (WCAG 2.4.7)
|
||||
- **Error:** Error message MUST be associated with `aria-describedby`
|
||||
- **Required:** Required state MUST be communicated with `aria-required` or `required` attribute
|
||||
- **Screen reader:** MUST announce label, current value, and error state
|
||||
- **Color:** Error state MUST NOT rely on color alone (icon or text required)
|
||||
|
||||
---
|
||||
|
||||
## 8. AI CONTRACT
|
||||
|
||||
| Scenario | AI Choice |
|
||||
|----------|-----------|
|
||||
| Free-form text entry | Input |
|
||||
| Predefined options | Select |
|
||||
| Boolean toggle | Switch / Checkbox |
|
||||
| Date/time | Date Picker |
|
||||
| File upload | File Input |
|
||||
| Search with suggestions | Search (specialized component) |
|
||||
|
||||
---
|
||||
|
||||
## 9. TOKEN DEPENDENCIES
|
||||
|
||||
| Token | Semantic Reference | Usage |
|
||||
|-------|-------------------|-------|
|
||||
| Token | Semantic Alias | Primitive | Usage |
|
||||
|-------|---------------|-----------|-------|
|
||||
| `input-bg` | `color.surface.default` | `gray-0` | Background |
|
||||
| `input-border` | `color.border.default` | `gray-200` | Default border |
|
||||
| `input-border-focus` | `color.border.focus` | `blue-500` | Focus border |
|
||||
| `input-border-error` | `color.border.error` | `red-500` | Error border |
|
||||
| `input-text` | `color.text.primary` | `gray-900` | Input text |
|
||||
| `input-placeholder` | `color.text.muted` | `gray-400` | Placeholder text |
|
||||
| `input-padding` | `space-3` | `12px` | Internal padding |
|
||||
| `input-radius` | `radius-md` | `8px` | Border radius |
|
||||
| `input-height-sm` | `space-8` | `32px` | Small height |
|
||||
| `input-height-md` | `space-10` | `40px` | Medium height |
|
||||
| `input-height-lg` | `space-12` | `48px` | Large height |
|
||||
| `input-error-bg` | `color.status.danger` (at 5%) | `red-50` | Error background tint |
|
||||
| `input-disabled-bg` | `color.surface.raised` | `gray-50` | Disabled background |
|
||||
| `input-disabled-text` | `color.text.muted` | `gray-400` | Disabled text |
|
||||
|
||||
---
|
||||
|
||||
## 10. LAYOUT
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Label (optional) │
|
||||
├─────────────────────────────────────┤
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Placeholder text... │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
│ Helper text / Error message │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Label:** Above input, left-aligned, `text-sm`, `font-medium`
|
||||
- **Input:** Full width of container, padding `space-3`
|
||||
- **Helper/Error:** Below input, `text-xs`, `color.text.muted` (helper) or `color.status.danger` (error)
|
||||
- **Gap:** `space-1` between label and input, `space-1` between input 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 input |
|
||||
| Ghost | No border, underline only | Minimal UI, filters |
|
||||
| Filled | Solid background, no border | High-contrast forms |
|
||||
|
||||
---
|
||||
|
||||
## 13. STATES
|
||||
|
||||
| State | Visual | Motion |
|
||||
|-------|--------|--------|
|
||||
| Default | `input-border`, `input-bg` | — |
|
||||
| Hover | Border darkens slightly | `duration-instant` |
|
||||
| Focus | `input-border-focus`, focus ring | `duration-instant` |
|
||||
| Active | Same as focus | — |
|
||||
| Disabled | `input-disabled-bg`, `input-disabled-text` | — |
|
||||
| Error | `input-border-error`, `input-error-bg` | `duration-interaction` |
|
||||
| Loading | Spinner inside input, disabled | `duration-attention` |
|
||||
| Valid | Green checkmark icon (optional) | `duration-interaction` |
|
||||
|
||||
---
|
||||
|
||||
## 14. KEYBOARD
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Tab` | Move focus to next field |
|
||||
| `Shift+Tab` | Move focus to previous field |
|
||||
| `Enter` | Submit form (single-line) |
|
||||
| `Escape` | Clear input (if clearable) |
|
||||
| `Ctrl+A` | Select all text |
|
||||
| `Ctrl+C/V/X` | Copy/paste/cut |
|
||||
|
||||
---
|
||||
|
||||
## 15. TOUCH
|
||||
|
||||
- Touch target: Minimum 40px height (Medium size)
|
||||
- Active state: Brief background tint
|
||||
- No gesture support (no swipe, pinch)
|
||||
|
||||
---
|
||||
|
||||
## 16. MOTION
|
||||
|
||||
| Transition | Animation | Duration | Easing |
|
||||
|-----------|-----------|----------|--------|
|
||||
| Focus | Border color change | `duration-instant` | `ease-out` |
|
||||
| Error appear | Border + background tint | `duration-interaction` | `ease-out` |
|
||||
| Error disappear | Revert to default | `duration-interaction` | `ease-in` |
|
||||
| Loading spinner | Rotate | `duration-background` | `linear` |
|
||||
|
||||
**Reduced Motion:** All transitions become instant (`duration-instant`).
|
||||
|
||||
---
|
||||
|
||||
## 17. ANTI-PATTERNS
|
||||
|
||||
- ❌ **Placeholder as label:** Placeholder disappears on typing, user loses context
|
||||
- ❌ **Validation only on submit:** User should know immediately if input is wrong
|
||||
- ❌ **No visual feedback on focus:** User cannot see where they are typing
|
||||
- ❌ **Error message in color only:** Screen reader users miss it
|
||||
- ❌ **Input without label:** Accessibility failure, unclear purpose
|
||||
- ❌ **Too many variations:** Stick to Default, Ghost, Filled
|
||||
|
||||
---
|
||||
|
||||
## 18. RELATED COMPONENTS
|
||||
|
||||
- **Select** — Predefined options instead of free-form
|
||||
- **Textarea** — Multi-line text entry
|
||||
- **Search** — Input with autocomplete and suggestions
|
||||
- **Form** — Groups inputs with validation logic
|
||||
- **Label** — Associated text for input
|
||||
|
||||
---
|
||||
|
||||
## 19. DDR REFERENCES
|
||||
|
||||
- **DDR-001:** Device Adaptive over Desktop First (affects input sizing)
|
||||
- **DDR-002:** Information-level typography (affects label and helper text)
|
||||
- **DDR-003:** Semantic color system (affects error and disabled states)
|
||||
|
||||
---
|
||||
|
||||
## 20. ACCEPTANCE CRITERIA
|
||||
|
||||
- [ ] All 12 Definition of Done criteria met
|
||||
- [ ] Works with screen reader (NVDA, VoiceOver, JAWS)
|
||||
- [ ] Keyboard navigation works (Tab, Shift+Tab, Enter, Escape)
|
||||
- [ ] Touch target minimum 40px on mobile
|
||||
- [ ] Error state visible without color alone
|
||||
- [ ] Placeholder does not replace label
|
||||
- [ ] Loading state prevents duplicate submission
|
||||
- [ ] Disabled state clearly distinct from enabled
|
||||
- [ ] Focus ring visible and logical
|
||||
- [ ] Reduced motion respected
|
||||
|
||||
---
|
||||
|
||||
## ÄNDRINGSHISTORIA
|
||||
|
||||
| Version | Datum | Beskrivning |
|
||||
|---------|-------|-------------|
|
||||
| 1.0 | 2026-07-02 | Initial RFC-002 following RFC-001 structure |
|
||||
|
||||
---
|
||||
|
||||
## STATUS
|
||||
|
||||
**DRAFT — Awaiting review**
|
||||
@@ -0,0 +1,293 @@
|
||||
# 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 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**
|
||||
@@ -0,0 +1,265 @@
|
||||
# RFC-004: Canonical Card Component
|
||||
|
||||
**Status:** 🟡 Draft — Under review
|
||||
**Tier:** 1 (Canonical)
|
||||
**Inherits From:** — (root component)
|
||||
**Author:** AI Agent
|
||||
**Date:** 2026-07-02
|
||||
|
||||
---
|
||||
|
||||
## 1. COGNITIVE PURPOSE
|
||||
|
||||
Card exists to group related information into a bounded, scannable unit. It creates visual hierarchy by separating content from its surroundings without requiring explicit borders or dividers.
|
||||
|
||||
**Design Goal:** The user can scan, compare, and act on grouped information as a single unit.
|
||||
|
||||
---
|
||||
|
||||
## 2. WHEN TO USE
|
||||
|
||||
- Grouping related data (KPI, dataset, asset summary)
|
||||
- Presenting actionable items that need context
|
||||
- Creating scannable lists of heterogeneous content
|
||||
- Elevating content above the page background
|
||||
- Containing forms or workflows in a bounded area
|
||||
|
||||
## 3. WHEN NOT TO USE
|
||||
|
||||
- Single line of text (use plain text or List Item)
|
||||
- Simple navigation (use Link or Navigation)
|
||||
- Modal content (use Modal)
|
||||
- Full-page content (use Page layout)
|
||||
- When elevation is unnecessary (use flat surface)
|
||||
|
||||
---
|
||||
|
||||
## 4. USER EXPECTATION
|
||||
|
||||
- Card is visually distinct from background
|
||||
- Card content is related and scannable
|
||||
- Card may be clickable (if actionable)
|
||||
- Card maintains consistent internal spacing
|
||||
- Card adapts to container width
|
||||
- Multiple cards align in grids or lists
|
||||
|
||||
---
|
||||
|
||||
## 5. INTERACTION CONTRACT
|
||||
|
||||
| Event | Response |
|
||||
|-------|----------|
|
||||
| Click (actionable) | Navigate or trigger primary action |
|
||||
| Hover (actionable) | Elevation increases, cursor changes |
|
||||
| Focus (actionable) | Focus ring appears |
|
||||
| None (static) | No interaction, purely presentational |
|
||||
|
||||
---
|
||||
|
||||
## 6. FAILURE BEHAVIOUR
|
||||
|
||||
| Scenario | Response |
|
||||
|----------|----------|
|
||||
| Content overflows | Scroll internally or truncate with ellipsis |
|
||||
| Image fails to load | Show placeholder or fallback icon |
|
||||
| Action fails | Show error state on card or in adjacent area |
|
||||
| Card is loading | Show skeleton state |
|
||||
| Card is empty | Show empty state with guidance |
|
||||
|
||||
---
|
||||
|
||||
## 7. ACCESSIBILITY CONTRACT
|
||||
|
||||
- **Role:** Static cards use no ARIA role. Actionable cards use `role="button"` or `role="link"`.
|
||||
- **Focus:** Actionable cards MUST have visible focus ring
|
||||
- **Label:** Actionable cards MUST have accessible name (aria-label or visible text)
|
||||
- **Screen reader:** MUST announce card as group or link/button
|
||||
- **Keyboard:** Actionable cards MUST be activatable with Enter or Space
|
||||
- **Color:** MUST NOT rely on color alone to indicate actionability
|
||||
|
||||
---
|
||||
|
||||
## 8. AI CONTRACT
|
||||
|
||||
| Scenario | AI Choice |
|
||||
|----------|-----------|
|
||||
| Group related data with actions | Card |
|
||||
| Single data point with label | List Item |
|
||||
| Navigation to another page | Link |
|
||||
| Modal content | Modal |
|
||||
| Full-page layout | Page |
|
||||
| Simple text without grouping | Plain text |
|
||||
|
||||
---
|
||||
|
||||
## 9. TOKEN DEPENDENCIES
|
||||
|
||||
| Token | Semantic Alias | Primitive | Usage |
|
||||
|-------|---------------|-----------|-------|
|
||||
| `card-bg` | `color.surface.raised` | `gray-50` | Background |
|
||||
| `card-border` | `color.border.default` | `gray-200` | Border (optional) |
|
||||
| `card-radius` | `radius-lg` | `12px` | Border radius |
|
||||
| `card-padding` | `space-4` | `16px` | Internal padding |
|
||||
| `card-shadow` | `shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Default elevation |
|
||||
| `card-shadow-hover` | `shadow-md` | `0 4px 6px rgba(0,0,0,0.07)` | Hover elevation (actionable) |
|
||||
| `card-shadow-active` | `shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Active elevation |
|
||||
| `card-divider` | `color.border.default` | `gray-200` | Divider between sections |
|
||||
| `card-header-text` | `color.text.primary` | `gray-900` | Header text |
|
||||
| `card-body-text` | `color.text.secondary` | `gray-600` | Body text |
|
||||
| `card-footer-text` | `color.text.muted` | `gray-400` | Footer text |
|
||||
| `card-loading-bg` | `color.surface.default` | `gray-0` | Skeleton background |
|
||||
|
||||
---
|
||||
|
||||
## 10. LAYOUT
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Header (title, action) │ │
|
||||
│ ├─────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ Body (content, data, media) │ │
|
||||
│ │ │ │
|
||||
│ ├─────────────────────────────────┤ │
|
||||
│ │ Footer (metadata, actions) │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Header:** Top section, title + optional action, padding `space-4`
|
||||
- **Body:** Main content, padding `space-4`, flexible height
|
||||
- **Footer:** Bottom section, metadata or actions, padding `space-4`, top border `card-divider`
|
||||
- **Divider:** 1px line between sections, `card-divider`
|
||||
- **Gap:** No gap between sections (padding handles spacing)
|
||||
|
||||
---
|
||||
|
||||
## 11. SIZES
|
||||
|
||||
| Size | Width | Padding | Usage |
|
||||
|------|-------|---------|-------|
|
||||
| Small | 240px | `space-3` | Dense grids, compact data |
|
||||
| Medium | 320px | `space-4` | Default, most content |
|
||||
| Large | 480px | `space-5` | Featured content, dashboards |
|
||||
| Fluid | 100% | `space-4` | Responsive, full-width |
|
||||
|
||||
---
|
||||
|
||||
## 12. VARIANTS
|
||||
|
||||
| Variant | Visual | Usage |
|
||||
|---------|--------|-------|
|
||||
| Default | Border, subtle shadow | Standard content grouping |
|
||||
| Elevated | No border, stronger shadow | Featured content, dashboards |
|
||||
| Outlined | Border, no shadow | Dense layouts, tables |
|
||||
| Interactive | Hover elevation, cursor pointer | Clickable cards |
|
||||
|
||||
---
|
||||
|
||||
## 13. STATES
|
||||
|
||||
| State | Visual | Motion |
|
||||
|-------|--------|--------|
|
||||
| Default | `card-bg`, `card-shadow` | — |
|
||||
| Hover (interactive) | `card-shadow-hover`, cursor pointer | `duration-interaction` |
|
||||
| Active (interactive) | `card-shadow-active` | `duration-instant` |
|
||||
| Focus (interactive) | Focus ring | `duration-instant` |
|
||||
| Loading | Skeleton state | `duration-background` |
|
||||
| Empty | Empty state illustration + text | — |
|
||||
| Error | Error border or error message | `duration-interaction` |
|
||||
|
||||
---
|
||||
|
||||
## 14. KEYBOARD
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Tab` | Move focus to next focusable element |
|
||||
| `Enter` / `Space` | Activate actionable card |
|
||||
| `Arrow keys` | Navigate between cards in grid/list |
|
||||
|
||||
---
|
||||
|
||||
## 15. TOUCH
|
||||
|
||||
- Touch target: Entire card if actionable
|
||||
- Active state: Brief elevation reduction
|
||||
- No gesture support (no swipe, pinch)
|
||||
- Scroll: Card content scrolls internally if overflow
|
||||
|
||||
---
|
||||
|
||||
## 16. MOTION
|
||||
|
||||
| Transition | Animation | Duration | Easing |
|
||||
|-----------|-----------|----------|--------|
|
||||
| Hover (interactive) | Elevation increase | `duration-interaction` | `ease-out` |
|
||||
| Active (interactive) | Elevation decrease | `duration-instant` | `ease-in` |
|
||||
| Focus | Focus ring appear | `duration-instant` | `ease-out` |
|
||||
| Loading skeleton | Shimmer animation | `duration-background` | `linear` |
|
||||
| Error appear | Border color change | `duration-interaction` | `ease-out` |
|
||||
|
||||
**Reduced Motion:** No elevation animation. Focus ring appears instantly.
|
||||
|
||||
---
|
||||
|
||||
## 17. ANTI-PATTERNS
|
||||
|
||||
- ❌ **Nested cards:** Cards inside cards create visual confusion
|
||||
- ❌ **Too much padding:** Wastes space, reduces information density
|
||||
- ❌ **No visual distinction from background:** Defeats purpose of card
|
||||
- ❌ **Inconsistent internal spacing:** Breaks scannability
|
||||
- ❌ **Actionable without focus ring:** Accessibility failure
|
||||
- ❌ **Clickable entire card with internal buttons:** Event collision
|
||||
- ❌ **Card as only layout tool:** Use grid or flexbox for layout, card for grouping
|
||||
- ❌ **Excessive elevation:** Competes with modals and overlays
|
||||
|
||||
---
|
||||
|
||||
## 18. RELATED COMPONENTS
|
||||
|
||||
- **Modal** — Overlay content, not inline
|
||||
- **Panel** — Larger container, often full-height
|
||||
- **List Item** — Single row, no grouping
|
||||
- **Tile** — Image-first card variant
|
||||
- **KPI Card** — Domain-specific card (Tier 3)
|
||||
- **Dataset Card** — Domain-specific card (Tier 3)
|
||||
|
||||
---
|
||||
|
||||
## 19. DDR REFERENCES
|
||||
|
||||
- **DDR-001:** Device Adaptive over Desktop First (affects sizing and grid)
|
||||
- **DDR-002:** Information-level typography (affects header and body text)
|
||||
- **DDR-003:** Semantic color system (affects states)
|
||||
- **DDR-005:** Elevation system (affects shadow tokens)
|
||||
|
||||
---
|
||||
|
||||
## 20. ACCEPTANCE CRITERIA
|
||||
|
||||
- [ ] All 12 Definition of Done criteria met
|
||||
- [ ] Works with screen reader (announces as group or link/button)
|
||||
- [ ] Keyboard navigation works (Tab, Enter, Space, Arrow keys)
|
||||
- [ ] Touch target minimum 44px for actionable cards
|
||||
- [ ] Focus ring visible on actionable cards
|
||||
- [ ] Loading skeleton state implemented
|
||||
- [ ] Empty state with guidance
|
||||
- [ ] Error state handled
|
||||
- [ ] Reduced motion respected
|
||||
- [ ] No nested cards
|
||||
|
||||
---
|
||||
|
||||
## ÄNDRINGSHISTORIA
|
||||
|
||||
| Version | Datum | Beskrivning |
|
||||
|---------|-------|-------------|
|
||||
| 1.0 | 2026-07-02 | Initial RFC-004 following RFC-001 structure |
|
||||
|
||||
---
|
||||
|
||||
## STATUS
|
||||
|
||||
**DRAFT — Awaiting review**
|
||||
Reference in New Issue
Block a user