02-Architecture / 02.03.UI-Design-System02.03.UI Design System
02.03. UI Design System
Overview
This document describes the LuxDrive design system implementation for Car Pulse Tracker. The system uses a design tokens architecture where all styling is controlled from a single CSS file (style.css), not hardcoded in Vue components.
Architecture
style.css
├── :root (Design Tokens)
│ ├── Colors
│ ├── Typography
│ ├── Spacing
│ ├── Border Radius
│ ├── Shadows
│ └── Transitions
├── @layer base (Base Styles)
├── @layer components (Semantic Classes)
└── @layer utilities (Utility Classes)
Design Tokens
Colors
| Token |
Value |
Usage |
--color-primary |
30 41 59 (deep indigo-slate) |
Primary brand color, buttons, links |
--color-primary-hover |
30 64 175 (indigo-700) |
Hover state for primary |
--color-background |
248 250 255 (cool off-white) |
Page background |
--color-surface |
238 242 255 (indigo-50) |
Card backgrounds, inputs |
--color-surface-dark |
15 23 42 (slate-900) |
Dark panels (brand selector) |
--color-foreground |
15 23 42 (slate-900) |
Primary text |
--color-foreground-light |
255 255 255 (white) |
Text on dark backgrounds |
--color-foreground-muted |
71 85 105 (slate-600) |
Secondary text |
--color-border |
226 232 240 (slate-200) |
Default borders |
Semantic Colors
| Token |
Value |
Usage |
--color-success |
34 197 94 (green-500) |
Success states |
--color-warning |
234 179 8 (yellow-500) |
Warning states |
--color-error |
239 68 68 (red-500) |
Error states |
--color-info |
59 130 246 (blue-500) |
Info states |
Typography
| Token |
Value |
--font-family-base |
'Inter', system-ui, sans-serif |
--font-family-display |
'Playfair Display', Georgia, serif |
Font Sizes:
- --font-size-xs: 0.625rem (10px)
- --font-size-sm: 0.75rem (12px)
- --font-size-base: 0.875rem (14px)
- --font-size-lg: 1rem (16px)
- --font-size-xl: 1.25rem (20px)
- --font-size-2xl: 1.5rem (24px)
- --font-size-3xl: 2rem (32px)
- --font-size-4xl: 2.5rem (40px)
- --font-size-7xl: 5rem (80px - hero)
Spacing
| Token |
Value |
--spacing-xs |
0.25rem (4px) |
--spacing-sm |
0.5rem (8px) |
--spacing-md |
1rem (16px) |
--spacing-lg |
1.5rem (24px) |
--spacing-xl |
2rem (32px) |
--spacing-2xl |
3rem (48px) |
Border Radius
| Token |
Value |
--radius-sm |
0.375rem (6px) |
--radius-md |
0.5rem (8px) |
--radius-lg |
0.75rem (12px) |
--radius-xl |
1rem (16px) |
--radius-2xl |
1.5rem (24px) |
--radius-full |
9999px |
Product Branding
Current CPT product branding is split into two separate concerns:
- CPT product logo: currently embedded directly in
src/components/Header.vue as inline SVG
- OEM manufacturer logos: stored separately under
src/assets/images/brands/
This distinction should remain explicit. Tesla, BMW, Audi, and other OEM marks are not substitutes for the CPT product mark.
Planned Branding Update
Approved concept source:
/Users/petrisandholm/Downloads/cpt-brand2.html
Implementation target:
- export reusable CPT branding asset(s), preferably SVG
- store them under
src/assets/images/branding/
- replace the inline logo in
Header.vue
- update header/logo styling in
style.css
Branding Rules
- Product logo should be asset-based, not embedded ad hoc in component markup
- Header logo must work on both desktop and mobile
- OEM logo styling and CPT product branding should not share the same asset path
- If the visible brand name changes from
Car Pulse Tracker to a stronger CPT lockup, config and product copy must be updated intentionally rather than incidentally
Component Classes
Navigation
<nav class="nav">
<div class="nav-brand">
<div class="logo-icon">
<span class="logo-icon-text">C</span>
</div>
<span>Car Pulse Tracker</span>
</div>
<div class="lang-selector">
<button class="lang-option lang-option-active">EN</button>
<button class="lang-option">FI</button>
</div>
</nav>
<!-- Primary button -->
<button class="btn btn-primary">Continue</button>
<!-- Secondary button -->
<button class="btn btn-secondary">Cancel</button>
<!-- Ghost button -->
<button class="btn btn-ghost">Learn More</button>
<!-- Full width -->
<button class="btn btn-primary w-full">Submit</button>
Cards
<!-- Standard card -->
<div class="card">Content</div>
<!-- Card with hover effect -->
<div class="card card-hover">Hoverable</div>
<!-- Selected card -->
<div class="card card-selected">Selected</div>
<!-- Dark card -->
<div class="card-dark">Dark content</div>
Plan Cards (Payment)
<div class="plan-grid">
<div class="plan-card">
<h3 class="plan-title">Basic Report</h3>
<p class="plan-price">€19</p>
<p class="plan-desc">Service history, battery health</p>
</div>
<div class="plan-card plan-card-popular">
<span class="plan-badge">Popular</span>
<h3 class="plan-title">Pro Report</h3>
<p class="plan-price">€29</p>
</div>
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" class="form-input" placeholder="you@example.com" />
</div>
<label class="terms-row">
<input type="checkbox" class="checkbox" />
<span class="terms-text">I agree to the terms</span>
</label>
Alerts
<div class="alert alert-error">Error message</div>
<div class="alert alert-success">Success message</div>
<div class="alert alert-warning">Warning message</div>
<div class="alert alert-info">Info message</div>
Report Sections
<div class="report-section">
<div class="report-section-header">
<span class="report-section-indicator indicator-success"></span>
<h3 class="report-section-title">Service History</h3>
</div>
<div class="data-grid">
<div class="data-item">
<span class="data-label">Battery Level</span>
<span class="data-value">85%</span>
</div>
</div>
</div>
Info Lists
<div class="info-list">
<div class="info-list-item">
<span class="info-list-label">Make:</span>
<span class="info-list-value">Tesla</span>
</div>
<div class="info-list-item">
<span class="info-list-label">Model:</span>
<span class="info-list-value">Model 3</span>
</div>
</div>
Alert Badges
<span class="alert-badge badge-high">High</span>
<span class="alert-badge badge-medium">Medium</span>
<span class="alert-badge badge-low">Low</span>
Modals
<div class="modal-backdrop">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Title</h2>
<button class="modal-close">×</button>
</div>
<div class="modal-body">Content</div>
<div class="modal-footer">
<button class="btn btn-secondary">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
Spinner
<!-- Default spinner -->
<div class="spinner"></div>
<!-- Light spinner (on dark background) -->
<div class="spinner spinner-light"></div>
Utility Classes
Layout
| Class |
Description |
flex-center |
Flex container, centered |
flex-between |
Flex container, space-between |
gap-sm |
8px gap |
gap-md |
16px gap |
gap-lg |
24px gap |
w-full |
100% width |
Spacing (Stack)
| Class |
Description |
stack-sm |
8px vertical spacing between children |
stack-md |
16px vertical spacing |
stack-lg |
24px vertical spacing |
stack-xl |
32px vertical spacing |
Typography
| Class |
Description |
text-primary |
Primary text color |
text-muted |
Muted text color |
text-subtle |
Subtle text color |
text-success |
Success color |
text-error |
Error color |
font-display |
Playfair Display font |
font-base |
Inter font |
font-black |
900 font weight |
Visibility
| Class |
Description |
hide-mobile |
Hidden on mobile, visible on desktop |
hide-desktop |
Visible on mobile, hidden on desktop |
no-print |
Hidden when printing |
Responsive Breakpoints
| Breakpoint |
Width |
| Mobile |
< 640px |
| Tablet |
640px - 768px |
| Desktop |
768px - 1024px |
| Large |
> 1024px |
Theming
To change the entire app's appearance, modify the CSS variables in :root:
:root {
/* Change primary color to blue */
--color-primary: 59 130 246;
/* Change to dark theme */
--color-background: 9 9 11;
--color-foreground: 255 255 255;
--color-surface: 24 24 27;
}
No component changes are required when modifying the theme.
Component File Reference
| Component |
Purpose |
Key Classes Used |
Header.vue |
Navigation bar |
nav, nav-brand, lang-selector |
Hero.vue |
Landing hero section |
hero-section, feature-grid, feature-card |
BrandSelector.vue |
Brand selection panel |
split-panel-dark, selection-item-dark |
ModelSelector.vue |
Model selection panel |
split-panel-light, model-item, stats-grid |
PaymentStep.vue |
Payment plan selection |
plan-grid, plan-card, payment-methods-grid |
PaymentGateway.vue |
Payment processing |
card, gateway-container |
PaymentSuccessStep.vue |
Success confirmation |
success-icon, success-title |
OAuthStep.vue |
OAuth connection |
card, stack-lg |
MockupTeslaLogin.vue |
Login form mockup |
login-card, form-group, form-input |
ServiceAgreement.vue |
Terms modal |
modal-backdrop, modal-content |
ReportDashboard.vue |
Final report display |
report-section, data-grid, info-list |
Build
cd bnc-cpt-wui/src/vue/app
npm run build
Output:
- JS: ~256 KB (gzip: ~85 KB)
- CSS: ~45 KB (gzip: ~8 KB)