yap-blog/src/app/globals.css
2026-07-02 21:32:33 -04:00

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);
}