chore: remove docs/superpowers/ from tracking

These are local-only planning docs, already in .gitignore.
Files were committed before the gitignore entry was added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-18 06:42:11 -05:00
parent e7af5e85ab
commit 3c3ddb8487
2 changed files with 0 additions and 1264 deletions

View File

@@ -1,972 +0,0 @@
# Deep Space UI Redesign — Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace the generic shadcn/slate/cyan aesthetic with the Deep Space design system — new colors, typography, component styling, and layout structure.
**Architecture:** Token-first approach. Phase 1 swaps CSS custom properties and fonts so the entire app transforms via cascade. Phase 2 refines shared UI components. Phase 3 restructures the layout shell (sidebar + context strip). Phase 4 polishes individual pages. Each phase produces a working, committable state.
**Tech Stack:** React 19, Tailwind 3, Radix UI, Manrope font, IBM Plex Mono font, CSS custom properties
**Spec:** `docs/superpowers/specs/2026-03-16-deep-space-ui-redesign.md`
---
## Chunk 1: Design Tokens & Fonts (Phase 1)
### Task 1: Install new fonts
**Files:**
- Modify: `frontend/package.json`
- Create: `frontend/src/assets/fonts/Manrope-Variable.woff2`
- Create: `frontend/src/assets/fonts/IBMPlexMono-Regular.woff2`
- Create: `frontend/src/assets/fonts/IBMPlexMono-Medium.woff2`
- [ ] **Step 1: Install fontsource packages**
```bash
cd frontend && npm install @fontsource-variable/manrope @fontsource/ibm-plex-mono
```
- [ ] **Step 2: Copy font files to assets for self-hosting**
Copy the woff2 files from node_modules to `src/assets/fonts/`:
```bash
cp node_modules/@fontsource-variable/manrope/files/manrope-latin-wght-normal.woff2 src/assets/fonts/Manrope-Variable.woff2
cp node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2 src/assets/fonts/IBMPlexMono-Regular.woff2
cp node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2 src/assets/fonts/IBMPlexMono-Medium.woff2
```
- [ ] **Step 3: Commit**
```bash
git add frontend/package.json frontend/package-lock.json frontend/src/assets/fonts/Manrope-Variable.woff2 frontend/src/assets/fonts/IBMPlexMono-Regular.woff2 frontend/src/assets/fonts/IBMPlexMono-Medium.woff2
git commit -m "chore: add Manrope and IBM Plex Mono font files"
```
---
### Task 2: Replace @font-face declarations
**Files:**
- Modify: `frontend/src/index.css` (lines 116)
- [ ] **Step 1: Replace the Geist @font-face blocks with Manrope + IBM Plex Mono**
Replace lines 116 of `frontend/src/index.css`:
```css
@font-face {
font-family: 'Manrope';
src: url('./assets/fonts/Manrope-Variable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'IBM Plex Mono';
src: url('./assets/fonts/IBMPlexMono-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'IBM Plex Mono';
src: url('./assets/fonts/IBMPlexMono-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
```
- [ ] **Step 2: Update the body font-family rule**
In `frontend/src/index.css`, find the body rule (around line 147) and change:
```css
font-family: 'Geist', 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
```
to:
```css
font-family: 'Manrope', system-ui, -apple-system, sans-serif;
```
- [ ] **Step 3: Update Tailwind font config**
In `frontend/tailwind.config.ts`, replace lines 6770:
```typescript
fontFamily: {
sans: ['Manrope', 'system-ui', '-apple-system', 'sans-serif'],
mono: ['IBM Plex Mono', 'ui-monospace', 'SFMono-Regular', 'monospace'],
},
```
- [ ] **Step 4: Verify fonts load**
Run: `cd frontend && npm run dev`
Open browser, inspect body element — font-family should show Manrope. Check any monospace element (IP address, code) shows IBM Plex Mono.
- [ ] **Step 5: Commit**
```bash
git add frontend/src/index.css frontend/tailwind.config.ts
git commit -m "feat(ui): swap Geist for Manrope + IBM Plex Mono"
```
---
### Task 3: Replace color tokens — both modes
**Files:**
- Modify: `frontend/src/index.css` (lines 3286 light root, lines 88123 dark)
- [ ] **Step 1: Replace the `:root` (light mode) custom properties**
Find the `:root { }` block in `index.css` (after the `@font-face` declarations) and replace the color custom properties with the Deep Space light mode palette:
```css
/* ── Deep Space Light Mode ── */
--background: 240 20% 99%; /* #fafafe */
--surface: 0 0% 100%; /* #ffffff */
--elevated: 240 33% 96%; /* #f0f0f8 */
--border: 0 0% 0% / 0.08; /* rgba(0,0,0,0.08) */
--border-bright: 240 14% 88%; /* #dcdce8 */
--text-primary: 240 7% 7%; /* #111113 */
--text-secondary: 249 14% 37%; /* #52526b */
--text-muted: 249 11% 57%; /* #8585a0 */
--accent: 239 76% 62%; /* #5558e6 */
--accent-hover: 244 75% 58%; /* #4f46e5 */
--accent-muted: 239 76% 62% / 0.1; /* accent-subtle */
--ring: 239 76% 62%;
--success: 142 71% 35%; /* #16a34a */
--warning: 32 95% 44%; /* #d97706 */
--error: 0 72% 51%; /* #dc2626 */
--info: 217 91% 50%; /* #2563eb */
--online: 142 71% 35%;
--offline: 0 72% 51%;
--unknown: 249 11% 57%;
--chart-1: 239 76% 62%;
--chart-2: 142 71% 35%;
--chart-3: 32 95% 44%;
--chart-4: 0 72% 51%;
--chart-5: 280 68% 50%;
--chart-6: 217 91% 50%;
```
- [ ] **Step 2: Replace the `.dark` custom properties**
Find the `.dark { }` block and replace:
```css
/* ── Deep Space Dark Mode ── */
--background: 240 7% 7%; /* #111113 */
--surface: 240 22% 10%; /* #141420 */
--elevated: 240 28% 14%; /* #1a1a2e */
--border: 0 0% 100% / 0.06; /* rgba(255,255,255,0.06) */
--border-bright: 240 24% 19%; /* #24243d */
--text-primary: 240 14% 91%; /* #e4e4ed */
--text-secondary: 249 9% 59%; /* #8a8aa0 */
--text-muted: 249 13% 44%; /* #62627f */
--accent: 235 91% 74%; /* #818cf8 */
--accent-hover: 239 84% 67%; /* #6366f1 */
--accent-muted: 239 84% 67% / 0.15; /* accent-subtle */
--ring: 235 91% 74%;
--success: 142 69% 58%; /* #22c55e → 4.8:1 on #111113 */
--warning: 38 92% 50%; /* #f59e0b */
--error: 0 84% 60%; /* #ef4444 */
--info: 217 91% 60%; /* #3b82f6 */
--online: 142 69% 58%;
--offline: 0 84% 60%;
--unknown: 249 9% 59%;
--chart-1: 235 91% 74%;
--chart-2: 142 69% 58%;
--chart-3: 38 92% 50%;
--chart-4: 0 84% 60%;
--chart-5: 280 68% 68%;
--chart-6: 217 91% 60%;
```
- [ ] **Step 3: Update the `--radius` default**
In the `:root` block, change `--radius` from `0.375rem` to `0.5rem` (8px for cards):
```css
--radius: 0.5rem;
```
- [ ] **Step 4: Verify color swap**
Run: `cd frontend && npm run dev`
Check both dark and light modes. The entire app should now use the Deep Space palette. Colors will look different but layout is unchanged.
- [ ] **Step 5: Commit**
```bash
git add frontend/src/index.css
git commit -m "feat(ui): replace color tokens with Deep Space palette"
```
---
### Task 4: Update Tailwind color mappings
**Files:**
- Modify: `frontend/tailwind.config.ts` (lines 1166 colors, lines 8187 radius)
- [ ] **Step 1: Fix Tailwind color mappings for alpha tokens**
The existing Tailwind config maps colors as `hsl(var(--token))`. Two tokens now have built-in alpha (`--border` and `--accent-muted`), which breaks the `hsl()` wrapper. Fix these specific mappings in `tailwind.config.ts`:
For `border` and `accent-muted`, change from:
```typescript
border: 'hsl(var(--border))',
'accent-muted': 'hsl(var(--accent-muted))',
```
to:
```typescript
border: 'hsl(var(--border))',
'accent-muted': 'hsl(var(--accent-muted))',
```
Actually, since the CSS values include the alpha channel inside the HSL declaration (e.g., `0 0% 100% / 0.06`), `hsl(var(--border))` produces `hsl(0 0% 100% / 0.06)` which is valid CSS. **No mapping change is needed** — the existing pattern works because CSS `hsl()` accepts the `/` alpha syntax. Verify this renders correctly in the browser after Task 3.
- [ ] **Step 2: Update border radius scale**
In `frontend/tailwind.config.ts`, update the borderRadius section (lines 8187):
```typescript
borderRadius: {
sm: '0.25rem', // 4px
DEFAULT: 'var(--radius)', // 8px (cards/panels)
md: '0.375rem', // 6px (buttons/inputs)
lg: 'var(--radius)', // 8px
xl: '0.75rem', // 12px
},
```
- [ ] **Step 3: Commit**
```bash
git add frontend/tailwind.config.ts
git commit -m "feat(ui): update Tailwind theme for Deep Space tokens"
```
---
### Task 5: Add base transition defaults
**Files:**
- Modify: `frontend/src/index.css`
- [ ] **Step 1: Add default transitions, tabular-nums, and sidebar animation**
Add to the base styles section of `index.css` (after the scrollbar styles):
```css
/* ── Deep Space transitions ── */
button, a, input, select, textarea,
[role="button"], [role="tab"], [role="menuitem"] {
transition: color 150ms ease, background-color 150ms ease, border-color 150ms ease, opacity 150ms ease;
}
/* Sidebar collapse uses a slower transition */
[data-sidebar] {
transition: width 200ms ease;
}
/* Dialog overlay and content animations */
[data-radix-dialog-overlay] {
transition: opacity 150ms ease;
}
[data-radix-dialog-content] {
transition: opacity 150ms ease, transform 150ms ease;
}
/* Monospace always uses tabular numerals for aligned columns */
.font-mono, [class*="font-mono"] {
font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
```
- [ ] **Step 2: Commit**
```bash
git add frontend/src/index.css
git commit -m "feat(ui): add Deep Space transition defaults"
```
---
## Chunk 2: Component Restyling (Phase 2)
### Task 6: Restyle Button component
**Files:**
- Modify: `frontend/src/components/ui/button.tsx` (50 lines)
- [ ] **Step 1: Update button variants**
Replace the CVA variants in `button.tsx`. Key changes:
- Default (primary): ghost-fill style — `bg-accent-muted text-accent hover:bg-accent/20`
- Solid primary (for critical CTAs): new variant — `bg-accent text-white hover:bg-accent-hover`
- Secondary: border-only — `border border-border text-text-secondary hover:text-text-primary hover:border-border-bright`
- Destructive: ghost-fill red — `bg-error/15 text-error hover:bg-error/20`
- Ghost: `hover:bg-elevated text-text-secondary hover:text-text-primary`
- Outline: `border border-border bg-transparent text-text-secondary hover:bg-elevated`
- All variants: `rounded-md` (6px), remove any `shadow` classes
```typescript
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-[hsl(var(--accent-muted))] text-accent hover:bg-accent/20',
solid: 'bg-accent text-white hover:bg-accent-hover',
destructive: 'bg-error/15 text-error hover:bg-error/20',
outline: 'border border-border bg-transparent text-text-secondary hover:bg-elevated hover:text-text-primary',
secondary: 'bg-elevated text-text-secondary hover:text-text-primary',
ghost: 'text-text-secondary hover:bg-elevated hover:text-text-primary',
link: 'text-accent underline-offset-4 hover:underline',
},
size: {
default: 'h-8 px-3 text-xs',
sm: 'h-7 px-2.5 text-xs',
lg: 'h-9 px-4 text-sm',
icon: 'h-8 w-8',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)
```
- [ ] **Step 2: Verify buttons render correctly**
Run dev server, check buttons across the app — nav items, dialogs, forms.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/ui/button.tsx
git commit -m "feat(ui): restyle Button with Deep Space variants"
```
---
### Task 7: Restyle Input component
**Files:**
- Modify: `frontend/src/components/ui/input.tsx` (23 lines)
- [ ] **Step 1: Update Input styling**
Replace the className in `input.tsx`:
```typescript
'flex h-8 w-full rounded-md border border-border bg-elevated/50 px-3 py-1 text-sm text-text-primary placeholder:text-text-muted transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus:border-accent focus:outline-none disabled:cursor-not-allowed disabled:opacity-50'
```
Key changes: remove `focus-visible:ring` classes, add `focus:border-accent`, use `bg-elevated/50`.
- [ ] **Step 2: Commit**
```bash
git add frontend/src/components/ui/input.tsx
git commit -m "feat(ui): restyle Input with Deep Space focus behavior"
```
---
### Task 8: Restyle Card component
**Files:**
- Modify: `frontend/src/components/ui/card.tsx` (54 lines)
- [ ] **Step 1: Update Card styling**
Key changes: remove `shadow-sm`, use `border-border`, ensure `rounded-lg` (8px via --radius):
```typescript
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn('rounded-lg border border-border bg-surface text-text-primary', className)} {...props} />
),
)
```
Remove any `shadow` classes from CardHeader, CardContent, CardFooter as well.
- [ ] **Step 2: Commit**
```bash
git add frontend/src/components/ui/card.tsx
git commit -m "feat(ui): restyle Card — borders over shadows"
```
---
### Task 9: Restyle Dialog component
**Files:**
- Modify: `frontend/src/components/ui/dialog.tsx` (95 lines)
- [ ] **Step 1: Update DialogOverlay**
Change overlay background to `bg-black/60 backdrop-blur-sm`.
- [ ] **Step 2: Update DialogContent**
Remove shadow classes, use border-border, ensure rounded-lg:
```
border border-border bg-surface text-text-primary rounded-lg
```
Remove any `focus:ring` or `focus:outline` classes from DialogContent.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/ui/dialog.tsx
git commit -m "feat(ui): restyle Dialog with Deep Space overlay and borders"
```
---
### Task 10: Restyle Select, Badge, Skeleton, Tabs, Checkbox
**Files:**
- Modify: `frontend/src/components/ui/select.tsx` (147 lines)
- Modify: `frontend/src/components/ui/badge.tsx` (25 lines)
- Modify: `frontend/src/components/ui/skeleton.tsx` (15 lines)
- Modify: `frontend/src/components/ui/tabs.tsx` (49 lines)
- Modify: `frontend/src/components/ui/checkbox.tsx` (25 lines)
- [ ] **Step 1: Restyle Select**
SelectTrigger: same as Input — `bg-elevated/50 border-border rounded-md focus:border-accent`, remove ring classes.
SelectContent: `bg-surface border-border rounded-md`, remove shadow.
SelectItem: `focus:bg-elevated focus:text-text-primary`.
- [ ] **Step 2: Restyle Badge**
Remove shadow, use `rounded-md` (6px), default variant: `bg-elevated text-text-secondary border border-border`.
- [ ] **Step 3: Restyle Skeleton**
Update shimmer gradient colors to use `--elevated` and `--surface` tokens. No other changes needed — the animation pattern stays.
- [ ] **Step 4: Restyle Tabs**
TabsList: `bg-transparent` (remove background).
TabsTrigger active: `bg-[hsl(var(--accent-muted))] text-accent font-semibold`. Inactive: `text-text-muted hover:text-text-secondary`. Remove underline indicators.
- [ ] **Step 5: Restyle Checkbox**
Border: `border-border`. Checked: `bg-accent border-accent text-white`. Focus: `border-accent`, no ring.
- [ ] **Step 6: Commit**
```bash
git add frontend/src/components/ui/select.tsx frontend/src/components/ui/badge.tsx frontend/src/components/ui/skeleton.tsx frontend/src/components/ui/tabs.tsx frontend/src/components/ui/checkbox.tsx
git commit -m "feat(ui): restyle Select, Badge, Skeleton, Tabs, Checkbox"
```
---
### Task 11: Restyle DropdownMenu and Popover
**Files:**
- Modify: `frontend/src/components/ui/dropdown-menu.tsx` (185 lines)
- Modify: `frontend/src/components/ui/popover.tsx` (28 lines)
- [ ] **Step 1: Restyle DropdownMenu**
DropdownMenuContent: `bg-surface border-border rounded-md`, remove shadow.
DropdownMenuItem: `focus:bg-elevated focus:text-text-primary`.
DropdownMenuSeparator: `bg-border`.
- [ ] **Step 2: Restyle Popover**
PopoverContent: `bg-surface border-border rounded-lg`, remove shadow.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/ui/dropdown-menu.tsx frontend/src/components/ui/popover.tsx
git commit -m "feat(ui): restyle DropdownMenu and Popover"
```
---
## Chunk 3: Layout Restructure (Phase 3)
### Task 12a: Build ContextStrip shell
**Files:**
- Create: `frontend/src/components/layout/ContextStrip.tsx`
- [ ] **Step 1: Create ContextStrip layout shell**
Create the 36px flex container with three sections (left/center/right), background `bg-[#0d0d11] dark:bg-[#0d0d11]` (or a custom token), thin bottom border. No data yet — just the structure with placeholder text.
```tsx
export function ContextStrip() {
return (
<div className="flex items-center h-9 bg-background/80 border-b border-border px-4 gap-4 flex-shrink-0">
{/* Left: org switcher */}
<div className="flex items-center gap-2 pr-4 border-r border-border">
<span className="text-xs font-medium text-text-secondary">Org placeholder</span>
</div>
{/* Center: status indicators */}
<div className="flex items-center gap-4 flex-1">
<span className="text-xs text-text-muted">Status loading...</span>
</div>
{/* Right: user controls */}
<div className="flex items-center gap-3">
<span className="text-xs text-text-muted">K</span>
</div>
</div>
)
}
```
- [ ] **Step 2: Commit**
```bash
git add frontend/src/components/layout/ContextStrip.tsx
git commit -m "feat(ui): add ContextStrip layout shell"
```
---
### Task 12b: ContextStrip — org switcher (left section)
**Files:**
- Modify: `frontend/src/components/layout/ContextStrip.tsx`
- [ ] **Step 1: Add org switcher**
Import `useUIStore` and tenant data. Render: colored initial icon (8px square, rounded, gradient background), tenant name, dropdown chevron. For single-org users, hide the chevron. Wire up the existing tenant selector logic from Header.tsx.
- [ ] **Step 2: Commit**
```bash
git add frontend/src/components/layout/ContextStrip.tsx
git commit -m "feat(ui): add org switcher to ContextStrip"
```
---
### Task 12c: ContextStrip — status indicators (center section)
**Files:**
- Modify: `frontend/src/components/layout/ContextStrip.tsx`
- [ ] **Step 1: Add live status indicators**
Use the same `useQuery` key as the fleet dashboard to subscribe to fleet summary data. Render four indicators:
- Offline count: red dot (with `shadow-[0_0_6px_rgba(239,68,68,0.4)]`) + "N down" in red text. Clickable — navigates to devices filtered by offline.
- Degraded count: amber dot + glow + "N degraded". Clickable.
- WiFi status: "WiFi OK" in green or "WiFi N issues" in amber. Clickable — navigates to wireless page.
- Bandwidth: "BW" label in text-muted + value in `font-mono text-text-primary`. Clickable — navigates to traffic page.
Each uses `useNavigate` from React Router. All text is 11px, font-medium.
- [ ] **Step 2: Commit**
```bash
git add frontend/src/components/layout/ContextStrip.tsx
git commit -m "feat(ui): add live status indicators to ContextStrip"
```
---
### Task 12d: ContextStrip — right section (palette, status, avatar)
**Files:**
- Modify: `frontend/src/components/layout/ContextStrip.tsx`
- [ ] **Step 1: Add right section**
- Command palette shortcut: `text-xs text-text-muted` showing "⌘K". Wire to existing `useCommandPalette` or keyboard shortcut handler.
- Connection status dot: 6px circle, green when connected, amber when reconnecting. Reuse logic from `Header.tsx` `ConnectionIndicator`.
- User avatar: 22px circle, `bg-elevated border border-border`, initials inside. Wrap with the existing DropdownMenu for logout/settings.
- [ ] **Step 2: Verify ContextStrip renders with live data**
Temporarily render ContextStrip above the current layout to test. Check org switcher, status counts, and avatar all work.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/layout/ContextStrip.tsx
git commit -m "feat(ui): complete ContextStrip with user controls"
```
---
### Task 13: Rebuild Sidebar
**Files:**
- Modify: `frontend/src/components/layout/Sidebar.tsx` (385 lines)
- [ ] **Step 1: Replace navigation sections**
Replace the current nav sections (Fleet / Manage / Monitor / Admin) with the new structure:
**Fleet:** Overview, Devices, Wireless, Traffic
**Config:** Editor, Templates, Firmware
**Admin:** Users, Audit Log, Settings
Update the Lucide icons for each item. Section labels use the micro-label style: `text-[10px] uppercase tracking-wider font-semibold text-text-muted`.
Active nav item: `bg-[hsl(var(--accent-muted))] text-accent rounded-md`.
Inactive: `text-text-muted hover:text-text-primary hover:bg-elevated/50 rounded-md`.
- [ ] **Step 2: Update dimensions and collapsed state**
Set the sidebar root element to `w-[180px]` expanded, `w-14` (56px) collapsed. Add `data-sidebar` attribute to the root `<nav>` element so the CSS transition rule from Task 5 applies (200ms ease width transition).
Collapsed: icon-only with tooltips on hover. Version identifier at bottom: `TOD v9.5` in `font-mono text-[9px] text-text-muted`.
- [ ] **Step 3: Remove alert badges from sidebar**
Alerts now surface through ContextStrip, not sidebar badges. Remove AlertBadge imports and rendering.
- [ ] **Step 4: Commit**
```bash
git add frontend/src/components/layout/Sidebar.tsx
git commit -m "feat(ui): rebuild Sidebar with Fleet/Config/Admin structure"
```
---
### Task 14: Replace AppLayout shell
**Files:**
- Modify: `frontend/src/components/layout/AppLayout.tsx` (31 lines)
- Modify: `frontend/src/components/layout/Header.tsx` (225 lines)
- [ ] **Step 1: Update AppLayout**
Replace the layout structure:
```tsx
<div className="flex h-screen overflow-hidden bg-background">
<Sidebar />
<div className="flex flex-col flex-1 overflow-hidden">
<ContextStrip />
<main id="main-content" className="flex-1 overflow-auto p-5">
<Outlet />
</main>
</div>
<CommandPalette />
<Toaster />
</div>
```
Import ContextStrip, remove Header import.
- [ ] **Step 2: Deprecate Header.tsx**
The Header component's responsibilities are now split:
- Connection status → ContextStrip (right section)
- Theme toggle → ContextStrip or Settings page
- User menu → ContextStrip (right section, avatar dropdown)
- Tenant selector → ContextStrip (left section)
Don't delete Header.tsx yet — rename it to `Header.tsx.bak` or add a deprecation comment. We may need to reference it during the transition.
- [ ] **Step 3: Verify the new shell renders**
Run dev server. The app should show: ContextStrip at top, Sidebar on left, main content in remaining space. Navigation should work.
- [ ] **Step 4: Commit**
```bash
git add frontend/src/components/layout/AppLayout.tsx frontend/src/components/layout/ContextStrip.tsx frontend/src/components/layout/Sidebar.tsx frontend/src/components/layout/Header.tsx
git commit -m "feat(ui): replace AppLayout with ContextStrip + rebuilt Sidebar"
```
---
### Task 15: Update route paths for new nav
**Files:**
- Modify: `frontend/src/routes/_authenticated/` (various route files)
- [ ] **Step 1: Map current routes to new nav structure**
Current routes → new sidebar mapping:
- Dashboard (`index.tsx`) → Fleet > Overview
- Devices (`tenants/$tenantId/devices/index.tsx`) → Fleet > Devices
- Alerts (`alerts.tsx`) → remove from nav (surfaced via ContextStrip + device detail)
- Config Editor (`config-editor.tsx`) → Config > Editor
- Templates (`templates.tsx`) → Config > Templates
- Firmware (`firmware.tsx`) → Config > Firmware
- Audit (`audit.tsx`) → Admin > Audit Log
- Settings (`settings.tsx`) → Admin > Settings
**Routes that don't exist yet:**
- Fleet > Wireless — no dedicated wireless page exists. For now, link to the dashboard or create a placeholder route (`_authenticated/wireless.tsx`) that shows wireless stats from the fleet summary.
- Fleet > Traffic — no dedicated traffic page exists. Same approach — create a placeholder route (`_authenticated/traffic.tsx`) or link to the dashboard bandwidth section.
These are placeholder pages (a heading + "coming soon" or a subset of dashboard widgets). Full implementations are outside this redesign scope.
Update the Sidebar nav item `to` props to match existing and new route paths.
- [ ] **Step 2: Verify all nav links work**
Click through every sidebar item and confirm it routes correctly.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/layout/Sidebar.tsx
git commit -m "feat(ui): wire sidebar nav items to existing routes"
```
---
## Chunk 4: Page-Level Polish (Phase 4)
### Task 16: Polish Fleet Overview (Dashboard)
**Files:**
- Modify: `frontend/src/components/fleet/FleetDashboard.tsx`
- Modify: KPI card components used by the dashboard
- [ ] **Step 1: Update KPI cards**
Apply the gradient background to KPI cards. Use mode-aware classes:
```
bg-gradient-to-br from-[#f8f8ff] to-elevated dark:from-elevated dark:to-[#16162a]
```
Light mode gets a subtle cool-white to elevated gradient. Dark mode gets the Deep Space elevated gradient.
KPI label: `text-[10px] font-medium text-text-muted`
KPI value: `text-2xl font-medium font-mono text-text-primary` (with `tabular-nums`)
- [ ] **Step 2: Update dashboard grid**
Ensure widget cards use `border-border bg-surface rounded-lg` without shadows.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/fleet/
git commit -m "feat(ui): polish Fleet Overview with Deep Space styling"
```
---
### Task 17: Polish device table
**Files:**
- Modify: Device table/list components in `frontend/src/components/fleet/` or route files
- [ ] **Step 1: Update table styling**
- Column headers: `text-[10px] uppercase tracking-wider font-semibold text-text-muted`
- Row borders: `border-b border-border` (subtle)
- No alternating row colors
- Status dots: 5px, border-radius 50%, inline with device name. Apply glow: `shadow-[0_0_6px_rgba(var(--status-color),0.3)]` where status-color matches online/degraded/offline.
- Data columns (IP, CPU, uptime): `font-mono text-text-secondary` (tabular-nums applied globally via Task 5)
- Device name: `font-medium text-text-primary`
- Model/metadata: `text-text-muted`
- [ ] **Step 2: Update filter tabs**
Active tab: `bg-[hsl(var(--accent-muted))] text-accent font-semibold rounded-md`
Inactive: `text-text-muted`
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/fleet/ frontend/src/routes/_authenticated/
git commit -m "feat(ui): polish device table with Deep Space styling"
```
---
### Task 18: Polish Config Editor
**Files:**
- Modify: `frontend/src/components/config-editor/ConfigEditorPage.tsx`
- Modify: `frontend/src/components/config-editor/MenuTree.tsx`
- Modify: `frontend/src/components/config-editor/EntryTable.tsx`
- Modify: `frontend/src/components/config-editor/EntryForm.tsx`
- Modify: `frontend/src/components/config-editor/CommandExecutor.tsx`
- [ ] **Step 1: Update MenuTree sidebar**
Section label: micro-label style. Active path: accent ghost-fill. Tree icons: `text-text-muted`. Custom path input: Deep Space input styling.
- [ ] **Step 2: Update EntryTable**
Apply the same table styling from Task 17: micro-label headers, subtle borders, monospace data.
- [ ] **Step 3: Update EntryForm dialog**
Dialog uses restyled Dialog component (Task 9). Inputs use restyled Input (Task 7). Checkboxes use restyled Checkbox (Task 10).
- [ ] **Step 4: Update CommandExecutor**
Command input: monospace, elevated background. Result area: `bg-surface border-border rounded-md font-mono text-xs`. Success/error indicators use status colors.
- [ ] **Step 5: Commit**
```bash
git add frontend/src/components/config-editor/
git commit -m "feat(ui): polish Config Editor with Deep Space styling"
```
---
### Task 19: WCAG contrast audit
**Files:**
- No file changes expected (unless issues found)
- [ ] **Step 1: Audit dark mode contrast**
Check every text/background combination:
- text-primary (`#e4e4ed`) on background (`#111113`): should be ~13:1
- text-secondary (`#8a8aa0`) on background: should be ~4.6:1
- text-muted (`#62627f`) on background: should be ~3.2:1 (OK for UI elements, not body text)
- accent (`#818cf8`) on background: should be ~5.5:1
- accent on surface (`#141420`): should be ~5:1
- status colors on background: all should be ≥3:1
- [ ] **Step 2: Audit light mode contrast**
- text-primary (`#111113`) on background (`#fafafe`): should be ~18:1
- text-secondary (`#52526b`) on background: should be ~6:1
- text-muted (`#8585a0`) on background: should be ~3.5:1
- accent (`#5558e6`) on background: should be ~4.4:1
- accent on surface (`#ffffff`): should be ~4.6:1
- [ ] **Step 3: Fix any failures**
If any combination fails its target ratio, adjust the token value and update `index.css`.
- [ ] **Step 4: Commit (if changes needed)**
```bash
git add frontend/src/index.css
git commit -m "fix(ui): adjust color tokens for WCAG AA compliance"
```
---
### Task 20: Responsive spot-check
**Files:**
- Possibly modify: `frontend/src/components/layout/ContextStrip.tsx`
- Possibly modify: `frontend/src/components/layout/Sidebar.tsx`
- [ ] **Step 1: Test at tablet width (768px)**
Resize browser to 768px. Verify:
- Sidebar collapses to drawer (existing mobile behavior should still work)
- KPI cards reflow to 2-column (`sm:grid-cols-2` should already handle this)
- ContextStrip content doesn't overflow
- [ ] **Step 2: Test at phone width (375px)**
Resize to 375px. Verify:
- Sidebar is hidden (hamburger menu)
- ContextStrip collapses — if it overflows, add responsive hiding: hide WiFi and BW indicators on `sm:` breakpoint, show only alert count badge
- Device table remains usable (existing responsive behavior)
- All touch targets are at least 44px height
- [ ] **Step 3: Fix any overflow issues**
Common fix for ContextStrip: wrap center indicators in a `hidden sm:flex` container so only the critical "N down" indicator shows on mobile. Full indicators show on `sm:` and above.
- [ ] **Step 4: Commit (if changes needed)**
```bash
git add frontend/src/components/layout/
git commit -m "fix(ui): responsive adjustments for ContextStrip and layout"
```
---
### Task 21: Remove old Geist font files
**Files:**
- Delete: `frontend/src/assets/fonts/Geist-Variable.woff2`
- Delete: `frontend/src/assets/fonts/GeistMono-Variable.woff2`
- Modify: `frontend/package.json` (remove `geist` dependency)
- [ ] **Step 1: Remove Geist font files and package**
```bash
cd frontend
rm src/assets/fonts/Geist-Variable.woff2 src/assets/fonts/GeistMono-Variable.woff2
npm uninstall geist
```
- [ ] **Step 2: Verify nothing references Geist**
```bash
grep -r "Geist" src/ --include="*.ts" --include="*.tsx" --include="*.css"
```
Should return no results.
- [ ] **Step 3: Commit**
```bash
git add -A frontend/src/assets/fonts/ frontend/package.json frontend/package-lock.json
git commit -m "chore: remove Geist font files and dependency"
```
---
### Task 22: Delete deprecated Header component
**Files:**
- Delete or clean up: `frontend/src/components/layout/Header.tsx`
- [ ] **Step 1: Verify Header is not imported anywhere**
```bash
grep -r "Header" frontend/src/ --include="*.ts" --include="*.tsx" | grep -v "DialogHeader\|CardHeader\|mockup-header\|TableHeader"
```
Should only show the backup file or no results.
- [ ] **Step 2: Delete Header.tsx**
```bash
rm frontend/src/components/layout/Header.tsx
```
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/layout/Header.tsx
git commit -m "chore: remove deprecated Header component (replaced by ContextStrip)"
```

View File

@@ -1,292 +0,0 @@
# Deep Space UI Redesign
Full visual overhaul of The Other Dude's frontend — replacing the generic shadcn/slate/cyan aesthetic with a distinctive identity built for network engineers and MSPs.
## Problem
The current UI is visually indistinguishable from every other AI-generated dashboard. The slate/cyan color scheme, default shadcn component styling, and feature-organized navigation are so generic that other MikroTik dashboard projects (e.g., MikroDash) look like the same app. TOD has no visual identity.
## Goals
- **Distinctive identity** — you should never confuse TOD with a template
- **Functional clarity** — DigitalOcean/Hetzner energy, not Unifi complexity
- **WCAG AA compliance** — 4.5:1 text contrast, 3:1 UI elements, both modes
- **Both modes polished** — dark is the primary identity, light is equally considered
- **Desktop-first** — responsive works, but optimized for laptop/desktop workflows
## Design Direction: Deep Space
Dark but not cold. Deep blue-blacks instead of gray-slate. Indigo/violet accent instead of cyan. Subtle gradients for depth. Typography and spacing create hierarchy instead of colored cards and heavy borders.
Reference points: DigitalOcean, Hetzner, Linode for functional clarity. cPanel for density that works. Not Unifi. Not generic shadcn.
---
## 1. Color System
### Dark Mode (Primary)
| Token | Value | Usage |
|-------|-------|-------|
| background | `#111113` | Page background, near-black with warm undertone |
| surface | `#141420` | Cards, panels, elevated content areas |
| elevated | `#1a1a2e` | Inputs, hover states, KPI card gradient base |
| border | `rgba(255,255,255,0.06)` | Default borders — thin, not heavy |
| border-bright | `#24243d` | Emphasized borders, section dividers |
| text-primary | `#e4e4ed` | Headings, device names, primary content |
| text-secondary | `#8a8aa0` | Metadata, secondary labels, IP addresses (4.6:1 on background) |
| text-muted | `#62627f` | Placeholders, column headers, tertiary info (3.2:1 on background) |
| accent | `#818cf8` | Primary interactive color (indigo-400) |
| accent-hover | `#6366f1` | Hover/pressed state (indigo-500) |
| accent-subtle | `rgba(99,102,241,0.15)` | Active nav, selected states, ghost-fill buttons |
### Light Mode
| Token | Value | Usage |
|-------|-------|-------|
| background | `#fafafe` | Page background, barely-tinted white |
| surface | `#ffffff` | Cards, panels |
| elevated | `#f0f0f8` | Inputs, hover states — blue-violet undertone |
| border | `rgba(0,0,0,0.08)` | Default borders |
| border-bright | `#dcdce8` | Emphasized borders |
| text-primary | `#111113` | Headings, primary content |
| text-secondary | `#52526b` | Metadata, secondary labels |
| text-muted | `#8585a0` | Placeholders, disabled text |
| accent | `#5558e6` | Primary interactive color (4.6:1 on white, 4.4:1 on background) |
| accent-hover | `#4f46e5` | Hover/pressed state (indigo-600) |
| accent-subtle | `rgba(99,102,241,0.1)` | Active states |
### Status Colors
| Status | Dark | Light | Meaning |
|--------|------|-------|---------|
| success | `#22c55e` | `#16a34a` | Online, healthy |
| warning | `#f59e0b` | `#d97706` | Degraded, attention needed |
| error | `#ef4444` | `#dc2626` | Offline, critical |
| info | `#3b82f6` | `#2563eb` | Informational |
### Gradient Pattern
KPI cards and elevated surfaces use a subtle directional gradient for depth:
```
Dark: linear-gradient(135deg, #1a1a2e 0%, #16162a 100%)
Light: linear-gradient(135deg, #f8f8ff 0%, #f0f0f8 100%)
```
Status indicator dots use a subtle glow:
```
box-shadow: 0 0 6px rgba({color}, 0.3)
```
---
## 2. Typography
### Fonts
- **UI text:** Manrope — geometric, semi-rounded, modern without being trendy
- **Data values:** IBM Plex Mono — wider and more readable than most monospaces, designed for infrastructure UIs
- **Fallbacks:** system-ui, -apple-system, sans-serif (UI) / ui-monospace, monospace (data)
### Type Scale
| Role | Size | Weight | Font | Extra |
|------|------|--------|------|-------|
| Page title | 18px | 600 | Manrope | letter-spacing: -0.3px |
| Section heading | 14px | 600 | Manrope | — |
| Body text | 13px | 500 | Manrope | Nav items, device names |
| Small text | 11px | 500 | Manrope | Metadata, model names |
| Micro label | 10px | 600 | Manrope | uppercase, letter-spacing: 1.2px |
| Mono large | 22-24px | 500 | IBM Plex Mono | tabular-nums, KPI values |
| Mono inline | 12px | 400 | IBM Plex Mono | tabular-nums, IPs/percentages |
| Mono small | 11px | 400 | IBM Plex Mono | tabular-nums, table cells |
### Principles
- No text larger than 18px — this is a dense tool, not a marketing site
- Monospace only for actual data values (numbers, IPs, percentages, durations)
- Uppercase micro labels for section/column headers create hierarchy without size
- `font-variant-numeric: tabular-nums` on all monospace so numbers align
### Font Loading
Self-host both fonts via `@fontsource/manrope` and `@fontsource-variable/ibm-plex-mono` npm packages. No Google Fonts CDN — the app may run on air-gapped or restricted networks. Import only the weights used (400, 500, 600, 700 for Manrope; 400, 500 for IBM Plex Mono).
### Transitions
All interactive state changes (hover, focus, active, color-scheme toggle): `150ms ease`. Sidebar collapse: `200ms ease`. Dialog enter/exit: `150ms ease`. Respect `prefers-reduced-motion` by disabling all transitions/animations.
---
## 3. Component Language
### Border Radius
- Cards/panels: 8px
- Buttons/inputs/pills: 6px
- Status dots/avatars: 50%
- No `rounded-full` pill buttons
### Depth Model
- **Borders over shadows** — zero box-shadows on cards. Thin borders for structure.
- Dark: `rgba(255,255,255,0.06)` default, `#24243d` emphasized
- Light: `rgba(0,0,0,0.08)` default, `#dcdce8` emphasized
### Buttons
- **Primary:** Ghost-fill — `background: accent-subtle; color: accent`. Solid indigo only for critical CTAs (e.g., "Add device" gets `background: accent; color: white`).
- **Secondary:** Border-only — `border: 1px solid border; color: text-secondary`
- **Destructive:** Ghost-fill in red — same pattern as primary but with error tokens
- All 6px radius, no pill shapes
- All button colors derive from mode-aware tokens — no hardcoded values
### Inputs
- Background: `#1a1a2e` (dark) / `#f0f0f8` (light)
- Border: thin, default color
- Focus: border-color changes to accent — no ring glow
- Radius: 6px
- Placeholder: text-muted color
### Tables
- No alternating row colors
- Thin bottom borders: `rgba(255,255,255,0.06)` (dark) / `rgba(0,0,0,0.06)` (light) — uses border token, intentionally subtle
- Column headers: uppercase micro labels in text-muted
- Status dots inline, not colored row backgrounds
- Monospace for data columns (IP, CPU, uptime)
### Tabs/Pills
- Active: ghost-fill accent background + accent text color
- Inactive: plain muted text, no background
- No underlines or heavy active indicators
### Dialogs/Modals
- Surface background with thin border
- Backdrop: `rgba(0,0,0,0.6)` with subtle blur
- Same 8px radius as cards
---
## 4. Layout Structure
### Context Strip (Top Bar — 36px height)
Always-visible horizontal bar providing at-a-glance status:
- **Left:** Org switcher — icon + name + dropdown chevron. Adaptive: prominent for multi-org MSPs, minimal for single-org users. Separated from status indicators by a thin vertical border.
- **Center:** Live status indicators — clickable items that filter/navigate:
- Red dot + "2 down" (navigates to offline devices)
- Amber dot + "4 degraded" (navigates to degraded devices)
- "WiFi OK" or "WiFi 3 issues" (navigates to wireless view)
- "BW 4.2G" in monospace (navigates to traffic view)
- **Right:** Command palette shortcut (⌘K), connection status dot, user avatar
The strip replaces the need for a separate alerts page as the primary "is anything broken" indicator.
**Data source:** The strip derives its counts from the existing fleet summary API (`metricsApi.fleetSummary`) which already returns device status counts and aggregate metrics. No new API endpoint needed — the strip subscribes to the same React Query cache that the dashboard uses. Status updates arrive via the existing WebSocket device-status subscription. WiFi and bandwidth indicators aggregate from the same data source.
### Sidebar (180px, collapsible to 56px icon-only)
Three sections organized by user intent:
**Fleet** (monitoring — "what's happening")
- Overview — dashboard with KPIs, fleet health
- Devices — full device table with search/filter
- Wireless — AP status, client counts, signal quality
- Traffic — bandwidth charts, top talkers
**Config** (changes — "do something")
- Editor — live config editor with menu tree
- Templates — config templates, push wizard
- Firmware — version management, upgrades
**Admin** (management — "who and how")
- Users — user management, roles
- Audit Log — action history
- Settings — tenant settings, alert rules, notifications
Section labels: uppercase micro labels in muted text. Active nav item: ghost-fill accent background. Inactive: plain muted text.
Version identifier at bottom: `TOD v9.5` in mono micro text.
### Main Content Area
- Full width of remaining space
- 20px padding
- Page header: title (18px semibold) + subtitle (11px muted) left, action buttons right
- KPI cards in a row below header where applicable
- Primary content (tables, charts, forms) below KPIs
---
## 5. Responsive Strategy
Desktop-first. Mobile works in a pinch.
- **Context strip:** Collapses to thin bar with alert count badge on mobile. Tap to expand.
- **Sidebar:** Slide-out drawer via hamburger on mobile.
- **KPI cards:** 4-col → 2-col (tablet) → 1-col (phone)
- **Device table:** Switches to card/list view on phone (no horizontal scroll tables)
- **Touch targets:** Minimum 44px height on interactive elements (WCAG 2.5.8)
- **Type scale:** No changes needed — 11-13px body is native mobile scale. 10px micro labels stay legible due to uppercase + wide letter-spacing.
---
## 6. Implementation Phases
### Phase 1 — Design Tokens & Foundation
**Risk: Low | Impact: High | Disruption: Minimal**
- Replace HSL color tokens in `index.css` with Deep Space palette (dark + light)
- Swap Geist for Manrope + IBM Plex Mono in Tailwind config
- Update custom properties and Tailwind theme mapping
- Add new border/radius/shadow conventions to base styles
- At end of phase: flip the switch — entire app gets new palette and fonts via tokens
Files touched: `index.css`, `tailwind.config.ts`, font imports
### Phase 2 — Component Restyling
**Risk: Low | Impact: High | Disruption: Low**
- Update shared UI primitives: Button, Input, Card, Dialog, Select, Checkbox, Skeleton, Badge, Tabs
- Kill box-shadows, update border radii, implement ghost-fill button style
- Update focus states (border-color change instead of ring glow)
- Changes propagate through entire app via shared components
Files touched: `components/ui/*.tsx`
### Phase 3 — Layout Restructure
**Risk: Medium | Impact: High | Disruption: Medium**
- Replace AppLayout shell — new sidebar structure, new Context Strip header
- Build the Context Strip component with live status indicators
- Reorganize sidebar navigation (Fleet / Config / Admin)
- Update route structure if nav paths change
- Remove old header component
Files touched: `components/layout/AppLayout.tsx`, `Sidebar.tsx`, new `ContextStrip.tsx`, route files
### Phase 4 — Page-Level Polish
**Risk: Low | Impact: Medium | Disruption: Low**
- Refine each page: dashboard KPIs, device table, config editor, alerts integration
- Ensure gradient KPI cards, table styling, form layouts match spec
- Light mode QA pass — verify WCAG contrast on every page
- Fix any spots where old layout assumptions break with new structure
Files touched: page components, dashboard widgets, table components
---
## 7. What This Is Not
- Not a rewrite — same React/Radix/Tailwind stack, same business logic
- Not a mobile redesign — responsive adaptations only
- Not a component library migration — still Radix UI primitives, restyled
**Note:** The Context Strip is the one piece of genuinely new UI (not just restyling). It replaces the current header and surfaces existing data in a new way. Phase 3 should be scoped accordingly — it's the only phase that introduces a new component with its own data needs.
## 8. Success Criteria
- The app is visually unrecognizable compared to its current state
- Nobody mistakes it for a shadcn template or another MikroTik dashboard
- Both dark and light modes pass WCAG AA contrast checks
- The indigo/violet accent and Manrope/IBM Plex Mono pairing create a recognizable identity
- Network engineers see a tool that respects their workflow, not a generic SaaS dashboard