fix: design system v4 polish — home icon, mobile hamburger, contrast, font-label cleanup

- Home sidebar icon: always cyan, bg-accent-dim only when route is "/"
- Mobile TopBar: add left padding so hamburger isn't hidden behind logo
- Landing page: bump card border color (#1e2130 → #2a2f3d) for better contrast
- Replace all font-label references (40 occurrences, 19 files) with font-mono or font-sans
- Remove deprecated --font-label CSS variable from index.css
- Convert hardcoded hex in layout inline styles to CSS variables (light-mode ready)
- Add @types/react-syntax-highlighter for script builder types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 19:19:44 +00:00
parent 83a27b4d9a
commit 2bcd3e2f3c
26 changed files with 84 additions and 76 deletions

View File

@@ -18,7 +18,6 @@
"@sentry/vite-plugin": "^5.1.1", "@sentry/vite-plugin": "^5.1.1",
"@stripe/stripe-js": "^8.7.0", "@stripe/stripe-js": "^8.7.0",
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"@types/react-syntax-highlighter": "^15.5.13",
"@xyflow/react": "^12.10.0", "@xyflow/react": "^12.10.0",
"axios": "^1.13.4", "axios": "^1.13.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@@ -51,6 +50,7 @@
"@types/node": "^24.10.9", "@types/node": "^24.10.9",
"@types/react": "^19.2.5", "@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.0.18", "@vitest/coverage-v8": "^4.0.18",
"eslint": "^9.39.1", "eslint": "^9.39.1",
@@ -3021,6 +3021,7 @@
"version": "15.5.13", "version": "15.5.13",
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz",
"integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/react": "*" "@types/react": "*"

View File

@@ -31,7 +31,6 @@
"@sentry/vite-plugin": "^5.1.1", "@sentry/vite-plugin": "^5.1.1",
"@stripe/stripe-js": "^8.7.0", "@stripe/stripe-js": "^8.7.0",
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"@types/react-syntax-highlighter": "^15.5.13",
"@xyflow/react": "^12.10.0", "@xyflow/react": "^12.10.0",
"axios": "^1.13.4", "axios": "^1.13.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@@ -64,6 +63,7 @@
"@types/node": "^24.10.9", "@types/node": "^24.10.9",
"@types/react": "^19.2.5", "@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.0.18", "@vitest/coverage-v8": "^4.0.18",
"eslint": "^9.39.1", "eslint": "^9.39.1",

View File

@@ -29,7 +29,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
{flowName || 'Untitled Flow'} {flowName || 'Untitled Flow'}
</span> </span>
</div> </div>
<div className="mt-1 flex items-center gap-3 font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground"> <div className="mt-1 flex items-center gap-3 font-sans text-[0.625rem] uppercase tracking-widest text-muted-foreground">
<span>{flowType || 'flow'}</span> <span>{flowType || 'flow'}</span>
{nodeCount !== undefined && <span>{nodeCount} nodes</span>} {nodeCount !== undefined && <span>{nodeCount} nodes</span>}
</div> </div>
@@ -44,7 +44,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
<div className="border-b px-3 py-2.5" style={{ borderColor: 'var(--glass-border)' }}> <div className="border-b px-3 py-2.5" style={{ borderColor: 'var(--glass-border)' }}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Icon className={`h-3.5 w-3.5 ${colorClass}`} /> <Icon className={`h-3.5 w-3.5 ${colorClass}`} />
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground"> <span className="font-sans text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{node.type} {node.type}
</span> </span>
</div> </div>

View File

@@ -28,7 +28,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
return ( return (
<div key={s.id} className="rounded-lg border border-border bg-card px-3 py-2"> <div key={s.id} className="rounded-lg border border-border bg-card px-3 py-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground"> <span className="font-sans text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{s.action_type.replace(/_/g, ' ')} {s.action_type.replace(/_/g, ' ')}
</span> </span>
<span className={`flex items-center gap-1 text-xs ${config.color}`}> <span className={`flex items-center gap-1 text-xs ${config.color}`}>
@@ -39,7 +39,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
{s.target_node_id && ( {s.target_node_id && (
<p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p> <p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p>
)} )}
<p className="mt-0.5 font-label text-[0.625rem] text-text-muted"> <p className="mt-0.5 font-sans text-[0.625rem] text-text-muted">
{new Date(s.created_at).toLocaleDateString()} {new Date(s.created_at).toLocaleDateString()}
</p> </p>
</div> </div>

View File

@@ -24,7 +24,7 @@ export function GuideCard({ guide }: GuideCardProps) {
<p className="text-xs text-muted-foreground leading-relaxed"> <p className="text-xs text-muted-foreground leading-relaxed">
{guide.summary} {guide.summary}
</p> </p>
<span className="mt-2 inline-block font-label text-[0.625rem] uppercase tracking-widest text-primary"> <span className="mt-2 inline-block font-sans text-[0.625rem] uppercase tracking-widest text-primary">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'} {guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span> </span>
</div> </div>

View File

@@ -18,7 +18,7 @@ export function GuideSection({ section, index }: GuideSectionProps) {
<ol className="space-y-3 pl-8"> <ol className="space-y-3 pl-8">
{section.steps.map((step, i) => ( {section.steps.map((step, i) => (
<li key={i} className="relative"> <li key={i} className="relative">
<span className="absolute -left-6 top-0.5 font-label text-[0.625rem] text-muted-foreground"> <span className="absolute -left-6 top-0.5 font-sans text-[0.625rem] text-muted-foreground">
{i + 1}. {i + 1}.
</span> </span>
<p <p

View File

@@ -96,9 +96,9 @@ export function AppLayout() {
/> />
<nav <nav
className="absolute inset-y-0 left-0 w-72 shadow-2xl animate-slide-in-left" className="absolute inset-y-0 left-0 w-72 shadow-2xl animate-slide-in-left"
style={{ background: '#0f1118', borderRight: '1px solid #1e2130' }} style={{ background: 'var(--color-bg-sidebar)', borderRight: '1px solid var(--color-border-default)' }}
> >
<div className="flex h-14 items-center justify-between px-4" style={{ borderBottom: '1px solid #1e2130' }}> <div className="flex h-14 items-center justify-between px-4" style={{ borderBottom: '1px solid var(--color-border-default)' }}>
<Link to="/" className="flex items-center gap-2.5"> <Link to="/" className="flex items-center gap-2.5">
<BrandLogo size="sm" /> <BrandLogo size="sm" />
<span className="text-sm font-heading font-bold text-text-heading">ResolutionFlow</span> <span className="text-sm font-heading font-bold text-text-heading">ResolutionFlow</span>
@@ -115,7 +115,7 @@ export function AppLayout() {
<div className="flex flex-col p-3"> <div className="flex flex-col p-3">
{/* User info */} {/* User info */}
<div className="mb-3 pb-3 px-3" style={{ borderBottom: '1px solid #1e2130' }}> <div className="mb-3 pb-3 px-3" style={{ borderBottom: '1px solid var(--color-border-default)' }}>
<p className="text-sm font-medium text-foreground">{user?.name || user?.email}</p> <p className="text-sm font-medium text-foreground">{user?.name || user?.email}</p>
{effectiveRole && effectiveRole !== 'engineer' && ( {effectiveRole && effectiveRole !== 'engineer' && (
<span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground"> <span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground">
@@ -151,7 +151,7 @@ export function AppLayout() {
</div> </div>
{/* Logout */} {/* Logout */}
<div className="mt-3 pt-3" style={{ borderTop: '1px solid #1e2130' }}> <div className="mt-3 pt-3" style={{ borderTop: '1px solid var(--color-border-default)' }}>
<button <button
type="button" type="button"
onClick={handleLogout} onClick={handleLogout}

View File

@@ -329,7 +329,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
placeholder="Search flows, ask a question, navigate…" placeholder="Search flows, ask a question, navigate…"
className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-hidden" className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-hidden"
/> />
<kbd className="rounded border border-border bg-background px-1.5 py-0.5 font-label text-[0.625rem] text-muted-foreground"> <kbd className="rounded border border-border bg-background px-1.5 py-0.5 font-mono text-[0.625rem] text-muted-foreground">
ESC ESC
</kbd> </kbd>
</div> </div>
@@ -354,7 +354,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
<div key={group.type}> <div key={group.type}>
{/* Section label */} {/* Section label */}
<div className="px-3 pt-2 pb-1"> <div className="px-3 pt-2 pb-1">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"> <span className="font-sans text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
{group.label} {group.label}
</span> </span>
</div> </div>
@@ -451,11 +451,11 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
{flatItems.length > 0 && ( {flatItems.length > 0 && (
<div className="flex items-center gap-4 border-t border-border px-4 py-2"> <div className="flex items-center gap-4 border-t border-border px-4 py-2">
<span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground"> <span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground">
<kbd className="rounded border border-border bg-background px-1 py-px font-label"></kbd> <kbd className="rounded border border-border bg-background px-1 py-px font-mono"></kbd>
Navigate Navigate
</span> </span>
<span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground"> <span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground">
<kbd className="rounded border border-border bg-background px-1 py-px font-label"></kbd> <kbd className="rounded border border-border bg-background px-1 py-px font-mono"></kbd>
Open Open
</span> </span>
</div> </div>

View File

@@ -87,7 +87,7 @@ export function NavItem({ href, icon: Icon, label, badge, iconColor, matchPaths,
badge === 'dot' ? ( badge === 'dot' ? (
<span className="ml-auto h-1.5 w-1.5 shrink-0 rounded-full bg-brand-gradient-from" /> <span className="ml-auto h-1.5 w-1.5 shrink-0 rounded-full bg-brand-gradient-from" />
) : ( ) : (
<span className="ml-auto shrink-0 rounded-full bg-card border border-border px-2 text-[0.6875rem] font-label text-muted-foreground"> <span className="ml-auto shrink-0 rounded-full bg-card border border-border px-2 text-[0.6875rem] font-mono text-muted-foreground">
{badge} {badge}
</span> </span>
) )
@@ -117,7 +117,7 @@ export function NavItem({ href, icon: Icon, label, badge, iconColor, matchPaths,
> >
<span className="truncate">{child.label}</span> <span className="truncate">{child.label}</span>
{child.count !== undefined && ( {child.count !== undefined && (
<span className="ml-auto shrink-0 rounded-full bg-card border border-border px-2 text-[0.6875rem] font-label text-muted-foreground"> <span className="ml-auto shrink-0 rounded-full bg-card border border-border px-2 text-[0.6875rem] font-mono text-muted-foreground">
{child.count} {child.count}
</span> </span>
)} )}

View File

@@ -144,11 +144,11 @@ export function QuickLaunch({ open, onClose }: QuickLaunchProps) {
<div className="flex items-center gap-4 border-t border-border px-4 py-2"> <div className="flex items-center gap-4 border-t border-border px-4 py-2">
<span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground"> <span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground">
<kbd className="rounded border border-border bg-background px-1 py-px font-label"></kbd> <kbd className="rounded border border-border bg-background px-1 py-px font-mono"></kbd>
Navigate Navigate
</span> </span>
<span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground"> <span className="flex items-center gap-1 text-[0.625rem] text-muted-foreground">
<kbd className="rounded border border-border bg-background px-1 py-px font-label"></kbd> <kbd className="rounded border border-border bg-background px-1 py-px font-mono"></kbd>
Open Open
</span> </span>
</div> </div>

View File

@@ -241,6 +241,9 @@ export function Sidebar() {
const Icon = item.icon const Icon = item.icon
const hasChildren = item.children && item.children.length > 0 const hasChildren = item.children && item.children.length > 0
// Home icon: always cyan icon, but only bg-accent-dim when route is exactly "/"
const isHome = item.href === '/' && item.shortLabel === 'Home'
return ( return (
<div <div
key={key} key={key}
@@ -253,16 +256,20 @@ export function Sidebar() {
onFocus={() => hasChildren && !sidebarPinned ? openFlyout(key) : undefined} onFocus={() => hasChildren && !sidebarPinned ? openFlyout(key) : undefined}
className={cn( className={cn(
'group relative flex flex-col items-center justify-center rounded-lg px-2 py-3 transition-all duration-150', 'group relative flex flex-col items-center justify-center rounded-lg px-2 py-3 transition-all duration-150',
active isHome
? 'bg-accent-dim text-accent-text' ? active
: 'text-text-rail-label hover:text-muted-foreground' ? 'bg-accent-dim text-accent-text'
: 'text-accent-text'
: active
? 'bg-accent-dim text-accent-text'
: 'text-text-rail-label hover:text-muted-foreground'
)} )}
title={item.label} title={item.label}
> >
<span className="relative"> <span className="relative">
<Icon size={24} strokeWidth={1.6} className={active ? 'opacity-100' : 'opacity-60 group-hover:opacity-85'} /> <Icon size={24} strokeWidth={1.6} className={active || isHome ? 'opacity-100' : 'opacity-60 group-hover:opacity-85'} />
{item.badge !== undefined && item.badge > 0 && ( {item.badge !== undefined && item.badge > 0 && (
<span className="absolute -right-2 -top-1.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-primary px-1 text-[0.5rem] font-bold text-[#0c0d10]"> <span className="absolute -right-2 -top-1.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-primary px-1 text-[0.5rem] font-bold text-background">
{item.badge > 99 ? '99+' : item.badge} {item.badge > 99 ? '99+' : item.badge}
</span> </span>
)} )}
@@ -301,14 +308,14 @@ export function Sidebar() {
{active && !isParentDimmed && ( {active && !isParentDimmed && (
<div <div
className="absolute left-0 top-1/2 h-6 w-[3px] -translate-y-1/2" className="absolute left-0 top-1/2 h-6 w-[3px] -translate-y-1/2"
style={{ background: '#22d3ee', borderRadius: '0 3px 3px 0' }} style={{ background: 'var(--color-accent)', borderRadius: '0 3px 3px 0' }}
/> />
)} )}
<Icon size={18} className={cn('shrink-0', active ? 'opacity-100' : 'opacity-70')} /> <Icon size={18} className={cn('shrink-0', active ? 'opacity-100' : 'opacity-70')} />
<span className="truncate">{item.label}</span> <span className="truncate">{item.label}</span>
{item.badge !== undefined && item.badge > 0 && ( {item.badge !== undefined && item.badge > 0 && (
<span className="ml-auto shrink-0 rounded-full px-2 text-[0.6875rem] font-mono text-text-muted" <span className="ml-auto shrink-0 rounded-full px-2 text-[0.6875rem] font-mono text-text-muted"
style={{ background: '#14161d', border: '1px solid #1e2130' }}> style={{ background: 'var(--color-bg-card)', border: '1px solid var(--color-border-default)' }}>
{item.badge} {item.badge}
</span> </span>
)} )}
@@ -338,7 +345,7 @@ export function Sidebar() {
<span className="truncate">{child.label}</span> <span className="truncate">{child.label}</span>
{child.count !== undefined && ( {child.count !== undefined && (
<span className="ml-auto shrink-0 rounded-full px-2 text-[0.6875rem] font-mono text-text-muted" <span className="ml-auto shrink-0 rounded-full px-2 text-[0.6875rem] font-mono text-text-muted"
style={{ background: '#14161d', border: '1px solid #1e2130' }}> style={{ background: 'var(--color-bg-card)', border: '1px solid var(--color-border-default)' }}>
{child.count} {child.count}
</span> </span>
)} )}
@@ -365,7 +372,7 @@ export function Sidebar() {
<nav <nav
ref={sidebarRef} ref={sidebarRef}
className="sidebar flex flex-col h-full" className="sidebar flex flex-col h-full"
style={{ background: '#0f1118', borderRight: '1px solid #1e2130' }} style={{ background: 'var(--color-bg-sidebar)', borderRight: '1px solid var(--color-border-default)' }}
onWheel={handleWheel} onWheel={handleWheel}
> >
{/* Pinned sidebar content */} {/* Pinned sidebar content */}
@@ -385,7 +392,7 @@ export function Sidebar() {
<div className="flex-1" /> <div className="flex-1" />
{/* Footer */} {/* Footer */}
<div className="px-3 pt-2 pb-4 space-y-0.5" style={{ borderTop: '1px solid #1e2130' }}> <div className="px-3 pt-2 pb-4 space-y-0.5" style={{ borderTop: '1px solid var(--color-border-default)' }}>
{footerItems.map((item, i) => renderPinnedItem(item, `footer-${i}`))} {footerItems.map((item, i) => renderPinnedItem(item, `footer-${i}`))}
<button <button
type="button" type="button"
@@ -411,7 +418,7 @@ export function Sidebar() {
<nav <nav
ref={sidebarRef} ref={sidebarRef}
className="sidebar flex flex-col items-center h-full shrink-0" className="sidebar flex flex-col items-center h-full shrink-0"
style={{ background: '#0f1118', borderRight: '1px solid #1e2130', width: '72px' }} style={{ background: 'var(--color-bg-sidebar)', borderRight: '1px solid var(--color-border-default)', width: '72px' }}
onWheel={handleWheel} onWheel={handleWheel}
> >
{/* Grouped nav items */} {/* Grouped nav items */}
@@ -422,7 +429,7 @@ export function Sidebar() {
<div className="flex-1" /> <div className="flex-1" />
{/* Footer: Account + Pin */} {/* Footer: Account + Pin */}
<div className="flex flex-col items-center w-full px-1 pb-5 pt-3 space-y-1.5" style={{ borderTop: '1px solid #1e2130' }}> <div className="flex flex-col items-center w-full px-1 pb-5 pt-3 space-y-1.5" style={{ borderTop: '1px solid var(--color-border-default)' }}>
{footerItems.map((item, i) => renderRailItem(item, `footer-${i}`))} {footerItems.map((item, i) => renderRailItem(item, `footer-${i}`))}
<button <button
type="button" type="button"
@@ -448,8 +455,8 @@ export function Sidebar() {
className="flex flex-col h-full overflow-y-auto py-4 px-2" className="flex flex-col h-full overflow-y-auto py-4 px-2"
style={{ style={{
width: drawerWidth, width: drawerWidth,
background: '#0f1118', background: 'var(--color-bg-sidebar)',
borderRight: '1px solid #1e2130', borderRight: '1px solid var(--color-border-default)',
boxShadow: '4px 0 12px rgba(0,0,0,0.2)', boxShadow: '4px 0 12px rgba(0,0,0,0.2)',
}} }}
> >

View File

@@ -55,10 +55,10 @@ export function TopBar() {
return ( return (
<> <>
<header <header
className="topbar relative z-10 flex items-center gap-4 px-4" className="topbar relative z-10 flex items-center gap-4 px-4 pl-14 md:pl-4"
style={{ style={{
background: '#0f1118', background: 'var(--color-bg-sidebar)',
borderBottom: '1px solid #1e2130', borderBottom: '1px solid var(--color-border-default)',
}} }}
> >
{/* Logo area */} {/* Logo area */}
@@ -85,17 +85,17 @@ export function TopBar() {
<div <div
className="w-full rounded-md py-2 pl-9 pr-14 text-[0.8125rem] text-muted-foreground cursor-pointer transition-colors" className="w-full rounded-md py-2 pl-9 pr-14 text-[0.8125rem] text-muted-foreground cursor-pointer transition-colors"
style={{ style={{
background: '#14161d', background: 'var(--color-bg-card)',
border: '1px solid #1e2130', border: '1px solid var(--color-border-default)',
}} }}
onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.borderColor = '#2a2f3d' }} onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.borderColor = 'var(--color-border-hover)' }}
onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.borderColor = '#1e2130' }} onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.borderColor = 'var(--color-border-default)' }}
> >
Search flows, sessions, tags... Search flows, sessions, tags...
</div> </div>
<span <span
className="absolute right-3 top-1/2 -translate-y-1/2 rounded px-1.5 py-0.5 font-mono text-[0.625rem] text-text-muted" className="absolute right-3 top-1/2 -translate-y-1/2 rounded px-1.5 py-0.5 font-mono text-[0.625rem] text-text-muted"
style={{ background: '#0c0d10', border: '1px solid #1e2130' }} style={{ background: 'var(--color-bg-page)', border: '1px solid var(--color-border-default)' }}
> >
{navigator.platform?.toLowerCase().includes('mac') ? '\u2318K' : 'Ctrl+K'} {navigator.platform?.toLowerCase().includes('mac') ? '\u2318K' : 'Ctrl+K'}
</span> </span>
@@ -143,9 +143,9 @@ export function TopBar() {
{userMenuOpen && ( {userMenuOpen && (
<div <div
className="absolute right-0 z-50 mt-2 w-56 rounded-lg p-1 shadow-xl animate-scale-in" className="absolute right-0 z-50 mt-2 w-56 rounded-lg p-1 shadow-xl animate-scale-in"
style={{ background: '#14161d', border: '1px solid #1e2130' }} style={{ background: 'var(--color-bg-card)', border: '1px solid var(--color-border-default)' }}
> >
<div className="px-3 py-2.5 mb-1" style={{ borderBottom: '1px solid #1e2130' }}> <div className="px-3 py-2.5 mb-1" style={{ borderBottom: '1px solid var(--color-border-default)' }}>
<p className="text-sm font-medium text-foreground truncate">{user?.name || user?.email}</p> <p className="text-sm font-medium text-foreground truncate">{user?.name || user?.email}</p>
{effectiveRole && effectiveRole !== 'engineer' && ( {effectiveRole && effectiveRole !== 'engineer' && (
<span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground"> <span className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground">
@@ -172,7 +172,7 @@ export function TopBar() {
Admin Panel Admin Panel
</Link> </Link>
)} )}
<div className="mt-1 pt-1" style={{ borderTop: '1px solid #1e2130' }}> <div className="mt-1 pt-1" style={{ borderTop: '1px solid var(--color-border-default)' }}>
<button <button
onClick={handleLogout} onClick={handleLogout}
className={cn( className={cn(

View File

@@ -96,7 +96,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
key={tab.id} key={tab.id}
onClick={() => setActiveTab(tab.id)} onClick={() => setActiveTab(tab.id)}
className={cn( className={cn(
"flex items-center gap-1.5 px-3 py-2 font-label text-[0.6875rem] uppercase tracking-wide transition-colors", "flex items-center gap-1.5 px-3 py-2 font-sans text-[0.6875rem] uppercase tracking-wide transition-colors",
activeTab === tab.id activeTab === tab.id
? "border-b-2 border-primary text-foreground" ? "border-b-2 border-primary text-foreground"
: "text-muted-foreground hover:text-foreground" : "text-muted-foreground hover:text-foreground"
@@ -112,7 +112,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
<div className="p-6"> <div className="p-6">
{activeTab === 'manual' && ( {activeTab === 'manual' && (
<div className="space-y-2"> <div className="space-y-2">
<label className="font-label text-[0.6875rem] uppercase tracking-wide text-muted-foreground"> <label className="font-sans text-[0.6875rem] uppercase tracking-wide text-muted-foreground">
Server names (one per line) Server names (one per line)
</label> </label>
<textarea <textarea

View File

@@ -86,7 +86,7 @@ export function BatchStatusCard({ session, targetLabel, treeId, batchId }: Batch
<div className="flex items-center gap-2 mt-0.5"> <div className="flex items-center gap-2 mt-0.5">
{isComplete && session?.outcome && ( {isComplete && session?.outcome && (
<span className={cn( <span className={cn(
"font-label text-[0.625rem] uppercase tracking-wide rounded-full px-2 py-0.5", "font-sans text-[0.625rem] uppercase tracking-wide rounded-full px-2 py-0.5",
OUTCOME_COLORS[session.outcome] ?? OUTCOME_COLORS.unresolved OUTCOME_COLORS[session.outcome] ?? OUTCOME_COLORS.unresolved
)}> )}>
{OUTCOME_LABELS[session.outcome] ?? session.outcome} {OUTCOME_LABELS[session.outcome] ?? session.outcome}

View File

@@ -91,7 +91,7 @@ export function PowerShellHighlighter({ script, className }: Props) {
} }
return ( return (
<pre className={className ?? "font-label text-sm bg-card rounded-xl p-4 overflow-x-auto"}> <pre className={className ?? "font-mono text-sm bg-card rounded-xl p-4 overflow-x-auto"}>
<code>{parts}</code> <code>{parts}</code>
</pre> </pre>
) )

View File

@@ -118,30 +118,30 @@ export function ScriptConfigurePane({ canGenerate, onBack }: Props) {
{selectedTemplate.requires_elevation && ( {selectedTemplate.requires_elevation && (
<span <span
title="Requires administrator elevation" title="Requires administrator elevation"
className="inline-flex items-center gap-1 font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border text-amber-400 bg-amber-400/10 border-amber-400/20" className="inline-flex items-center gap-1 font-sans text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border text-amber-400 bg-amber-400/10 border-amber-400/20"
> >
<ShieldAlert size={11} /> <ShieldAlert size={11} />
Elevated Elevated
</span> </span>
)} )}
<span className={cn( <span className={cn(
'font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border', 'font-sans text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border',
COMPLEXITY_CLASSES[selectedTemplate.complexity] COMPLEXITY_CLASSES[selectedTemplate.complexity]
)}> )}>
{selectedTemplate.complexity} {selectedTemplate.complexity}
</span> </span>
{categoryName && ( {categoryName && (
<span className="font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border border-border text-muted-foreground bg-white/5"> <span className="font-sans text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border border-border text-muted-foreground bg-white/5">
{categoryName} {categoryName}
</span> </span>
)} )}
{displayTags.map(tag => ( {displayTags.map(tag => (
<span key={tag} className="font-label text-[0.625rem] px-1.5 py-0.5 rounded border border-border text-muted-foreground bg-white/5"> <span key={tag} className="font-sans text-[0.625rem] px-1.5 py-0.5 rounded border border-border text-muted-foreground bg-white/5">
{tag} {tag}
</span> </span>
))} ))}
{extraTagCount > 0 && ( {extraTagCount > 0 && (
<span className="font-label text-[0.625rem] text-muted-foreground">+{extraTagCount}</span> <span className="font-sans text-[0.625rem] text-muted-foreground">+{extraTagCount}</span>
)} )}
</div> </div>
</div> </div>

View File

@@ -41,7 +41,7 @@ export function ScriptFilterBar({ inputValue, setInputValue }: Props) {
type="button" type="button"
onClick={() => setCategory(null)} onClick={() => setCategory(null)}
className={cn( className={cn(
'font-label text-xs px-3 py-1.5 rounded-full border transition-all', 'font-sans text-xs px-3 py-1.5 rounded-full border transition-all',
activeCategoryId === null activeCategoryId === null
? 'bg-primary/10 border-primary/30 text-foreground border-l-[3px] border-l-primary' ? 'bg-primary/10 border-primary/30 text-foreground border-l-[3px] border-l-primary'
: 'border-border text-muted-foreground hover:border-[rgba(255,255,255,0.12)] hover:text-foreground' : 'border-border text-muted-foreground hover:border-[rgba(255,255,255,0.12)] hover:text-foreground'
@@ -55,7 +55,7 @@ export function ScriptFilterBar({ inputValue, setInputValue }: Props) {
type="button" type="button"
onClick={() => setCategory(cat.id)} onClick={() => setCategory(cat.id)}
className={cn( className={cn(
'font-label text-xs px-3 py-1.5 rounded-full border transition-all', 'font-sans text-xs px-3 py-1.5 rounded-full border transition-all',
activeCategoryId === cat.id activeCategoryId === cat.id
? 'bg-primary/10 border-primary/30 text-foreground border-l-[3px] border-l-primary' ? 'bg-primary/10 border-primary/30 text-foreground border-l-[3px] border-l-primary'
: 'border-border text-muted-foreground hover:border-[rgba(255,255,255,0.12)] hover:text-foreground' : 'border-border text-muted-foreground hover:border-[rgba(255,255,255,0.12)] hover:text-foreground'

View File

@@ -57,7 +57,7 @@ export function ScriptParameterForm({ canGenerate }: Props) {
{ungrouped.map(renderParam)} {ungrouped.map(renderParam)}
{groupOrder.map(group => ( {groupOrder.map(group => (
<div key={group}> <div key={group}>
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3"> <p className="font-sans text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">
{group} {group}
</p> </p>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">

View File

@@ -31,7 +31,7 @@ export function TemplateCard({ template, onConfigure }: Props) {
<ShieldAlert size={13} className="text-amber-400" /> <ShieldAlert size={13} className="text-amber-400" />
</span> </span>
)} )}
<span className={cn('font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded', COMPLEXITY_CLASSES[template.complexity])}> <span className={cn('font-sans text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded', COMPLEXITY_CLASSES[template.complexity])}>
{template.complexity} {template.complexity}
</span> </span>
</div> </div>
@@ -44,7 +44,7 @@ export function TemplateCard({ template, onConfigure }: Props) {
)} )}
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-3 text-[0.625rem] text-muted-foreground font-label"> <div className="flex items-center gap-3 text-[0.625rem] text-muted-foreground font-sans">
<span>{template.usage_count}× used</span> <span>{template.usage_count}× used</span>
{template.tags.length > 0 && ( {template.tags.length > 0 && (
<div className="flex gap-1 flex-wrap"> <div className="flex gap-1 flex-wrap">

View File

@@ -91,12 +91,12 @@ export function ActivityItem({
{/* Ticket number or timestamp */} {/* Ticket number or timestamp */}
{ticketNumber && !isRecent && ( {ticketNumber && !isRecent && (
<span className="shrink-0 font-label text-[0.5625rem] text-[#60a5fa]"> <span className="shrink-0 font-mono text-[0.5625rem] text-[#60a5fa]">
{ticketNumber} {ticketNumber}
</span> </span>
)} )}
{isRecent && timestamp && ( {isRecent && timestamp && (
<span className="shrink-0 font-label text-[0.5625rem] text-text-muted"> <span className="shrink-0 font-mono text-[0.5625rem] text-text-muted">
{formatRelativeTime(timestamp)} {formatRelativeTime(timestamp)}
</span> </span>
)} )}

View File

@@ -47,7 +47,7 @@ export function CategoryList({ categories, activeId, onSelect }: CategoryListPro
style={{ backgroundColor: cat.color }} style={{ backgroundColor: cat.color }}
/> />
<span className="flex-1 truncate text-left">{cat.name}</span> <span className="flex-1 truncate text-left">{cat.name}</span>
<span className="font-label text-[0.6875rem] text-[var(--text-dimmed)]">{cat.count}</span> <span className="font-mono text-[0.6875rem] text-[var(--text-dimmed)]">{cat.count}</span>
</button> </button>
))} ))}
{hasMore && ( {hasMore && (

View File

@@ -22,7 +22,7 @@ export function SidebarActivityFeed({
{/* Header */} {/* Header */}
<div className="flex items-center gap-1.5 px-2.5 py-1 mb-0.5"> <div className="flex items-center gap-1.5 px-2.5 py-1 mb-0.5">
<Clock size={10} style={{ color: '#34d399' }} /> <Clock size={10} style={{ color: '#34d399' }} />
<span className="font-label text-[0.5625rem] uppercase tracking-[0.12em] text-text-muted"> <span className="font-sans text-[0.5625rem] uppercase tracking-[0.12em] text-text-muted">
Activity Activity
</span> </span>
</div> </div>

View File

@@ -55,36 +55,36 @@ export function SidebarStatsBar({ resolved, active, completedMinutes, activeSess
> >
<div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center"> <div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center">
<div <div
className="font-label text-sm font-semibold leading-none" className="font-mono text-sm font-semibold leading-none"
style={{ color: '#34d399' }} style={{ color: '#34d399' }}
aria-label={`${resolved} resolved today`} aria-label={`${resolved} resolved today`}
> >
{resolved} {resolved}
</div> </div>
<div className="mt-1 font-label text-[7px] uppercase tracking-[0.1em] text-text-muted"> <div className="mt-1 font-mono text-[7px] uppercase tracking-[0.1em] text-text-muted">
Resolved Resolved
</div> </div>
</div> </div>
<div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center"> <div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center">
<div <div
className="font-label text-sm font-semibold leading-none" className="font-mono text-sm font-semibold leading-none"
style={{ color: '#22d3ee' }} style={{ color: '#22d3ee' }}
aria-label={`${active} active sessions`} aria-label={`${active} active sessions`}
> >
{active} {active}
</div> </div>
<div className="mt-1 font-label text-[7px] uppercase tracking-[0.1em] text-text-muted"> <div className="mt-1 font-mono text-[7px] uppercase tracking-[0.1em] text-text-muted">
Active Active
</div> </div>
</div> </div>
<div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center"> <div className="flex-1 rounded-md bg-[rgba(255,255,255,0.02)] px-1 py-1.5 text-center">
<div <div
className="font-label text-sm font-semibold leading-none text-muted-foreground" className="font-mono text-sm font-semibold leading-none text-muted-foreground"
aria-label={`${formatDuration(totalSeconds)} total session time today`} aria-label={`${formatDuration(totalSeconds)} total session time today`}
> >
{formatDuration(totalSeconds)} {formatDuration(totalSeconds)}
</div> </div>
<div className="mt-1 font-label text-[7px] uppercase tracking-[0.1em] text-text-muted"> <div className="mt-1 font-mono text-[7px] uppercase tracking-[0.1em] text-text-muted">
Total Time Total Time
</div> </div>
</div> </div>

View File

@@ -22,7 +22,7 @@ export function TagCloud({ tags, activeTags = [], onTagClick }: TagCloudProps) {
key={tag} key={tag}
onClick={() => onTagClick(tag)} onClick={() => onTagClick(tag)}
className={cn( className={cn(
'rounded-md border px-2 py-0.5 font-label text-[0.625rem] transition-colors', 'rounded-md border px-2 py-0.5 font-sans text-[0.625rem] transition-colors',
isActive isActive
? 'border-primary/30 bg-primary/10 text-primary' ? 'border-primary/30 bg-primary/10 text-primary'
: 'border-border bg-card text-muted-foreground hover:border-primary/20 hover:text-foreground' : 'border-border bg-card text-muted-foreground hover:border-primary/20 hover:text-foreground'

View File

@@ -73,7 +73,7 @@
--font-sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif; --font-sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
--font-heading: 'Bricolage Grotesque', system-ui, sans-serif; --font-heading: 'Bricolage Grotesque', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace; --font-mono: 'JetBrains Mono', monospace;
--font-label: 'JetBrains Mono', monospace; /* deprecated — use font-mono for new code */ /* font-label removed — migrated to font-mono or font-sans */
/* ── Animations ────────────────────────────────── */ /* ── Animations ────────────────────────────────── */
--animate-fade-in: fade-in 200ms ease-out; --animate-fade-in: fade-in 200ms ease-out;

View File

@@ -630,7 +630,7 @@
padding: 2rem; padding: 2rem;
border-radius: 16px; border-radius: 16px;
background: #14161d; background: #14161d;
border: 1px solid #1e2130; border: 1px solid #2a2f3d;
transition: border-color 0.3s, transform 0.3s; transition: border-color 0.3s, transform 0.3s;
} }
@@ -748,7 +748,7 @@
padding: 2rem; padding: 2rem;
border-radius: 16px; border-radius: 16px;
background: #14161d; background: #14161d;
border: 1px solid #1e2130; border: 1px solid #2a2f3d;
position: relative; position: relative;
counter-increment: landing-step; counter-increment: landing-step;
transition: border-color 0.3s, transform 0.3s; transition: border-color 0.3s, transform 0.3s;
@@ -912,7 +912,7 @@
padding: 2rem; padding: 2rem;
border-radius: 16px; border-radius: 16px;
background: #14161d; background: #14161d;
border: 1px solid #1e2130; border: 1px solid #2a2f3d;
transition: border-color 0.3s ease, transform 0.3s ease; transition: border-color 0.3s ease, transform 0.3s ease;
} }
@@ -966,7 +966,7 @@
padding: 2.5rem 2rem; padding: 2.5rem 2rem;
border-radius: 16px; border-radius: 16px;
background: #14161d; background: #14161d;
border: 1px solid #1e2130; border: 1px solid #2a2f3d;
transition: border-color 0.3s, transform 0.3s; transition: border-color 0.3s, transform 0.3s;
} }
@@ -1080,7 +1080,7 @@
.landing-pricing-btn.outline { .landing-pricing-btn.outline {
color: #e2e5eb; color: #e2e5eb;
border: 1px solid #1e2130; border: 1px solid #2a2f3d;
background: transparent; background: transparent;
} }