diff --git a/src/frontend/app.js b/src/frontend/app.js
index 7597f5e..1fe9d38 100644
--- a/src/frontend/app.js
+++ b/src/frontend/app.js
@@ -2447,6 +2447,9 @@ function clearGitProjectFilter() {
// ── Themes ─────────────────────────────────────────────────────
function setTheme(theme) {
+ // Add transition class briefly so theme swap cross-fades smoothly.
+ // Removing it afterward keeps individual hover/click transitions snappy.
+ document.documentElement.classList.add('theme-transition');
if (theme === 'dark') {
document.body.removeAttribute('data-theme');
} else if (theme === 'system') {
@@ -2460,6 +2463,9 @@ function setTheme(theme) {
document.body.setAttribute('data-theme', theme);
}
localStorage.setItem('codedash-theme', theme);
+ setTimeout(function () {
+ document.documentElement.classList.remove('theme-transition');
+ }, 220);
}
function saveThemePref(val) {
diff --git a/src/frontend/index.html b/src/frontend/index.html
index e28c1ff..c12b3cd 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -4,6 +4,9 @@
codbash
+
+
+
diff --git a/src/frontend/styles.css b/src/frontend/styles.css
index 96f61db..1bb70ad 100644
--- a/src/frontend/styles.css
+++ b/src/frontend/styles.css
@@ -10,43 +10,60 @@
:root {
color-scheme: dark;
- --bg-primary: #1a1d23;
- --bg-secondary: #22262e;
- --bg-card: #2a2e37;
- --bg-card-hover: #333842;
- --bg-input: #2a2e37;
- --text-primary: #e4e7eb;
- --text-secondary: #8b919a;
- --text-muted: #5f6571;
- --accent-green: #4ade80;
+ /* Neuraldeep dark palette — deep cool blacks + neon brand green.
+ Tokens kept as hex for legacy compatibility; rgb triplet aliases below
+ enable alpha-modulation via `rgb(var(--c-accent) / 0.12)`. */
+ --bg-primary: #08090c;
+ --bg-secondary: #121418;
+ --bg-card: #121418;
+ --bg-card-hover: #1a1d23;
+ --bg-input: #121418;
+ --text-primary: #ebedf0;
+ --text-secondary: #b6bcc8;
+ --text-muted: #9499a5;
+ --accent-green: #00ff88;
--accent-blue: #60a5fa;
--accent-orange: #fb923c;
--accent-purple: #c084fc;
--accent-red: #f87171;
--accent-cyan: #22d3ee;
- --border: #363b44;
- --sidebar-bg: #1e2128;
+ --border: #2a2d33;
+ --sidebar-bg: #08090c;
+ --c-accent: 0 255 136;
+ --c-bg: 8 9 12;
+ --c-panel: 18 20 24;
+ --c-border: 42 45 51;
+ --c-fg: 235 237 240;
+ --c-muted: 148 153 165;
}
/* ── Light Theme ───────────────────────────────────────────────── */
[data-theme="light"] {
- --bg-primary: #f5f5f7;
+ color-scheme: light;
+ /* Cool off-white + AA-safe deeper accent — neuraldeep light palette. */
+ --bg-primary: #fafbfc;
--bg-secondary: #ffffff;
--bg-card: #ffffff;
- --bg-card-hover: #f0f0f2;
+ --bg-card-hover: #f3f4f6;
--bg-input: #ffffff;
- --text-primary: #1d1d1f;
- --text-secondary: #6e6e73;
- --text-muted: #a1a1a6;
- --accent-green: #34c759;
+ --text-primary: #111827;
+ --text-secondary: #57606d;
+ --text-muted: #6b7280;
+ --accent-green: #10804a;
--accent-blue: #007aff;
--accent-orange: #ff9500;
--accent-purple: #af52de;
--accent-red: #ff3b30;
--accent-cyan: #00b4d8;
- --border: #d2d2d7;
- --sidebar-bg: #f5f5f7;
+ --border: #e1e4e8;
+ --sidebar-bg: #f3f4f6;
+ --c-accent: 16 128 74;
+ --c-bg: 250 251 252;
+ --c-panel: 255 255 255;
+ --c-border: 225 228 232;
+ --c-fg: 17 24 39;
+ --c-muted: 87 96 109;
}
/* ── Monokai Theme ─────────────────────────────────────────────── */
@@ -70,10 +87,50 @@
--sidebar-bg: #1e1f1c;
}
+/* ── Theme transitions ─────────────────────────────────────────────
+ Smooth 180ms cross-fade when switching dark/light/monokai. Class
+ toggled by the theme switcher in app.js; bare CSS would otherwise
+ transition every hover, which feels mushy. */
+html.theme-transition,
+html.theme-transition * {
+ transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;
+}
+
+/* ── CTA pulse — subtle attention without seizing the eye ────────── */
+@keyframes accent-pulse {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--c-accent) / 0); }
+ 50% { box-shadow: 0 0 0 8px rgba(var(--c-accent) / 0.12); }
+}
+.cta-pulse {
+ animation: accent-pulse 2.4s ease-in-out infinite;
+}
+
+/* ── Reveal-on-scroll — session cards fade+slide on entry ─────────
+ Activated only when JS sets `html.js-reveal`; without JS the
+ elements stay visible (no flash-of-hidden-content for crawlers). */
+html.js-reveal [data-reveal]:not([data-revealed="true"]) {
+ opacity: 0;
+ transform: translateY(14px);
+}
+[data-reveal] {
+ transition: opacity 500ms cubic-bezier(0.22, 1, 0.36, 1),
+ transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
+ will-change: opacity, transform;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html.theme-transition,
+ html.theme-transition * { transition: none !important; }
+ [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
+ .cta-pulse { animation: none !important; }
+}
+
/* ── Body & Layout ─────────────────────────────────────────────── */
body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
background: var(--bg-primary);
color: var(--text-primary);
height: 100vh;
diff --git a/src/server.js b/src/server.js
index fc25aae..da5911b 100644
--- a/src/server.js
+++ b/src/server.js
@@ -104,7 +104,10 @@ function startServer(host, port, openBrowser = true) {
'Content-Security-Policy': [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
- "style-src 'self' 'unsafe-inline'",
+ // fonts.googleapis.com serves the stylesheet, gstatic.com serves the
+ // actual woff2 — both required by the Inter font link in index.html
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
+ "font-src 'self' https://fonts.gstatic.com",
"connect-src 'self'",
"img-src 'self' data:",
"frame-ancestors 'none'",