592 lines
15 KiB
CSS
592 lines
15 KiB
CSS
@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 <html> 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);
|
|
}
|