Redesign sidebar nav and stretch Glance layout to fill viewport
Sidebar: prominent gold arc logo using a cleaned transparent-background asset (keys out the source PNG's baked-in brown texture), horizontal icon+label nav items with larger spacing, and a filled gold-tinted highlight box on the selected item. Layout: main content is now a flex column filling the viewport - hero fixed at top, middle row (Resource Overview / Recent Activity / Top Alerts) stretches to fill available height with items distributed, and the bottom row (Network Traffic / Shortcuts) anchored to the bottom. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
This commit is contained in:
parent
40bd0c9ec7
commit
1cbc1dab40
4 changed files with 48 additions and 46 deletions
BIN
public/archnest-logo-clean.png
Normal file
BIN
public/archnest-logo-clean.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
30
src/App.tsx
30
src/App.tsx
|
|
@ -23,12 +23,11 @@ function App() {
|
||||||
<TopBar />
|
<TopBar />
|
||||||
|
|
||||||
<section
|
<section
|
||||||
className="w-full overflow-y-auto"
|
className="flex w-full flex-col overflow-hidden"
|
||||||
style={{ height: 'calc(100vh - 56px)', scrollbarWidth: 'none', padding: '16px 24px 32px 24px' }}
|
style={{ height: 'calc(100vh - 56px)', scrollbarWidth: 'none', padding: '16px 24px 24px 24px', gap: '20px' }}
|
||||||
>
|
>
|
||||||
<div className="flex w-full max-w-none flex-col gap-0">
|
{/* Hero + KPI overlap — KPI bottom aligns with banner bottom */}
|
||||||
{/* Hero + KPI overlap — KPI bottom aligns with banner bottom */}
|
<div className="relative w-full shrink-0 overflow-hidden" style={{ height: '240px' }}>
|
||||||
<div className="relative w-full overflow-hidden" style={{ height: '260px' }}>
|
|
||||||
<img
|
<img
|
||||||
src="/archnest-hero-banner.png"
|
src="/archnest-hero-banner.png"
|
||||||
alt="ArchNest Banner"
|
alt="ArchNest Banner"
|
||||||
|
|
@ -53,22 +52,19 @@ function App() {
|
||||||
'radial-gradient(ellipse 75% 100% at center, transparent 55%, var(--color-page) 100%)',
|
'radial-gradient(ellipse 75% 100% at center, transparent 55%, var(--color-page) 100%)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* KPI cards positioned so their bottom edge aligns with banner bottom */}
|
{/* KPI cards positioned so their bottom edge aligns with banner bottom */}
|
||||||
<div className="absolute bottom-0 left-0 right-0 z-10 px-4">
|
<div className="absolute bottom-0 left-0 right-0 z-10 px-4">
|
||||||
<StatusCards />
|
<StatusCards />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 24px breathing room between KPI row and middle row */}
|
{/* Middle Row — stretches to fill available vertical space */}
|
||||||
<div style={{ height: '24px' }} />
|
<div className="min-h-0 flex-1">
|
||||||
|
|
||||||
{/* Middle Row */}
|
|
||||||
<MiddleRow />
|
<MiddleRow />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Gap */}
|
{/* Bottom Row — anchored to the bottom */}
|
||||||
<div style={{ height: '24px' }} />
|
<div className="shrink-0">
|
||||||
|
|
||||||
{/* Bottom Row */}
|
|
||||||
<BottomRow />
|
<BottomRow />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,9 @@ const cardBase: React.CSSProperties = {
|
||||||
transition: 'border-color 0.2s ease',
|
transition: 'border-color 0.2s ease',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBarColor(percentage: number) {
|
function getBarColor(percentage: number) {
|
||||||
|
|
@ -42,7 +45,7 @@ function getBarColor(percentage: number) {
|
||||||
|
|
||||||
export default function MiddleRow() {
|
export default function MiddleRow() {
|
||||||
return (
|
return (
|
||||||
<div className="grid w-full grid-cols-[1fr_1.4fr_1fr] gap-6">
|
<div className="grid h-full w-full grid-cols-[1fr_1.4fr_1fr] gap-6">
|
||||||
{/* Resource Overview */}
|
{/* Resource Overview */}
|
||||||
<div style={cardBase} className="hover:!border-gold/15 group">
|
<div style={cardBase} className="hover:!border-gold/15 group">
|
||||||
{/* Subtle hex pattern overlay */}
|
{/* Subtle hex pattern overlay */}
|
||||||
|
|
@ -50,7 +53,7 @@ export default function MiddleRow() {
|
||||||
{/* Gold top edge lighting */}
|
{/* Gold top edge lighting */}
|
||||||
<div style={{ position: 'absolute', top: 0, left: '10%', right: '10%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(200,164,52,0.15), transparent)', pointerEvents: 'none' }} />
|
<div style={{ position: 'absolute', top: 0, left: '10%', right: '10%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(200,164,52,0.15), transparent)', pointerEvents: 'none' }} />
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative z-10 flex flex-1 flex-col">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
||||||
Resource Overview
|
Resource Overview
|
||||||
|
|
@ -59,7 +62,7 @@ export default function MiddleRow() {
|
||||||
<X size={14} />
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-1 flex-col justify-around gap-4">
|
||||||
{resources.map((res) => {
|
{resources.map((res) => {
|
||||||
const percentage = res.unit === '%' ? res.current : (res.current / res.max) * 100
|
const percentage = res.unit === '%' ? res.current : (res.current / res.max) * 100
|
||||||
const displayValue = res.unit === '%' ? `${res.current}%` : `${res.current} / ${res.max}${res.unit}`
|
const displayValue = res.unit === '%' ? `${res.current}%` : `${res.current} / ${res.max}${res.unit}`
|
||||||
|
|
@ -84,7 +87,7 @@ export default function MiddleRow() {
|
||||||
{/* Gold top edge */}
|
{/* Gold top edge */}
|
||||||
<div style={{ position: 'absolute', top: 0, left: '5%', right: '5%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(200,164,52,0.2), transparent)', pointerEvents: 'none' }} />
|
<div style={{ position: 'absolute', top: 0, left: '5%', right: '5%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(200,164,52,0.2), transparent)', pointerEvents: 'none' }} />
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative z-10 flex flex-1 flex-col">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
||||||
Recent Activity
|
Recent Activity
|
||||||
|
|
@ -93,7 +96,7 @@ export default function MiddleRow() {
|
||||||
<X size={14} />
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-1 flex-col justify-around gap-3">
|
||||||
{activities.map((item, i) => {
|
{activities.map((item, i) => {
|
||||||
const Icon = item.icon
|
const Icon = item.icon
|
||||||
return (
|
return (
|
||||||
|
|
@ -118,14 +121,14 @@ export default function MiddleRow() {
|
||||||
{/* Amber edge lighting */}
|
{/* Amber edge lighting */}
|
||||||
<div style={{ position: 'absolute', top: 0, left: '10%', right: '10%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(231,126,34,0.15), transparent)', pointerEvents: 'none' }} />
|
<div style={{ position: 'absolute', top: 0, left: '10%', right: '10%', height: '1px', background: 'linear-gradient(90deg, transparent, rgba(231,126,34,0.15), transparent)', pointerEvents: 'none' }} />
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative z-10 flex flex-1 flex-col">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
<h3 style={{ fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1.5px', color: '#7A7D85', fontWeight: 500 }}>
|
||||||
Top Alerts
|
Top Alerts
|
||||||
</h3>
|
</h3>
|
||||||
<a href="#" style={{ fontSize: '11px', color: '#C8A434', textDecoration: 'none' }}>View all</a>
|
<a href="#" style={{ fontSize: '11px', color: '#C8A434', textDecoration: 'none' }}>View all</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-1 flex-col justify-around gap-3">
|
||||||
{alerts.map((alert, i) => (
|
{alerts.map((alert, i) => (
|
||||||
<div key={i} className="flex items-start gap-3">
|
<div key={i} className="flex items-start gap-3">
|
||||||
<div style={{ width: '8px', height: '8px', borderRadius: '50%', flexShrink: 0, marginTop: '5px', backgroundColor: alert.severity === 'high' ? '#E74C3C' : '#E67E22', boxShadow: alert.severity === 'high' ? '0 0 6px rgba(231,76,60,0.3)' : '0 0 6px rgba(230,126,34,0.2)' }} />
|
<div style={{ width: '8px', height: '8px', borderRadius: '50%', flexShrink: 0, marginTop: '5px', backgroundColor: alert.severity === 'high' ? '#E74C3C' : '#E67E22', boxShadow: alert.severity === 'high' ? '0 0 6px rgba(231,76,60,0.3)' : '0 0 6px rgba(230,126,34,0.2)' }} />
|
||||||
|
|
|
||||||
|
|
@ -28,51 +28,54 @@ export default function Sidebar({ collapsed, onToggle }: SidebarProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
className="fixed left-0 top-0 z-50 h-screen overflow-hidden flex flex-col items-center py-5"
|
className="fixed left-0 top-0 z-50 h-screen overflow-hidden flex flex-col py-6"
|
||||||
style={{ width: `${width}px`, backgroundColor: '#0A0B0D' }}
|
style={{ width: `${width}px`, backgroundColor: '#0A0B0D' }}
|
||||||
>
|
>
|
||||||
{/* Logo — larger, aligned with top bar */}
|
{/* Logo — prominent, centered at top. Blend mode hides the baked-in
|
||||||
<div className="flex flex-col items-center mb-6" style={{ paddingTop: '8px' }}>
|
dark background of the source PNG so only the gold arc shows. */}
|
||||||
|
<div className="flex flex-col items-center mb-10" style={{ paddingTop: '4px' }}>
|
||||||
<img
|
<img
|
||||||
src="/archnest-logo.png"
|
src="/archnest-logo-clean.png"
|
||||||
alt="ArchNest"
|
alt="ArchNest"
|
||||||
className="mb-1.5"
|
|
||||||
style={{
|
style={{
|
||||||
width: collapsed ? '28px' : '44px',
|
width: collapsed ? '56px' : '168px',
|
||||||
height: collapsed ? '28px' : '44px',
|
height: 'auto',
|
||||||
filter: 'drop-shadow(0 0 8px rgba(200,164,52,0.5))',
|
filter: 'drop-shadow(0 0 10px rgba(200,164,52,0.3))',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!collapsed && (
|
|
||||||
<span style={{ fontSize: '9px', fontWeight: 700, letterSpacing: '2.5px', color: '#C8A434', textTransform: 'uppercase' }}>
|
|
||||||
ArchNest
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Nav Items */}
|
{/* Nav Items */}
|
||||||
<nav className="flex-1 flex flex-col justify-center gap-6 w-full">
|
<nav className="flex-1 flex flex-col gap-2 w-full" style={{ padding: collapsed ? '0 8px' : '0 12px' }}>
|
||||||
{navItems.map((item) => {
|
{navItems.map((item) => {
|
||||||
const Icon = item.icon
|
const Icon = item.icon
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
key={item.label}
|
key={item.label}
|
||||||
href={item.route}
|
href={item.route}
|
||||||
className="relative flex w-full flex-col items-center justify-center gap-1.5 px-2 py-2 text-center no-underline transition-all duration-200"
|
className={`relative flex items-center no-underline transition-all duration-200 ${collapsed ? 'justify-center' : ''}`}
|
||||||
style={{ color: item.active ? '#C8A434' : '#7A7D85' }}
|
style={{
|
||||||
|
color: item.active ? '#C8A434' : '#7A7D85',
|
||||||
|
gap: collapsed ? '0' : '12px',
|
||||||
|
padding: collapsed ? '12px 0' : '12px 14px',
|
||||||
|
borderRadius: '10px',
|
||||||
|
backgroundColor: item.active ? 'rgba(200,164,52,0.1)' : 'transparent',
|
||||||
|
border: item.active ? '1px solid rgba(200,164,52,0.18)' : '1px solid transparent',
|
||||||
|
boxShadow: item.active ? '0 0 14px rgba(200,164,52,0.06)' : 'none',
|
||||||
|
}}
|
||||||
title={collapsed ? item.label : undefined}
|
title={collapsed ? item.label : undefined}
|
||||||
onMouseEnter={(e) => { if (!item.active) e.currentTarget.style.backgroundColor = 'rgba(200,164,52,0.05)'; e.currentTarget.style.color = '#C8A434' }}
|
onMouseEnter={(e) => { if (!item.active) { e.currentTarget.style.backgroundColor = 'rgba(200,164,52,0.05)'; e.currentTarget.style.color = '#C8A434' } }}
|
||||||
onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = 'transparent'; if (!item.active) e.currentTarget.style.color = '#7A7D85' }}
|
onMouseLeave={(e) => { if (!item.active) { e.currentTarget.style.backgroundColor = 'transparent'; e.currentTarget.style.color = '#7A7D85' } }}
|
||||||
>
|
>
|
||||||
{item.active && (
|
{item.active && (
|
||||||
<div
|
<div
|
||||||
className="absolute left-0 top-1/2 -translate-y-1/2 rounded-r"
|
className="absolute left-0 top-1/2 -translate-y-1/2"
|
||||||
style={{ width: '3px', height: '26px', backgroundColor: '#C8A434', boxShadow: '0 0 6px rgba(200,164,52,0.5)' }}
|
style={{ width: '3px', height: '22px', backgroundColor: '#C8A434', borderRadius: '0 3px 3px 0', boxShadow: '0 0 6px rgba(200,164,52,0.5)' }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Icon className="h-5 w-5 shrink-0" strokeWidth={item.active ? 2 : 1.5} />
|
<Icon className="h-5 w-5 shrink-0" strokeWidth={item.active ? 2 : 1.5} />
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className="max-w-[90px] truncate leading-tight font-medium" style={{ fontSize: '10px' }}>
|
<span className="truncate leading-tight font-medium" style={{ fontSize: '13px' }}>
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -84,7 +87,7 @@ export default function Sidebar({ collapsed, onToggle }: SidebarProps) {
|
||||||
{/* Collapse Toggle */}
|
{/* Collapse Toggle */}
|
||||||
<button
|
<button
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
className="p-1.5 rounded cursor-pointer bg-transparent transition-colors"
|
className="p-1.5 rounded cursor-pointer bg-transparent transition-colors self-center"
|
||||||
style={{ border: '1px solid #1E2025', color: '#7A7D85', marginBottom: '12px' }}
|
style={{ border: '1px solid #1E2025', color: '#7A7D85', marginBottom: '12px' }}
|
||||||
>
|
>
|
||||||
{collapsed ? <ChevronRight size={12} /> : <ChevronLeft size={12} />}
|
{collapsed ? <ChevronRight size={12} /> : <ChevronLeft size={12} />}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue