Files
boc/docs/design/LANDVEX_DESIGN_SPECIFICATION.md
T

1096 lines
32 KiB
Markdown
Raw Normal View History

# LANDVEX DESIGN SPECIFICATION
**Technical Contract Between UX, UI, Frontend, AI Agents, QA, and Codebase**
| | |
|---|---|
| **Version** | 1.0-skeleton |
| **Status** | DRAFT — Under utveckling |
| **Scope** | Landvex Enterprise Platform |
| **Authority** | DESIGN_SPECIFICATION (nivå 5 i dokumenthierarkin) |
| **Parent** | `LANDVEX_DESIGN_CONSTITUTION.md` |
---
## PART A — GOVERNANCE
## 0. Token Philosophy
See `TOKEN_PHILOSOPHY.md` for the complete token governance rules.
**Quick Reference:**
- Three levels: Primitive → Semantic → Component
- Semantic first, primitive only when necessary
- DTCG JSON as canonical format
- Semantic versioning for token changes
- Deprecation policy: mark, maintain 2 versions, then remove
- Token lifecycle: Draft → Experimental → Stable → Deprecated → Removed
- Design Review Gate required before new token creation
---
> **Om detta dokument**
> Detta är ett levande kontrakt. Varje kapitel fylls på successivt.
> Ingen implementation får bryta mot en specificerad regel.
> Ospecificerade områden är fria att utforskas, men bör dokumenteras här när de stabiliseras.
---
## Innehållsförteckning
### PART A — GOVERNANCE
0. [Token Philosophy](#0-token-philosophy)
### PART B — TOKENS
1. [Design Tokens](#1-design-tokens)
- 1.1 Primitive Tokens
- 1.2 Semantic Tokens
- 1.3 Component Tokens
2. [Layout System](#2-layout-system)
3. [Typography](#3-typography)
4. [Color System](#4-color-system)
5. [Motion System](#5-motion-system)
6. [Elevation & Shadows](#6-elevation--shadows)
7. [Iconography](#7-iconography)
### PART C — COMPONENTS
8. [Component Architecture](#8-component-architecture)
- 8.0 Component Tiers (Tier 1/2/3)
- 8.1 Component Inheritance
- 8.2 New Component Rule
- 8.3 Component Specifications
9. [Maps & Geospatial Layer](#9-maps--geospatial-layer)
10. [Accessibility](#10-accessibility)
11. [Responsive Behavior](#11-responsive-behavior)
12. [Empty States](#12-empty-states)
13. [Loading States](#13-loading-states)
14. [Error States](#14-error-states)
### PART D — VALIDATION
15. [Acceptance Criteria](#15-acceptance-criteria)
16. [Design QA](#16-design-qa)
17. [Visual Regression](#17-visual-regression)
18. [Release Checklist](#18-release-checklist)
---
## PART B — TOKENS
## 1. Design Tokens
> **Status:** ⏳ Ej påbörjad
> **Parent:** `TOKEN_PHILOSOPHY.md`
### 1.0 Token Architecture
All tokens follow the three-level hierarchy defined in `TOKEN_PHILOSOPHY.md`:
```
Level 1: Primitive Tokens → Raw values (numbers, colors, dimensions)
Level 2: Semantic Tokens → Purpose-driven (surface-primary, text-muted)
Level 3: Component Tokens → Component-specific (button-primary-bg-hover)
```
**MUST:** Reference Level 2 (Semantic) by default. Level 3 (Component) only when component-specific override is required. **MUST NOT:** Reference Level 1 (Primitive) directly from components.
### 1.1 Primitive Tokens
#### 1.1.1 Spacing Scale
| Token | Value | Usage |
|-------|-------|-------|
| `space-0` | 0px | — |
| `space-1` | 4px | Tight padding, icon gaps |
| `space-2` | 8px | Default element padding |
| `space-3` | 12px | Component internal spacing |
| `space-4` | 16px | Standard gap |
| `space-5` | 24px | Section spacing |
| `space-6` | 32px | Large section spacing |
| `space-7` | 48px | Page-level spacing |
| `space-8` | 64px | Major section breaks |
| `space-9` | 96px | Hero/page top |
#### 1.1.2 Border Radius
| Token | Value | Usage |
|-------|-------|-------|
| `radius-0` | 0px | Sharp edges (data tables, maps) |
| `radius-1` | 2px | Subtle rounding |
| `radius-2` | 4px | Default (buttons, inputs) |
| `radius-3` | 8px | Cards, panels |
| `radius-4` | 12px | Large cards, modals |
| `radius-full` | 9999px | Pills, avatars |
#### 1.1.3 Z-Index Scale
| Token | Value | Usage |
|-------|-------|-------|
| `z-base` | 0 | Default layer |
| `z-dropdown` | 100 | Dropdowns, popovers |
| `z-sticky` | 200 | Sticky headers |
| `z-modal` | 300 | Modals, dialogs |
| `z-tooltip` | 400 | Tooltips |
| `z-toast` | 500 | Notifications |
| `z-overlay` | 600 | Full-screen overlays |
#### 1.1.4 Opacity Scale
| Token | Value | Usage |
|-------|-------|-------|
| `opacity-0` | 0% | Hidden |
| `opacity-25` | 25% | Disabled text |
| `opacity-50` | 50% | Placeholder text |
| `opacity-75` | 75% | Secondary text |
| `opacity-100` | 100% | Primary content |
#### 1.1.5 Blur Scale
| Token | Value | Usage |
|-------|-------|-------|
| `blur-0` | 0px | No blur |
| `blur-sm` | 4px | Subtle backdrop |
| `blur-md` | 8px | Modal backdrop |
| `blur-lg` | 16px | Full-screen overlay |
#### 1.1.6 Border Width
| Token | Value | Usage |
|-------|-------|-------|
| `border-0` | 0px | No border |
| `border-1` | 1px | Default dividers |
| `border-2` | 2px | Focus rings, active states |
| `border-4` | 4px | Emphasis, errors |
### 1.2 Semantic Tokens
#### 1.2.1 Surface Colors
| Token | Primitive Reference | Usage |
|-------|---------------------|-------|
| `surface-primary` | `neutral-0` | Main background |
| `surface-secondary` | `neutral-50` | Card background |
| `surface-elevated` | `neutral-0` + shadow | Modal, popover |
| `surface-overlay` | `neutral-1000` @ 50% | Backdrop |
#### 1.2.2 Text Colors
| Token | Primitive Reference | Usage |
|-------|---------------------|-------|
| `text-primary` | `neutral-1000` | Headings, body |
| `text-secondary` | `neutral-600` | Captions, metadata |
| `text-muted` | `neutral-400` | Placeholders |
| `text-inverse` | `neutral-0` | On dark surfaces |
#### 1.2.3 Border Colors
| Token | Primitive Reference | Usage |
|-------|---------------------|-------|
| `border-default` | `neutral-200` | Dividers |
| `border-focus` | `brand-500` | Focus rings |
| `border-error` | `semantic-red-500` | Error states |
### 1.3 Component Tokens
#### 1.3.1 Button
| Token | Semantic Reference | Usage |
|-------|-------------------|-------|
| `button-primary-bg` | `surface-primary` | Default background |
| `button-primary-bg-hover` | `brand-600` | Hover state |
| `button-primary-text` | `text-inverse` | Label color |
#### 1.3.2 Data Grid
| Token | Semantic Reference | Usage |
|-------|-------------------|-------|
| `table-row-hover` | `neutral-50` | Hover highlight |
| `table-header-bg` | `neutral-100` | Header background |
### 1.4 Export Format
Canonical: DTCG JSON. Derived: CSS custom properties, Tailwind config, Swift, Kotlin.
See `TOKEN_PHILOSOPHY.md` §3 for full specification.
---
## 2. Layout System
> **Status:** ⏳ Ej påbörjad
### 2.1 Grid
### 2.2 Breakpoints
| Name | Width | Target |
|------|-------|--------|
| `xs` | 0px | Phone portrait |
| `sm` | 640px | Phone landscape |
| `md` | 768px | Tablet portrait |
| `lg` | 1024px | Tablet landscape / small desktop |
| `xl` | 1280px | Desktop |
| `2xl` | 1536px | Large desktop |
### 2.3 Container Widths
### 2.4 Sidebar / Panel System
---
## 3. Typography
> **Status:** ✅ Specified
> **Principle:** Typography is the foundation of information hierarchy. It must be readable, scannable, and calm.
### 3.1 Font Stack
| Role | Font | Fallback | Usage |
|------|------|----------|-------|
| **Primary** | Inter | system-ui, -apple-system, sans-serif | All UI text, headings, body |
| **Monospace** | JetBrains Mono | ui-monospace, SFMono-Regular, monospace | Data, code, coordinates, tabular figures |
**Rationale:** Inter is designed for screen readability at small sizes. JetBrains Mono distinguishes similar characters (0/O, 1/l/I) — critical for geodata.
### 3.2 Font Fallback Strategy
```css
/* Primary */
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
/* Monospace */
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
```
### 3.3 Variable Font
- **Inter:** Variable font enabled (weight 100900)
- **JetBrains Mono:** Static weights only (400, 500, 700)
- **Performance:** Subset to Latin Extended + Swedish characters
### 3.4 Type Scale
| Token | Size | Line Height | Weight | Letter Spacing | Usage |
|-------|------|-------------|--------|----------------|-------|
| `text-xs` | 12px | 16px | 400 | 0.01em | Captions, metadata, timestamps |
| `text-sm` | 14px | 20px | 400 | 0 | Body small, secondary text |
| `text-base` | 16px | 24px | 400 | 0 | Body, paragraphs |
| `text-lg` | 18px | 28px | 400 | -0.01em | Lead paragraph, emphasis |
| `text-xl` | 20px | 28px | 500 | -0.02em | Section headers, card titles |
| `text-2xl` | 24px | 32px | 500 | -0.02em | Page titles, modal headers |
| `text-3xl` | 30px | 36px | 600 | -0.02em | Major headings, dashboard titles |
| `text-4xl` | 36px | 40px | 600 | -0.03em | Hero titles, empty states |
### 3.5 Information Levels
Typography is defined by **function**, not heading level.
| Level | Token | Weight | Usage | Frequency |
|-------|-------|--------|-------|-----------|
| **Display** | `text-4xl` | 600 | Landing pages, special overviews | Very rare |
| **Section Title** | `text-3xl` | 600 | Page titles, major sections | Rare |
| **Panel Title** | `text-2xl` | 500 | Card titles, modal headers | Common |
| **Data Heading** | `text-xl` | 500 | Table headers, KPI groups, map layers | Frequent |
| **Body** | `text-base` | 400 | Running text, descriptions | Default |
| **UI Label** | `text-sm` | 500 | Form labels, control labels | Frequent |
| **Metadata** | `text-sm` | 400 | Dates, status, secondary info | Frequent |
| **Caption** | `text-xs` | 400 | Help text, explanations | Common |
| **Code / Numeric** | `text-sm` | 400 | Code, coordinates, IDs, numbers | As needed |
**Rationale:** Enterprise users scan for information, not headings. Functional levels map to mental models better than H1H6.
**Note:** When HTML heading elements are required for accessibility, map functionally:
- Display → H1 (once per page)
- Section Title → H2
- Panel Title → H3
- Data Heading → H4
- UI Label → label element (not heading)
### 3.6 Font Weights
| Token | Value | Usage |
|-------|-------|-------|
| `font-normal` | 400 | Body, descriptions |
| `font-medium` | 500 | Emphasis, labels, buttons |
| `font-semibold` | 600 | Headings, active states |
| `font-bold` | 700 | Strong emphasis, alerts |
### 3.7 Line Height Rules
| Context | Rule | Rationale |
|---------|------|-----------|
| Body text | 1.5× font size | Readability for long text |
| Headings | 1.21.3× font size | Tight, scannable |
| Data/numbers | 1.25× font size | Alignment in tables |
| Captions | 1.4× font size | Small text needs air |
### 3.8 Letter Spacing
| Context | Value | Rationale |
|---------|-------|-----------|
| Body | 0 | Natural reading |
| Headings | -0.02em | Tighter, more impactful |
| All-caps labels | 0.05em | Improves legibility |
| Captions | 0.01em | Slight openness |
### 3.9 Max Line Length
| Context | Max Width | Rationale |
|---------|-----------|-----------|
| Body text | 65ch | Optimal reading width |
| Data tables | none | Full width |
| Side panels | 45ch | Narrow column readability |
### 3.10 Monospace Usage
| Context | Font | Features |
|---------|------|----------|
| Coordinates | JetBrains Mono | Tabular figures, always |
| Financial data | JetBrains Mono | Tabular figures, decimal alignment |
| Code snippets | JetBrains Mono | Ligatures disabled |
| Timestamps | JetBrains Mono | Fixed width for alignment |
### 3.11 Tabular Figures
**Always enabled for:**
- Data tables
- Financial values
- Coordinates (lat/long)
- Timestamps
- IDs and reference numbers
```css
font-variant-numeric: tabular-nums;
```
### 3.12 Numerical Typography
| Rule | Value | Example |
|------|-------|---------|
| Decimal separator | `.` (period) | `123.45` |
| Thousands separator | ` ` (space) | `1 234 567` |
| Negative numbers | `-` prefix | `-456.78` |
| Percentage | No space before `%` | `42.5%` |
| Currency | Symbol before, space after | `$ 1 234.56` |
---
## 4. Color System
> **Status:** ✅ Foundation specified, Brand & Map pending
> **Principle:** Information dominates color. Color supports, never competes.
### 4.1 Foundation Colors (Neutral Palette)
**Primitive Tokens:**
| Token | Hex | Usage |
|-------|-----|-------|
| `neutral-0` | `#ffffff` | Pure white, highest elevation |
| `neutral-50` | `#f8f9fa` | Card backgrounds, hover states |
| `neutral-100` | `#f1f3f5` | Subtle backgrounds, table headers |
| `neutral-200` | `#e9ecef` | Borders, dividers |
| `neutral-300` | `#dee2e6` | Disabled backgrounds |
| `neutral-400` | `#ced4da` | Placeholder text, inactive icons |
| `neutral-500` | `#adb5bd` | Secondary text, muted elements |
| `neutral-600` | `#868e96` | Captions, metadata |
| `neutral-700` | `#495057` | Body text secondary |
| `neutral-800` | `#343a40` | Body text primary |
| `neutral-900` | `#212529` | Headings, primary text |
| `neutral-1000` | `#0f172a` | Deepest surfaces, overlays |
**Rationale:** 12-step scale provides sufficient granularity without excess. Based on open-source neutral scales (Open Color, Tailwind) for familiarity.
### 4.2 Semantic Colors
**Rule:** Semantic colors communicate meaning. Brand colors communicate identity. Never mix the two.
#### 4.2.1 Neutral Semantic
| Token | Primitive | Usage |
|-------|-----------|-------|
| `surface-primary` | `neutral-0` | Main background |
| `surface-secondary` | `neutral-50` | Card background |
| `surface-tertiary` | `neutral-100` | Table header, subtle fill |
| `surface-overlay` | `neutral-1000` @ 50% | Modal backdrop |
| `text-primary` | `neutral-900` | Headings, body |
| `text-secondary` | `neutral-600` | Captions, metadata |
| `text-muted` | `neutral-400` | Placeholders, disabled |
| `text-inverse` | `neutral-0` | On dark surfaces |
| `border-default` | `neutral-200` | Dividers, outlines |
| `border-strong` | `neutral-300` | Focus states, active |
#### 4.2.2 Status Semantic
| Token | Primitive | Usage |
|-------|-----------|-------|
| `success-surface` | `green-50` | Success background |
| `success-border` | `green-200` | Success outline |
| `success-text` | `green-700` | Success message |
| `warning-surface` | `yellow-50` | Warning background |
| `warning-border` | `yellow-200` | Warning outline |
| `warning-text` | `yellow-700` | Warning message |
| `danger-surface` | `red-50` | Error background |
| `danger-border` | `red-200` | Error outline |
| `danger-text` | `red-700` | Error message |
| `info-surface` | `blue-50` | Info background |
| `info-border` | `blue-200` | Info outline |
| `info-text` | `blue-700` | Info message |
**Rule:** Status colors are never used for branding. Brand colors are never used for status.
### 4.3 Brand Palette
> **Status:** ⏳ Pending brand definition
Landvex brand colors are used sparingly (510% of surface area).
| Token | Hex | Usage |
|-------|-----|-------|
| `brand-50` | — | Lightest tint |
| `brand-100` | — | Hover backgrounds |
| `brand-200` | — | Subtle highlights |
| `brand-300` | — | — |
| `brand-400` | — | — |
| `brand-500` | — | Primary action, links |
| `brand-600` | — | Hover state |
| `brand-700` | — | Active state |
| `brand-800` | — | — |
| `brand-900` | — | Deepest shade |
**MUST NOT:** Use brand colors in data visualization or map layers. **MUST NOT:** Use brand colors to communicate status. Brand = identity. Semantic = meaning. **MUST NOT** mix.
### 4.4 Semantic Status Colors
| Token | Hex | Usage |
|-------|-----|-------|
| `semantic-green-500` | `#40c057` | Success, active, positive |
| `semantic-yellow-500` | `#fcc419` | Warning, pending, attention |
| `semantic-red-500` | `#fa5252` | Error, danger, negative |
| `semantic-blue-500` | `#339af0` | Info, neutral highlight |
### 4.5 Data Visualization Colors
> **Status:** ⏳ Pending data viz requirements
Separate palette for charts, graphs, KPIs. Must be:
- Colorblind-safe
- Distinguishable in monochrome
- Consistent across all data products
### 4.6 Map Layer Palette
> **Status:** ⏳ Pending Mapbox configuration
Dedicated palette for geospatial visualization. Defined by **layer**, not individual color:
| Layer | Token | Rationale |
|-------|-------|-----------|
| Base Surface | `map-base-surface` | Subtle, non-competing |
| Terrain | `map-terrain` | Elevation indication |
| Water | `map-water` | Consistent with cartographic convention |
| Road Network | `map-road` | Hierarchy (motorway → path) |
| Administrative Boundaries | `map-boundary` | Dashed, muted |
| Building Footprints | `map-building` | Subtle 3D effect |
| Parcel Boundaries | `map-parcel` | Land ownership |
| Mission Layer | `map-mission` | Brand color, high visibility |
| Heatmap | `map-heatmap` | Gradient from neutral to alert |
| Selection | `map-selection` | Clear, non-intrusive |
| Hover | `map-hover` | Interactive feedback |
| Overlay | `map-overlay` | Contextual information |
| Night Accent | `map-night` | Dark mode adaptation |
| Satellite Accent | `map-satellite` | Satellite imagery overlay |
**Principle:** Map colors must work in all lighting conditions and never compete with data overlays.
**Rule:** Changing map style (light/dark/satellite) should only require swapping the map layer palette, not component colors.
---
## 5. Motion System
> **Status:** ✅ Specified
> **Principle:** Motion supports understanding. It never decorates.
### 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 Tokens (Intention-Based)
Tokens are named by **intention**, not duration. AI and developers reason about purpose, not milliseconds.
| Token | Value | Intention | Usage |
|-------|-------|-----------|-------|
| `duration-instant` | 0ms | No perceived time | State changes that must feel immediate |
| `duration-interaction` | 100ms | Micro-feedback | Hover, focus, press, toggle |
| `duration-transition` | 200ms | Element state change | Appear, disappear, expand, collapse |
| `duration-navigation` | 300ms | View change | Page transition, drawer, modal |
| `duration-layout` | 400ms | Structural shift | Sidebar expand, grid reorder |
| `duration-context-change` | 500ms | Major context switch | Full-screen overlay, wizard step |
| `duration-attention` | 600ms | Draw attention | Toast, alert, notification |
| `duration-background` | 1000ms | Ambient, non-blocking | Skeleton shimmer, progress sweep |
**Mapping to traditional terms:**
- `duration-interaction` ≈ "fast"
- `duration-transition` ≈ "normal"
- `duration-navigation` ≈ "slow"
- `duration-context-change` ≈ "slower"
### 5.3 Easing Functions
| Token | Value | Usage |
|-------|-------|-------|
| `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.4 Component-Specific Motion
#### 5.4.1 Button
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Hover | Background color shift | `duration-interaction` | ease-out |
| Press | Scale to 0.98 | `duration-instant` | ease-in |
| Release | Scale to 1.0 | `duration-interaction` | ease-out |
| Loading | Spinner rotation | `duration-background` | linear infinite |
| Disabled | Opacity to 0.5 | `duration-interaction` | ease-in-out |
#### 5.4.2 Modal
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Open | Fade in + scale from 0.95 | `duration-navigation` | ease-out |
| Close | Fade out + scale to 0.95 | `duration-transition` | ease-in |
| Backdrop | Fade to 50% opacity | `duration-navigation` | ease-in-out |
#### 5.4.3 Drawer
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Open | Slide from edge | `duration-navigation` | ease-out |
| Close | Slide to edge | `duration-transition` | ease-in |
| Backdrop | Fade to 50% opacity | `duration-navigation` | ease-in-out |
#### 5.4.4 Map Transitions
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Pan | None (instant) | `duration-instant` | — |
| Zoom | Smooth interpolation | `duration-navigation` | ease-in-out |
| Layer toggle | Cross-fade | `duration-transition` | ease-in-out |
| Feature select | Highlight pulse | `duration-navigation` | ease-out |
**MUST NOT:** Animate map panning. Users need instant feedback on drag.
#### 5.4.5 Skeleton
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Loading | Shimmer sweep | `duration-background` | ease-in-out infinite |
| Complete | Fade to content | `duration-transition` | ease-out |
#### 5.4.6 Progress
| State | Animation | Duration | Easing |
|-------|-----------|----------|--------|
| Indeterminate | Sweep | `duration-background` | linear infinite |
| Determinate | Width growth | `duration-navigation` | ease-out |
| Complete | Checkmark draw | `duration-transition` | 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
**MUST:** Animations 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) | `duration-navigation` | Indicate invalid input |
| Success | Checkmark draw + fade | `duration-layout` | Confirm completion |
| Warning | Pulse border color | `duration-attention` | Draw attention without alarm |
---
## 6. Elevation & Shadows
> **Status:** ⏳ Ej påbörjad
### 6.1 Shadow Scale
### 6.2 Usage Rules
---
## 7. Iconography
> **Status:** ⏳ Ej påbörjad
### 7.1 Icon Set
### 7.2 Icon Sizes
### 7.3 Icon + Text Pairing
---
## PART C — COMPONENTS
## 8. Component Architecture
> **Status:** ✅ Specified
> **Principle:** The system shall be minimal, not maximal. Extend only when a recurring need cannot be solved with existing components.
### 8.0 Component Tiers
#### Tier 1 — Canonical Components
These define the entire design system. If these are correct, everything else follows.
| Component | Purpose | Inherits From |
|-----------|---------|---------------|
| **Button** | Primary action trigger | — |
| **Input** | Text entry, form fields | — |
| **Select** | Choice from predefined options | — |
| **Card** | Content container, elevation | — |
| **Modal** | Overlay dialog, focus trap | — |
| **Navigation** | Primary wayfinding | — |
| **Table** | Tabular data display | — |
| **Data Grid** | Complex data with sorting/filtering | Table |
| **Search** | Find and filter | Input |
| **Map Panel** | Geospatial information overlay | Card |
#### Tier 2 — Derived Components
These build primarily on Tier 1. They introduce no new visual rules.
| Component | Purpose | Inherits From |
|-----------|---------|---------------|
| **Badge** | Status indicator | — |
| **Alert** | Contextual message | Card |
| **Toast** | Transient notification | Card |
| **Tooltip** | Contextual help | — |
| **Accordion** | Collapsible content sections | Card |
| **Tabs** | Content switching | — |
| **Breadcrumb** | Location indicator | — |
| **Pagination** | Page navigation | Button |
| **Empty State** | No content placeholder | Card |
| **Skeleton** | Loading placeholder | — |
#### Tier 3 — Domain Components
Landvex-specific. They explicitly build on Tier 1 and Tier 2. No new visual rules.
| Component | Purpose | Inherits From |
|-----------|---------|---------------|
| **KPI Card** | Key metric display | Card |
| **Map Overlay Panel** | Geospatial tool panel | Map Panel |
| **Layer Selector** | Map layer toggle | Select |
| **Mission Status** | Mission state indicator | Badge |
| **Dataset Card** | Data source preview | Card |
| **Geospatial Inspector** | Feature detail view | Card |
| **Asset Summary** | Asset overview panel | Card |
| **Coordinate Picker** | Lat/long input | Input |
| **Timeline Panel** | Temporal data view | Card |
### 8.1 Component Inheritance
Components inherit visual properties, states, and behavior from their parent. Child components may add but never override parent rules.
```
Button
├── Icon Button (adds icon, keeps all Button states)
├── Split Button (adds dropdown, keeps primary action)
└── Floating Action Button (fixed position, keeps Button behavior)
Card
├── KPI Card (adds metric layout, keeps Card elevation)
├── Dataset Card (adds metadata, keeps Card structure)
└── Mission Card (adds status, keeps Card padding)
Table
├── Data Grid (adds sorting/filtering, keeps Table structure)
├── Inspection Grid (adds checkboxes, keeps Data Grid behavior)
└── Asset Grid (adds thumbnails, keeps Data Grid behavior)
```
### 8.2 New Component Rule
**New component is the last resort.**
Before a new component may be created, verify:
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 may a new component be defined.
**New components must:**
- Inherit from an existing component when possible
- Follow the three-tier hierarchy
- Include full specification (states, ARIA, keyboard, touch, animation, acceptance criteria)
- Pass Design Review Gate
---
## 8.3 Component Specifications
> **Princip:** Varje komponent har en egen specifikation med states, ARIA, keyboard, touch, animation och acceptance criteria.
### RFC-001: Canonical Button Component
**Status:** ✅ Approved as reference implementation
**Tier:** 1 (Canonical)
**Inherits From:** — (root component)
---
#### Cognitive Purpose
Button exists to trigger actions that modify state or advance workflow. It is the primary interface between user intent and system response.
#### When to Use
- Primary user action (submit, confirm, save)
- Form submission
- Dialog confirmation
- Action that requires explicit user commitment
#### When NOT to Use
- Navigation (use Link)
- Toggling state (use Switch)
- Selecting from options (use Select)
- Secondary information reveal (use Accordion)
#### User Expectation
- Button is clickable
- Button responds immediately to press
- Button indicates if action is in progress
- Button indicates if action is unavailable
#### Interaction Contract
| Event | Response |
|-------|----------|
| Click | Action executes |
| Hover | Visual feedback only |
| Focus | Keyboard can activate |
| Loading | Further clicks ignored |
| Disabled | Action unavailable |
#### Failure Behaviour
| Scenario | Response |
|----------|----------|
| Action fails | Button returns to default, error shown elsewhere |
| Network fails | Button remains loading until timeout, then error |
| Validation fails | Button remains enabled, form shows errors |
#### Accessibility Contract
- Screen reader announces button role and label
- Focus is visible and logical
- Disabled state is communicated
- Loading state is communicated
#### AI Contract
| Scenario | AI Choice |
|----------|-----------|
| Primary action | Button |
| Navigation | Link |
| Toolbar action | Icon Button |
| Primary + secondary options | Split Button |
---
#### 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:**
| 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 `duration-interaction` ease-out |
| Focus | 2px ring `border-focus` | Ring appear `duration-interaction` ease-out |
| Pressed | Scale 0.98 | Scale `duration-instant` ease-in |
| Loading | Spinner replaces text | Spinner `duration-background` 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
**Anti-Patterns:**
- ❌ Two primary buttons in same view
- ❌ Disabled button without explanation
- ❌ Button that looks like link
- ❌ Loading button that remains clickable
**Related Components:**
- Split Button (extends Button)
- Icon Button (extends Button)
- Floating Action Button (extends Button)
**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
### 8.3.3 Select / Dropdown
### 8.3.4 Checkbox
### 8.3.5 Radio Button
### 8.3.6 Toggle / Switch
### 8.3.7 Card
### 8.3.8 Modal / Dialog
### 8.3.9 Toast / Notification
### 8.3.10 Tooltip
### 8.3.11 Data Table
### 8.3.12 Tabs
### 8.3.13 Navigation
### 8.3.14 Sidebar
### 8.3.15 Map Overlay
---
## 9. Maps & Geospatial Layer
> **Status:** ⏳ Ej påbörjad
### 9.1 Mapbox Configuration
### 9.2 Layer Styles
### 9.3 Interaction Patterns
### 9.4 Geometries & Coordinates Display
---
## 10. Accessibility
> **Status:** ⏳ Ej påbörjad
### 10.1 WCAG 2.1 AA Compliance
### 10.2 Keyboard Navigation
### 10.3 Screen Reader Support
### 10.4 Focus Management
### 10.5 Color Contrast
### 10.6 Reduced Motion
---
## 11. Responsive Behavior
> **Status:** ⏳ Ej påbörjad
### 11.1 Device Adaptive Principles
### 11.2 Phone (< 768px)
### 11.3 Tablet (768px - 1024px)
### 11.4 Desktop (> 1024px)
---
## 12. Empty States
> **Status:** ⏳ Ej påbörjad
### 12.1 Principles
### 12.2 Patterns by Context
---
## 13. Loading States
> **Status:** ⏳ Ej påbörjad
### 13.1 Skeleton Patterns
### 13.2 Progress Indicators
### 13.3 Stepped Loading
---
## 14. Error States
> **Status:** ⏳ Ej påbörjad
### 14.1 Error Message Structure
### 14.2 Inline Errors
### 14.3 Page-Level Errors
### 14.4 Toast Errors
---
## PART D — VALIDATION
## 15. Acceptance Criteria
> **Status:** ⏳ Ej påbörjad
### 15.1 Definition of Done (Design)
### 15.2 Checklist per Component
### 15.3 Cross-Browser Requirements
---
## 16. Design QA
> **Status:** ⏳ Ej påbörjad
### 16.1 Manual Review Process
### 16.2 Automated Checks (Future)
### 16.3 Screenshot Review
---
## 17. Visual Regression
> **Status:** ⏳ Ej påbörjad — Fas 3
### 17.1 Scope
### 17.2 Tools
### 17.3 Thresholds
---
## 18. Release Checklist
> **Status:** ⏳ Ej påbörjad
### 18.1 Pre-Release Design Review
### 18.2 Post-Release Verification
---
## ÄNDRINGSHISTORIA
| Version | Datum | Beskrivning |
|---------|-------|-------------|
| 1.0-skeleton | 2026-07-02 | Initial struktur med 18 kapitel, tomt innehåll |
---
## STATUS
**DRAFT — Under utveckling**
- Fyll på kapitel successivt
- Markera kapitel som ✅ när de är specificerade och godkända
- Brytande ändringar: Kräver Architecture Review