# 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 100–900) - **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 H1–H6. **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.2–1.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 (5–10% 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