feat: dark theme foundation, new header/footer, shared site primitives

Adds dark design tokens, Inter + JetBrains Mono, a header with Notes,
Process, About and a Book a call button, and a footer with the availability
line driven by the tested domain core. Shared primitives live in
src/components/site, site constants in src/content/site.ts. Legacy pages
(blog, legal, contact, tags, technologies) keep a light surface via
LegacyLightSurface. Stub routes for /notes, /process and /book.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 19:00:07 +02:00
co-authored by Claude Sonnet 5.5
parent d33a5d6c63
commit f258e2a483
24 changed files with 579 additions and 317 deletions
+58 -12
View File
@@ -2,6 +2,40 @@
@tailwind components;
@tailwind utilities;
/* Theme tokens. Dark is the site default; legacy pages (blog, legal, ...) opt into
the light variant by wrapping their content in `.theme-light` (LegacyLightSurface). */
:root {
--color-bg: #0a0d12;
--color-surface: #0d1219;
--color-fg: #e6edf3;
--color-muted: #8b98a8;
--color-faint: #5b6879;
--color-border: #1f2630;
--color-border-strong: #2b3544;
--color-accent: #3fb98a;
--color-accent-hover: #55c99d;
--color-accent-fg: #0a0d12;
--color-code-bg: #0d1219;
--color-code-border: #1f2630;
--color-scrollbar-track: #0a0d12;
--color-scrollbar-thumb: #2b3544;
}
.theme-light {
--color-bg: #ffffff;
--color-surface: #f8fafc;
--color-fg: #0f172a;
--color-muted: #334155;
--color-faint: #64748b;
--color-border: #e2e8f0;
--color-border-strong: #cbd5e1;
--color-code-bg: #f8fafc;
--color-code-border: #f1f5f9;
background-color: var(--color-bg);
color: #1e293b;
font-family: var(--font-newsreader), Georgia, serif;
}
/* Base styles - Tailwind only */
@layer base {
html {
@@ -9,7 +43,7 @@
}
body {
@apply bg-white text-slate-800 font-serif antialiased selection:bg-slate-900 selection:text-white;
@apply bg-bg text-fg font-sans antialiased;
line-height: 1.6;
}
@@ -20,7 +54,7 @@
h4,
h5,
h6 {
@apply font-sans font-bold text-slate-900 tracking-tighter;
@apply font-sans font-bold text-fg tracking-tighter;
}
h1 {
@@ -40,7 +74,7 @@
}
p {
@apply mb-6 text-base leading-relaxed text-slate-700;
@apply mb-6 text-base leading-relaxed text-muted;
}
.lead {
@@ -49,7 +83,7 @@
}
a {
@apply text-slate-900 hover:text-slate-700 transition-colors no-underline;
@apply text-fg hover:text-muted transition-colors no-underline;
}
ul,
@@ -62,22 +96,34 @@
}
code:not([class*="language-"]) {
@apply bg-slate-50 px-1.5 py-0.5 rounded-md font-mono text-[0.9em] text-slate-800 border border-slate-100;
@apply px-1.5 py-0.5 rounded-md font-mono text-[0.9em] text-fg border;
background-color: var(--color-code-bg);
border-color: var(--color-code-border);
}
blockquote {
@apply border-l-4 border-slate-900 pl-6 italic text-slate-700 my-8 text-lg md:text-2xl font-serif;
@apply border-l-4 border-fg pl-6 italic text-muted my-8 text-lg md:text-2xl font-serif;
}
/* Focus states */
a:focus,
button:focus,
input:focus,
textarea:focus {
outline: none !important;
box-shadow: none !important;
}
/* Keep mouse focus quiet, but never hide the keyboard focus ring */
a:focus:not(:focus-visible),
button:focus:not(:focus-visible) {
outline: none;
}
a:focus-visible,
button:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
/* Interactive elements pointer */
a,
button,
@@ -328,11 +374,11 @@
}
::-webkit-scrollbar-track {
background: #f1f5f9;
background: var(--color-scrollbar-track);
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
background: var(--color-scrollbar-thumb);
border-radius: 4px;
transition: background 0.2s ease;
}
@@ -343,8 +389,8 @@
/* Selection color */
::selection {
background: #0f172a;
color: #ffffff;
background: var(--color-accent);
color: var(--color-accent-fg);
}
/* Tag Styles */