prose.css (14008B)
1 /* ============================================================================ 2 Prose — how a rendered cheatsheet reads. 3 4 The body is paper. The code is not: every block is a dark plate, in both 5 modes, exactly as the main site sets them. That is the page's core 6 rhythm — terminal output pasted into a printed document — and it is why 7 the blocks do not follow the theme. A code block that turns cream in 8 light mode stops being a terminal and becomes a quotation. 9 ========================================================================== */ 10 11 .prose { color: var(--fg); font-size: 17.5px; line-height: 1.68; letter-spacing: -0.003em; max-width: 78ch; } 12 .prose > * + * { margin-top: 1.15rem; } 13 14 .prose h2, .prose h3, .prose h4 { font-family: var(--font-display); scroll-margin-top: 92px; } 15 /* Empty MkDocs-anchor aliases inside mirrored headings (see astro.config.mjs). 16 They are the scroll target for canonical upstream deep links, so they need 17 the same offset or the heading lands behind the sticky header. */ 18 .prose .anchor-alias { scroll-margin-top: 92px; } 19 .prose h2 { 20 font-size: clamp(1.35rem, 2.2vw, 1.75rem); 21 font-weight: 700; 22 font-stretch: 84%; 23 letter-spacing: -0.03em; 24 line-height: 1; 25 text-transform: uppercase; 26 margin-top: 2.6rem; 27 padding-bottom: 0.5rem; 28 border-bottom: 1px solid var(--fg); 29 } 30 .prose h3 { 31 font-size: 1.18rem; font-weight: 700; letter-spacing: -0.02em; 32 line-height: 1.15; margin-top: 2rem; color: var(--fg); 33 } 34 .prose h4 { 35 font-family: var(--font-mono); 36 font-size: 11px; letter-spacing: var(--track-micro); text-transform: uppercase; 37 margin-top: 1.6rem; color: var(--fg-faint); 38 } 39 40 .prose p { color: var(--fg); text-wrap: pretty; } 41 .prose strong { color: var(--fg); font-weight: 600; } 42 .prose em { color: var(--fg-dim); } 43 44 /* ---- Drop cap ------------------------------------------------------------ */ 45 /* The opening letter of a sheet, floated three lines deep in EB Garamond. 46 Set on a span rather than with `::first-letter` for two reasons: the 47 pseudo-element cannot be given a different family and weight reliably 48 across engines at this size, and — the real one — the letter stays real 49 selectable, searchable text this way, read as part of the word it opens 50 rather than as decoration sitting beside it. 51 52 The rehype pass that splits it out (see `rehypeDropCap` in 53 astro.config.mjs) skips short openers, so an image caption or a 54 one-line note is left alone. */ 55 .prose .cap { 56 float: left; 57 font-family: var(--font-lockup); 58 font-size: 72px; 59 font-weight: 600; 60 line-height: 0.74; 61 color: var(--love); 62 margin: 5px 0.85rem 0 0; 63 } 64 65 .prose a { 66 color: var(--accent); 67 text-decoration: underline; 68 text-decoration-color: color-mix(in oklab, var(--accent) 40%, transparent); 69 text-underline-offset: 3px; 70 transition: text-decoration-color 200ms ease; 71 } 72 .prose a:hover { text-decoration-color: var(--accent); } 73 74 .prose ul, .prose ol { padding-left: 1.4rem; } 75 .prose li { margin-top: 0.35rem; } 76 .prose ul li::marker { color: var(--accent); } 77 .prose ol li::marker { color: var(--fg-faint); font-family: var(--font-mono); } 78 79 .prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2.4rem 0; } 80 81 /* Inline code — a tinted slug on the page, square, hairline-bordered. This 82 is the one place the paper carries code, and it stays paper-coloured; 83 the dark treatment is reserved for blocks. */ 84 .prose :not(pre) > code { 85 font-family: var(--font-mono); 86 font-size: 0.86em; 87 padding: 0.14em 0.4em; 88 background: var(--code-bg); 89 border: 1px solid var(--rule); 90 color: var(--rose); 91 word-break: break-word; 92 } 93 94 /* ---- Callouts (blockquotes) --------------------------------------------- */ 95 /* Every blockquote is a terminal callout: a square plate framed by corner 96 brackets (`.corners`, added in app.ts) with a left accent bar and, when the 97 source used a GitHub/Obsidian `> [!type]` marker, a mono type label 98 (`enhanceCallouts` in app.ts strips the marker and inserts `.callout__label`). 99 Cross-theme by contract, mirroring the code panes in reverse: on the cream 100 page a callout is a NIGHT plate so it reads as a terminal; on the night page 101 it flips to a DAWN (cream) plate — paper floating on the dark. Plain 102 blockquotes with no marker get the same frame, just no label. */ 103 .prose blockquote { 104 /* Default (cream page): pin the night palette for this subtree, like 105 `.plate`, so the callout is a terminal. */ 106 --base: #191724; --surface: #1f1d2e; --overlay: #26233a; 107 --text: #e0def4; --subtle: #908caa; --muted: #8b87a3; 108 --love: #eb6f92; --gold: #f6c177; --rose: #ebbcba; 109 --pine: #9ccfd8; --foam: #9ccfd8; --iris: #c4a7e7; 110 --rule: rgba(224, 222, 244, 0.16); 111 --fg: var(--text); --fg-dim: var(--subtle); --fg-faint: var(--muted); 112 --accent: var(--iris); 113 --callout-accent: var(--foam); 114 --bracket: var(--love); 115 position: relative; 116 background: var(--surface); 117 color: var(--fg-dim); 118 border: 1px solid var(--rule); 119 border-left: 2px solid var(--callout-accent); 120 padding: 0.95rem 1.15rem; 121 margin-block: 1.5rem; 122 } 123 /* Night page: flip the same object to a dawn (cream) plate. */ 124 :root[data-theme='dark'] .prose blockquote { 125 --base: #faf4ed; --surface: #fffaf3; --overlay: #f2e9e1; 126 --text: #575279; --subtle: #635f7c; --muted: #6e6a86; 127 --love: #b4637a; --gold: #ea9d34; --rose: #d7827e; 128 --pine: #286983; --foam: #56949f; --iris: #907aa9; 129 --rule: rgba(87, 82, 121, 0.22); 130 --accent: var(--iris); 131 } 132 .prose blockquote > :first-child { margin-top: 0; } 133 .prose blockquote > :last-child { margin-bottom: 0; } 134 .prose blockquote p { margin-top: 0.55rem; color: var(--fg-dim); } 135 .prose blockquote strong { color: var(--fg); } 136 .prose blockquote em { color: var(--fg-dim); } 137 .prose blockquote a { color: var(--accent); text-decoration-color: color-mix(in oklab, var(--accent) 45%, transparent); } 138 .prose blockquote ul li::marker { color: var(--callout-accent); } 139 /* Inline code in a callout rides the pinned overlay, so it keeps contrast on 140 the night plate and on the dawn plate both. */ 141 .prose blockquote :not(pre) > code { 142 background: var(--overlay); 143 border-color: var(--rule); 144 color: var(--rose); 145 } 146 147 /* The typed label — the mono micro-header a `[!type]` marker becomes. */ 148 .callout__label { 149 display: inline-flex; align-items: center; gap: 0.5rem; 150 font-family: var(--font-mono); font-size: 10px; 151 letter-spacing: var(--track-micro); text-transform: uppercase; 152 line-height: 1; color: var(--callout-accent); 153 margin-bottom: 0.7rem; 154 } 155 .callout__label::before { content: '//'; opacity: 0.6; } 156 157 /* Re-key the accent bar + label per kind. Qualified with `.prose blockquote` 158 so these out-rank the base blockquote's own `--callout-accent`. */ 159 .prose blockquote[data-callout] { border-left-color: var(--callout-accent); } 160 .prose blockquote[data-callout='warning'] { --callout-accent: var(--gold); } 161 .prose blockquote[data-callout='danger'] { --callout-accent: var(--love); --bracket: var(--love); } 162 .prose blockquote[data-callout='tip'], 163 .prose blockquote[data-callout='success'] { --callout-accent: var(--foam); } 164 .prose blockquote[data-callout='note'], 165 .prose blockquote[data-callout='info'] { --callout-accent: var(--pine); } 166 .prose blockquote[data-callout='question'], 167 .prose blockquote[data-callout='example'], 168 .prose blockquote[data-callout='quote'] { --callout-accent: var(--iris); } 169 170 /* Tables — a ruled table, not a card with a table in it. */ 171 .prose .table-scroll { overflow-x: auto; border: 1px solid var(--rule); } 172 .prose table { width: 100%; border-collapse: collapse; font-size: 0.9rem; } 173 .prose thead th { 174 background: var(--wash-strong); 175 font-family: var(--font-mono); font-size: var(--step-micro); 176 letter-spacing: var(--track-micro); 177 text-transform: uppercase; color: var(--fg-dim); 178 text-align: left; padding: 0.62rem 0.8rem; white-space: nowrap; 179 border-bottom: 1px solid var(--rule); 180 } 181 .prose tbody td { padding: 0.58rem 0.8rem; border-top: 1px solid var(--rule); vertical-align: top; } 182 .prose tbody tr:hover { background: var(--wash); } 183 184 .prose img { border: 1px solid var(--rule); margin-inline: auto; } 185 186 /* Rendered workflow diagrams are supplied alongside editable Mermaid source. 187 The caption behaves like a terminal status bar while the Dawn artwork stays 188 on its intended light plate in either site theme. */ 189 .prose .diagram-plate { 190 position: relative; 191 max-width: min(100%, 96ch); 192 border: 1px solid var(--rule); 193 background: #faf4ed; 194 overflow: hidden; 195 } 196 .prose .diagram-plate__image { 197 display: block; 198 padding: clamp(0.65rem, 2vw, 1.25rem); 199 background: #faf4ed; 200 text-decoration: none; 201 } 202 .prose .diagram-plate img { 203 width: auto; 204 max-height: min(76vh, 1050px); 205 border: 0; 206 object-fit: contain; 207 } 208 .prose .diagram-plate figcaption { 209 display: flex; 210 align-items: center; 211 justify-content: space-between; 212 gap: 0.65rem 1.2rem; 213 padding: 0.65rem 0.8rem; 214 border-top: 1px solid var(--rule); 215 background: var(--surface); 216 color: var(--fg-dim); 217 font-family: var(--font-mono); 218 font-size: var(--step-micro); 219 letter-spacing: var(--track-micro); 220 text-transform: uppercase; 221 } 222 .prose .diagram-plate__downloads { display: flex; flex-wrap: wrap; gap: 0.85rem; } 223 .prose .diagram-plate__downloads a { color: var(--accent); text-decoration: none; } 224 .prose .diagram-plate__downloads a:hover { text-decoration: underline; } 225 @media (max-width: 560px) { 226 .prose .diagram-plate figcaption { align-items: flex-start; flex-direction: column; } 227 } 228 229 /* ---- Code blocks --------------------------------------------------------- */ 230 /* A figure on the plate, with its caption above the code: the language at 231 the left, the copy control at the right, both in the 9.5px mono the rest 232 of the site labels things in. No window chrome, no traffic-light dots — 233 this is a printed listing of terminal output, not a simulated window. */ 234 .code-pane { 235 position: relative; 236 background: var(--plate); 237 padding: 1rem 1.4rem 1.4rem; 238 /* Runs past the prose measure at wide viewports, because a fixed-width 239 terminal listing is the one thing on the page that genuinely needs 240 more than 74ch and wraps badly under it. */ 241 max-width: min(100%, 92ch); 242 } 243 /* app.ts tags the pane `.plate`, so `--iris`/`--foam`/`--love` resolve to 244 their night values in this subtree in both modes — the caption colours and 245 the corner brackets stay terminal-toned on the cream page without hardcoded 246 hex. The titlebar closes with a hairline rule, tmux-style. */ 247 .code-pane__bar { 248 display: flex; align-items: center; justify-content: space-between; 249 gap: 1rem; 250 margin-bottom: 0.9rem; 251 padding-bottom: 0.7rem; 252 border-bottom: 1px solid var(--rule); 253 } 254 .code-pane__lang { 255 font-family: var(--font-mono); font-size: 9.5px; 256 letter-spacing: 0.18em; text-transform: uppercase; 257 color: var(--iris); 258 } 259 .code-copy { 260 display: inline-flex; align-items: center; gap: 0.45rem; 261 font-family: var(--font-mono); font-size: 9.5px; 262 letter-spacing: 0.18em; text-transform: uppercase; 263 color: var(--foam); 264 transition: color 200ms ease; 265 } 266 .code-copy:hover { color: var(--text); } 267 .code-copy.copied { color: var(--gold); } 268 .code-copy svg { width: 11px; height: 11px; } 269 270 /* The scanline film sits at z-index 1 over the plate; the titlebar and the 271 listing ride above it so the CRT texture shows through the transparent code 272 background between glyphs without ever washing over the text. Softened for 273 code because legibility outranks the effect here. */ 274 .code-pane > .code-pane__bar, 275 .code-pane > pre { position: relative; z-index: 2; } 276 .code-pane > .scanlines { opacity: 0.5; } 277 278 .prose pre.astro-code { 279 margin: 0; 280 padding: 0; 281 overflow-x: auto; 282 font-size: 13px; 283 line-height: 1.7; 284 background: transparent !important; 285 color: #e0def4; 286 } 287 .prose pre.astro-code code { 288 font-family: var(--font-mono); 289 display: block; 290 /* Wrapped rather than scrolled: a cheatsheet's value is the whole 291 command being visible and copyable at a glance, and a long 292 `--script-args` tail hidden behind a horizontal scrollbar is the one 293 thing most likely to be pasted wrong. */ 294 white-space: pre-wrap; 295 word-break: break-word; 296 } 297 298 /* First heading of the doc is the page title (rendered separately) */ 299 .prose > h1:first-child { display: none; } 300 301 /* ---- Credited screenshot (`<figure class="shot">`) ---------------------- 302 A screenshot that belongs to someone else. The frame is the diagram plate's 303 (square, ruled, mono caption) but the caption carries a second line: who the 304 image belongs to. That credit is per-image rather than one blanket line per 305 page because an unlicensed upstream is exactly the case where "which of 306 these pictures is yours?" must have an answer on the image itself. 307 308 The `<img>` is remote by design — pinned to an upstream commit, never 309 copied into this repo — so the box has to survive the hotlink failing: 310 a min-height and the alt text keep the caption meaningful on a broken 311 image instead of collapsing the figure to a caption floating in space. */ 312 .prose figure.shot { 313 margin: 1.6rem 0; 314 border: 1px solid var(--rule); 315 background: var(--surface); 316 } 317 .prose figure.shot img { 318 display: block; 319 width: 100%; 320 height: auto; 321 min-height: 2.5rem; 322 margin: 0; 323 border: 0; 324 border-bottom: 1px solid var(--rule); 325 /* Screenshots are mostly debugger and console captures on a light chrome; 326 letterbox rather than crop so register values stay readable. */ 327 object-fit: contain; 328 background: #faf4ed; 329 } 330 .prose figure.shot figcaption { 331 padding: 0.6rem 0.8rem; 332 color: var(--fg-dim); 333 font-size: var(--step--1, 0.9em); 334 line-height: 1.5; 335 } 336 .prose figure.shot figcaption code { font-size: 0.95em; } 337 .prose figure.shot .shot-credit { 338 display: block; 339 margin-top: 0.3rem; 340 color: var(--fg-faint); 341 font-family: var(--font-term, 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, monospace); 342 font-size: var(--step-micro); 343 letter-spacing: var(--track-micro); 344 text-transform: uppercase; 345 }