From fdd456864b9d941615a1f5287e8652b14330fc8c Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 20 May 2026 18:45:21 -0500 Subject: [PATCH] ;s; --- app/globals.css | 257 ++++++++++++++++++++++--------------- app/layout.tsx | 22 +--- app/page.tsx | 35 ++--- app/read/page.tsx | 4 +- components/BookCover.tsx | 38 +++--- components/ReadSidebar.tsx | 4 +- components/SiteHeader.tsx | 6 +- components/ThemeToggle.tsx | 106 +++------------ lib/theme.ts | 29 +++++ next.config.ts | 3 +- public/theme.js | 162 +++++++++++++++++++++++ 11 files changed, 405 insertions(+), 261 deletions(-) create mode 100644 lib/theme.ts create mode 100644 public/theme.js diff --git a/app/globals.css b/app/globals.css index 8eaaaf5..8dd5001 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,53 +1,83 @@ @import "tailwindcss"; -:root { - --background: #f4f7fb; - --foreground: #0c1929; - --muted: #5b6b7c; - --accent: #0d9488; +@custom-variant dark (&:where(.dark, .dark *)); + +/* Light theme — defaults (overridden by inline vars on when toggling) */ +html[data-theme="light"], +:root:not(.dark) { + color-scheme: light; + --background: #faf8f5; + --foreground: #1a1614; + --muted: #6b6560; + --accent: #4f46e5; --accent-secondary: #ea580c; - --accent-tertiary: #0284c7; - --surface: rgba(255, 255, 255, 0.78); - --surface-solid: #ffffff; - --border: rgba(13, 148, 136, 0.14); - --gradient-1: #0d9488; - --gradient-2: #f59e0b; - --gradient-3: #0284c7; - --glow-sm: 0 8px 32px rgba(13, 148, 136, 0.2); - --glow-lg: 0 24px 64px rgba(234, 88, 12, 0.18); - --mesh-1: rgba(13, 148, 136, 0.32); - --mesh-2: rgba(245, 158, 11, 0.26); - --mesh-3: rgba(2, 132, 199, 0.22); - --prose: #334155; - --card-shadow: 0 4px 24px rgba(12, 25, 41, 0.07); - --callout-bg: rgba(13, 148, 136, 0.08); - --callout-border: rgba(13, 148, 136, 0.22); + --accent-tertiary: #db2777; + --surface: #ffffff; + --surface-elevated: #ffffff; + --border: #e8e4df; + --border-strong: #d6d0c8; + --gradient-1: #4f46e5; + --gradient-2: #ea580c; + --gradient-3: #db2777; + --glow-sm: 0 8px 30px rgba(79, 70, 229, 0.12); + --glow-lg: 0 20px 50px rgba(234, 88, 12, 0.14); + --mesh-1: rgba(79, 70, 229, 0.14); + --mesh-2: rgba(234, 88, 12, 0.1); + --mesh-3: rgba(219, 39, 119, 0.08); + --prose: #44403c; + --card-shadow: 0 1px 2px rgba(26, 22, 20, 0.04), 0 8px 24px rgba(26, 22, 20, 0.06); + --callout-bg: rgba(79, 70, 229, 0.06); + --callout-border: rgba(79, 70, 229, 0.18); + --cover-from: #3730a3; + --cover-via: #7c2d12; + --cover-to: #831843; + --cover-glow: rgba(79, 70, 229, 0.35); + --cta-from: #4338ca; + --cta-via: #c2410c; + --cta-to: #9d174d; + --cta-text: #faf8f5; + --cta-btn-bg: #faf8f5; + --cta-btn-text: #3730a3; --read-max: 42rem; --header-h: 4.25rem; } -.dark { - --background: #060d18; - --foreground: #e8eef4; - --muted: #8fa3b8; - --accent: #2dd4bf; - --accent-secondary: #fb923c; - --accent-tertiary: #38bdf8; - --surface: rgba(12, 25, 41, 0.72); - --surface-solid: #0c1929; - --border: rgba(45, 212, 191, 0.14); - --gradient-1: #14b8a6; +/* Dark theme */ +html[data-theme="dark"], +html.dark { + color-scheme: dark; + --background: #0f0e0d; + --foreground: #f5f2ed; + --muted: #a39e97; + --accent: #a5b4fc; + --accent-secondary: #fdba74; + --accent-tertiary: #f9a8d4; + --surface: #1a1816; + --surface-elevated: #242120; + --border: #2e2b28; + --border-strong: #3d3935; + --gradient-1: #818cf8; --gradient-2: #fb923c; - --gradient-3: #38bdf8; - --glow-sm: 0 8px 32px rgba(20, 184, 166, 0.22); - --glow-lg: 0 24px 64px rgba(251, 146, 60, 0.16); - --mesh-1: rgba(20, 184, 166, 0.35); - --mesh-2: rgba(251, 146, 60, 0.28); - --mesh-3: rgba(56, 189, 248, 0.25); - --prose: #b8c5d4; - --card-shadow: 0 4px 28px rgba(0, 0, 0, 0.4); - --callout-bg: rgba(20, 184, 166, 0.1); - --callout-border: rgba(45, 212, 191, 0.25); + --gradient-3: #f472b6; + --glow-sm: 0 8px 32px rgba(129, 140, 248, 0.18); + --glow-lg: 0 20px 56px rgba(251, 146, 60, 0.12); + --mesh-1: rgba(129, 140, 248, 0.12); + --mesh-2: rgba(251, 146, 60, 0.1); + --mesh-3: rgba(244, 114, 182, 0.08); + --prose: #c9c2ba; + --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 12px 32px rgba(0, 0, 0, 0.35); + --callout-bg: rgba(129, 140, 248, 0.08); + --callout-border: rgba(165, 180, 252, 0.22); + --cover-from: #312e81; + --cover-via: #7c2d12; + --cover-to: #701a3a; + --cover-glow: rgba(129, 140, 248, 0.4); + --cta-from: #1e1b4b; + --cta-via: #431407; + --cta-to: #500724; + --cta-text: #f5f2ed; + --cta-btn-bg: #f5f2ed; + --cta-btn-text: #312e81; } @theme inline { @@ -63,37 +93,23 @@ } html { - color-scheme: light; scroll-padding-top: calc(var(--header-h) + 1rem); } -html.dark { - color-scheme: dark; -} - body { - background: var(--background); + background-color: var(--background); color: var(--foreground); font-family: var(--font-dm-sans), system-ui, sans-serif; + transition: background-color 0.25s ease, color 0.25s ease; } -/* Latitude grid + ambient mesh */ .mesh-bg { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; - background-image: - linear-gradient(rgba(13, 148, 136, 0.04) 1px, transparent 1px), - linear-gradient(90deg, rgba(13, 148, 136, 0.04) 1px, transparent 1px); - background-size: 48px 48px; -} - -.dark .mesh-bg { - background-image: - linear-gradient(rgba(45, 212, 191, 0.06) 1px, transparent 1px), - linear-gradient(90deg, rgba(45, 212, 191, 0.06) 1px, transparent 1px); + background-color: var(--background); } .mesh-bg::before, @@ -101,56 +117,56 @@ body { content: ""; position: absolute; border-radius: 50%; - filter: blur(88px); - animation: mesh-float 20s ease-in-out infinite alternate; + filter: blur(100px); + animation: mesh-float 24s ease-in-out infinite alternate; } .mesh-bg::before { - width: min(72vw, 540px); - height: min(72vw, 540px); - top: -14%; - right: -6%; - background: radial-gradient(circle, var(--mesh-1), transparent 68%); + width: min(65vw, 480px); + height: min(65vw, 480px); + top: -10%; + right: -5%; + background: radial-gradient(circle, var(--mesh-1), transparent 70%); } .mesh-bg::after { - width: min(58vw, 420px); - height: min(58vw, 420px); - bottom: 8%; - left: -12%; + width: min(55vw, 400px); + height: min(55vw, 400px); + bottom: 5%; + left: -8%; background: radial-gradient(circle, var(--mesh-2), transparent 70%); - animation-delay: -7s; + animation-delay: -8s; } .mesh-orb { position: absolute; border-radius: 50%; - filter: blur(76px); - animation: mesh-float 24s ease-in-out infinite alternate-reverse; + filter: blur(90px); + animation: mesh-float 28s ease-in-out infinite alternate-reverse; } -.mesh-orb--sky { - width: min(48vw, 340px); - height: min(48vw, 340px); - top: 42%; - left: 38%; +.mesh-orb--accent { + width: min(42vw, 300px); + height: min(42vw, 300px); + top: 45%; + left: 40%; background: radial-gradient(circle, var(--mesh-3), transparent 70%); } @keyframes mesh-float { 0% { - transform: translate(0, 0) scale(1); + transform: translate(0, 0); } 100% { - transform: translate(20px, -28px) scale(1.06); + transform: translate(16px, -20px); } } .gradient-text { background: linear-gradient( - 125deg, + 120deg, var(--gradient-1) 0%, - var(--gradient-2) 45%, + var(--gradient-2) 52%, var(--gradient-3) 100% ); -webkit-background-clip: text; @@ -161,10 +177,13 @@ body { .glass-card { background: var(--surface); border: 1px solid var(--border); - backdrop-filter: blur(16px); box-shadow: var(--card-shadow); } +.dark .glass-card { + background: var(--surface-elevated); +} + .btn-primary { display: inline-flex; align-items: center; @@ -175,22 +194,21 @@ body { border-radius: 9999px; font-size: 0.875rem; font-weight: 600; - color: white; + color: #fff; background: linear-gradient( - 125deg, + 120deg, var(--gradient-1), var(--gradient-2), var(--gradient-3) ); - background-size: 200% 200%; box-shadow: var(--glow-sm); - transition: transform 0.2s, box-shadow 0.2s, background-position 0.4s; + transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease; } .btn-primary:hover { transform: translateY(-1px); box-shadow: var(--glow-lg); - background-position: 100% 50%; + filter: brightness(1.05); } .btn-secondary { @@ -205,17 +223,39 @@ body { font-weight: 600; color: var(--foreground); background: var(--surface); - border: 1px solid var(--border); - backdrop-filter: blur(12px); - transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s; + border: 1px solid var(--border-strong); + transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease; } .btn-secondary:hover { - border-color: color-mix(in srgb, var(--accent) 45%, transparent); + border-color: var(--accent); box-shadow: var(--glow-sm); transform: translateY(-1px); } +.cta-panel { + background: linear-gradient( + 135deg, + var(--cta-from), + var(--cta-via), + var(--cta-to) + ); +} + +.cta-panel__title, +.cta-panel__eyebrow { + color: var(--cta-text); +} + +.cta-panel__body { + color: color-mix(in srgb, var(--cta-text) 88%, transparent); +} + +.cta-panel__btn { + background: var(--cta-btn-bg); + color: var(--cta-btn-text); +} + .font-display { font-family: var(--font-plus-jakarta), var(--font-dm-sans), system-ui, sans-serif; } @@ -224,17 +264,29 @@ body { animation: cover-float 7s ease-in-out infinite; } +.cover-card { + background: linear-gradient( + 145deg, + var(--cover-from), + var(--cover-via), + var(--cover-to) + ); +} + +.cover-glow { + background: var(--cover-glow); +} + @keyframes cover-float { 0%, 100% { - transform: translateY(0) rotate(-0.5deg); + transform: translateY(0); } 50% { - transform: translateY(-12px) rotate(0.5deg); + transform: translateY(-10px); } } -/* Hub marquee */ .marquee-track { display: flex; width: max-content; @@ -250,15 +302,14 @@ body { } } -/* Reading progress */ .reading-progress { position: fixed; top: var(--header-h); left: 0; z-index: 40; - height: 3px; + height: 2px; width: 100%; - background: transparent; + background: var(--border); pointer-events: none; } @@ -272,10 +323,8 @@ body { var(--gradient-3) ); transition: width 0.1s linear; - box-shadow: 0 0 12px color-mix(in srgb, var(--gradient-1) 50%, transparent); } -/* Prose */ .prose-ebook { font-size: 1.0625rem; line-height: 1.88; @@ -341,7 +390,6 @@ body { border-bottom: 1px solid var(--callout-border); font-size: 0.98rem; color: var(--foreground); - font-style: normal; } .prose-ebook blockquote p { @@ -349,9 +397,8 @@ body { color: inherit; } -/* TOC active state */ .toc-link--active { - background: color-mix(in srgb, var(--gradient-1) 12%, transparent); + background: color-mix(in srgb, var(--gradient-1) 14%, transparent); color: var(--foreground); font-weight: 600; } @@ -366,8 +413,10 @@ body { .marquee-track, .mesh-bg::before, .mesh-bg::after, - .mesh-orb { + .mesh-orb, + body { animation: none; + transition: none; } .btn-primary:hover, diff --git a/app/layout.tsx b/app/layout.tsx index 2ac5720..a5b788f 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,8 @@ import type { Metadata } from "next"; +import Script from "next/script"; import { DM_Sans, Plus_Jakarta_Sans, Source_Serif_4 } from "next/font/google"; import "./globals.css"; -import { brand, ebookMeta } from "@/content/ebook"; +import { ebookMeta } from "@/content/ebook"; const dmSans = DM_Sans({ variable: "--font-dm-sans", @@ -39,19 +40,6 @@ export const metadata: Metadata = { }, }; -const themeScript = ` -(function () { - try { - var stored = localStorage.getItem('theme'); - var dark = - stored === 'dark' || - (stored !== 'light' && - window.matchMedia('(prefers-color-scheme: dark)').matches); - document.documentElement.classList.toggle('dark', dark); - } catch (e) {} -})(); -`; - export default function RootLayout({ children, }: Readonly<{ @@ -63,12 +51,10 @@ export default function RootLayout({ suppressHydrationWarning className={`${dmSans.variable} ${plusJakarta.variable} ${sourceSerif.variable} h-full scroll-smooth antialiased`} > - -