@import "tailwindcss"; /* * Theming architecture * -------------------- * 1. Raw Solarized palette (--sol-*). Components never reference these. * 2. Semantic tokens (--background, --ink, ...) — the theme contract. * :root carries Solarized Dark; each additional theme overrides ONLY * the tokens, keyed by the data-theme attribute the root layout sets * on from the site settings (see [data-theme="solarized-light"] * below). The eight Solarized accent colors are shared by design — * only the base tones flip between dark and light. * 3. `@theme inline` exposes the semantic tokens as Tailwind utilities * (bg-background, text-ink, border-edge, ...). The generated utilities * resolve through var() at runtime, so swapping tokens re-skins the app * without touching any component. * * Fonts follow the same pattern: --font-body defaults to Geist and is * remapped per [data-font="..."]; Tailwind's font-sans resolves through it. * * Adding a theme = one override block here + one enum value in * src/db/schema.ts + one entry in src/lib/themes.ts (the Record type * makes a missing entry a compile error). Fonts additionally need a * next/font instance in src/app/layout.tsx. */ :root { --sol-base03: #002b36; --sol-base02: #073642; --sol-base01: #586e75; --sol-base00: #657b83; --sol-base0: #839496; --sol-base1: #93a1a1; --sol-base2: #eee8d5; --sol-base3: #fdf6e3; --sol-yellow: #b58900; --sol-orange: #cb4b16; --sol-red: #dc322f; --sol-magenta: #d33682; --sol-violet: #6c71c4; --sol-blue: #268bd2; --sol-cyan: #2aa198; --sol-green: #859900; /* Semantic tokens (Solarized Dark) */ --background: var(--sol-base03); --surface: var(--sol-base02); --edge: color-mix(in oklab, var(--sol-base02) 70%, var(--sol-base01)); --edge-strong: color-mix(in oklab, var(--sol-base02) 35%, var(--sol-base01)); --ink: var(--sol-base0); --ink-muted: color-mix(in oklab, var(--sol-base01) 78%, var(--sol-base0)); --ink-strong: var(--sol-base1); --ink-bright: var(--sol-base2); --ink-inverse: var(--sol-base03); --link: var(--sol-blue); --link-hover: var(--sol-cyan); --accent: var(--sol-violet); --danger: var(--sol-red); --warning: var(--sol-yellow); --success: var(--sol-green); --code: var(--sol-cyan); --focus: var(--sol-blue); } @theme inline { --color-background: var(--background); --color-surface: var(--surface); --color-edge: var(--edge); --color-edge-strong: var(--edge-strong); --color-ink: var(--ink); --color-ink-muted: var(--ink-muted); --color-ink-strong: var(--ink-strong); --color-ink-bright: var(--ink-bright); --color-ink-inverse: var(--ink-inverse); --color-link: var(--link); --color-link-hover: var(--link-hover); --color-accent: var(--accent); --color-danger: var(--danger); --color-warning: var(--warning); --color-success: var(--success); --color-code: var(--code); --color-focus: var(--focus); --font-sans: var(--font-body); --font-mono: var(--font-geist-mono); } /* Solarized Light: same accents, base tones mirrored around the midpoint. */ [data-theme="solarized-light"] { color-scheme: light; --background: var(--sol-base3); --surface: var(--sol-base2); --edge: color-mix(in oklab, var(--sol-base2) 70%, var(--sol-base1)); --edge-strong: color-mix(in oklab, var(--sol-base2) 35%, var(--sol-base1)); --ink: var(--sol-base00); --ink-muted: color-mix(in oklab, var(--sol-base1) 55%, var(--sol-base00)); --ink-strong: var(--sol-base01); --ink-bright: var(--sol-base02); /* --ink-inverse stays base03: dark-on-blue keeps AA-ish contrast in both themes. */ } /* Dracula — https://draculatheme.com (bg 282a36, fg f8f8f2, comment 6272a4) */ [data-theme="dracula"] { color-scheme: dark; --background: #282a36; --surface: #21222c; --edge: color-mix(in oklab, #21222c 65%, #6272a4); --edge-strong: color-mix(in oklab, #21222c 30%, #6272a4); --ink: color-mix(in oklab, #f8f8f2 78%, #6272a4); --ink-muted: #6272a4; --ink-strong: #f8f8f2; --ink-bright: #ffffff; --ink-inverse: #282a36; --link: #bd93f9; --link-hover: #ff79c6; --accent: #8be9fd; --danger: #ff5555; --warning: #f1fa8c; --success: #50fa7b; --code: #8be9fd; --focus: #bd93f9; } /* Nord — https://nordtheme.com (Polar Night / Snow Storm / Frost / Aurora) */ [data-theme="nord"] { color-scheme: dark; --background: #2e3440; --surface: #3b4252; --edge: #434c5e; --edge-strong: #4c566a; --ink: #d8dee9; --ink-muted: color-mix(in oklab, #4c566a 45%, #d8dee9); --ink-strong: #e5e9f0; --ink-bright: #eceff4; --ink-inverse: #2e3440; --link: #88c0d0; --link-hover: #8fbcbb; --accent: #b48ead; --danger: #bf616a; --warning: #ebcb8b; --success: #a3be8c; --code: #8fbcbb; --focus: #88c0d0; } /* Gruvbox Dark — https://github.com/morhetz/gruvbox (medium contrast) */ [data-theme="gruvbox-dark"] { color-scheme: dark; --background: #282828; --surface: #3c3836; --edge: color-mix(in oklab, #3c3836 65%, #928374); --edge-strong: color-mix(in oklab, #3c3836 30%, #928374); --ink: #d5c4a1; --ink-muted: #928374; --ink-strong: #ebdbb2; --ink-bright: #fbf1c7; --ink-inverse: #282828; --link: #fe8019; --link-hover: #fabd2f; --accent: #d3869b; --danger: #fb4934; --warning: #fabd2f; --success: #b8bb26; --code: #8ec07c; --focus: #fe8019; } /* Catppuccin Mocha — https://catppuccin.com (base/mantle + pastel accents) */ [data-theme="catppuccin-mocha"] { color-scheme: dark; --background: #1e1e2e; --surface: #181825; --edge: color-mix(in oklab, #181825 65%, #6c7086); --edge-strong: color-mix(in oklab, #181825 35%, #6c7086); --ink: #bac2de; --ink-muted: color-mix(in oklab, #6c7086 70%, #a6adc8); --ink-strong: #cdd6f4; --ink-bright: color-mix(in oklab, #cdd6f4 70%, white); --ink-inverse: #1e1e2e; --link: #89b4fa; --link-hover: #b4befe; --accent: #cba6f7; --danger: #f38ba8; --warning: #f9e2af; --success: #a6e3a1; --code: #94e2d5; --focus: #89b4fa; } /* Catppuccin Latte — the light Catppuccin flavor */ [data-theme="catppuccin-latte"] { color-scheme: light; --background: #eff1f5; --surface: #e6e9ef; --edge: #ccd0da; --edge-strong: #acb0be; --ink: #5c5f77; --ink-muted: #8c8fa1; --ink-strong: #4c4f69; --ink-bright: color-mix(in oklab, #4c4f69 75%, black); --ink-inverse: #eff1f5; --link: #1e66f5; --link-hover: #8839ef; --accent: #8839ef; --danger: #d20f39; --warning: #df8e1d; --success: #40a02b; --code: #179299; --focus: #1e66f5; } /* Tokyo Night — https://github.com/tokyo-night (storm-free classic) */ [data-theme="tokyo-night"] { color-scheme: dark; --background: #1a1b26; --surface: #16161e; --edge: #292e42; --edge-strong: #3b4261; --ink: #a9b1d6; --ink-muted: #565f89; --ink-strong: #c0caf5; --ink-bright: color-mix(in oklab, #c0caf5 75%, white); --ink-inverse: #1a1b26; --link: #7aa2f7; --link-hover: #7dcfff; --accent: #bb9af7; --danger: #f7768e; --warning: #e0af68; --success: #9ece6a; --code: #7dcfff; --focus: #7aa2f7; } /* One Dark — Atom's classic */ [data-theme="one-dark"] { color-scheme: dark; --background: #282c34; --surface: #21252b; --edge: color-mix(in oklab, #21252b 60%, #4b5263); --edge-strong: #4b5263; --ink: #abb2bf; --ink-muted: #5c6370; --ink-strong: #d7dae0; --ink-bright: color-mix(in oklab, #d7dae0 70%, white); --ink-inverse: #282c34; --link: #61afef; --link-hover: #56b6c2; --accent: #c678dd; --danger: #e06c75; --warning: #e5c07b; --success: #98c379; --code: #56b6c2; --focus: #61afef; } /* Rosé Pine — https://rosepinetheme.com (main variant) */ [data-theme="rose-pine"] { color-scheme: dark; --background: #191724; --surface: #1f1d2e; --edge: #26233a; --edge-strong: #403d52; --ink: color-mix(in oklab, #e0def4 75%, #908caa); --ink-muted: #908caa; --ink-strong: #e0def4; --ink-bright: color-mix(in oklab, #e0def4 75%, white); --ink-inverse: #191724; --link: #c4a7e7; --link-hover: #ebbcba; --accent: #9ccfd8; --danger: #eb6f92; --warning: #f6c177; --success: #9ccfd8; --code: #ebbcba; --focus: #c4a7e7; } /* Everforest Dark — https://github.com/sainnhe/everforest (medium) */ [data-theme="everforest-dark"] { color-scheme: dark; --background: #2d353b; --surface: #343f44; --edge: #475258; --edge-strong: #4f585e; --ink: color-mix(in oklab, #d3c6aa 82%, #859289); --ink-muted: #859289; --ink-strong: #d3c6aa; --ink-bright: color-mix(in oklab, #d3c6aa 78%, white); --ink-inverse: #2d353b; --link: #7fbbb3; --link-hover: #83c092; --accent: #d699b6; --danger: #e67e80; --warning: #dbbc7f; --success: #a7c080; --code: #83c092; --focus: #7fbbb3; } /* Monokai — the TextMate/Sublime classic */ [data-theme="monokai"] { color-scheme: dark; --background: #272822; --surface: #1e1f1c; --edge: #3e3d32; --edge-strong: #57584f; --ink: color-mix(in oklab, #f8f8f2 78%, #75715e); --ink-muted: #75715e; --ink-strong: #f8f8f2; --ink-bright: #ffffff; --ink-inverse: #272822; --link: #66d9ef; --link-hover: #a6e22e; --accent: #ae81ff; --danger: #f92672; --warning: #e6db74; --success: #a6e22e; --code: #e6db74; --focus: #66d9ef; } /* GitHub Light — the default github.com palette */ [data-theme="github-light"] { color-scheme: light; --background: #ffffff; --surface: #f6f8fa; --edge: #d8dee4; --edge-strong: #afb8c1; --ink: #24292f; --ink-muted: #656d76; --ink-strong: #1f2328; --ink-bright: #000000; --ink-inverse: #ffffff; --link: #0969da; --link-hover: #0550ae; --accent: #8250df; --danger: #cf222e; --warning: #9a6700; --success: #1a7f37; --code: #953800; --focus: #0969da; } /* White on Black — the inverted companion to Black & White. */ [data-theme="mono-dark"] { color-scheme: dark; --background: #0a0a0a; --surface: #171717; --edge: #2e2e2e; --edge-strong: #454545; --ink: #d4d4d4; --ink-muted: #8a8a8a; --ink-strong: #f5f5f5; --ink-bright: #ffffff; --ink-inverse: #0a0a0a; --link: #ffffff; --link-hover: #b3b3b3; --accent: #ffffff; --danger: #f0f0f0; --warning: #bdbdbd; --success: #f0f0f0; --code: #fafafa; --focus: #ffffff; } /* Black & White — plain paper: grayscale only, by design. */ [data-theme="mono"] { color-scheme: light; --background: #ffffff; --surface: #f5f5f5; --edge: #e2e2e2; --edge-strong: #c6c6c6; --ink: #333333; --ink-muted: #6e6e6e; --ink-strong: #111111; --ink-bright: #000000; --ink-inverse: #ffffff; --link: #000000; --link-hover: #555555; --accent: #000000; --danger: #1a1a1a; --warning: #4a4a4a; --success: #1a1a1a; --code: #111111; --focus: #000000; } /* Body font: default Geist, remapped per data-font (see layout.tsx). */ :root { --font-body: var(--font-geist-sans); } [data-font="inter"] { --font-body: var(--font-inter); } [data-font="lora"] { --font-body: var(--font-lora); } [data-font="merriweather"] { --font-body: var(--font-merriweather); } [data-font="jetbrains-mono"] { --font-body: var(--font-jetbrains-mono); } [data-font="source-serif"] { --font-body: var(--font-source-serif); } [data-font="eb-garamond"] { --font-body: var(--font-eb-garamond); } [data-font="playfair-display"] { --font-body: var(--font-playfair); } [data-font="open-sans"] { --font-body: var(--font-open-sans); } [data-font="work-sans"] { --font-body: var(--font-work-sans); } [data-font="atkinson-hyperlegible"] { --font-body: var(--font-atkinson); } [data-font="space-grotesk"] { --font-body: var(--font-space-grotesk); } html { color-scheme: dark; } body { background: var(--background); color: var(--ink); } /* Token-driven so every theme gets a sensible selection color. */ ::selection { background: var(--link); color: var(--ink-inverse); } :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 2px; } .container-site { margin-inline: auto; width: 100%; max-width: 72rem; padding-inline: 1rem; } @media (min-width: 640px) { .container-site { padding-inline: 1.5rem; } } /* Rendered Markdown (posts, pages, editor preview) */ .markdown-body { line-height: 1.75; overflow-wrap: break-word; } .markdown-body > * + * { margin-top: 1em; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: var(--ink-strong); font-weight: 600; line-height: 1.3; margin-top: 1.6em; } .markdown-body h1 { font-size: 1.6rem; } .markdown-body h2 { font-size: 1.35rem; } .markdown-body h3 { font-size: 1.15rem; } .markdown-body h4 { font-size: 1rem; } .markdown-body a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; } .markdown-body a:hover { color: var(--link-hover); } .markdown-body strong { color: var(--ink-strong); font-weight: 600; } .markdown-body ul, .markdown-body ol { padding-left: 1.5rem; } .markdown-body ul { list-style: disc; } .markdown-body ol { list-style: decimal; } .markdown-body li + li { margin-top: 0.35em; } .markdown-body li::marker { color: var(--ink-muted); } .markdown-body blockquote { border-left: 3px solid var(--ink-muted); padding-left: 1rem; color: var(--ink-strong); font-style: italic; } .markdown-body code { font-family: var(--font-geist-mono), monospace; font-size: 0.875em; color: var(--code); background: var(--surface); border: 1px solid var(--edge); border-radius: 4px; padding: 0.125em 0.375em; } .markdown-body pre { background: var(--surface); border: 1px solid var(--edge); border-radius: 8px; padding: 1rem; overflow-x: auto; } .markdown-body pre code { background: none; border: none; padding: 0; color: var(--ink-strong); font-size: 0.85rem; } .markdown-body table { width: 100%; border-collapse: collapse; font-size: 0.9rem; display: block; overflow-x: auto; } .markdown-body th, .markdown-body td { border: 1px solid var(--edge-strong); padding: 0.5rem 0.75rem; text-align: left; } .markdown-body th { background: var(--surface); color: var(--ink-strong); font-weight: 600; } .markdown-body img { max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--edge); } .markdown-body hr { border: none; border-top: 1px solid var(--edge-strong); margin-block: 2rem; } /* Rich text editor (Tiptap) chrome */ .editor-shell:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; } .editor-shell .tiptap p.is-editor-empty:first-child::before { content: attr(data-placeholder); float: left; height: 0; pointer-events: none; color: var(--ink-muted); } .editor-shell .tiptap img.ProseMirror-selectednode { outline: 2px solid var(--focus); outline-offset: 2px; } .editor-shell .tiptap .selectedCell { position: relative; } .editor-shell .tiptap .selectedCell::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in oklab, var(--focus) 18%, transparent); } .editor-shell .tiptap table { display: table; /* editable tables need real table layout, not scroll wrapper */ } .editor-shell .tiptap .ProseMirror-gapcursor:after { border-top: 1px solid var(--ink-strong); }