# ROA2WEB Design System **Version:** 1.0.0 | **Updated:** 2026-03-23 | **Status:** Source of Truth --- ## 1. Design Philosophy ROA2WEB is an ERP application for accounting and financial data management. The design prioritizes: - **Data density over whitespace** — Users process hundreds of records daily; every pixel earns its space - **Scan-friendly hierarchy** — Bold totals, muted labels, color-coded sections guide the eye - **Compact, not cramped** — 32px input height, 8px gaps, systematic spacing scale - **Professional restraint** — No decorative elements, no hero sections, no gratuitous animation - **Trust at the pixel level** — Financial data demands precision; misaligned inputs erode confidence - **System fonts for speed** — No custom font loading; native OS typography feels fast and familiar - **Progressive disclosure** — Show summary first, reveal detail on demand (collapsible sections, tooltips, bottom sheets) --- ## 2. Visual Identity ### Brand Colors | Role | Token | Hex (Light) | Usage | |------|-------|-------------|-------| | Primary | `--color-primary` | #2563eb | Actions, links, focus rings, active states | | Primary Dark | `--color-primary-dark` | #1d4ed8 | Hover states, pressed buttons | | Success | `--color-success` | #059669 | Approvals, positive values, confirmations | | Warning | `--color-warning` | #d97706 | Pending states, caution alerts, mismatches | | Error | `--color-error` | #dc2626 | Rejections, validation errors, negative values | | Info | `--color-info` | #0891b2 | Informational badges, OCR hints | ### Surface Hierarchy ``` Ground (#f8fafc) → Section → Card (#ffffff) → Overlay ↓ darkest ↓ lightest ↓ modal/dropdown ``` Each level uses progressively elevated surface tokens. In dark mode, the scale inverts (darker = lower, lighter = higher). | Token | Light | Dark | Purpose | |-------|-------|------|---------| | `--surface-ground` | #f8fafc | #0f172a | Page background | | `--surface-card` | #ffffff | #1e293b | Card/container backgrounds | | `--surface-hover` | #f1f5f9 | #334155 | Hover states, chips | | `--surface-border` | #e2e8f0 | #475569 | Borders, dividers | | `--text-color` | #111827 | #f9fafb | Primary text | | `--text-color-secondary` | #6b7280 | #d1d5db | Labels, hints, metadata | > **Full palette reference**: [DESIGN_TOKENS.md](./DESIGN_TOKENS.md) --- ## 3. Color System ### Semantic Color Roles Always use semantic tokens, never raw hex values: ```css /* Backgrounds */ background: var(--surface-card); /* Cards, modals */ background: var(--surface-ground); /* Page background */ background: var(--surface-hover); /* Hover states, chips */ /* Text */ color: var(--text-color); /* Primary text */ color: var(--text-color-secondary); /* Labels, hints */ /* Borders */ border-color: var(--surface-border); /* Standard borders */ ``` ### Status Color Scales Each status has a 50-900 scale. Use **100** for backgrounds, **800** for text in light mode: | Status | Background | Text | Border | Use For | |--------|-----------|------|--------|---------| | Success | `--green-100` | `--green-800` | `--green-300` | Approvals, positive values, OCR high confidence | | Warning | `--yellow-100` | `--yellow-800` | `--yellow-300` | Pending, caution, OCR medium confidence | | Error | `--red-100` | `--red-800` | `--red-300` | Rejections, errors, OCR low confidence | | Info | `--blue-100` | `--blue-800` | `--blue-300` | Informational, OCR hints, categorization | ### OCR Confidence Colors | Level | Threshold | Background | Text | Icon | |-------|-----------|-----------|------|------| | High | >= 85% | `--green-100` | `--green-800` | `pi-check-circle` | | Medium | >= 60% | `--yellow-100` | `--yellow-800` | `pi-exclamation-circle` | | Low | < 60% | `--red-100` | `--red-800` | `pi-question-circle` | ### Color-Coded Form Sections - **Green background** (`--green-50`): Financial values (TOTAL, payments, TVA) - **Blue background** (`--blue-50`): Categorization (expense type, description) - **Cyan background** (`--cyan-50`): Client/B2B information - **No background**: Standard form fields (supplier, document) --- ## 4. Typography ### Font Stack ```css font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-family: var(--font-mono); /* For financial values, code */ ``` ### Scale | Token | Size | Usage | |-------|------|-------| | `--text-xs` | 12px | Labels, badges, metadata, timestamps, hints | | `--text-sm` | 14px | Table cells, form inputs, secondary text, buttons | | `--text-base` | 16px | Body text, primary content (default) | | `--text-lg` | 18px | Subheadings | | `--text-xl` | 20px | Card titles, section headings | | `--text-2xl` | 24px | Page titles, metric values, KPI numbers | | `--text-3xl` | 32px | Dashboard hero numbers | | `--text-4xl` | 40px | Reserved for exceptional emphasis | ### Weight Rules | Token | Value | Usage | |-------|-------|-------| | `--font-normal` | 400 | Body text, table cells, descriptions | | `--font-medium` | 500 | Labels, buttons, badges, emphasis | | `--font-semibold` | 600 | Headings, financial totals, card titles | | `--font-bold` | 700 | TOTAL labels, strong emphasis, alerts | ### Financial Numbers All financial values use `font-variant-numeric: tabular-nums` for column alignment. --- ## 5. Spacing & Layout ### Spacing Scale | Token | Value | Primary Use | |-------|-------|------------| | `--space-xs` | 4px | Icon gaps, badge padding, tight internal spacing | | `--space-sm` | 8px | Related item gaps, compact form spacing | | `--space-md` | 16px | Standard component padding, section gaps | | `--space-lg` | 24px | Card padding, section separation | | `--space-xl` | 32px | Page margins, major separations | | `--space-2xl` | 48px | Layout sections | | `--space-3xl` | 64px | Page-level spacing | ### Form Layout Rules - **Input height**: 32px for compact forms, 40px for standalone forms - **Input font**: `--text-sm` (14px) for form inputs - **Numeric inputs**: `text-align: right` always - **Labels**: `--text-xs` (12px), `--font-semibold`, `--text-color-secondary` - **Layout**: Inline labels next to inputs on desktop, stacked on mobile - **Row gaps**: `--space-sm` (8px) between form rows - **Row borders**: `1px solid var(--surface-border)` between rows ### Breakpoints | Name | Width | Layout Behavior | |------|-------|----------------| | Extra-small | ≤360px | Payment inputs stack vertically, minimal padding | | Mobile | ≤768px | Single column, stacked forms, bottom sheets for filters | | Tablet | 769-1023px | Hybrid layout, wider cards | | Desktop | ≥1024px | Full multi-column, inline forms, side-by-side panels | | Wide | ≥1400px | Max content width, centered layout | --- ## 6. Component Patterns ### Cards ```css /* Standard card */ background: var(--surface-card); border-radius: var(--radius-md); /* 8px */ padding: var(--space-lg); /* 24px */ box-shadow: var(--shadow-sm); /* Compact card (data-dense views) */ padding: var(--space-md); /* 16px */ /* Metric/KPI card */ /* Icon + --text-2xl value + --text-xs label + trend indicator */ ``` ### Buttons - Primary: `--color-primary` background, white text - Secondary: Outlined or text-only (`severity="secondary"`) - Minimum touch target: 44x44px on mobile - Icon buttons: 32x32px on desktop, 44x44px on mobile ### Tables (PrimeVue DataTable) - Separate Debit/Credit columns (never stacked in one cell) - Filter buttons on dedicated row below filters - Export exports ALL data, not just current page - All overrides in `vendor/primevue-overrides.css` — never use `:deep()` in components ### Dropdowns in Compact Forms Use `dropdown-borderless` class for inline dropdowns: ```html ``` > **Full pattern reference**: [CSS_PATTERNS.md](./CSS_PATTERNS.md) --- ## 7. Form Design ### Receipt Form Layout (UnifiedReceiptForm) Ultra-compact flat layout with color-coded sections: ``` ┌─────────────────────────────────────────────┐ │ Furnizor [AutoComplete____] CUI [___] 🟢 ↻ │ ← white bg ├─────────────────────────────────────────────┤ │ OCR: Lidl Discount SRL (22891860) │ ← blue-50 hint ├─────────────────────────────────────────────┤ │ [Bon fiscal ▾] Nr. [___] Data [___] 🟢 │ ← white bg ├─────────────────────────────────────────────┤ │ TOTAL [_140px_] 🟢 Card [_120px_] │ ← green-50 bg │ Cash [_120px_] │ │ Alte [_120px_] 🟢 │ ├─────────────────────────────────────────────┤ │ TVA: [19% ▾][___] [9% ▾][___] 🟢 │ ← white bg ├─────────────────────────────────────────────┤ │ [Cheltuiala... ▾] │ ← blue-50 bg │ [Descriere...________________________] │ ├─────────────────────────────────────────────┤ │ Atasamente: [📄 bon.pdf ✕] [➕] │ ← white bg └─────────────────────────────────────────────┘ ``` ### OCR Confidence Badges - **Placement**: Inline, next to the input they describe - **Display**: Icon-only in compact form (percentage hidden, available via tooltip) - **Sizing**: Fixed reserved width (20px) to prevent input misalignment - **Hover**: `opacity: 0.85 → 1` transition for discoverability - **Interaction**: `cursor: help`, tooltip shows full percentage on hover/tap ### Input Conventions ```css /* All compact form inputs */ height: 32px; padding: var(--space-xs) var(--space-sm); font-size: var(--text-sm); border: 1px solid var(--surface-border); border-radius: var(--radius-sm); /* Financial inputs additionally */ text-align: right; font-variant-numeric: tabular-nums; ``` --- ## 8. Mobile Strategy ### Philosophy MD3-inspired (Material Design 3) touch-first design. Mobile is not "desktop shrunk" — it gets intentional layout. ### Required Components (ALL mobile views) | Component | Location | Purpose | |-----------|----------|---------| | MobileTopBar | `@shared/components/mobile/MobileTopBar.vue` | Fixed header (56px), title, actions | | MobileBottomNav | `@shared/components/mobile/MobileBottomNav.vue` | Fixed footer (56px), tab navigation | | BottomSheet | `@shared/components/mobile/BottomSheet.vue` | Filters, extended options (slides up) | | MobileSelectionFooter | `@shared/components/mobile/MobileSelectionFooter.vue` | Batch actions when items selected | ### Content Padding ```css .mobile-content { padding-top: 56px; /* MobileTopBar height */ padding-bottom: 56px; /* MobileBottomNav height */ } ``` ### Mobile Form Rules - **Full-width inputs**: Stacked vertically, `width: 100%` - **Payment inputs**: Equal-width side-by-side (`flex: 1 1 0%`), stack on extra-small (≤360px) - **Confidence badges**: Icon-only, no percentage text - **Filters**: Always in BottomSheet, never inline - **Selection actions**: Always in MobileSelectionFooter, never in header - **Upload zone**: Reduced height (24px min) after file selection ### Touch Targets - **Minimum**: 44x44px for all interactive elements (buttons, links, checkboxes) - **Input font**: 16px minimum to prevent iOS zoom - **Active feedback**: `scale(0.98)` on touch for responsive feel > **Full mobile guide**: [MOBILE_PATTERNS.md](./MOBILE_PATTERNS.md) --- ## 9. Dark Mode ### Three-Tier System | Mode | Trigger | CSS Selector | |------|---------|-------------| | Auto (default) | OS preference | `@media (prefers-color-scheme: dark)` + `:root:not([data-theme])` | | Light | User toggle | `[data-theme="light"]` on `` | | Dark | User toggle | `[data-theme="dark"]` on `` | ### CSS Priority ``` [data-theme="dark/light"] > @media (prefers-color-scheme) > :root (light default) ``` ### Implementation Pattern ```css /* Light mode (default) */ .component { background: var(--green-100); color: var(--green-800); } /* Dark mode — explicit toggle */ [data-theme="dark"] .component { background: rgba(34, 197, 94, 0.2); color: var(--green-400); } /* Dark mode — auto (prefers-color-scheme) */ @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .component { background: rgba(34, 197, 94, 0.2); color: var(--green-400); } } ``` ### Key Rules - **Always** use semantic tokens (`--surface-card`, `--text-color`) — they auto-adapt - **Status colors**: Use `rgba()` overlays in dark mode, not the inverted scale tokens - **Persistence**: `localStorage['user-theme']` stores user choice - **Toggle**: Theme button in `AppHeader.vue` cycles auto → light → dark ### Testing Checklist 1. Click theme button in header (cycles auto → light → dark) 2. DevTools → Rendering → Emulate CSS media → `prefers-color-scheme: dark` 3. Verify all text remains readable 4. Verify colored sections (green, blue) are visible but not harsh --- ## 10. Accessibility ### Targets - **WCAG 2.1 AA** compliance - **Color contrast**: `--color-text` provides 16.9:1 ratio, `--color-text-secondary` provides 4.6:1 ### Focus States ```css outline: 2px solid var(--color-primary); outline-offset: 2px; ``` ### Touch Targets - All interactive elements: minimum 44x44px on mobile - Added via `min-height: 44px; min-width: 44px` on touch devices ### Heading Hierarchy - Sequential: h1 > h2 > h3 (no skipping levels) - One `

` per page (page title) - `

` for sections, `

` for subsections ### Screen Reader Support - `.sr-only` class for visually hidden but accessible text - ARIA landmarks on major regions - Meaningful `alt` text on all images ### Reduced Motion ```css @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } ``` --- ## 11. Reference Index | Document | Purpose | Read When | |----------|---------|-----------| | [DESIGN_TOKENS.md](./DESIGN_TOKENS.md) | Complete token reference with all variables | Looking up specific token values | | [CSS_PATTERNS.md](./CSS_PATTERNS.md) | Reusable component CSS patterns | Building or styling components | | [ONBOARDING_CSS.md](./ONBOARDING_CSS.md) | CSS system quick start (5 min) | New to the project's frontend | | [MOBILE_PATTERNS.md](./MOBILE_PATTERNS.md) | Mobile component library and patterns | Building mobile views | | [ARCHITECTURE-DECISIONS.md](./ARCHITECTURE-DECISIONS.md) | Technical architecture decisions | Understanding "why" behind choices | ### CSS File Structure ``` src/assets/css/ ├── core/ # Foundation: variables, tokens, md3-tokens, reset, typography ├── layout/ # Grid, containers, navigation ├── components/ # Cards, buttons, tables, forms, stats ├── patterns/ # Interactive, dashboard, animations ├── utilities/ # Spacing, display, text, flex, colors ├── vendor/ # PrimeVue overrides (centralized, never :deep()) ├── mobile.css # Mobile optimizations (imported last) └── main.css # Entry point with import cascade ```