From 5717b3deb44e09ddabdca12380b87aaca166bd06 Mon Sep 17 00:00:00 2001 From: Jason Staack Date: Mon, 16 Mar 2026 17:13:57 -0500 Subject: [PATCH] docs: add Deep Space UI redesign implementation plan 22 tasks across 4 phases: tokens/fonts, component restyling, layout restructure (ContextStrip + sidebar), page-level polish. Includes responsive spot-check and WCAG contrast audit. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-03-16-deep-space-ui-redesign.md | 972 ++++++++++++++++++ 1 file changed, 972 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-16-deep-space-ui-redesign.md diff --git a/docs/superpowers/plans/2026-03-16-deep-space-ui-redesign.md b/docs/superpowers/plans/2026-03-16-deep-space-ui-redesign.md new file mode 100644 index 0000000..0264cc7 --- /dev/null +++ b/docs/superpowers/plans/2026-03-16-deep-space-ui-redesign.md @@ -0,0 +1,972 @@ +# 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 1–16) + +- [ ] **Step 1: Replace the Geist @font-face blocks with Manrope + IBM Plex Mono** + +Replace lines 1–16 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 67–70: +```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 32–86 light root, lines 88–123 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 11–66 colors, lines 81–87 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 81–87): +```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>( + ({ className, ...props }, ref) => ( +
+ ), +) +``` + +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 ( +
+ {/* Left: org switcher */} +
+ Org placeholder +
+ {/* Center: status indicators */} +
+ Status loading... +
+ {/* Right: user controls */} +
+ ⌘K +
+
+ ) +} +``` + +- [ ] **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 `