From c178fc63a1fd0bec11cd6a1a7cf0d254b50c861c Mon Sep 17 00:00:00 2001 From: Krishna Ayyalasomayajula Date: Mon, 1 Jun 2026 19:38:18 -0500 Subject: [PATCH] style: add Tailwind v4 design system with dark mode, KaTeX overrides, scroll effects --- app/globals.css | 214 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 196 insertions(+), 18 deletions(-) diff --git a/app/globals.css b/app/globals.css index a2dc41e..7827bc2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,204 @@ @import "tailwindcss"; -:root { - --background: #ffffff; - --foreground: #171717; -} +/* Dark mode: class-based (for @wrksz/themes) */ +@custom-variant dark (&:where(.dark, .dark *)); -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} +/* === Design Tokens === */ +@theme { + /* Colors — Light mode defaults */ + --color-canvas: oklch(0.99 0 0); + --color-surface: oklch(0.97 0.005 240); + --color-ink: oklch(0.15 0.006 240); + --color-ink-soft: oklch(0.45 0.008 240); + --color-border: oklch(0.88 0.008 240); + --color-accent: oklch(0.55 0.18 250); -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; + /* Fonts — override defaults */ + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; + --font-serif: "Merriweather", ui-serif, Georgia, serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + + /* Typography scale */ + --text-xs: 0.75rem; --text-xs--line-height: 1.2; + --text-sm: 0.875rem; --text-sm--line-height: 1.4; + --text-base: 1rem; --text-base--line-height: 1.5; + --text-lg: 1.125rem; --text-lg--line-height: 1.6; + --text-xl: 1.25rem; --text-xl--line-height: 1.6; + --text-2xl: 1.5rem; --text-2xl--line-height: 2rem; + --text-3xl: 1.875rem; --text-3xl--line-height: 2.25rem; + --text-4xl: 2.25rem; --text-4xl--line-height: 2.5rem; + + /* Shadows */ + --shadow-card: 0 1px 3px rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + + /* Animations */ + --animate-fade-in: fade-in 0.3s ease-out; + --animate-fade-in-up: fade-in-up 0.4s ease-out; + --animate-slide-up: slide-up 0.5s ease-out; + + @keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } + } + @keyframes fade-in-up { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); } + } + @keyframes slide-up { + from { opacity: 0; transform: translateY(24px); } + to { opacity: 1; transform: translateY(0); } } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +/* === Dark mode token overrides === */ +.dark { + --color-canvas: oklch(0.12 0.02 240); + --color-surface: oklch(0.18 0.02 240); + --color-ink: oklch(0.96 0.005 240); + --color-ink-soft: oklch(0.75 0.01 240); + --color-border: oklch(0.28 0.025 240); +} + +/* === Blog typography === */ +@plugin "@tailwindcss/typography"; + +@utility prose { + --tw-prose-body: var(--color-ink-soft); + --tw-prose-headings: var(--color-ink); + --tw-prose-links: var(--color-accent); + --tw-prose-bold: var(--color-ink); + --tw-prose-quote-borders: var(--color-border); + --tw-prose-code: var(--color-accent); + --tw-prose-pre-bg: var(--color-surface); +} + +/* === KaTeX dark mode overrides === */ +.dark .katex, +.dark .katex * { + color: currentColor !important; +} +.prose .katex-display { + color: currentColor !important; +} +.prose .katex-display .katex .base { + color: currentColor !important; +} +.katex .mfrac .frac-line, +.katex .sqrt .sqrt-line, +.katex .overline .overline-line, +.katex .underline .underline-line { + border-color: currentColor !important; +} +.katex { + font-size: 1.1em !important; +} +.katex-display { + margin: 1.5rem 0; + overflow-x: auto; +} +.katex .katex-mathml { + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + padding: 0; + border: 0; +} + +/* === Scroll progress bar (CSS animation-timeline + JS fallback) === */ +.scroll-progress { + position: fixed; + top: 0; + left: 0; + height: 2px; + width: 0%; + background: var(--color-ink); + z-index: 9999; + will-change: width; +} + +@supports (animation-timeline: scroll()) { + .scroll-progress { + width: 0%; + animation: scroll-progress linear forwards; + animation-timeline: scroll(root block); + } + @keyframes scroll-progress { + from { width: 0%; } + to { width: 100%; } + } +} + +/* === Scroll-to-top button === */ +.scroll-to-top { + position: fixed; + bottom: 24px; + right: 24px; + width: 40px; + height: 40px; + border: 1px solid var(--color-border); + border-radius: 50%; + background: var(--color-canvas); + color: var(--color-ink); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transform: translateY(10px); + pointer-events: none; + transition: opacity 0.3s ease, transform 0.3s ease, border-color 0.2s ease; + z-index: 9997; + will-change: opacity, transform; +} +.scroll-to-top.visible { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} +.scroll-to-top:hover { + border-color: var(--color-ink); +} + +/* === Reading progress vertical bar === */ +.reading-progress { + position: fixed; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 60px; + background: rgba(0, 0, 0, 0.08); + border-radius: 2px; + z-index: 9998; + overflow: hidden; +} +.reading-progress-fill { + width: 100%; + height: 0%; + background: var(--color-ink); + border-radius: 2px; +} +@supports (animation-timeline: scroll()) { + .reading-progress-fill { + animation: reading-progress linear forwards; + animation-timeline: scroll(root block); + } + @keyframes reading-progress { + from { height: 0%; } + to { height: 100%; } + } +} + +/* === Global transition for theme switch === */ +* { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; +} + +/* === Reduced motion === */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + .scroll-progress { display: none; } }