PlanTempusApp/OPTIMIZATION_PLAN.md
Janus C. H. Knudsen 15579acba8 Various CSS work
2026-01-12 22:10:57 +01:00

20 KiB

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

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

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:

/* 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:

<!-- Before -->
<swp-user-avatar class="purple">JK</swp-user-avatar>

<!-- After -->
<swp-avatar class="md purple">JK</swp-avatar>

2. Badge System

New Implementation:

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:

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:

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:

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

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

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