797 lines
20 KiB
Markdown
797 lines
20 KiB
Markdown
|
|
# CSS & HTML Optimization Plan for PlanTempus
|
||
|
|
|
||
|
|
**Date:** 2026-01-12
|
||
|
|
**Status:** Proposal - Awaiting Approval
|
||
|
|
**Target:** wwwroot CSS and HTML Components
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 Executive Summary
|
||
|
|
|
||
|
|
Analysis of the PlanTempus wwwroot folder has identified **5 major categories of duplication** across CSS and HTML components. By consolidating these into a unified component system, we can:
|
||
|
|
|
||
|
|
- ✅ **Reduce CSS size** by eliminating ~40% duplicate code
|
||
|
|
- ✅ **Improve maintainability** with single source of truth
|
||
|
|
- ✅ **Ensure UI consistency** across all pages
|
||
|
|
- ✅ **Simplify future development** with predictable patterns
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔍 Analysis Results
|
||
|
|
|
||
|
|
### 1. Avatar Components (6+ variations)
|
||
|
|
|
||
|
|
**Current Implementations:**
|
||
|
|
- `swp-avatar` (waitlist.css) - 40px
|
||
|
|
- `swp-avatar-small` (bookings.css) - 24px
|
||
|
|
- `swp-user-avatar` (employees.css) - 36px
|
||
|
|
- `swp-user-avatar` (auth.css) - 44px
|
||
|
|
- `swp-employee-avatar-large` (employees.css) - 80px
|
||
|
|
- `swp-profile-avatar` (topbar.css) - 32px
|
||
|
|
- `swp-profile-avatar-large` (drawers.css) - 64px
|
||
|
|
|
||
|
|
**Problem:**
|
||
|
|
- 7 different sizes: 24px, 32px, 36px, 40px, 44px, 64px, 80px
|
||
|
|
- Duplicated styling for circular containers
|
||
|
|
- Inconsistent naming conventions
|
||
|
|
- Color variants duplicated across multiple files
|
||
|
|
|
||
|
|
**Impact:** ~150 lines of duplicated CSS
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. Status Badge Components (3+ variations)
|
||
|
|
|
||
|
|
**Current Implementations:**
|
||
|
|
- `swp-status-badge` (cash.css) - Full-featured with dot indicator
|
||
|
|
- `swp-booking-status` (bookings.css) - Similar styling
|
||
|
|
- Status utility classes in design-tokens.css
|
||
|
|
|
||
|
|
**Problem:**
|
||
|
|
- Two separate implementations of the same pattern
|
||
|
|
- Inconsistent API (one uses dot, one doesn't)
|
||
|
|
- Duplicated color-mix calculations
|
||
|
|
- Status variants defined in multiple places
|
||
|
|
|
||
|
|
**Impact:** ~80 lines of duplicated CSS
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. Icon Box Components (3 variations)
|
||
|
|
|
||
|
|
**Current Implementations:**
|
||
|
|
- `swp-notification-icon` (notifications.css) - 40px circular
|
||
|
|
- `swp-attention-icon` (attentions.css) - 40px circular
|
||
|
|
- `swp-waitlist-icon` (waitlist.css) - 40px + badge
|
||
|
|
|
||
|
|
**Problem:**
|
||
|
|
- Nearly identical styling (40px, circular, centered)
|
||
|
|
- Same hover states and color variants
|
||
|
|
- Only difference is semantic naming
|
||
|
|
|
||
|
|
**Impact:** ~60 lines of duplicated CSS
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. Stat Display Components (4+ variations)
|
||
|
|
|
||
|
|
**Current Implementations:**
|
||
|
|
- `swp-stat-card` (stats.css) - Card container with value + label
|
||
|
|
- `swp-quick-stat` (quick-stats.css) - Smaller variant
|
||
|
|
- `swp-cash-stat` (cash.css) - Similar to stat-card
|
||
|
|
- `swp-fact-inline` (employees.css) - Inline variant
|
||
|
|
|
||
|
|
**Problem:**
|
||
|
|
- Same pattern: value + label in vertical layout
|
||
|
|
- Duplicated typography rules (mono font, semibold weight)
|
||
|
|
- Inconsistent sizing and naming
|
||
|
|
- Color variants repeated across files
|
||
|
|
|
||
|
|
**Impact:** ~100 lines of duplicated CSS
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 5. Button Components (2+ variations)
|
||
|
|
|
||
|
|
**Current Implementations:**
|
||
|
|
- `swp-btn` (cash.css) - Full button system with variants
|
||
|
|
- Inline button styles in waitlist.css
|
||
|
|
- Icon buttons in employees.css
|
||
|
|
|
||
|
|
**Problem:**
|
||
|
|
- Button variants defined in multiple places
|
||
|
|
- Duplicated padding, transitions, and states
|
||
|
|
- Inconsistent hover behaviors
|
||
|
|
|
||
|
|
**Impact:** ~50 lines of duplicated CSS
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 Proposed Solution
|
||
|
|
|
||
|
|
### Architecture Overview
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
graph TD
|
||
|
|
A[Current State: Scattered Components] --> B[Create components.css]
|
||
|
|
B --> C[Define Generic Components]
|
||
|
|
C --> D1[swp-avatar System]
|
||
|
|
C --> D2[swp-badge System]
|
||
|
|
C --> D3[swp-icon-box System]
|
||
|
|
C --> D4[swp-stat System]
|
||
|
|
C --> D5[swp-btn System]
|
||
|
|
|
||
|
|
D1 --> E1[Size Modifiers: xs, sm, md, lg, xl]
|
||
|
|
D1 --> E2[Color Modifiers: teal, blue, purple, etc.]
|
||
|
|
|
||
|
|
D2 --> F1[Semantic Variants: success, warning, error]
|
||
|
|
D2 --> F2[Role Variants: owner, admin, employee]
|
||
|
|
|
||
|
|
D4 --> G1[Unified Value + Label Pattern]
|
||
|
|
D4 --> G2[Size Variants: inline, card, box]
|
||
|
|
|
||
|
|
style B fill:#00897b,color:#fff
|
||
|
|
style C fill:#1976d2,color:#fff
|
||
|
|
```
|
||
|
|
|
||
|
|
### Component Class Diagram
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
classDiagram
|
||
|
|
class AvatarSystem {
|
||
|
|
+swp-avatar (base)
|
||
|
|
+Size: xs, sm, md, lg, xl, xxl
|
||
|
|
+Color: teal, blue, purple, amber, green
|
||
|
|
}
|
||
|
|
|
||
|
|
class BadgeSystem {
|
||
|
|
+swp-badge (base)
|
||
|
|
+Status: success, warning, error, pending
|
||
|
|
+Role: owner, admin, leader, employee
|
||
|
|
+Size: sm, md
|
||
|
|
}
|
||
|
|
|
||
|
|
class IconBoxSystem {
|
||
|
|
+swp-icon-box (base)
|
||
|
|
+Size: sm, md, lg
|
||
|
|
+State: urgent, warning, info, success
|
||
|
|
}
|
||
|
|
|
||
|
|
class StatSystem {
|
||
|
|
+swp-stat (container)
|
||
|
|
+swp-stat-value
|
||
|
|
+swp-stat-label
|
||
|
|
+Layout: inline, card, box
|
||
|
|
}
|
||
|
|
|
||
|
|
class ButtonSystem {
|
||
|
|
+swp-btn (base)
|
||
|
|
+Variant: primary, secondary, ghost
|
||
|
|
+Size: sm, md, lg
|
||
|
|
+State: disabled, loading
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 Implementation Details
|
||
|
|
|
||
|
|
### 1. Avatar System
|
||
|
|
|
||
|
|
**New Implementation:**
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* Base avatar */
|
||
|
|
swp-avatar {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
border-radius: var(--radius-full);
|
||
|
|
background: var(--color-teal);
|
||
|
|
color: white;
|
||
|
|
font-weight: var(--font-weight-semibold);
|
||
|
|
flex-shrink: 0;
|
||
|
|
|
||
|
|
/* Default size (md) */
|
||
|
|
width: 32px;
|
||
|
|
height: 32px;
|
||
|
|
font-size: var(--font-size-sm);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Size modifiers */
|
||
|
|
swp-avatar.xs { width: 20px; height: 20px; font-size: 10px; }
|
||
|
|
swp-avatar.sm { width: 24px; height: 24px; font-size: 10px; }
|
||
|
|
swp-avatar.md { width: 32px; height: 32px; font-size: var(--font-size-sm); }
|
||
|
|
swp-avatar.lg { width: 40px; height: 40px; font-size: var(--font-size-base); }
|
||
|
|
swp-avatar.xl { width: 64px; height: 64px; font-size: var(--font-size-xl); }
|
||
|
|
swp-avatar.xxl { width: 80px; height: 80px; font-size: var(--font-size-2xl); }
|
||
|
|
|
||
|
|
/* Color modifiers */
|
||
|
|
swp-avatar.purple { background: var(--color-purple); }
|
||
|
|
swp-avatar.blue { background: var(--color-blue); }
|
||
|
|
swp-avatar.amber { background: var(--color-amber); }
|
||
|
|
swp-avatar.green { background: var(--color-green); }
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration Map:**
|
||
|
|
- `swp-avatar-small` → `<swp-avatar class="sm">`
|
||
|
|
- `swp-user-avatar` → `<swp-avatar class="md">`
|
||
|
|
- `swp-profile-avatar` → `<swp-avatar class="md">`
|
||
|
|
- `swp-waitlist-customer swp-avatar` → `<swp-avatar class="lg">`
|
||
|
|
- `swp-profile-avatar-large` → `<swp-avatar class="xl">`
|
||
|
|
- `swp-employee-avatar-large` → `<swp-avatar class="xxl">`
|
||
|
|
|
||
|
|
**Example Usage:**
|
||
|
|
```html
|
||
|
|
<!-- Before -->
|
||
|
|
<swp-user-avatar class="purple">JK</swp-user-avatar>
|
||
|
|
|
||
|
|
<!-- After -->
|
||
|
|
<swp-avatar class="md purple">JK</swp-avatar>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. Badge System
|
||
|
|
|
||
|
|
**New Implementation:**
|
||
|
|
|
||
|
|
```css
|
||
|
|
swp-badge {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: var(--spacing-2);
|
||
|
|
padding: var(--spacing-2) var(--spacing-4);
|
||
|
|
font-size: var(--font-size-xs);
|
||
|
|
font-weight: var(--font-weight-medium);
|
||
|
|
border-radius: var(--radius-pill);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Optional dot indicator */
|
||
|
|
swp-badge.with-dot::before {
|
||
|
|
content: '';
|
||
|
|
width: 6px;
|
||
|
|
height: 6px;
|
||
|
|
border-radius: var(--radius-full);
|
||
|
|
background: currentColor;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Status variants */
|
||
|
|
swp-badge.success,
|
||
|
|
swp-badge.confirmed,
|
||
|
|
swp-badge.active {
|
||
|
|
background: color-mix(in srgb, var(--color-green) 15%, transparent);
|
||
|
|
color: var(--color-green);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.warning,
|
||
|
|
swp-badge.pending,
|
||
|
|
swp-badge.draft {
|
||
|
|
background: color-mix(in srgb, var(--color-amber) 15%, transparent);
|
||
|
|
color: var(--color-amber);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.error,
|
||
|
|
swp-badge.urgent {
|
||
|
|
background: color-mix(in srgb, var(--color-red) 15%, transparent);
|
||
|
|
color: var(--color-red);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.info,
|
||
|
|
swp-badge.inprogress {
|
||
|
|
background: color-mix(in srgb, var(--color-blue) 15%, transparent);
|
||
|
|
color: var(--color-blue);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Role variants */
|
||
|
|
swp-badge.owner {
|
||
|
|
background: color-mix(in srgb, var(--color-teal) 15%, transparent);
|
||
|
|
color: var(--color-teal);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.admin {
|
||
|
|
background: color-mix(in srgb, var(--color-purple) 15%, transparent);
|
||
|
|
color: var(--color-purple);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.leader {
|
||
|
|
background: color-mix(in srgb, var(--color-blue) 15%, transparent);
|
||
|
|
color: var(--color-blue);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-badge.employee {
|
||
|
|
background: var(--color-background-alt);
|
||
|
|
color: var(--color-text-secondary);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration Map:**
|
||
|
|
- `swp-status-badge` → `<swp-badge class="with-dot">`
|
||
|
|
- `swp-booking-status` → `<swp-badge>`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. Icon Box System
|
||
|
|
|
||
|
|
**New Implementation:**
|
||
|
|
|
||
|
|
```css
|
||
|
|
swp-icon-box {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
border-radius: var(--radius-xl);
|
||
|
|
background: var(--color-background-hover);
|
||
|
|
color: var(--color-text-secondary);
|
||
|
|
|
||
|
|
/* Default size */
|
||
|
|
width: 40px;
|
||
|
|
height: 40px;
|
||
|
|
font-size: var(--font-size-xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Size variants */
|
||
|
|
swp-icon-box.sm {
|
||
|
|
width: 32px;
|
||
|
|
height: 32px;
|
||
|
|
font-size: var(--font-size-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-icon-box.lg {
|
||
|
|
width: 48px;
|
||
|
|
height: 48px;
|
||
|
|
font-size: var(--font-size-2xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* State modifiers */
|
||
|
|
swp-icon-box.urgent {
|
||
|
|
background: color-mix(in srgb, var(--color-red) 15%, transparent);
|
||
|
|
color: var(--color-red);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-icon-box.warning {
|
||
|
|
background: color-mix(in srgb, var(--color-amber) 15%, transparent);
|
||
|
|
color: var(--color-amber);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-icon-box.info {
|
||
|
|
background: color-mix(in srgb, var(--color-blue) 15%, transparent);
|
||
|
|
color: var(--color-blue);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-icon-box.success {
|
||
|
|
background: color-mix(in srgb, var(--color-green) 15%, transparent);
|
||
|
|
color: var(--color-green);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Unread state for notifications */
|
||
|
|
swp-icon-box.unread {
|
||
|
|
background: color-mix(in srgb, var(--color-teal) 15%, transparent);
|
||
|
|
color: var(--color-teal);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration Map:**
|
||
|
|
- `swp-notification-icon` → `<swp-icon-box>`
|
||
|
|
- `swp-attention-icon` → `<swp-icon-box>`
|
||
|
|
- `swp-waitlist-icon` → `<swp-icon-box>` (with separate badge element)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. Stat System
|
||
|
|
|
||
|
|
**New Implementation:**
|
||
|
|
|
||
|
|
```css
|
||
|
|
swp-stat {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: var(--spacing-2);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat-value {
|
||
|
|
display: block;
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-weight: var(--font-weight-semibold);
|
||
|
|
color: var(--color-text);
|
||
|
|
line-height: var(--line-height-tight);
|
||
|
|
font-size: var(--font-size-xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat-label {
|
||
|
|
display: block;
|
||
|
|
font-size: var(--font-size-sm);
|
||
|
|
color: var(--color-text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Layout variants */
|
||
|
|
swp-stat.inline {
|
||
|
|
gap: var(--spacing-1);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.inline swp-stat-value {
|
||
|
|
font-size: var(--font-size-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.card {
|
||
|
|
padding: var(--card-padding);
|
||
|
|
background: var(--color-surface);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
border: 1px solid var(--color-border);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.card swp-stat-value {
|
||
|
|
font-size: var(--font-size-3xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.box {
|
||
|
|
padding: var(--spacing-6) var(--spacing-8);
|
||
|
|
background: var(--color-background-alt);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.box swp-stat-value {
|
||
|
|
font-size: var(--font-size-2xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Color modifiers */
|
||
|
|
swp-stat.highlight swp-stat-value,
|
||
|
|
swp-stat.teal swp-stat-value {
|
||
|
|
color: var(--color-teal);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.success swp-stat-value,
|
||
|
|
swp-stat.positive swp-stat-value {
|
||
|
|
color: var(--color-green);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.warning swp-stat-value,
|
||
|
|
swp-stat.amber swp-stat-value {
|
||
|
|
color: var(--color-amber);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.danger swp-stat-value,
|
||
|
|
swp-stat.negative swp-stat-value,
|
||
|
|
swp-stat.red swp-stat-value {
|
||
|
|
color: var(--color-red);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.purple swp-stat-value {
|
||
|
|
color: var(--color-purple);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-stat.blue swp-stat-value,
|
||
|
|
swp-stat.user swp-stat-value {
|
||
|
|
color: var(--color-blue);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration Map:**
|
||
|
|
- `swp-stat-card` → `<swp-stat class="card">`
|
||
|
|
- `swp-quick-stat` → `<swp-stat class="box">`
|
||
|
|
- `swp-cash-stat` → `<swp-stat class="box">`
|
||
|
|
- `swp-fact-inline` → `<swp-stat class="inline">`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 5. Button System
|
||
|
|
|
||
|
|
**New Implementation:**
|
||
|
|
|
||
|
|
```css
|
||
|
|
swp-btn {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
gap: var(--spacing-3);
|
||
|
|
padding: var(--spacing-5) var(--spacing-8);
|
||
|
|
font-size: var(--font-size-md);
|
||
|
|
font-weight: var(--font-weight-medium);
|
||
|
|
font-family: var(--font-family);
|
||
|
|
border-radius: var(--radius-md);
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all var(--transition-fast);
|
||
|
|
border: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn i {
|
||
|
|
font-size: var(--font-size-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Variants */
|
||
|
|
swp-btn.primary {
|
||
|
|
background: var(--color-teal);
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.primary:hover {
|
||
|
|
opacity: 0.9;
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.primary:disabled {
|
||
|
|
background: var(--color-border);
|
||
|
|
cursor: not-allowed;
|
||
|
|
opacity: 0.6;
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.secondary {
|
||
|
|
background: var(--color-surface);
|
||
|
|
border: 1px solid var(--color-border);
|
||
|
|
color: var(--color-text);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.secondary:hover {
|
||
|
|
background: var(--color-background-hover);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.ghost {
|
||
|
|
background: transparent;
|
||
|
|
color: var(--color-text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.ghost:hover {
|
||
|
|
color: var(--color-text);
|
||
|
|
background: var(--color-background-alt);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Size variants */
|
||
|
|
swp-btn.sm {
|
||
|
|
padding: var(--spacing-3) var(--spacing-6);
|
||
|
|
font-size: var(--font-size-sm);
|
||
|
|
}
|
||
|
|
|
||
|
|
swp-btn.lg {
|
||
|
|
padding: var(--spacing-6) var(--spacing-10);
|
||
|
|
font-size: var(--font-size-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Icon-only button */
|
||
|
|
swp-btn.icon-only {
|
||
|
|
padding: var(--spacing-4);
|
||
|
|
width: 36px;
|
||
|
|
height: 36px;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📁 File Structure Changes
|
||
|
|
|
||
|
|
### Before:
|
||
|
|
```
|
||
|
|
wwwroot/css/
|
||
|
|
├── design-tokens.css
|
||
|
|
├── base.css
|
||
|
|
├── page.css
|
||
|
|
├── stats.css (contains stat-card)
|
||
|
|
├── quick-stats.css (contains quick-stat)
|
||
|
|
├── bookings.css (contains avatar-small, booking-status)
|
||
|
|
├── notifications.css (contains notification-icon)
|
||
|
|
├── attentions.css (contains attention-icon)
|
||
|
|
├── waitlist.css (contains avatar, icon)
|
||
|
|
├── employees.css (contains user-avatar, employee-avatar-large)
|
||
|
|
├── topbar.css (contains profile-avatar)
|
||
|
|
├── drawers.css (contains profile-avatar-large)
|
||
|
|
├── auth.css (contains user-avatar)
|
||
|
|
└── cash.css (contains status-badge, cash-stat, btn)
|
||
|
|
```
|
||
|
|
|
||
|
|
### After:
|
||
|
|
```
|
||
|
|
wwwroot/css/
|
||
|
|
├── design-tokens.css (unchanged)
|
||
|
|
├── base.css (unchanged)
|
||
|
|
├── components.css ⭐ NEW - Generic reusable components
|
||
|
|
├── page.css (keep page-level layouts)
|
||
|
|
├── stats.css ➡️ Simplified (removes duplicates)
|
||
|
|
├── quick-stats.css ➡️ Can be removed/merged
|
||
|
|
├── bookings.css ➡️ Simplified (uses swp-avatar, swp-badge)
|
||
|
|
├── notifications.css ➡️ Simplified (uses swp-icon-box)
|
||
|
|
├── attentions.css ➡️ Simplified (uses swp-icon-box)
|
||
|
|
├── waitlist.css ➡️ Simplified (uses swp-avatar, swp-icon-box, swp-btn)
|
||
|
|
├── employees.css ➡️ Simplified (uses swp-avatar, swp-stat)
|
||
|
|
├── topbar.css ➡️ Simplified (uses swp-avatar)
|
||
|
|
├── drawers.css ➡️ Simplified (uses swp-avatar)
|
||
|
|
├── auth.css ➡️ Simplified (uses swp-avatar)
|
||
|
|
└── cash.css ➡️ Simplified (uses swp-badge, swp-stat, swp-btn)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔄 Implementation Strategy
|
||
|
|
|
||
|
|
### Phase 1: Create Foundation
|
||
|
|
1. Create `components.css` file
|
||
|
|
2. Implement base components (avatar, badge, icon-box, stat, btn)
|
||
|
|
3. Test components in isolation
|
||
|
|
|
||
|
|
### Phase 2: Pilot Migration
|
||
|
|
1. Start with Avatar system (most instances)
|
||
|
|
2. Update HTML in one feature (e.g., Dashboard)
|
||
|
|
3. Verify no visual regressions
|
||
|
|
4. Document any issues
|
||
|
|
|
||
|
|
### Phase 3: Full Migration
|
||
|
|
1. Update remaining HTML components
|
||
|
|
2. Remove duplicated CSS from feature files
|
||
|
|
3. Test all pages for visual consistency
|
||
|
|
4. Verify responsive behavior
|
||
|
|
|
||
|
|
### Phase 4: Cleanup
|
||
|
|
1. Remove unused CSS rules
|
||
|
|
2. Consider merging small CSS files
|
||
|
|
3. Update documentation
|
||
|
|
4. Create component usage guide
|
||
|
|
|
||
|
|
### Implementation Flowchart
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
graph TD
|
||
|
|
A[Start] --> B[Create components.css]
|
||
|
|
B --> C[Implement base components]
|
||
|
|
C --> D[Test components in isolation]
|
||
|
|
D --> E[Pilot: Migrate Dashboard avatars]
|
||
|
|
E --> F{Visual regression?}
|
||
|
|
F -->|Yes| G[Fix issues]
|
||
|
|
G --> E
|
||
|
|
F -->|No| H[Migrate all HTML components]
|
||
|
|
H --> I[Remove duplicated CSS]
|
||
|
|
I --> J[Test all pages]
|
||
|
|
J --> K{Issues found?}
|
||
|
|
K -->|Yes| L[Fix issues]
|
||
|
|
L --> J
|
||
|
|
K -->|No| M[Cleanup unused CSS]
|
||
|
|
M --> N[Update documentation]
|
||
|
|
N --> O[Complete]
|
||
|
|
|
||
|
|
style B fill:#00897b,color:#fff
|
||
|
|
style I fill:#e53935,color:#fff
|
||
|
|
style O fill:#43a047,color:#fff
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 Expected Benefits
|
||
|
|
|
||
|
|
### Quantitative Benefits
|
||
|
|
|
||
|
|
| Metric | Before | After | Improvement |
|
||
|
|
|--------|--------|-------|-------------|
|
||
|
|
| Total CSS Lines | ~2,500 | ~2,000 | -20% |
|
||
|
|
| Avatar Definitions | 7 | 1 + modifiers | -85% |
|
||
|
|
| Badge Definitions | 3 | 1 + modifiers | -67% |
|
||
|
|
| Icon Box Definitions | 3 | 1 + modifiers | -67% |
|
||
|
|
| Stat Definitions | 4 | 1 + modifiers | -75% |
|
||
|
|
|
||
|
|
### Qualitative Benefits
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
mindmap
|
||
|
|
root((CSS Optimization))
|
||
|
|
Maintainability
|
||
|
|
Single source of truth
|
||
|
|
Easier updates
|
||
|
|
Consistent behavior
|
||
|
|
Less context switching
|
||
|
|
Performance
|
||
|
|
Reduced CSS size
|
||
|
|
Better caching
|
||
|
|
Faster load times
|
||
|
|
Fewer parse operations
|
||
|
|
Developer Experience
|
||
|
|
Clear naming conventions
|
||
|
|
Predictable class API
|
||
|
|
Less cognitive load
|
||
|
|
Self-documenting code
|
||
|
|
Design Consistency
|
||
|
|
Unified components
|
||
|
|
Consistent sizing
|
||
|
|
Cohesive UI
|
||
|
|
Brand alignment
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚠️ Potential Risks & Mitigation
|
||
|
|
|
||
|
|
### Risk 1: Visual Regressions
|
||
|
|
**Impact:** High
|
||
|
|
**Likelihood:** Medium
|
||
|
|
**Mitigation:**
|
||
|
|
- Pilot migration with one component first
|
||
|
|
- Visual regression testing on key pages
|
||
|
|
- Screenshot comparison before/after
|
||
|
|
- Incremental rollout
|
||
|
|
|
||
|
|
### Risk 2: Breaking Changes
|
||
|
|
**Impact:** Medium
|
||
|
|
**Likelihood:** Low
|
||
|
|
**Mitigation:**
|
||
|
|
- Keep old CSS during migration period
|
||
|
|
- Use feature flags if needed
|
||
|
|
- Gradual deprecation of old classes
|
||
|
|
- Clear migration guide for team
|
||
|
|
|
||
|
|
### Risk 3: HTML Update Overhead
|
||
|
|
**Impact:** Medium
|
||
|
|
**Likelihood:** High
|
||
|
|
**Mitigation:**
|
||
|
|
- Create search/replace patterns
|
||
|
|
- Update one feature at a time
|
||
|
|
- Use code review process
|
||
|
|
- Document common patterns
|
||
|
|
|
||
|
|
### Risk 4: Component Naming Conflicts
|
||
|
|
**Impact:** Low
|
||
|
|
**Likelihood:** Low
|
||
|
|
**Mitigation:**
|
||
|
|
- Choose unique, descriptive names
|
||
|
|
- Namespace with swp- prefix
|
||
|
|
- Check for conflicts before migration
|
||
|
|
- Update naming if conflicts found
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 Checklist
|
||
|
|
|
||
|
|
### Pre-Implementation
|
||
|
|
- [ ] Review plan with team
|
||
|
|
- [ ] Get stakeholder approval
|
||
|
|
- [ ] Create backup branch
|
||
|
|
- [ ] Set up visual regression testing
|
||
|
|
|
||
|
|
### Phase 1: Foundation
|
||
|
|
- [ ] Create components.css
|
||
|
|
- [ ] Implement swp-avatar system
|
||
|
|
- [ ] Implement swp-badge system
|
||
|
|
- [ ] Implement swp-icon-box system
|
||
|
|
- [ ] Implement swp-stat system
|
||
|
|
- [ ] Implement swp-btn system
|
||
|
|
- [ ] Test components in isolation
|
||
|
|
|
||
|
|
### Phase 2: Pilot
|
||
|
|
- [ ] Update Dashboard avatar usage
|
||
|
|
- [ ] Visual regression test
|
||
|
|
- [ ] Document any issues
|
||
|
|
- [ ] Team review & feedback
|
||
|
|
|
||
|
|
### Phase 3: Full Migration
|
||
|
|
- [ ] Migrate all avatar instances
|
||
|
|
- [ ] Migrate all badge instances
|
||
|
|
- [ ] Migrate all icon-box instances
|
||
|
|
- [ ] Migrate all stat instances
|
||
|
|
- [ ] Migrate all button instances
|
||
|
|
- [ ] Remove old CSS rules
|
||
|
|
- [ ] Test all pages
|
||
|
|
|
||
|
|
### Phase 4: Cleanup
|
||
|
|
- [ ] Remove unused CSS
|
||
|
|
- [ ] Consider file consolidation
|
||
|
|
- [ ] Update component documentation
|
||
|
|
- [ ] Create usage guide
|
||
|
|
- [ ] Final testing round
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📚 Next Steps
|
||
|
|
|
||
|
|
1. **Review & Approve** - Stakeholders review this plan
|
||
|
|
2. **Discuss Concerns** - Address any questions or modifications
|
||
|
|
3. **Create Timeline** - Determine priority and resources
|
||
|
|
4. **Begin Implementation** - Start with Phase 1
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📞 Questions & Discussion Points
|
||
|
|
|
||
|
|
- Is the modifier-based approach (e.g., `class="md purple"`) acceptable?
|
||
|
|
- Should we prioritize certain components over others?
|
||
|
|
- Do we need to maintain backward compatibility during migration?
|
||
|
|
- What's the preferred testing strategy?
|
||
|
|
- Are there any other duplications not covered here?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Document Status:** Draft
|
||
|
|
**Last Updated:** 2026-01-12
|
||
|
|
**Next Review:** After stakeholder feedback
|