diff --git a/docs/design/COMPONENT_DECISION_TREE.md b/docs/design/COMPONENT_DECISION_TREE.md new file mode 100644 index 000000000..1f573195d --- /dev/null +++ b/docs/design/COMPONENT_DECISION_TREE.md @@ -0,0 +1,222 @@ +# COMPONENT DECISION TREE + +**Which Component Should I Use?** + +| | | +|---|---| +| **Version** | 1.0 | +| **Status** | LOCKED | +| **Scope** | All Landvex & quiXzoom products | + +--- + +## How to Use This Document + +Start at the top. Answer each question. Follow the path to the recommended component. + +If no existing component fits, apply the **New Component Rule** (see Design Specification §8.2): +1. Can an existing component be used? +2. Can a variant be created? +3. Can a modifier be used? +4. Can a new state solve the problem? + +Only then define a new component. + +--- + +## User Actions + +``` +Need user action? +├── Yes → Primary action? +│ ├── Yes → Button (Primary) +│ └── No → Multiple options? +│ ├── Yes → Select / Dropdown +│ └── No → Toggle state? +│ ├── Yes → Switch / Checkbox +│ └── No → Navigation? +│ ├── Yes → Navigation / Sidebar +│ └── No → Search? +│ ├── Yes → Search +│ └── No → Command Palette +└── No → Need information display? + └── Yes → See Information Display +``` + +## Information Display + +``` +Need to display information? +├── Yes → Tabular data? +│ ├── Yes → Simple table? +│ │ ├── Yes → Table +│ │ └── No → Sorting, filtering, pagination? +│ │ ├── Yes → Data Grid +│ │ └── No → Table +│ └── No → Spatial data? +│ ├── Yes → Map Panel +│ └── No → Summary metric? +│ ├── Yes → KPI Card +│ └── No → Grouped content? +│ ├── Yes → Card / Accordion +│ └── No → Single item? +│ ├── Yes → Badge / Alert +│ └── No → Tooltip +└── No → Need user input? + └── Yes → See User Input +``` + +## User Input + +``` +Need user input? +├── Yes → Text input? +│ ├── Yes → Single line? +│ │ ├── Yes → Input +│ │ └── No → Textarea +│ └── No → Choice from options? +│ ├── Yes → Single choice? +│ │ ├── Yes → Radio / Select +│ │ └── No → Multiple choice? +│ │ ├── Yes → Checkbox / Multi-select +│ │ └── No → Toggle? +│ │ ├── Yes → Switch +│ │ └── No → Date / time? +│ │ ├── Yes → Date Picker +│ │ └── No → Coordinate? +│ │ ├── Yes → Coordinate Picker +│ │ └── No → File? +│ │ ├── Yes → File Upload +│ │ └── No → Input (custom) +│ └── No → Search / filter? +│ ├── Yes → Search +│ └── No → Command Palette +└── No → Need feedback? + └── Yes → See Feedback +``` + +## Feedback + +``` +Need to provide feedback? +├── Yes → Blocking action? +│ ├── Yes → Modal / Dialog +│ └── No → Transient notification? +│ ├── Yes → Toast +│ └── No → Contextual message? +│ ├── Yes → Alert (inline) / Tooltip +│ └── No → Status indicator? +│ ├── Yes → Badge +│ └── No → Progress? +│ ├── Yes → Progress / Skeleton +│ └── No → Empty State +└── No → Navigation or structure? + └── Yes → See Navigation +``` + +## Navigation + +``` +Need navigation? +├── Yes → Primary navigation? +│ ├── Yes → Top-level sections? +│ │ ├── Yes → Navigation +│ │ └── No → Sidebar +│ └── No → Secondary navigation? +│ ├── Yes → Tabs / Breadcrumb +│ └── No → Contextual navigation? +│ ├── Yes → Pagination / Menu +│ └── No → Command Palette +└── No → Consider: is this a new component? + └── Apply New Component Rule +``` + +## Landvex-Specific (Domain Components) + +``` +Need Landvex-specific component? +├── Yes → Geospatial data? +│ ├── Yes → Map visualization? +│ │ ├── Yes → Map Panel +│ │ └── No → Layer control? +│ │ ├── Yes → Layer Selector +│ │ └── No → Feature inspection? +│ │ ├── Yes → Geospatial Inspector +│ │ └── No → Coordinate input? +│ │ └── Yes → Coordinate Picker +│ └── No → Mission data? +│ ├── Yes → Mission status? +│ │ ├── Yes → Mission Status +│ │ └── No → Mission timeline? +│ │ └── Yes → Timeline Panel +│ └── No → Asset data? +│ ├── Yes → Asset summary? +│ │ ├── Yes → Asset Summary +│ │ └── No → Dataset preview? +│ │ └── Yes → Dataset Card +│ └── No → KPI display? +│ └── Yes → KPI Card +└── No → See general decision trees above +``` + +--- + +## Quick Reference Table + +| Need | Component | Tier | +|------|-----------|------| +| Primary action | Button | 1 | +| Text input | Input | 1 | +| Choice from list | Select | 1 | +| Content container | Card | 1 | +| Overlay dialog | Modal | 1 | +| Navigation | Navigation / Sidebar | 1 | +| Tabular data | Table / Data Grid | 1 | +| Search | Search | 1 | +| Map display | Map Panel | 1 | +| Status indicator | Badge | 2 | +| Message | Alert / Toast | 2 | +| Help text | Tooltip | 2 | +| Collapsible content | Accordion | 2 | +| Content switching | Tabs | 2 | +| Location indicator | Breadcrumb | 2 | +| Page navigation | Pagination | 2 | +| No content | Empty State | 2 | +| Loading | Skeleton | 2 | +| Key metric | KPI Card | 3 | +| Map tools | Map Overlay Panel | 3 | +| Layer control | Layer Selector | 3 | +| Mission state | Mission Status | 3 | +| Data preview | Dataset Card | 3 | +| Feature details | Geospatial Inspector | 3 | +| Asset overview | Asset Summary | 3 | +| Coordinate input | Coordinate Picker | 3 | +| Time data | Timeline Panel | 3 | + +--- + +## Relationship to Other Documents + +| Document | Role | +|----------|------| +| `LANDVEX_DESIGN_SPECIFICATION.md` §8 | Component tiers and inheritance | +| `COMPONENT_TEMPLATE.md` | How to document a component | +| `DESIGN_ANTI_PATTERNS.md` | What not to do | +| `COMPONENT_DECISION_TREE.md` | Which component to use | + +--- + +## ÄNDRINGSHISTORIA + +| Version | Datum | Beskrivning | +|---------|-------|-------------| +| 1.0 | 2026-07-02 | Initial decision trees for actions, information, input, feedback, navigation, and Landvex-specific | + +--- + +## STATUS + +**LOCKED** + +- Mindre revideringar: 1.x-serien +- Brytande ändringar: Kräver Architecture Review, ny major-version (2.0+) diff --git a/docs/design/COMPONENT_TEMPLATE.md b/docs/design/COMPONENT_TEMPLATE.md new file mode 100644 index 000000000..bcc03ec19 --- /dev/null +++ b/docs/design/COMPONENT_TEMPLATE.md @@ -0,0 +1,143 @@ +# COMPONENT TEMPLATE + +**Template for All Component Specifications** + +| | | +|---|---| +| **Version** | 1.0 | +| **Status** | LOCKED | +| **Scope** | All Landvex & quiXzoom components | + +--- + +## Instructions + +Copy this template for each new component. Remove sections that do not apply. Add sections that are component-specific. + +--- + +## [Component Name] + +**Purpose:** One sentence describing what this component does. + +**Design Goal:** One sentence describing the ideal user experience. + +**Tier:** [1 (Canonical) / 2 (Derived) / 3 (Domain)] + +**Inherits From:** [Parent component, or "—" if canonical] + +--- + +### Token Dependencies + +| Token | Semantic Reference | Usage | +|-------|-------------------|-------| +| `component-token-1` | `semantic-token-1` | Description | +| `component-token-2` | `semantic-token-2` | Description | + +--- + +### Layout + +- Dimensions +- Padding +- Margins +- Border radius +- Internal structure + +--- + +### Sizes + +| Size | Dimensions | Usage | +|------|-----------|-------| +| Small | [dimensions] | [context] | +| Default | [dimensions] | [context] | +| Large | [dimensions] | [context] | + +--- + +### Variants + +| Variant | Visual | Usage | +|---------|--------|-------| +| Variant 1 | [description] | [context] | +| Variant 2 | [description] | [context] | + +--- + +### States + +| State | Visual | Motion | +|-------|--------|--------| +| Default | [description] | [animation] | +| Hover | [description] | [animation] | +| Focus | [description] | [animation] | +| Active | [description] | [animation] | +| Disabled | [description] | [animation] | +| Loading | [description] | [animation] | +| Error | [description] | [animation] | + +--- + +### Keyboard + +| Key | Action | +|-----|--------| +| `Tab` | [behavior] | +| `Enter` / `Space` | [behavior] | +| `Escape` | [behavior] | +| Arrow keys | [behavior] | + +--- + +### Touch + +- Touch target size +- Active state behavior +- Gesture support (if any) + +--- + +### Accessibility + +- ARIA roles +- ARIA states +- Screen reader behavior +- Focus management + +--- + +### Motion + +| Transition | Animation | Duration | Easing | +|-----------|-----------|----------|--------| +| [state change] | [description] | [duration] | [easing] | + +--- + +### Acceptance Criteria + +- [ ] Criterion 1 +- [ ] Criterion 2 +- [ ] Criterion 3 + +--- + +### Anti-Patterns + +- ❌ [What not to do] +- ❌ [What not to do] + +--- + +### Related Components + +- [Component A] — [relationship] +- [Component B] — [relationship] + +--- + +### DDR References + +- DDR-XXX: [Decision rationale] diff --git a/docs/design/LANDVEX_DESIGN_SPECIFICATION.md b/docs/design/LANDVEX_DESIGN_SPECIFICATION.md index bbf1e1b1f..ec11f1358 100644 --- a/docs/design/LANDVEX_DESIGN_SPECIFICATION.md +++ b/docs/design/LANDVEX_DESIGN_SPECIFICATION.md @@ -511,29 +511,129 @@ Dedicated palette for geospatial visualization. Defined by **layer**, not indivi ## 5. Motion System -> **Status:** ⏳ Ej påbörjad +> **Status:** ✅ Specified +> **Principle:** Motion supports understanding. It never decorates. -### 5.1 Duration Scale +### 5.1 Motion Principles + +**When to animate:** +- State changes that need attention (modal open, toast appear) +- Spatial relationships (drawer slide, page transition) +- Feedback on user action (button press, form submit) +- Loading and progress (skeleton, progress bar) + +**When NOT to animate:** +- Static content that does not change +- Data updates that are frequent (live dashboards) +- Elements entering viewport on scroll (use Intersection Observer, not animation) +- Anything that delays user action + +**Rules:** +- Animations must be interruptible +- Multiple animations must not conflict +- Motion must respect `prefers-reduced-motion` + +### 5.2 Duration Scale | Token | Value | Usage | |-------|-------|-------| | `duration-instant` | 0ms | No animation | -| `duration-fast` | 100ms | Hover states | +| `duration-fast` | 100ms | Hover states, focus rings | | `duration-normal` | 200ms | Standard transitions | -| `duration-slow` | 300ms | Page transitions | -| `duration-slower` | 500ms | Complex animations | +| `duration-slow` | 300ms | Page transitions, drawers | +| `duration-slower` | 500ms | Complex animations, modals | -### 5.2 Easing Functions +### 5.3 Easing Functions | Token | Value | Usage | |-------|-------|-------| -| `ease-linear` | linear | Continuous motion | -| `ease-in` | cubic-bezier(0.4, 0, 1, 1) | Exit animations | -| `ease-out` | cubic-bezier(0, 0, 0.2, 1) | Enter animations | -| `ease-in-out` | cubic-bezier(0.4, 0, 0.2, 1) | Standard | -| `ease-spring` | cubic-bezier(0.34, 1.56, 0.64, 1) | Playful interactions | +| `ease-linear` | linear | Continuous motion, progress bars | +| `ease-in` | cubic-bezier(0.4, 0, 1, 1) | Exit animations, dismiss | +| `ease-out` | cubic-bezier(0, 0, 0.2, 1) | Enter animations, appear | +| `ease-in-out` | cubic-bezier(0.4, 0, 0.2, 1) | Standard, symmetric | +| `ease-spring` | cubic-bezier(0.34, 1.56, 0.64, 1) | Playful, overshoot | -### 5.3 Motion Principles +### 5.4 Component-Specific Motion + +#### 5.4.1 Button + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Hover | Background color shift | 100ms | ease-out | +| Press | Scale to 0.98 | 50ms | ease-in | +| Release | Scale to 1.0 | 100ms | ease-out | +| Loading | Spinner rotation | 1s | linear infinite | +| Disabled | Opacity to 0.5 | 100ms | ease-in-out | + +#### 5.4.2 Modal + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Open | Fade in + scale from 0.95 | 300ms | ease-out | +| Close | Fade out + scale to 0.95 | 200ms | ease-in | +| Backdrop | Fade to 50% opacity | 300ms | ease-in-out | + +#### 5.4.3 Drawer + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Open | Slide from edge | 300ms | ease-out | +| Close | Slide to edge | 200ms | ease-in | +| Backdrop | Fade to 50% opacity | 300ms | ease-in-out | + +#### 5.4.4 Map Transitions + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Pan | None (instant) | 0ms | — | +| Zoom | Smooth interpolation | 300ms | ease-in-out | +| Layer toggle | Cross-fade | 200ms | ease-in-out | +| Feature select | Highlight pulse | 300ms | ease-out | + +**Rule:** Map panning is never animated. Users need instant feedback on drag. + +#### 5.4.5 Skeleton + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Loading | Shimmer sweep | 1.5s | ease-in-out infinite | +| Complete | Fade to content | 200ms | ease-out | + +#### 5.4.6 Progress + +| State | Animation | Duration | Easing | +|-------|-----------|----------|--------| +| Indeterminate | Sweep | 1s | linear infinite | +| Determinate | Width growth | 300ms | ease-out | +| Complete | Checkmark draw | 200ms | ease-out | + +### 5.5 Reduced Motion + +When `prefers-reduced-motion: reduce` is active: + +- All animations become instant (0ms) +- Skeleton shows static placeholder (no shimmer) +- Progress shows static bar (no sweep) +- Map zoom uses instant jump (no interpolation) +- Modals and drawers appear instantly (no fade/scale) + +**Exception:** Loading spinners may rotate slowly (2s per rotation) to indicate activity without motion. + +### 5.6 Interruptions + +**Rule:** Animations must be interruptible. + +- If a modal is closing and user opens another, the first must complete or reverse instantly +- If a drawer is sliding and user taps backdrop, it must reverse direction +- No animation queue buildup + +### 5.7 Error and Success Animations + +| Type | Animation | Duration | Purpose | +|------|-----------|----------|---------| +| Error | Subtle shake (4px, 3 cycles) | 300ms | Indicate invalid input | +| Success | Checkmark draw + fade | 400ms | Confirm completion | +| Warning | Pulse border color | 2s | Draw attention without alarm | --- @@ -666,46 +766,86 @@ Only then may a new component be defined. ### 8.3.1 Button -**States:** -- Default -- Hover -- Focus -- Pressed (Active) -- Loading -- Disabled +**Purpose:** Trigger primary user actions. The most used interactive element. + +**Design Goal:** Immediately recognizable, consistently responsive, accessible without thought. + +**Token Dependencies:** +- `button-primary-bg` → `brand-500` +- `button-primary-bg-hover` → `brand-600` +- `button-primary-text` → `text-inverse` +- `button-secondary-bg` → `surface-secondary` +- `button-secondary-border` → `border-default` +- `button-ghost-text` → `brand-500` +- `space-padding-button` → `space-3` (12px vertical, `space-4` horizontal) +- `radius-button` → `radius-2` (4px) + +**Layout:** +- Height: 40px (default), 32px (small), 48px (large) +- Padding: 12px vertical, 16px horizontal +- Icon + text gap: 8px +- Border radius: 4px + +**Sizes:** + +| Size | Height | Padding | Font | Usage | +|------|--------|---------|------|-------| +| Small | 32px | 8px 12px | `text-sm` | Dense UIs, tables | +| Default | 40px | 12px 16px | `text-base` | Standard | +| Large | 48px | 16px 24px | `text-lg` | Hero actions, mobile | **Variants:** -- Primary -- Secondary -- Tertiary (Ghost) -- Danger -- Icon-only -**ARIA:** +| Variant | Background | Border | Text | Usage | +|---------|------------|--------|------|-------| +| Primary | `brand-500` | None | White | Main action | +| Secondary | `surface-secondary` | `border-default` | `text-primary` | Alternative action | +| Ghost | Transparent | None | `brand-500` | Low emphasis | +| Danger | `semantic-red-500` | None | White | Destructive action | +| Icon-only | Any above | None | CurrentColor | Toolbar, compact | + +**States:** + +| State | Visual | Motion | +|-------|--------|--------| +| Default | As variant | None | +| Hover | Darken 10% | Background shift 100ms ease-out | +| Focus | 2px ring `border-focus` | Ring appear 100ms ease-out | +| Pressed | Scale 0.98 | Scale 50ms ease-in | +| Loading | Spinner replaces text | Spinner 1s linear infinite | +| Disabled | Opacity 0.5, no pointer | None | + +**Keyboard:** +- `Tab`: Focus +- `Enter` / `Space`: Activate +- Disabled: Not focusable + +**Touch:** +- Min 44x44px touch target +- Active state on press (immediate) +- No hover state on touch + +**Accessibility:** - `role="button"` - `aria-label` for icon-only - `aria-disabled` for disabled - `aria-busy` for loading +- Focus ring always visible -**Keyboard:** -- `Enter` / `Space` to activate -- `Tab` to focus +**Anti-Patterns:** +- ❌ Two primary buttons in same view +- ❌ Disabled button without explanation +- ❌ Button that looks like link +- ❌ Loading button that remains clickable -**Touch:** -- Min 44x44px touch target -- Active state on press +**Related Components:** +- Split Button (extends Button) +- Icon Button (extends Button) +- Floating Action Button (extends Button) -**Animation:** -- Hover: background-color 100ms ease-out -- Press: scale(0.98) 50ms -- Loading: spinner rotation 1s linear infinite - -**Acceptance Criteria:** -- [ ] All states are visually distinct -- [ ] Focus ring is visible -- [ ] Loading state prevents double-submit -- [ ] Disabled state is not focusable -- [ ] Touch target meets 44px minimum +**DDR References:** +- DDR-001: Why buttons use brand color for primary action +- DDR-002: Why ghost variant exists (low emphasis without border) ### 8.3.2 Input / Text Field