dev_arc_aws/design-decisions.md
Claude f24edb74b2
Add avatar upload to Settings, document Settings page, update README
- Profile section: click avatar to upload a personal photo (FileReader
  preview, hover camera icon overlay), replacing static initials
- README.md rewritten with project-specific page status table, dev
  setup, tech stack, and deployment notes
- design-decisions.md: add Settings Page subsection documenting layout,
  section-switching, and the avatar upload technique

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-18 18:50:43 +00:00

12 KiB

ArchNest — Design Decisions & Lessons Learned

This file captures all visual/UX decisions made during Glance page development. Apply these consistently to ALL future pages to avoid repeated iteration.


Global Rules (Apply to Every Page)

Sidebar

  • Expanded width: 200px (matches mockup proportions — needs room for labels)
  • Collapsed width: 64px (icon only)
  • User can manually collapse/expand via toggle button (not just responsive)
  • Main content margin-left must match sidebar width exactly

Page Title (Top Bar)

  • Color: Gold (#C8A434) — NOT white. Use inline style={{ color: '#C8A434' }} if Tailwind class doesn't apply
  • Font: 18px, bold, uppercase, tracking-wide
  • No border on top bar — blends into the page background

Colors — Use Inline Styles When Tailwind Fails

  • Tailwind v4 @theme custom colors (text-gold, bg-card, etc.) may not always apply
  • If a color isn't rendering correctly, fall back to inline style={{ color: '#C8A434' }}
  • Always verify visually after changes

Content Alignment

  • All rows must share the same horizontal padding (px-6 applied once at the parent container level)
  • Do NOT use different padding for different rows — this causes misalignment
  • The hero banner, status cards, middle row, and bottom row must all line up left and right edges

Hero Banner + KPI Overlap

  • Banner height: 200px
  • Status cards overlap via negative margin: -mt-12
  • Banner image: object-cover with object-position: center 25% (show the top/skyline, not center)
  • Cards use backdrop-blur-sm and bg-card/95 for glass effect over the banner

KPI Card Sizing

  • KPI 1 (System Status) and KPI 4 (Network): wider1.3fr
  • KPI 2 (Infrastructure) and KPI 3 (Security): standard1fr
  • Grid: grid-cols-[1.3fr_1fr_1fr_1.3fr]
  • Cards have compact padding: p-4 (not p-5 or p-6)
  • The mockup does NOT have a footer/status bar
  • Do not add one unless explicitly requested

Target Display

  • Primary design target: 16-inch screen / 1920px width
  • Lots of horizontal space available — don't constrain content width unnecessarily
  • Design should feel spacious, not cramped

Typography Sizes (smaller than default)

  • Card titles: 10-11px, uppercase, tracking-[1.5px], secondary color, font-medium
  • Large numbers: 24-28px, bold, primary color
  • Subtitles/labels: 10-11px, secondary color
  • Body text in lists: 13px, primary color
  • Timestamps: 11px, secondary color
  • Breakdowns: 9-10px, secondary color

Animations

  • Card hover: border → gold, 0.2s ease
  • Progress ring: animates from 0 to value in 1s
  • Sparklines: draw animation 1s
  • Progress bars: fill animation 0.8s

Icons

  • Source: Lucide React (imported per component, tree-shaken)
  • Size: 14-18px depending on context
  • Color: gold for active/accent, text-secondary for inactive
  • Gold glow on active sidebar items: shadow-[0_0_6px_rgba(200,164,52,0.5)]

Page-Specific Notes

Glance Page

  • No footer
  • Status cards overlap hero banner
  • Middle row: 3 equal-ish columns (30/40/30)
  • Bottom row: 2 columns (65/35)
  • Network Traffic card has its own background image at low opacity
  • User avatar dropdown has: Profile, Appearance, Security, Help & Support, Sign Out

Infrastructure Page

  • Hero banner rendered at the App.tsx layout level (not per-page), so it can extend behind the sticky TopBar — conditional via showHero = location.pathname === '/infrastructure'. TopBar and the search input are transparent on hero routes so the banner shows through.
  • Hero image: object-position: center 5% to reveal the full arch + sky; faded out via linear-gradient mask (vertical) + a radial-gradient(ellipse 70% 100% at center, ...) overlay (sides/corners) — borderless, blends into page background.
  • Sub-tabs trimmed to Overview only (Compute → Tags are future work, not built yet).
  • Status cards: rgba(10,10,12,0.5) background (more transparent than other pages), content centered (justify-content/alignItems: center) with a fixed row height (110px) so there's breathing room instead of empty space below left-aligned content.
  • Middle row (grid-cols-[1fr_1.6fr]): Resource Distribution (donut) and Node Status (server tile grid). Both use the /blank-kpi-bg.png background art with a cardDim (semi-transparent dark overlay) + cardVignette (radial-gradient closest-side blend) combo — keeps the background pattern visible but subdued, with borders blended rather than hard-edged. Card titles are rendered as our own text, NOT baked into the image (baked-in labels got covered by the dim overlay).
  • Node Status card: originally a world-map-style region dot plot, replaced with a 4-column tile grid (one tile per server, colored status dot + name) — a world map didn't make sense for a small/single-site infra. Reuse this "small-scale" reasoning for any future map-like cards.
  • Bottom row (grid-cols-[1.4fr_1fr_1fr]): Resource Trend / Cost Breakdown / Recent Activity — left plain/regular, no dim/vignette blending (explicit user preference, only the middle row gets the hero-style blend). Resource Trend uses the /archnest-network-traffic-bg.png background (plain, no dim/vignette) with 4 trend lines: blue #3B82F6 (compute), orange #E67E22 (storage), green #2ECC71 (database), brown #8B5E3C (network).
  • cardVignette radial-gradient must use the closest-side keyword (not a fixed %) — otherwise straight edges of the card don't reach full opacity and a hard border line remains visible (only corners fade correctly with a fixed percentage).

BookNest Page

  • Hero banner reused at the App.tsx layout level (showHero now includes /booknest), with page-specific tuning via small lookup maps in App.tsx keyed on location.pathname: heroPaddingTop (how far content sits below the hero top) and heroObjectPosition (horizontal/vertical crop of the arch image) — 70px / 54% 8% for BookNest vs. 72px / center 5% for Infrastructure. Extend these maps rather than hardcoding a single value when a future page needs different hero framing.
  • Large hero title + subtitle: unlike other pages, BookNest's TopBar title is NOT the small 18px uppercase label — it's rendered at 28px with a subtitle line ("Your Digital Library") underneath, driven by a new pageSubtitles map in TopBar.tsx. When a page has a subtitle, the header height grows from 56px → 72px (TopBar.tsx), and App.tsx's topBarHeight lookup keeps the content section's calc(100vh - Npx) in sync — both must be updated together or the layout will clip/gap.
  • Stats row lives directly under the hero subtitle (Links / Categories / Favorites), not in its own separate bar — matches the blueprint's hero-header block grouping.
  • "Quick Access" section label added above the 5 quick-access category cards (gold, same sectionTitle style) — the row is intentionally pulled up via a small negative hero-padding tune so it slightly overlaps the bottom edge of the hero, like the blueprint.
  • "+ Add Bookmark" button: same gold-fill button style as Infrastructure's "+ Add Resource", placed inline next to the "Quick Access" label rather than the page-stats row.
  • Right sidebar spans both grid rows (gridRow: '1 / span 2' in a gridTemplateRows: 'auto 1fr' grid) so the Favorites card can rise up near the hero while the main column's page-stats row stays in row 1 of column 1 only — keeps the two from overlapping/clipping. Negative margins were tried first and discarded: content pushed above the scroll container's natural top edge gets clipped by overflow-y-auto, so prefer reshaping the grid/flow over negative-margin hacks when something needs to "reach upward."
  • Sidebar cards stretch to match the main column's full height so the last card's (Category Breakdown) bottom border lines up with the bottom of the bookmark groups grid: sidebar wrapper is display:flex; flex-direction:column; height:100% (grid's default align-items: stretch already gives it the matching height), and each card uses flex: 1 0 auto (Favorites gets flex: 1.4 0 auto to read as visibly taller, per explicit request) so they share the leftover vertical space instead of all packing tight at content height.
  • lucide-react gotcha (see Global Rules candidate): the installed version does not export brand/wordmark icons (Github, Gitlab, Linkedin, Youtube) even though TypeScript's type declarations list them — tsc --noEmit stays clean while the page renders blank with a runtime SyntaxError only visible in the Vite dev log. Verify icon names against Object.keys(require('lucide-react')) before importing anything brand-flavored; substitutes used here: GitBranch/GitFork (GitHub/GitLab/Gitea), SquarePlay (YouTube), Briefcase (LinkedIn Learning).

Settings Page

  • No mockup image existed for this page — built directly from the blueprint's Page 6 spec rather than iterating against a screenshot.
  • Layout: fixed-width (200px) left nav listing the 6 sections (Profile, Appearance, Integrations, Notifications, Data & Backup, About) + a scrollable content panel on the right showing the active section. No hero banner (not in the blueprint spec for this page, and a settings page doesn't need one).
  • Active section is local component state (a string id) mapped through a sectionComponents record to the corresponding section-renderer function — simplest approach for a page with no routing/deep-linking requirement.
  • Shared style helpers (cardBase, sectionTitle, labelStyle, inputStyle) plus two small reusable components defined in the same file: Toggle (on/off pill switch) and GoldButton (gold-filled primary / danger-outline variant) — kept local to Settings.tsx rather than extracted, since no other page needs them yet.
  • Integrations cards mask secret fields (API keys/tokens) behind dots with an eye icon to reveal/hide, plus a "Test Connection" button per card — matches the blueprint's explicit "masked secrets with eye toggle" instruction.
  • Avatar upload (Profile section): the avatar circle is clickable and opens a hidden <input type="file" accept="image/*"> via a useRef + .click() call rather than a visible file input — keeps the round avatar as the only visible control. On change, FileReader.readAsDataURL converts the selected image to a base64 data URL stored in component state, which becomes the circle's backgroundImage (cover-fit), replacing the "AO" initials fallback. A hover-only camera-icon overlay (Tailwind group / group-hover:opacity-100) signals the circle is clickable without cluttering the default state. This is a frontend-only preview (no backend upload endpoint exists yet).

Future Integration Notes

Live Provider Data (AWS, Linode, etc.)

  • All KPI/status card data (resource counts, health, pricing, budgets, cost breakdowns, utilization, regions/map data) is currently mocked/static.
  • The Infrastructure page (and likely Glance) should eventually integrate with real cloud provider APIs — AWS, Linode, or any other VPC/cloud provider — via user-supplied API keys, to pull live data such as:
    • Resource inventory/counts and health status
    • Pricing and budget/cost data (replacing the static Cost Breakdown numbers)
    • Resource utilization metrics
    • Region/datacenter info for the Infrastructure Map
  • Design the data layer so it's provider-agnostic (a common interface/adapter per provider) since users may connect more than one provider's API key.