docs: Motion System v1 + Button gold standard + Component Template + Decision Tree

- Motion System v1 (chapter 5):
  - Motion principles: when to animate, when NOT to animate
  - Duration scale (instant to slower: 0ms to 500ms)
  - Easing functions (linear, in, out, in-out, spring)
  - Component-specific motion: Button, Modal, Drawer, Map, Skeleton,
    Progress, Error/Success
  - Reduced motion support (prefers-reduced-motion)
  - Interruption rules (animations must be interruptible)
  - Error/success animations (shake, checkmark, pulse)

- Button gold standard (chapter 8.3.1):
  - Full specification: purpose, design goal, token dependencies,
    layout, sizes, variants, states, keyboard, touch, accessibility,
    anti-patterns, related components, DDR references
  - Sets the bar for all future component specifications

- COMPONENT_TEMPLATE.md (v1.0, LOCKED):
  - Standard template for all component specifications
  - Sections: purpose, tokens, layout, sizes, variants, states,
    keyboard, touch, accessibility, motion, acceptance criteria,
    anti-patterns, related components, DDR references

- COMPONENT_DECISION_TREE.md (v1.0, LOCKED):
  - Decision trees for: user actions, information display, user input,
    feedback, navigation, Landvex-specific
  - Quick reference table mapping needs to components
  - Prevents invention of unnecessary components

Rationale: Motion before brand (affects every interaction). Button as
reference implementation. Template ensures consistency. Decision tree
guides AI agents and developers to correct component choice.
This commit is contained in:
Bernt
2026-07-02 08:53:20 +00:00
parent 48236658e8
commit 1e40d384ca
3 changed files with 547 additions and 42 deletions
+222
View File
@@ -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+)
+143
View File
@@ -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]
+182 -42
View File
@@ -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