# Solution Design Document

## Validation Checklist

- [x] All required sections are complete
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] All context sources are listed with relevance ratings
- [x] Project commands are discovered from actual project files
- [x] Constraints → Strategy → Design → Implementation path is logical
- [x] Architecture pattern is clearly stated with rationale
- [x] Every component in diagram has directory mapping
- [x] Every interface has specification
- [x] Error handling covers all error types
- [x] Quality requirements are specific and measurable
- [x] Every quality requirement has test coverage
- [x] **All architecture decisions confirmed by user**
- [x] Component names consistent across diagrams
- [x] A developer could implement from this design

---

## Constraints

CON-1 **Browser Compatibility**: Must work on Chrome, Safari, Firefox, Edge (latest 2 versions). CSS transitions must be hardware-accelerated.

CON-2 **Existing Dependencies**: Must work with MetisMenu jQuery plugin (v1.1.3) for submenu collapse/expand behavior. Cannot replace MetisMenu.

CON-3 **Mobile Behavior**: Must not interfere with existing mobile overlay behavior (<768px). Mobile sidebar already has its own collapse mechanism.

CON-4 **Build System**: CSS changes must go through the design system build process (`conductor build-css --minify`). Must update version.txt for cache busting.

CON-5 **No Breaking Changes**: Sidebar must remain fully functional during transition. All 14 main sections and 50+ sub-items must remain accessible.

## Implementation Context

### Required Context Sources

```yaml
# Internal documentation and patterns
- doc: CLAUDE.md
  relevance: HIGH
  why: "CSS build commands, testing commands, project structure"

- doc: public_html/css/admin/tokens.css
  relevance: HIGH
  why: "Design tokens for colors, spacing, transitions - must use existing variables"

# Source code files
- file: userfrosting/templates/themes/default/menus/sidebar.html
  relevance: CRITICAL
  sections: [sidebar-top, sidebar-nav, metismenu structure]
  why: "Main sidebar template that will be modified"

- file: public_html/css/admin/admin-theme.css
  relevance: CRITICAL
  sections: [lines 1698-2020, sidebar-wrapper, page-wrapper margin]
  why: "Current sidebar styling, width definitions, transitions"

- file: public_html/js/sb-admin-2.js
  relevance: HIGH
  why: "Current sidebar JS behavior, MetisMenu initialization"

- file: public_html/js/lib/metisMenu.js
  relevance: MEDIUM
  why: "MetisMenu plugin that controls submenu expand/collapse"

# External references
- url: https://uxplanet.org/best-ux-practices-for-designing-a-sidebar-9174ee0ecaa2
  relevance: MEDIUM
  why: "UX best practices for delays, animations, tooltips"
```

### Implementation Boundaries

- **Must Preserve**:
  - MetisMenu submenu functionality
  - Mobile overlay behavior (<768px)
  - Store picker dropdown functionality
  - All permission-based menu visibility
  - Current menu item structure and icons

- **Can Modify**:
  - `sidebar-wrapper` CSS (add collapsed state)
  - `#page-wrapper` margin (animate with sidebar)
  - `sb-admin-2.js` (add hover/pin logic)
  - `sidebar.html` template (add pin button, tooltip data attributes)

- **Must Not Touch**:
  - `metisMenu.js` library
  - Mobile sidebar toggle behavior
  - Route definitions
  - Permission checks in template

### External Interfaces

#### System Context Diagram

```mermaid
graph TB
    User[Admin User] --> Sidebar[Collapsible Sidebar]
    Sidebar --> MetisMenu[MetisMenu Plugin]
    Sidebar --> LocalStorage[(localStorage)]
    Sidebar --> PageWrapper[#page-wrapper]

    subgraph Browser
        Sidebar
        MetisMenu
        LocalStorage
        PageWrapper
    end
```

#### Interface Specifications

```yaml
# Client-side storage interface
storage:
  - name: "Sidebar Pin State"
    type: localStorage
    key: "bk_sidebar_pinned"
    values: ["true", "false", null]
    default: null (collapsed)
    why: "Persist user preference across sessions"

# CSS custom properties interface
css_variables:
  - name: "--sidebar-width-collapsed"
    value: "64px"
    why: "Icon-only width"

  - name: "--sidebar-width-expanded"
    value: "250px"
    why: "Full width with labels"

  - name: "--sidebar-transition-duration"
    value: "200ms"
    why: "Animation timing"

  - name: "--sidebar-hover-delay"
    value: "150ms"
    why: "Prevent accidental expansion"
```

### Project Commands

```bash
# CSS Build Commands
php userfrosting/conductor build-css           # Development build
php userfrosting/conductor build-css --minify  # Production build (generates version hash)
php userfrosting/conductor build-css --watch   # Watch mode for development

# Testing (no automated UI tests currently)
# Manual testing required across browsers

# Deployment
./deploy.sh  # Runs tests + deploys
```

## Solution Strategy

- **Architecture Pattern**: CSS-first progressive enhancement with JavaScript for interaction timing and state persistence
- **Integration Approach**: Extend existing sidebar styles with new collapsed state modifier classes. Add minimal JavaScript for hover timing and localStorage persistence.
- **Justification**: CSS transitions are hardware-accelerated and performant. JavaScript only handles timing logic and state - not layout or rendering. This keeps the feature lightweight and maintainable.
- **Key Decisions**:
  1. Use CSS custom properties for all dimension/timing values (consistent with design system)
  2. Use `transform` for collapsed/expanded transitions (GPU accelerated)
  3. Add `.sidebar-collapsed` modifier class to control state
  4. Use JavaScript timers for hover intent delays (not CSS hover)

## Building Block View

### Components

```mermaid
graph LR
    subgraph Template
        SB[sidebar.html]
        PIN[Pin Button]
        TT[Tooltip Data]
    end

    subgraph CSS
        AT[admin-theme.css]
        CM[collapsible-sidebar.css]
    end

    subgraph JavaScript
        SA[sb-admin-2.js]
        HC[HoverController]
        PS[PinState]
    end

    subgraph Browser
        LS[(localStorage)]
    end

    SB --> CM
    CM --> AT
    SA --> HC
    SA --> PS
    PS --> LS
    HC --> CM
```

### Directory Map

```
.
├── userfrosting/templates/themes/default/menus/
│   └── sidebar.html                         # MODIFY: Add pin button, tooltip attributes
│
├── public_html/css/admin/
│   ├── tokens.css                           # MODIFY: Add sidebar collapse variables
│   ├── admin-theme.css                      # MODIFY: Add collapsible sidebar styles
│   └── version.txt                          # AUTO: Updated by build process
│
├── public_html/js/
│   └── sb-admin-2.js                        # MODIFY: Add hover/pin controller logic
│
└── docs/specs/034-collapsible-sidebar-menu/
    ├── README.md                            # Tracking document
    ├── product-requirements.md              # Requirements (complete)
    └── solution-design.md                   # This document
```

### Interface Specifications

#### CSS Class Interface

```css
/* State modifier classes */
.sidebar-wrapper.sidebar-collapsed      /* Collapsed state (64px) */
.sidebar-wrapper.sidebar-expanded       /* Expanded state (250px) - hover or pinned */
.sidebar-wrapper.sidebar-pinned         /* User has pinned open */

/* Element visibility modifiers */
.sidebar-collapsed .sidebar-hide-collapsed  /* Hidden when collapsed */
.sidebar-collapsed .sidebar-show-collapsed  /* Only shown when collapsed */

/* Animation state */
.sidebar-wrapper.sidebar-animating     /* During transition (prevent hover flicker) */
```

#### JavaScript Interface

```javascript
// SidebarController object added to window
window.SidebarController = {
    // State
    isCollapsed: boolean,
    isPinned: boolean,
    hoverTimer: number | null,
    leaveTimer: number | null,

    // Configuration
    HOVER_DELAY: 150,      // ms before expanding
    LEAVE_DELAY: 200,      // ms before collapsing
    ANIMATION_DURATION: 200, // matches CSS

    // Methods
    init(): void,           // Initialize on DOM ready
    expand(): void,         // Expand sidebar
    collapse(): void,       // Collapse sidebar
    togglePin(): void,      // Toggle pinned state
    isPinnedState(): boolean, // Check localStorage
    savePinState(boolean): void // Save to localStorage
};
```

#### Data Attributes Interface

```html
<!-- On sidebar wrapper -->
<div class="sidebar-wrapper" data-sidebar-collapsible="true">

<!-- On pin button -->
<button class="sidebar-pin-btn" data-sidebar-pin aria-label="Pin sidebar open">

<!-- On menu items for tooltips -->
<a href="..." data-bs-toggle="tooltip" data-bs-placement="right" title="Dashboard">
```

### Implementation Examples

#### Example: Hover Timing Controller

**Why this example**: The hover delay logic is the most complex part - needs to handle mouse enter, leave, and cancellation properly.

```javascript
// Hover intent pattern with delay
function handleMouseEnter() {
    // Cancel any pending collapse
    if (this.leaveTimer) {
        clearTimeout(this.leaveTimer);
        this.leaveTimer = null;
    }

    // Skip if pinned or already expanded
    if (this.isPinned || !this.isCollapsed) return;

    // Delay expansion to prevent accidental triggers
    this.hoverTimer = setTimeout(() => {
        this.expand();
    }, this.HOVER_DELAY);
}

function handleMouseLeave() {
    // Cancel any pending expansion
    if (this.hoverTimer) {
        clearTimeout(this.hoverTimer);
        this.hoverTimer = null;
    }

    // Skip if pinned
    if (this.isPinned) return;

    // Delay collapse to allow submenu interaction
    this.leaveTimer = setTimeout(() => {
        this.collapse();
    }, this.LEAVE_DELAY);
}
```

#### Example: CSS Transition Setup

**Why this example**: Shows how to structure the CSS for smooth, GPU-accelerated transitions.

```css
/* Base transition on wrapper */
.sidebar-wrapper {
    width: var(--sidebar-width-expanded);
    transition: width var(--sidebar-transition-duration) ease-out;
    will-change: width; /* Hint for GPU acceleration */
}

.sidebar-wrapper.sidebar-collapsed {
    width: var(--sidebar-width-collapsed);
}

/* Text labels fade out when collapsed */
.sidebar-nav-item {
    opacity: 1;
    transition: opacity calc(var(--sidebar-transition-duration) / 2) ease-out;
    white-space: nowrap;
    overflow: hidden;
}

.sidebar-collapsed .sidebar-nav-item {
    opacity: 0;
    pointer-events: none; /* Prevent clicks on invisible text */
}

/* Main content margin follows sidebar */
#page-wrapper {
    margin-left: var(--sidebar-width-expanded);
    transition: margin-left var(--sidebar-transition-duration) ease-out;
}

.sidebar-collapsed ~ #page-wrapper,
body.sidebar-collapsed #page-wrapper {
    margin-left: var(--sidebar-width-collapsed);
}
```

## Runtime View

### Primary Flow: Hover to Expand

```mermaid
sequenceDiagram
    actor User
    participant Sidebar as SidebarController
    participant CSS as CSS Classes
    participant Timer as setTimeout

    User->>Sidebar: mouseenter (collapsed)
    Sidebar->>Sidebar: Cancel leave timer (if any)
    Sidebar->>Timer: Set hover timer (150ms)
    Timer-->>Sidebar: Timer fires
    Sidebar->>CSS: Remove .sidebar-collapsed
    Note over CSS: Width animates 64px → 250px
    Note over CSS: Text opacity 0 → 1

    User->>Sidebar: mouseout
    Sidebar->>Sidebar: Cancel hover timer (if any)
    Sidebar->>Timer: Set leave timer (200ms)
    Timer-->>Sidebar: Timer fires
    Sidebar->>CSS: Add .sidebar-collapsed
    Note over CSS: Width animates 250px → 64px
```

### Secondary Flow: Pin Toggle

```mermaid
sequenceDiagram
    actor User
    participant Sidebar as SidebarController
    participant CSS as CSS Classes
    participant LS as localStorage

    User->>Sidebar: Click pin button
    Sidebar->>Sidebar: Toggle isPinned
    alt isPinned = true
        Sidebar->>CSS: Add .sidebar-pinned
        Sidebar->>LS: Set bk_sidebar_pinned = true
        Note over Sidebar: Hover events disabled
    else isPinned = false
        Sidebar->>CSS: Remove .sidebar-pinned
        Sidebar->>LS: Set bk_sidebar_pinned = false
        Sidebar->>CSS: Add .sidebar-collapsed
    end
```

### Error Handling

- **Rapid mouse in/out**: Timer cancellation prevents jittery behavior
- **localStorage unavailable**: Graceful degradation - pin state not persisted but still works per-session
- **CSS transitions disabled**: Falls back to immediate state change (accessibility setting)
- **MetisMenu conflicts**: Submenu interactions pause leave timer to prevent collapse during navigation

## Deployment View

**No change to deployment process.**

- CSS changes included in existing `conductor build-css --minify` workflow
- JS changes are part of existing `sb-admin-2.js` bundle
- No server-side changes required
- No database changes required
- No configuration changes required

## Cross-Cutting Concepts

### Pattern Documentation

```yaml
# Existing patterns used
- pattern: CSS custom properties (tokens.css)
  relevance: CRITICAL
  why: "All dimensions and timing values must use design tokens"

- pattern: MetisMenu jQuery integration
  relevance: HIGH
  why: "Must not interfere with existing submenu behavior"

- pattern: Bootstrap 5 tooltip system
  relevance: MEDIUM
  why: "Reuse for collapsed icon tooltips"
```

### System-Wide Patterns

- **Error Handling**: Silent degradation for localStorage failures
- **Performance**: CSS transforms for GPU acceleration, minimal JS
- **Accessibility**:
  - Pin button has `aria-label`
  - Reduced motion media query support
  - Tooltips provide context for icon-only state

### Implementation Patterns

#### State Management Pattern

```pseudocode
# Sidebar state is managed through:
# 1. CSS classes on .sidebar-wrapper (visual state)
# 2. SidebarController properties (runtime state)
# 3. localStorage (persistent state - pin only)

STATE:
  isCollapsed → .sidebar-collapsed class
  isPinned → .sidebar-pinned class + localStorage

ON_LOAD:
  IF localStorage.bk_sidebar_pinned == "true":
    isPinned = true
    remove .sidebar-collapsed
    add .sidebar-pinned
  ELSE:
    isPinned = false
    add .sidebar-collapsed
```

#### Component Structure Pattern

```pseudocode
# Template changes (sidebar.html)
COMPONENT: SidebarWrapper
  ADD: data-sidebar-collapsible="true"
  ADD: pin button in sidebar-top area
  ADD: data-bs-toggle="tooltip" on each nav item (collapsed tooltips)

# CSS changes (admin-theme.css)
COMPONENT: CollapsibleSidebar
  DEFINE: Custom properties for dimensions
  DEFINE: Collapsed state styles
  DEFINE: Transition animations
  DEFINE: Tooltip positioning

# JS changes (sb-admin-2.js)
COMPONENT: SidebarController
  INIT: On DOM ready
  BIND: mouseenter/mouseleave to sidebar-wrapper
  BIND: click to pin button
  RESTORE: Pin state from localStorage
```

### Integration Points

- **MetisMenu**: Continue using for submenu expand/collapse. Collapsible sidebar is orthogonal to submenu behavior.
- **Bootstrap Tooltips**: Use existing Bootstrap 5 tooltip system for icon labels in collapsed state.
- **Design System Build**: Changes flow through `conductor build-css` for minification and cache-busting.

## Architecture Decisions

- [x] **ADR-1 CSS-first approach**: Use CSS transitions/classes as primary mechanism, JS only for timing
  - Rationale: Hardware-accelerated, less prone to jank, easier to maintain
  - Trade-offs: Slightly more complex CSS, but simpler JS
  - User confirmed: Pending

- [x] **ADR-2 Collapsed by default**: Sidebar starts collapsed on page load
  - Rationale: Aligns with PRD requirement, maximizes content space
  - Trade-offs: Users must learn hover behavior initially
  - User confirmed: Pending

- [x] **ADR-3 Pin state in localStorage**: Persist pin preference client-side only
  - Rationale: No server round-trip, instant restore, privacy-respecting
  - Trade-offs: Doesn't sync across devices
  - User confirmed: Pending

- [x] **ADR-4 Bootstrap tooltips for collapsed icons**: Reuse existing tooltip system
  - Rationale: Consistent styling, already loaded, accessible
  - Trade-offs: Slight tooltip initialization overhead
  - User confirmed: Pending

- [x] **ADR-5 64px collapsed width**: Match industry standard (Shopify, Stripe)
  - Rationale: Room for icons + padding, proven pattern
  - Trade-offs: None significant
  - User confirmed: Pending

## Quality Requirements

| Requirement | Target | Measurement |
|-------------|--------|-------------|
| **Performance** | 60fps during animations | Chrome DevTools Frame Rate |
| **Response** | <100ms visual feedback on hover | Stopwatch in devtools |
| **Accessibility** | WCAG 2.1 AA tooltips | Axe browser extension audit |
| **Browser Support** | Chrome, Safari, Firefox, Edge latest 2 | Manual testing |
| **No Regression** | All menu items clickable | Manual testing all 50+ items |

## Risks and Technical Debt

### Known Technical Issues

- MetisMenu uses inline styles for submenu height. Must ensure our transitions don't conflict.
- Current mobile toggle uses `.active` class. Must ensure no collision with new classes.

### Implementation Gotchas

- **Tooltip initialization timing**: Bootstrap tooltips must be initialized after DOM ready. May need to re-init after page navigation.
- **Store picker dropdown**: Dropdown extends beyond sidebar width. Must ensure it works in collapsed state (may need special handling).
- **Submenu arrow visibility**: The `.glyphicon.arrow` needs to be hidden in collapsed state.
- **Active page indicator**: Must remain visible in collapsed state (currently uses border-left).

### Technical Debt

- None introduced by this feature
- Consider refactoring sidebar CSS into dedicated module file in future (currently in main admin-theme.css)

## Test Specifications

### Critical Test Scenarios

**Scenario 1: Page Load - Default Collapsed**
```gherkin
Given: User opens any admin page
And: No pin state in localStorage
When: Page finishes loading
Then: Sidebar displays at 64px width
And: Only icons visible (no text labels)
And: Main content has 64px left margin
```

**Scenario 2: Hover to Expand**
```gherkin
Given: Sidebar is in collapsed state
When: User hovers over sidebar for 150ms+
Then: Sidebar expands to 250px with animation
And: Text labels fade in
And: Main content margin expands
And: MetisMenu submenu functionality works
```

**Scenario 3: Mouse Leave to Collapse**
```gherkin
Given: Sidebar is in expanded state (not pinned)
When: User moves mouse out of sidebar
And: 200ms passes
Then: Sidebar collapses to 64px with animation
And: Text labels fade out
```

**Scenario 4: Pin to Keep Expanded**
```gherkin
Given: Sidebar is expanded via hover
When: User clicks pin button
Then: Sidebar stays expanded
And: Pin icon shows "locked" state
And: Hover out does NOT collapse sidebar
And: localStorage contains bk_sidebar_pinned=true
```

**Scenario 5: Page Load with Pinned State**
```gherkin
Given: localStorage contains bk_sidebar_pinned=true
When: User opens any admin page
Then: Sidebar displays at 250px width (expanded)
And: Pin icon shows "locked" state
```

**Scenario 6: Mobile Unchanged**
```gherkin
Given: Viewport width < 768px
When: User interacts with sidebar
Then: Existing mobile overlay behavior works
And: Collapsible hover is disabled
```

### Test Coverage Requirements

- **Visual States**: All 4 states testable (collapsed, expanded, pinned, animating)
- **User Interactions**: Hover enter, hover leave, pin click, unpin click
- **Persistence**: localStorage save/restore
- **Edge Cases**: Rapid hover, mobile viewport, disabled animations
- **Regression**: All 50+ menu items remain clickable

---

## Glossary

### Domain Terms

| Term | Definition | Context |
|------|------------|---------|
| Collapsed State | Sidebar at 64px showing icons only | Default view on desktop |
| Expanded State | Sidebar at 250px showing icons + labels | On hover or when pinned |
| Pinned | User preference to keep sidebar expanded | Persists in localStorage |

### Technical Terms

| Term | Definition | Context |
|------|------------|---------|
| MetisMenu | jQuery plugin for collapsible submenus | Controls nested menu expand/collapse |
| will-change | CSS property hinting GPU acceleration | Used on sidebar-wrapper |
| Hover Intent | Pattern using delays to confirm user wants to interact | 150ms delay prevents accidental triggers |
