From 34023f426e0080df0af4b93b2fb0cfae03d55d50 Mon Sep 17 00:00:00 2001 From: Dev M Date: Sun, 20 Sep 2026 07:28:47 +0530 Subject: [PATCH] ui: modern surface revamp for light and dark themes Bright high-contrast dark typography, fully light chrome in light mode, contained report pane with thin auto-hide scrollbar, Inter + JetBrains Mono, and theme CSS variables replacing hard-coded dark surfaces. --- src/app/globals.css | 364 +++++++++++++++++++----- src/app/layout.tsx | 19 +- src/components/synthesis/AgentGraph.tsx | 44 ++- src/components/synthesis/App.tsx | 172 ++++++----- src/components/synthesis/ReportView.tsx | 7 +- src/components/synthesis/Timeline.tsx | 34 +-- 6 files changed, 463 insertions(+), 177 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 30f6b6e..b09fcd0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,26 +1,76 @@ @import "tailwindcss"; @plugin "@tailwindcss/typography"; +/* Class-based dark mode (html.dark / data-theme) for Tailwind dark: variants */ +@custom-variant dark (&:where(.dark, .dark *)); + +@theme inline { + --font-sans: var(--font-inter), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: var(--font-jetbrains), ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace; + --color-bg: var(--bg); + --color-surface: var(--surface); + --color-surface-solid: var(--surface-solid); + --color-surface-raised: var(--surface-raised); + --color-fg: var(--fg); + --color-fg-heading: var(--fg-heading); + --color-muted: var(--muted); + --color-muted-strong: var(--muted-strong); + --color-border: var(--border); + --color-input: var(--input-bg); + --color-inset: var(--inset); + --color-header: var(--header-bg); + --color-accent: var(--accent); + --color-accent-2: var(--accent-2); +} + :root, [data-theme="dark"] { color-scheme: dark; - --bg: #05060f; - --surface: rgba(255, 255, 255, 0.03); - --fg: #e2e8f0; + --bg: #030712; + --surface: rgba(255, 255, 255, 0.035); + --surface-solid: #0a0e1a; + --surface-raised: rgba(255, 255, 255, 0.055); + --fg: #f8fafc; + --fg-heading: #ffffff; --muted: #94a3b8; + --muted-strong: #cbd5e1; --border: rgba(255, 255, 255, 0.1); + --input-bg: #0a0e1a; + --inset: rgba(0, 0, 0, 0.28); + --header-bg: rgba(3, 7, 18, 0.82); + --accent: #a78bfa; + --accent-2: #22d3ee; + --glow-violet: rgba(124, 58, 237, 0.22); + --glow-cyan: rgba(6, 182, 212, 0.14); + --glow-fuchsia: rgba(217, 70, 239, 0.1); + --grid-line: rgba(148, 163, 184, 0.055); } [data-theme="light"] { color-scheme: light; - --bg: #f4f2fb; + --bg: #f8fafc; --surface: #ffffff; + --surface-solid: #ffffff; + --surface-raised: #f1f5f9; --fg: #0f172a; - --muted: #475569; - --border: rgba(15, 23, 42, 0.12); + --fg-heading: #020617; + --muted: #64748b; + --muted-strong: #334155; + --border: rgba(15, 23, 42, 0.1); + --input-bg: #ffffff; + --inset: rgba(15, 23, 42, 0.04); + --header-bg: rgba(255, 255, 255, 0.88); + --accent: #7c3aed; + --accent-2: #0891b2; + --glow-violet: rgba(124, 58, 237, 0.1); + --glow-cyan: rgba(6, 182, 212, 0.08); + --glow-fuchsia: rgba(217, 70, 239, 0.06); + --grid-line: rgba(15, 23, 42, 0.05); } -html[data-theme="light"] { color-scheme: light; } +html[data-theme="light"] { + color-scheme: light; +} html { scroll-behavior: smooth; @@ -30,14 +80,19 @@ html { body { background-color: var(--bg); color: var(--fg); + font-family: var(--font-sans); -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } -/* Ambient background: deep space gradient + faint engineering grid */ +/* Ambient background: deep space / soft cool light + faint engineering grid */ .app-bg { position: relative; isolation: isolate; + min-height: 100vh; + background-color: var(--bg); + color: var(--fg); } .app-bg::before { content: ""; @@ -45,9 +100,10 @@ body { inset: 0; z-index: -2; background: - radial-gradient(1100px 700px at 12% -8%, rgba(124, 58, 237, 0.22), transparent 60%), - radial-gradient(1000px 620px at 92% 4%, rgba(6, 182, 212, 0.16), transparent 60%), - radial-gradient(900px 600px at 50% 120%, rgba(217, 70, 239, 0.12), transparent 60%); + radial-gradient(1100px 700px at 12% -8%, var(--glow-violet), transparent 60%), + radial-gradient(1000px 620px at 92% 4%, var(--glow-cyan), transparent 60%), + radial-gradient(900px 600px at 50% 120%, var(--glow-fuchsia), transparent 60%), + var(--bg); } .app-bg::after { content: ""; @@ -55,14 +111,109 @@ body { inset: 0; z-index: -1; background-image: - linear-gradient(to right, rgba(148, 163, 184, 0.06) 1px, transparent 1px), - linear-gradient(to bottom, rgba(148, 163, 184, 0.06) 1px, transparent 1px); + linear-gradient(to right, var(--grid-line) 1px, transparent 1px), + linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px); background-size: 44px 44px; mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%); -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%); + pointer-events: none; } -/* Overlay hover-reveal scrollbars: invisible until the pointer is over that pane. */ +/* Semantic surface primitives — flip with [data-theme] */ +.panel { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 1rem; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} +.panel-solid { + background: var(--surface-solid); + border: 1px solid var(--border); + border-radius: 0.75rem; +} +.surface-inset { + background: var(--inset); + border: 1px solid var(--border); +} +.chrome-header { + background: var(--header-bg); + border-bottom: 1px solid var(--border); + backdrop-filter: blur(16px) saturate(1.2); + -webkit-backdrop-filter: blur(16px) saturate(1.2); +} +.input-surface { + background: var(--input-bg); + border: 1px solid var(--border); + color: var(--fg); +} +.input-surface::placeholder { + color: color-mix(in oklab, var(--muted) 75%, transparent); +} +.tab-rail { + background: var(--inset); + border: 1px solid color-mix(in oklab, var(--border) 70%, transparent); + border-radius: 0.5rem; +} +.text-heading { + color: var(--fg-heading); +} +.text-body { + color: var(--fg); +} +.text-soft { + color: var(--muted-strong); +} +.text-dim { + color: var(--muted); +} +.border-theme { + border-color: var(--border); +} + +/* Overlay / auto-hide thin scrollbars — appear on hover, focus, or scroll intent */ +.scrollbar-overlay { + scrollbar-width: thin; + scrollbar-color: transparent transparent; + scrollbar-gutter: stable; +} +.scrollbar-overlay:hover, +.scrollbar-overlay:focus, +.scrollbar-overlay:focus-within, +.scrollbar-overlay.is-scrolling { + scrollbar-color: color-mix(in oklab, var(--fg) 35%, transparent) transparent; +} +.scrollbar-overlay::-webkit-scrollbar { + width: 6px; + height: 6px; + background: transparent; +} +.scrollbar-overlay::-webkit-scrollbar-track, +.scrollbar-overlay::-webkit-scrollbar-corner { + background: transparent; +} +.scrollbar-overlay::-webkit-scrollbar-button { + display: none; + width: 0; + height: 0; +} +.scrollbar-overlay::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 999px; + border: 1px solid transparent; + background-clip: padding-box; +} +.scrollbar-overlay:hover::-webkit-scrollbar-thumb, +.scrollbar-overlay:focus::-webkit-scrollbar-thumb, +.scrollbar-overlay:focus-within::-webkit-scrollbar-thumb, +.scrollbar-overlay.is-scrolling::-webkit-scrollbar-thumb { + background-color: color-mix(in oklab, var(--fg) 35%, transparent); +} +.scrollbar-overlay:hover::-webkit-scrollbar-thumb:hover { + background-color: color-mix(in oklab, var(--fg) 55%, transparent); +} + +/* Global overlay scrollbars for fine pointers */ @media (hover: hover) and (pointer: fine) { * { scrollbar-width: thin; @@ -74,8 +225,8 @@ body { scrollbar-color: color-mix(in oklab, var(--fg) 32%, transparent) transparent; } *::-webkit-scrollbar { - width: 8px; - height: 8px; + width: 6px; + height: 6px; background: transparent; } *::-webkit-scrollbar-track, @@ -90,7 +241,7 @@ body { *::-webkit-scrollbar-thumb { background-color: transparent; border-radius: 999px; - border: 2px solid transparent; + border: 1px solid transparent; background-clip: padding-box; } *:not(html):not(body):hover::-webkit-scrollbar-thumb, @@ -103,42 +254,83 @@ body { } } -/* Markdown theming (overrides Tailwind typography vars for a premium dark look) */ +/* Report / content pane: contained reading height */ +.report-pane { + max-height: min(70vh, 720px); + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + overscroll-behavior: contain; +} + +/* Markdown theming */ .markdown-body { - --tw-prose-body: #cbd5e1; - --tw-prose-headings: #f8fafc; - --tw-prose-lead: #94a3b8; - --tw-prose-links: #c4b5fd; - --tw-prose-bold: #ffffff; - --tw-prose-counters: #a78bfa; - --tw-prose-bullets: #6366f1; - --tw-prose-hr: rgba(148, 163, 184, 0.18); - --tw-prose-quotes: #e2e8f0; - --tw-prose-quote-borders: #8b5cf6; - --tw-prose-captions: #94a3b8; - --tw-prose-th-borders: rgba(148, 163, 184, 0.25); - --tw-prose-td-borders: rgba(148, 163, 184, 0.15); - --tw-prose-code: #e9d5ff; -} -.markdown-body :where(h2, h3, h4) { scroll-margin-top: 6rem; } + --tw-prose-body: var(--fg); + --tw-prose-headings: var(--fg-heading); + --tw-prose-lead: var(--muted); + --tw-prose-links: var(--accent); + --tw-prose-bold: var(--fg-heading); + --tw-prose-counters: var(--accent); + --tw-prose-bullets: var(--accent); + --tw-prose-hr: var(--border); + --tw-prose-quotes: var(--fg); + --tw-prose-quote-borders: var(--accent); + --tw-prose-captions: var(--muted); + --tw-prose-th-borders: var(--border); + --tw-prose-td-borders: var(--border); + --tw-prose-code: color-mix(in oklab, var(--accent) 85%, var(--fg)); + color: var(--fg); + font-size: 0.9375rem; + line-height: 1.7; +} +.markdown-body :where(h1, h2, h3, h4) { + color: var(--fg-heading); + font-weight: 700; + letter-spacing: -0.02em; +} +.markdown-body :where(h2, h3, h4) { + scroll-margin-top: 6rem; +} .markdown-body :where(h2) { - border-bottom: 1px solid rgba(148, 163, 184, 0.15); + border-bottom: 1px solid var(--border); padding-bottom: 0.4rem; } -.markdown-body :where(table) { font-size: 0.875rem; overflow: hidden; border-radius: 0.6rem; } +.markdown-body :where(p, li) { + color: var(--fg); +} +.markdown-body :where(table) { + font-size: 0.875rem; + overflow: hidden; + border-radius: 0.6rem; +} .markdown-body :where(th) { - background: rgba(139, 92, 246, 0.12); - color: #f1f5f9; + background: color-mix(in oklab, var(--accent) 14%, transparent); + color: var(--fg-heading); font-weight: 600; } -.markdown-body :where(tr):nth-child(even) td { background: rgba(148, 163, 184, 0.04); } +.markdown-body :where(tr):nth-child(even) td { + background: color-mix(in oklab, var(--muted) 6%, transparent); +} .markdown-body :where(blockquote) { - background: linear-gradient(180deg, rgba(139, 92, 246, 0.1), rgba(139, 92, 246, 0.03)); + background: linear-gradient( + 180deg, + color-mix(in oklab, var(--accent) 12%, transparent), + color-mix(in oklab, var(--accent) 3%, transparent) + ); border-left-width: 3px; border-radius: 0 0.6rem 0.6rem 0; padding-top: 0.6rem; padding-bottom: 0.6rem; } +.markdown-body :where(code) { + font-family: var(--font-mono); + font-size: 0.875em; +} +.markdown-body :where(pre) { + background: var(--inset) !important; + border: 1px solid var(--border); + border-radius: 0.75rem; +} /* Animated gradient text */ .text-gradient { @@ -150,53 +342,69 @@ body { animation: shimmer 6s linear infinite; } @keyframes shimmer { - to { background-position: 200% center; } + to { + background-position: 200% center; + } } -.pulse-dot { animation: pulse-dot 1.8s ease-in-out infinite; } +.pulse-dot { + animation: pulse-dot 1.8s ease-in-out infinite; +} @keyframes pulse-dot { - 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.6); } - 50% { opacity: 0.7; box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); } + 0%, + 100% { + opacity: 1; + box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.6); + } + 50% { + opacity: 0.7; + box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); + } } -[data-theme="light"] .app-bg::before { - background: - radial-gradient(1100px 700px at 12% -8%, rgba(124, 58, 237, 0.12), transparent 60%), - radial-gradient(1000px 620px at 92% 4%, rgba(6, 182, 212, 0.10), transparent 60%), - radial-gradient(900px 600px at 50% 120%, rgba(217, 70, 239, 0.08), transparent 60%); +.theme-toggle { + width: 36px; + height: 36px; + padding: 0; + flex: 0 0 36px; + display: grid; + place-items: center; + position: relative; + border-radius: 8px; + cursor: pointer; + border: 0; + background: transparent; + color: var(--fg); + box-shadow: 0 0 0 1px color-mix(in oklab, var(--fg) 18%, transparent); + transition: background 200ms ease, box-shadow 200ms ease; } -[data-theme="light"] .app-bg::after { - background-image: - linear-gradient(to right, rgba(15, 23, 42, 0.06) 1px, transparent 1px), - linear-gradient(to bottom, rgba(15, 23, 42, 0.06) 1px, transparent 1px); +.theme-toggle:hover { + background: var(--surface-raised); +} +.theme-toggle .icon { + grid-area: 1 / 1; + display: grid; + place-items: center; + transition: opacity 300ms cubic-bezier(0.2, 0, 0, 1), transform 300ms cubic-bezier(0.2, 0, 0, 1), + filter 300ms cubic-bezier(0.2, 0, 0, 1); } -[data-theme="light"] header { - background-color: color-mix(in oklab, var(--surface) 80%, transparent) !important; - border-color: var(--border) !important; +.theme-toggle .icon.on { + opacity: 1; + transform: scale(1); + filter: blur(0); } -[data-theme="light"] .text-white { color: var(--fg) !important; } -[data-theme="light"] .border-white\/10 { border-color: var(--border) !important; } -[data-theme="light"] .bg-white\/\[0\.03\] { background-color: var(--surface) !important; } -[data-theme="light"] .markdown-body { - --tw-prose-body: #334155; - --tw-prose-headings: #0f172a; - --tw-prose-lead: #475569; - --tw-prose-links: #6d28d9; - --tw-prose-bold: #0f172a; - --tw-prose-code: #5b21b6; +.theme-toggle .icon.off { + opacity: 0; + transform: scale(0.25); + filter: blur(4px); } -.theme-toggle { - width: 36px; height: 36px; padding: 0; flex: 0 0 36px; - display: grid; place-items: center; position: relative; - border-radius: 8px; cursor: pointer; border: 0; - background: transparent; color: inherit; - box-shadow: 0 0 0 1px color-mix(in oklab, currentColor 18%, transparent); +/* Agent graph pending nodes adapt to theme */ +[data-theme="light"] .graph-node-pending { + fill: #e2e8f0; + stroke: #94a3b8; } -.theme-toggle .icon { - grid-area: 1 / 1; display: grid; place-items: center; - transition: opacity 300ms cubic-bezier(0.2,0,0,1), transform 300ms cubic-bezier(0.2,0,0,1), filter 300ms cubic-bezier(0.2,0,0,1); +[data-theme="dark"] .graph-node-pending { + fill: #1e293b; + stroke: #475569; } -.theme-toggle .icon.on { opacity: 1; transform: scale(1); filter: blur(0); } -.theme-toggle .icon.off { opacity: 0; transform: scale(0.25); filter: blur(4px); } - diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d6b2e71..49fe5a2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,20 @@ import type { Metadata } from "next"; import type { ReactNode } from "react"; +import { Inter, JetBrains_Mono } from "next/font/google"; import "./globals.css"; +const inter = Inter({ + subsets: ["latin"], + variable: "--font-inter", + display: "swap", +}); + +const jetbrains = JetBrains_Mono({ + subsets: ["latin"], + variable: "--font-jetbrains", + display: "swap", +}); + const faviconSvg = "data:image/svg+xml," + encodeURIComponent( @@ -36,7 +49,7 @@ export const metadata: Metadata = { description: "Plan → research → synthesize → critique → finalize. A cited, confidence-scored agentic research system.", type: "website", - images: [{ url: 'https://synthesis-gold.vercel.app/og.png', width: 1200, height: 630, alt: 'synthesis' }], + images: [{ url: "https://synthesis-gold.vercel.app/og.png", width: 1200, height: 630, alt: "synthesis" }], }, }; @@ -44,11 +57,11 @@ const THEME_BOOT = `(function(){try{var k="synthesis-theme";var t=localStorage.g export default function RootLayout({ children }: { children: ReactNode }) { return ( - +