max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01EEmeiSJnmyL98QzA5Dwsvz
11 files changed,
+1961 insertions,
-1 deletion
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | use std::collections::hash_map::DefaultHasher; | |
| 2 | 2 | use std::fs; | |
| 3 | 3 | use std::hash::{Hash, Hasher}; | |
| 4 | - | use std::path::Path; | |
| 4 | + | use std::path::{Path, PathBuf}; | |
| 5 | 5 | use std::process::Command; | |
| 6 | 6 | ||
| 7 | 7 | fn main() { | |
| @@ -50,6 +50,10 @@ | |||
| 50 | 50 | // `build_frontend` — a build-script rerun recompiles the crate, which is | |
| 51 | 51 | // when `include_dir!` re-reads the emitted bundles. | |
| 52 | 52 | println!("cargo::rerun-if-changed=static/fonts"); | |
| 53 | + | ||
| 54 | + | // Before the hash below, so `?v=` reflects the sheet this run just wrote. | |
| 55 | + | concat_site_sheet(); | |
| 56 | + | ||
| 53 | 57 | let static_files = ["static/style.css", "static/htmx.min.js", "static/mt.js"]; | |
| 54 | 58 | ||
| 55 | 59 | let mut hasher = DefaultHasher::new(); | |
| @@ -98,6 +102,41 @@ | |||
| 98 | 102 | } | |
| 99 | 103 | } | |
| 100 | 104 | ||
| 105 | + | /// Join `css/*.css` into `static/style.css`. | |
| 106 | + | /// | |
| 107 | + | /// The parts are contiguous line ranges of what the sheet already was, loaded | |
| 108 | + | /// in name order, so the join is byte-identical and the split changed no | |
| 109 | + | /// rendering. This sheet uses no `@layer`, so the join is the whole of it and | |
| 110 | + | /// every part is standalone-valid CSS on its own. | |
| 111 | + | /// | |
| 112 | + | /// The parts live in `css/` rather than `static/css/` because | |
| 113 | + | /// `src/static_assets.rs` embeds `static/` wholesale with `include_dir!`, so | |
| 114 | + | /// parts kept there would ship twice. | |
| 115 | + | /// | |
| 116 | + | /// The output stays committed: it is what `include_dir!` embeds and what the | |
| 117 | + | /// head links, and 1,900 lines of annotated CSS keep their blame. | |
| 118 | + | fn concat_site_sheet() { | |
| 119 | + | let mut parts: Vec<PathBuf> = fs::read_dir("css") | |
| 120 | + | .expect("read css/") | |
| 121 | + | .map(|e| e.expect("css/ entry").path()) | |
| 122 | + | .filter(|p| p.extension().is_some_and(|e| e == "css")) | |
| 123 | + | .collect(); | |
| 124 | + | parts.sort(); | |
| 125 | + | println!("cargo::rerun-if-changed=css"); | |
| 126 | + | ||
| 127 | + | let mut out = String::new(); | |
| 128 | + | for part in &parts { | |
| 129 | + | println!("cargo::rerun-if-changed={}", part.display()); | |
| 130 | + | out.push_str( | |
| 131 | + | &fs::read_to_string(part).unwrap_or_else(|e| panic!("read {}: {e}", part.display())), | |
| 132 | + | ); | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | // `write_if_changed`, not `fs::write`: this is a file the script both writes | |
| 136 | + | // and watches, and an unconditional write reruns the script on every build. | |
| 137 | + | write_if_changed(Path::new("static/style.css"), &out); | |
| 138 | + | } | |
| 139 | + | ||
| 101 | 140 | /// Recursively hash every `.js` file under `dir` into `hasher`, in a | |
| 102 | 141 | /// deterministic order. A missing directory is a no-op, the first build on a | |
| 103 | 142 | /// fresh checkout runs before the frontend has been compiled. |
| @@ -1,0 +1,132 @@ | |||
| 1 | + | /* Multithreaded, Forum Stylesheet. Adapted from Makenot.work design language */ | |
| 2 | + | ||
| 3 | + | /* FONTS */ | |
| 4 | + | ||
| 5 | + | @font-face { | |
| 6 | + | font-family: "Young Serif"; | |
| 7 | + | src: url("/static/fonts/ysrf.woff2") format("woff2"), | |
| 8 | + | url("/static/fonts/ysrf.ttf") format("truetype"); | |
| 9 | + | font-display: swap; | |
| 10 | + | } | |
| 11 | + | ||
| 12 | + | @font-face { | |
| 13 | + | font-family: "IBM Plex Mono"; | |
| 14 | + | src: url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2"), | |
| 15 | + | url("/static/fonts/IBMPlexMono-Regular.ttf") format("truetype"); | |
| 16 | + | font-weight: normal; | |
| 17 | + | font-display: swap; | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | @font-face { | |
| 21 | + | font-family: "IBM Plex Mono"; | |
| 22 | + | src: url("/static/fonts/IBMPlexMono-Bold.woff2") format("woff2"), | |
| 23 | + | url("/static/fonts/IBMPlexMono-Bold.ttf") format("truetype"); | |
| 24 | + | font-weight: bold; | |
| 25 | + | font-display: swap; | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | @font-face { | |
| 29 | + | font-family: "Lato"; | |
| 30 | + | src: url("/static/fonts/Lato-Regular.woff2") format("woff2"), | |
| 31 | + | url("/static/fonts/Lato-Regular.ttf") format("truetype"); | |
| 32 | + | font-weight: normal; | |
| 33 | + | font-display: swap; | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | @font-face { | |
| 37 | + | font-family: "Lato"; | |
| 38 | + | src: url("/static/fonts/Lato-Bold.woff2") format("woff2"), | |
| 39 | + | url("/static/fonts/Lato-Bold.ttf") format("truetype"); | |
| 40 | + | font-weight: bold; | |
| 41 | + | font-display: swap; | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | /* VARIABLES */ | |
| 45 | + | ||
| 46 | + | :root { | |
| 47 | + | --background: #ede8e1; | |
| 48 | + | --detail: #3d3530; | |
| 49 | + | --highlight: #6c5ce7; | |
| 50 | + | --light-background: #f4f0eb; | |
| 51 | + | ||
| 52 | + | --surface-muted: #ddd7c5; | |
| 53 | + | --surface-alt: #ebe7db; | |
| 54 | + | --surface-border: #d0cbb8; | |
| 55 | + | --border: #d0cbb8; | |
| 56 | + | --text-muted: #8a8480; | |
| 57 | + | --input-background: #e2dad2; | |
| 58 | + | ||
| 59 | + | --primary-dark: #000000; | |
| 60 | + | --primary-light: #ffffff; | |
| 61 | + | --success: #2d5016; | |
| 62 | + | --warning: #8b7355; | |
| 63 | + | --danger: #d62828; | |
| 64 | + | --error: #d62828; | |
| 65 | + | ||
| 66 | + | --focus-ring: #6c5ce7; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | /* RESET */ | |
| 70 | + | ||
| 71 | + | * { | |
| 72 | + | margin: 0; | |
| 73 | + | padding: 0; | |
| 74 | + | box-sizing: border-box; | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | /* BASE */ | |
| 78 | + | ||
| 79 | + | body { | |
| 80 | + | margin: 0; | |
| 81 | + | background-color: var(--background); | |
| 82 | + | color: var(--detail); | |
| 83 | + | font-family: "Lato", sans-serif; | |
| 84 | + | line-height: 1.5; | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | h1 { | |
| 88 | + | font-family: "Young Serif", serif; | |
| 89 | + | font-weight: normal; | |
| 90 | + | color: var(--detail); | |
| 91 | + | text-align: left; | |
| 92 | + | font-size: 1.5rem; | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | h2, | |
| 96 | + | h3 { | |
| 97 | + | font-family: "IBM Plex Mono", monospace; | |
| 98 | + | font-weight: normal; | |
| 99 | + | color: var(--detail); | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | p { | |
| 103 | + | font-family: "Lato", sans-serif; | |
| 104 | + | color: var(--detail); | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | a { | |
| 108 | + | color: var(--detail); | |
| 109 | + | text-decoration: none; | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | a:hover { | |
| 113 | + | text-decoration: underline; | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | /* The signature dot */ | |
| 117 | + | .dot { | |
| 118 | + | color: var(--highlight); | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | /* LAYOUT */ | |
| 122 | + | ||
| 123 | + | .container { | |
| 124 | + | max-width: 1200px; | |
| 125 | + | margin: 0 auto; | |
| 126 | + | padding: 1rem; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | .padded-page { | |
| 130 | + | padding: 1.25rem; | |
| 131 | + | } | |
| 132 | + |
| @@ -1,0 +1,86 @@ | |||
| 1 | + | /* SITE HEADER & NAV */ | |
| 2 | + | ||
| 3 | + | .site-header { | |
| 4 | + | display: flex; | |
| 5 | + | justify-content: space-between; | |
| 6 | + | align-items: center; | |
| 7 | + | padding: 0.75rem 1.25rem; | |
| 8 | + | margin-bottom: 0.5rem; | |
| 9 | + | } | |
| 10 | + | ||
| 11 | + | .site-header nav { | |
| 12 | + | margin: 0; | |
| 13 | + | } | |
| 14 | + | ||
| 15 | + | .site-header .nav-links { | |
| 16 | + | display: flex; | |
| 17 | + | gap: 1.5rem; | |
| 18 | + | align-items: center; | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | .nav-links a { | |
| 22 | + | color: var(--detail); | |
| 23 | + | text-decoration: none; | |
| 24 | + | font-family: "IBM Plex Mono", monospace; | |
| 25 | + | transition: opacity 0.2s ease; | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | .nav-links a:hover { | |
| 29 | + | opacity: 0.6; | |
| 30 | + | text-decoration: none; | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | .site-logo { | |
| 34 | + | font-family: "Young Serif", serif; | |
| 35 | + | font-size: 1.25rem; | |
| 36 | + | text-decoration: none; | |
| 37 | + | color: var(--detail); | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | .site-logo:hover { | |
| 41 | + | opacity: 0.8; | |
| 42 | + | text-decoration: none; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | .link-button { | |
| 46 | + | background: none; | |
| 47 | + | border: none; | |
| 48 | + | color: var(--detail); | |
| 49 | + | font-family: "IBM Plex Mono", monospace; | |
| 50 | + | font-size: 1rem; | |
| 51 | + | cursor: pointer; | |
| 52 | + | padding: 0; | |
| 53 | + | text-decoration: none; | |
| 54 | + | transition: opacity 0.2s ease; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | .link-button:hover { | |
| 58 | + | opacity: 0.6; | |
| 59 | + | text-decoration: underline; | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | .nav-form { | |
| 63 | + | display: contents; | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | /* Hamburger toggle */ | |
| 67 | + | .nav-toggle-checkbox { | |
| 68 | + | display: none; | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | .nav-toggle-label { | |
| 72 | + | display: none; | |
| 73 | + | cursor: pointer; | |
| 74 | + | padding: 0.5rem; | |
| 75 | + | z-index: 11; | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | .nav-toggle-label span { | |
| 79 | + | display: block; | |
| 80 | + | width: 22px; | |
| 81 | + | height: 2px; | |
| 82 | + | background: var(--detail); | |
| 83 | + | margin: 5px 0; | |
| 84 | + | transition: transform 0.2s ease, opacity 0.2s ease; | |
| 85 | + | } | |
| 86 | + |
| @@ -1,0 +1,195 @@ | |||
| 1 | + | /* BUTTONS */ | |
| 2 | + | ||
| 3 | + | button { | |
| 4 | + | color: var(--detail); | |
| 5 | + | background: var(--light-background); | |
| 6 | + | padding: 6px 12px; | |
| 7 | + | border: 1px solid var(--detail); | |
| 8 | + | font-family: inherit; | |
| 9 | + | cursor: pointer; | |
| 10 | + | transition: opacity 0.2s ease; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | button:hover { | |
| 14 | + | opacity: 0.8; | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | button.primary, | |
| 18 | + | .btn-primary { | |
| 19 | + | background: var(--primary-dark); | |
| 20 | + | color: var(--primary-light); | |
| 21 | + | border: none; | |
| 22 | + | padding: 0.5rem 1rem; | |
| 23 | + | font-family: inherit; | |
| 24 | + | font-size: 0.85rem; | |
| 25 | + | cursor: pointer; | |
| 26 | + | transition: opacity 0.2s ease; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | button.primary:hover, | |
| 30 | + | .btn-primary:hover { | |
| 31 | + | opacity: 0.8; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | button.secondary, | |
| 35 | + | .btn-secondary { | |
| 36 | + | background: var(--surface-muted); | |
| 37 | + | color: var(--detail); | |
| 38 | + | border: none; | |
| 39 | + | padding: 0.5rem 1rem; | |
| 40 | + | font-family: "IBM Plex Mono", monospace; | |
| 41 | + | font-size: 0.85rem; | |
| 42 | + | cursor: pointer; | |
| 43 | + | transition: opacity 0.2s ease; | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | button.secondary:hover, | |
| 47 | + | .btn-secondary:hover { | |
| 48 | + | opacity: 0.8; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | /* Irreversible actions. The class was in the markup before it was in here (the | |
| 52 | + | admin clean-slate button, the chat wipe), rendering as an ordinary button, | |
| 53 | + | which is the one thing a destructive control must not look like. */ | |
| 54 | + | button.danger { | |
| 55 | + | background: var(--danger); | |
| 56 | + | color: var(--primary-light); | |
| 57 | + | border: none; | |
| 58 | + | padding: 0.5rem 1rem; | |
| 59 | + | font-family: inherit; | |
| 60 | + | font-size: 0.85rem; | |
| 61 | + | cursor: pointer; | |
| 62 | + | transition: opacity 0.2s ease; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | button.danger:hover { | |
| 66 | + | opacity: 0.8; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | /* FORMS */ | |
| 70 | + | ||
| 71 | + | form { | |
| 72 | + | display: flex; | |
| 73 | + | flex-direction: column; | |
| 74 | + | gap: 1rem; | |
| 75 | + | width: 100%; | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | .form-container { | |
| 79 | + | max-width: 600px; | |
| 80 | + | background: var(--light-background); | |
| 81 | + | padding: 1.5rem; | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | label { | |
| 85 | + | font-family: "IBM Plex Mono", monospace; | |
| 86 | + | color: var(--detail); | |
| 87 | + | font-size: 14px; | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | input[type="text"], | |
| 91 | + | input[type="email"], | |
| 92 | + | input[type="password"], | |
| 93 | + | input[type="number"], | |
| 94 | + | textarea, | |
| 95 | + | select { | |
| 96 | + | width: 100%; | |
| 97 | + | background: var(--input-background); | |
| 98 | + | border: none; | |
| 99 | + | padding: 0.6rem; | |
| 100 | + | font-family: "IBM Plex Mono", monospace; | |
| 101 | + | font-size: 0.85rem; | |
| 102 | + | color: var(--detail); | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | input::placeholder, | |
| 106 | + | textarea::placeholder { | |
| 107 | + | opacity: 0.5; | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | input[type="text"]:focus, | |
| 111 | + | input[type="email"]:focus, | |
| 112 | + | input[type="password"]:focus, | |
| 113 | + | input[type="number"]:focus, | |
| 114 | + | textarea:focus, | |
| 115 | + | select:focus { | |
| 116 | + | outline: none; | |
| 117 | + | border: 2px solid var(--highlight); | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | textarea { | |
| 121 | + | min-height: 150px; | |
| 122 | + | resize: vertical; | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | textarea.drag-over { | |
| 126 | + | border: 2px dashed var(--highlight); | |
| 127 | + | background: var(--surface-muted); | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | /* Inline form validation error, inserted by mt.js on a 422 submit, replacing | |
| 131 | + | the old error toast. Persists next to the form with the input preserved. */ | |
| 132 | + | .form-error { | |
| 133 | + | background: var(--surface-muted); | |
| 134 | + | border-left: 3px solid var(--error); | |
| 135 | + | color: var(--error); | |
| 136 | + | padding: 0.6rem 0.75rem; | |
| 137 | + | margin-bottom: 0.75rem; | |
| 138 | + | font-size: 0.85rem; | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | input[aria-invalid="true"], | |
| 142 | + | textarea[aria-invalid="true"], | |
| 143 | + | select[aria-invalid="true"] { | |
| 144 | + | border: 2px solid var(--error); | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | select { | |
| 148 | + | cursor: pointer; | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | input[type="submit"], | |
| 152 | + | form button { | |
| 153 | + | font-family: "Young Serif", serif; | |
| 154 | + | font-size: 16px; | |
| 155 | + | color: var(--detail); | |
| 156 | + | background: var(--light-background); | |
| 157 | + | padding: 10px 20px; | |
| 158 | + | border: 1px solid var(--detail); | |
| 159 | + | cursor: pointer; | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | input[type="submit"]:hover, | |
| 163 | + | form button:hover { | |
| 164 | + | background: var(--background); | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | .form-row { | |
| 168 | + | display: grid; | |
| 169 | + | grid-template-columns: 1fr 1fr; | |
| 170 | + | gap: 1rem; | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | .form-group { | |
| 174 | + | display: flex; | |
| 175 | + | flex-direction: column; | |
| 176 | + | gap: 0.25rem; | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | .form-actions { | |
| 180 | + | display: flex; | |
| 181 | + | gap: 1rem; | |
| 182 | + | margin-top: 0.5rem; | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | .form-help { | |
| 186 | + | display: block; | |
| 187 | + | font-size: 0.8rem; | |
| 188 | + | color: var(--text-muted); | |
| 189 | + | margin-top: 0.25rem; | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | .input-narrow { | |
| 193 | + | width: 6rem; | |
| 194 | + | } | |
| 195 | + |
| @@ -1,0 +1,305 @@ | |||
| 1 | + | /* DIRECTORY TABLE (forum home) */ | |
| 2 | + | ||
| 3 | + | .directory-table { | |
| 4 | + | width: 100%; | |
| 5 | + | border-collapse: collapse; | |
| 6 | + | font-size: 0.85rem; | |
| 7 | + | font-family: "Lato", sans-serif; | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | .directory-table thead { | |
| 11 | + | background: var(--surface-muted); | |
| 12 | + | } | |
| 13 | + | ||
| 14 | + | .directory-table th { | |
| 15 | + | text-align: left; | |
| 16 | + | padding: 0.35rem 0.75rem; | |
| 17 | + | font-family: "IBM Plex Mono", monospace; | |
| 18 | + | font-size: 0.7rem; | |
| 19 | + | font-weight: normal; | |
| 20 | + | text-transform: uppercase; | |
| 21 | + | letter-spacing: 0.05em; | |
| 22 | + | color: var(--text-muted); | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | .directory-table tbody tr { | |
| 26 | + | transition: background 0.1s ease; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | .directory-table tbody tr:nth-child(odd) { | |
| 30 | + | background: var(--surface-alt); | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | .directory-table tbody tr:nth-child(even) { | |
| 34 | + | background: var(--light-background); | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | .directory-table tbody tr:hover { | |
| 38 | + | background: var(--surface-muted); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | .directory-table td { | |
| 42 | + | padding: 0.4rem 0.75rem; | |
| 43 | + | border-top: 1px solid var(--border); | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | .directory-name { | |
| 47 | + | font-family: "Young Serif", serif; | |
| 48 | + | font-size: 1rem; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | .directory-name a { | |
| 52 | + | color: var(--detail); | |
| 53 | + | text-decoration: none; | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | .directory-name a:hover { | |
| 57 | + | text-decoration: underline; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | .directory-desc { | |
| 61 | + | font-size: 0.8rem; | |
| 62 | + | color: var(--text-muted); | |
| 63 | + | margin-top: 0.1rem; | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | .directory-table .col-creator { | |
| 67 | + | white-space: nowrap; | |
| 68 | + | width: 130px; | |
| 69 | + | font-family: "IBM Plex Mono", monospace; | |
| 70 | + | font-size: 0.8rem; | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | .directory-table .col-type { | |
| 74 | + | white-space: nowrap; | |
| 75 | + | width: 100px; | |
| 76 | + | font-family: "IBM Plex Mono", monospace; | |
| 77 | + | font-size: 0.8rem; | |
| 78 | + | color: var(--text-muted); | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | .directory-table .col-items { | |
| 82 | + | white-space: nowrap; | |
| 83 | + | width: 60px; | |
| 84 | + | text-align: right; | |
| 85 | + | font-family: "IBM Plex Mono", monospace; | |
| 86 | + | font-size: 0.8rem; | |
| 87 | + | color: var(--text-muted); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | /* DATA TABLE (dense thread listings) */ | |
| 91 | + | ||
| 92 | + | .data-table { | |
| 93 | + | width: 100%; | |
| 94 | + | border-collapse: collapse; | |
| 95 | + | font-size: 0.85rem; | |
| 96 | + | font-family: "Lato", sans-serif; | |
| 97 | + | margin-bottom: 1rem; | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | .data-table thead { | |
| 101 | + | background: var(--surface-muted); | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | .data-table th { | |
| 105 | + | text-align: left; | |
| 106 | + | padding: 0.35rem 0.75rem; | |
| 107 | + | font-family: "IBM Plex Mono", monospace; | |
| 108 | + | font-size: 0.7rem; | |
| 109 | + | font-weight: normal; | |
| 110 | + | text-transform: uppercase; | |
| 111 | + | letter-spacing: 0.05em; | |
| 112 | + | color: var(--text-muted); | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | .data-table th.sortable { | |
| 116 | + | cursor: pointer; | |
| 117 | + | transition: background 0.2s ease; | |
| 118 | + | user-select: none; | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | .data-table th.sortable:hover { | |
| 122 | + | background: var(--border); | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | .data-table th.sortable a { | |
| 126 | + | color: inherit; | |
| 127 | + | text-decoration: none; | |
| 128 | + | display: block; | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | .data-table th.sortable a:hover { | |
| 132 | + | text-decoration: none; | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | .data-table th.sort-active { | |
| 136 | + | color: var(--detail); | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | .data-table tbody tr { | |
| 140 | + | transition: background 0.1s ease; | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | .data-table tbody tr:nth-child(odd) { | |
| 144 | + | background: var(--surface-alt); | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | .data-table tbody tr:nth-child(even) { | |
| 148 | + | background: var(--light-background); | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | .data-table tbody tr:hover { | |
| 152 | + | background: var(--surface-muted); | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | .data-table td { | |
| 156 | + | padding: 0.4rem 0.75rem; | |
| 157 | + | border-top: 1px solid var(--border); | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | /* Thread-specific table columns */ | |
| 161 | + | .data-table .thread-title { | |
| 162 | + | font-weight: bold; | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | .data-table .thread-title a { | |
| 166 | + | color: var(--detail); | |
| 167 | + | text-decoration: none; | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | .data-table .thread-title a:hover { | |
| 171 | + | text-decoration: underline; | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | .data-table .col-replies, | |
| 175 | + | .data-table .col-activity { | |
| 176 | + | white-space: nowrap; | |
| 177 | + | text-align: right; | |
| 178 | + | width: 100px; | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | .data-table .col-author { | |
| 182 | + | white-space: nowrap; | |
| 183 | + | width: 130px; | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | /* Member list columns */ | |
| 187 | + | .data-table .col-role { | |
| 188 | + | white-space: nowrap; | |
| 189 | + | width: 100px; | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | .data-table .col-joined { | |
| 193 | + | white-space: nowrap; | |
| 194 | + | text-align: right; | |
| 195 | + | width: 100px; | |
| 196 | + | font-family: "IBM Plex Mono", monospace; | |
| 197 | + | font-size: 0.8rem; | |
| 198 | + | color: var(--text-muted); | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | /* Pinned row highlight */ | |
| 202 | + | .data-table tr.pinned { | |
| 203 | + | background: var(--surface-muted); | |
| 204 | + | } | |
| 205 | + | ||
| 206 | + | .data-table tr.pinned:hover { | |
| 207 | + | background: var(--border); | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | /* BADGES */ | |
| 211 | + | ||
| 212 | + | .badge { | |
| 213 | + | display: inline-block; | |
| 214 | + | padding: 0.15rem 0.4rem; | |
| 215 | + | font-size: 0.7rem; | |
| 216 | + | font-family: "IBM Plex Mono", monospace; | |
| 217 | + | background: var(--primary-dark); | |
| 218 | + | color: var(--primary-light); | |
| 219 | + | } | |
| 220 | + | ||
| 221 | + | .badge-success { | |
| 222 | + | background: var(--success); | |
| 223 | + | } | |
| 224 | + | ||
| 225 | + | .badge-warning { | |
| 226 | + | background: var(--warning); | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | .badge-danger { | |
| 230 | + | background: var(--danger); | |
| 231 | + | } | |
| 232 | + | ||
| 233 | + | .badge-muted { | |
| 234 | + | background: var(--text-muted); | |
| 235 | + | } | |
| 236 | + | ||
| 237 | + | .badge-ban { | |
| 238 | + | background: var(--danger); | |
| 239 | + | color: var(--primary-light); | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | .badge-mute-type { | |
| 243 | + | background: var(--warning); | |
| 244 | + | color: var(--primary-light); | |
| 245 | + | } | |
| 246 | + | ||
| 247 | + | /* Role badges */ | |
| 248 | + | .badge-role-owner { | |
| 249 | + | background: var(--primary-dark); | |
| 250 | + | color: var(--primary-light); | |
| 251 | + | } | |
| 252 | + | ||
| 253 | + | .badge-role-moderator { | |
| 254 | + | background: var(--warning); | |
| 255 | + | color: var(--primary-light); | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | .badge-role-member { | |
| 259 | + | background: var(--surface-muted); | |
| 260 | + | color: var(--detail); | |
| 261 | + | border: 1px solid var(--border); | |
| 262 | + | } | |
| 263 | + | ||
| 264 | + | .badge-role-guest { | |
| 265 | + | background: var(--surface-muted); | |
| 266 | + | color: var(--text-muted); | |
| 267 | + | border: 1px solid var(--border); | |
| 268 | + | } | |
| 269 | + | ||
| 270 | + | /* Thread status badges, text-only */ | |
| 271 | + | .badge-pinned { | |
| 272 | + | background: var(--surface-muted); | |
| 273 | + | color: var(--detail); | |
| 274 | + | border: 1px solid var(--border); | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | .badge-locked { | |
| 278 | + | background: var(--surface-muted); | |
| 279 | + | color: var(--text-muted); | |
| 280 | + | border: 1px solid var(--border); | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | /* BREADCRUMBS */ | |
| 284 | + | ||
| 285 | + | .breadcrumb { | |
| 286 | + | font-size: 0.8rem; | |
| 287 | + | color: var(--text-muted); | |
| 288 | + | margin-bottom: 0.5rem; | |
| 289 | + | font-family: "IBM Plex Mono", monospace; | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | .breadcrumb a { | |
| 293 | + | color: var(--detail); | |
| 294 | + | text-decoration: none; | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | .breadcrumb a:hover { | |
| 298 | + | text-decoration: underline; | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | .breadcrumb .sep { | |
| 302 | + | margin: 0 0.3rem; | |
| 303 | + | opacity: 0.5; | |
| 304 | + | } | |
| 305 | + |
| @@ -1,0 +1,182 @@ | |||
| 1 | + | /* POST ITEM (thread view) */ | |
| 2 | + | ||
| 3 | + | .post-list { | |
| 4 | + | display: flex; | |
| 5 | + | flex-direction: column; | |
| 6 | + | gap: 0; | |
| 7 | + | } | |
| 8 | + | ||
| 9 | + | .post-item { | |
| 10 | + | padding: 0.75rem 1rem; | |
| 11 | + | border-bottom: 1px solid var(--border); | |
| 12 | + | } | |
| 13 | + | ||
| 14 | + | .post-item:first-child { | |
| 15 | + | border-top: 1px solid var(--border); | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | .post-header { | |
| 19 | + | display: flex; | |
| 20 | + | justify-content: space-between; | |
| 21 | + | align-items: baseline; | |
| 22 | + | margin-bottom: 0.25rem; | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | .post-author { | |
| 26 | + | font-family: "IBM Plex Mono", monospace; | |
| 27 | + | font-weight: bold; | |
| 28 | + | font-size: 0.85rem; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | .post-timestamp { | |
| 32 | + | font-family: "IBM Plex Mono", monospace; | |
| 33 | + | font-size: 0.75rem; | |
| 34 | + | color: var(--text-muted); | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | .post-edited { | |
| 38 | + | font-family: "IBM Plex Mono", monospace; | |
| 39 | + | font-size: 0.7rem; | |
| 40 | + | color: var(--text-muted); | |
| 41 | + | font-style: italic; | |
| 42 | + | margin-left: 0.5rem; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | .post-body { | |
| 46 | + | font-size: 0.9rem; | |
| 47 | + | line-height: 1.5; | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | .post-body p { | |
| 51 | + | margin-bottom: 0.5rem; | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | .post-body p:last-child { | |
| 55 | + | margin-bottom: 0; | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | .post-body code { | |
| 59 | + | font-family: "IBM Plex Mono", monospace; | |
| 60 | + | font-size: 0.8rem; | |
| 61 | + | background: var(--surface-muted); | |
| 62 | + | padding: 0.1rem 0.35rem; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | .post-body pre { | |
| 66 | + | background: var(--primary-dark); | |
| 67 | + | color: var(--primary-light); | |
| 68 | + | padding: 0.75rem; | |
| 69 | + | overflow-x: auto; | |
| 70 | + | margin: 0.5rem 0; | |
| 71 | + | font-family: "IBM Plex Mono", monospace; | |
| 72 | + | font-size: 0.8rem; | |
| 73 | + | line-height: 1.5; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | .post-body pre code { | |
| 77 | + | background: none; | |
| 78 | + | padding: 0; | |
| 79 | + | color: inherit; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | .post-body blockquote { | |
| 83 | + | border-left: 3px solid var(--border); | |
| 84 | + | padding-left: 1rem; | |
| 85 | + | margin: 0.5rem 0; | |
| 86 | + | color: var(--text-muted); | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | .post-body img { | |
| 90 | + | max-width: 100%; | |
| 91 | + | height: auto; | |
| 92 | + | border-radius: 4px; | |
| 93 | + | margin: 0.5rem 0; | |
| 94 | + | cursor: pointer; | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | /* Post action links (edit/delete) */ | |
| 98 | + | .post-actions { | |
| 99 | + | margin-left: 0.75rem; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | .post-action-link { | |
| 103 | + | font-family: "IBM Plex Mono", monospace; | |
| 104 | + | font-size: 0.7rem; | |
| 105 | + | color: var(--text-muted); | |
| 106 | + | background: none; | |
| 107 | + | border: none; | |
| 108 | + | padding: 0; | |
| 109 | + | cursor: pointer; | |
| 110 | + | text-decoration: none; | |
| 111 | + | margin-left: 0.5rem; | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | .post-action-link:hover { | |
| 115 | + | color: var(--detail); | |
| 116 | + | text-decoration: underline; | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | .post-action-delete:hover { | |
| 120 | + | color: var(--danger); | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | /* Deleted posts */ | |
| 124 | + | .post-deleted .post-body { | |
| 125 | + | color: var(--text-muted); | |
| 126 | + | font-style: italic; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | /* OP indicator */ | |
| 130 | + | .post-item.op { | |
| 131 | + | background: var(--light-background); | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | /* DRAFT INDICATOR */ | |
| 135 | + | ||
| 136 | + | .draft-indicator { | |
| 137 | + | font-family: "IBM Plex Mono", monospace; | |
| 138 | + | font-size: 0.75rem; | |
| 139 | + | color: var(--text-muted); | |
| 140 | + | margin-bottom: 0.25rem; | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | .draft-discard { | |
| 144 | + | cursor: pointer; | |
| 145 | + | color: var(--text-muted); | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | .draft-discard:hover { | |
| 149 | + | color: var(--danger); | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | /* REPLY FORM */ | |
| 153 | + | ||
| 154 | + | .reply-section { | |
| 155 | + | padding: 1rem; | |
| 156 | + | margin-top: 0.5rem; | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | .reply-section h3 { | |
| 160 | + | margin-bottom: 0.75rem; | |
| 161 | + | } | |
| 162 | + | ||
| 163 | + | /* PAGE HEADER (title + actions row) */ | |
| 164 | + | ||
| 165 | + | .page-header { | |
| 166 | + | display: flex; | |
| 167 | + | justify-content: space-between; | |
| 168 | + | align-items: center; | |
| 169 | + | margin-bottom: 0.5rem; | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | .page-header h1 { | |
| 173 | + | text-align: left; | |
| 174 | + | font-size: 1.5rem; | |
| 175 | + | margin: 0; | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | .page-header h2 { | |
| 179 | + | font-size: 1.2rem; | |
| 180 | + | margin: 0; | |
| 181 | + | } | |
| 182 | + |
| @@ -1,0 +1,523 @@ | |||
| 1 | + | /* TOAST NOTIFICATIONS */ | |
| 2 | + | ||
| 3 | + | .toast-container { | |
| 4 | + | position: fixed; | |
| 5 | + | bottom: 1.5rem; | |
| 6 | + | right: 1.5rem; | |
| 7 | + | z-index: 1000; | |
| 8 | + | display: flex; | |
| 9 | + | flex-direction: column; | |
| 10 | + | gap: 0.5rem; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | .toast { | |
| 14 | + | background: var(--primary-dark); | |
| 15 | + | color: var(--primary-light); | |
| 16 | + | padding: 0.6rem 1rem; | |
| 17 | + | font-family: "IBM Plex Mono", monospace; | |
| 18 | + | font-size: 0.85rem; | |
| 19 | + | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
| 20 | + | animation: toast-in 0.3s ease-out; | |
| 21 | + | max-width: 300px; | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | .toast-success { | |
| 25 | + | background: var(--success); | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | .toast-error { | |
| 29 | + | background: var(--error); | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | .toast-warning { | |
| 33 | + | background: var(--warning); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | .toast-info { | |
| 37 | + | background: var(--primary-dark); | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | .toast-retry-btn { | |
| 41 | + | background: none; | |
| 42 | + | border: 1px solid currentColor; | |
| 43 | + | color: inherit; | |
| 44 | + | font-family: "IBM Plex Mono", monospace; | |
| 45 | + | font-size: 0.75rem; | |
| 46 | + | font-weight: 600; | |
| 47 | + | padding: 0.1rem 0.4rem; | |
| 48 | + | margin-left: 0.75rem; | |
| 49 | + | cursor: pointer; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | .toast-retry-btn:hover { | |
| 53 | + | background: rgba(255, 255, 255, 0.15); | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | .toast.fade-out { | |
| 57 | + | animation: toast-out 0.3s ease-in forwards; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | @keyframes toast-in { | |
| 61 | + | from { transform: translateX(100%); } | |
| 62 | + | to { transform: translateX(0); } | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | @keyframes toast-out { | |
| 66 | + | from { transform: translateX(0); } | |
| 67 | + | to { transform: translateX(100%); } | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | /* SETTINGS PAGE */ | |
| 71 | + | ||
| 72 | + | .settings-section { | |
| 73 | + | margin-bottom: 2rem; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | .settings-section h3 { | |
| 77 | + | margin-bottom: 0.75rem; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | .settings-table { | |
| 81 | + | width: 100%; | |
| 82 | + | border-collapse: collapse; | |
| 83 | + | font-size: 0.85rem; | |
| 84 | + | font-family: "Lato", sans-serif; | |
| 85 | + | margin-bottom: 1rem; | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | .settings-table thead { | |
| 89 | + | background: var(--surface-muted); | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | .settings-table th { | |
| 93 | + | text-align: left; | |
| 94 | + | padding: 0.35rem 0.75rem; | |
| 95 | + | font-family: "IBM Plex Mono", monospace; | |
| 96 | + | font-size: 0.7rem; | |
| 97 | + | font-weight: normal; | |
| 98 | + | text-transform: uppercase; | |
| 99 | + | letter-spacing: 0.05em; | |
| 100 | + | color: var(--text-muted); | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | .settings-table tbody tr { | |
| 104 | + | transition: background 0.1s ease; | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | .settings-table tbody tr:nth-child(odd) { | |
| 108 | + | background: var(--surface-alt); | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | .settings-table tbody tr:nth-child(even) { | |
| 112 | + | background: var(--light-background); | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | .settings-table tbody tr:hover { | |
| 116 | + | background: var(--surface-muted); | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | .settings-table td { | |
| 120 | + | padding: 0.4rem 0.75rem; | |
| 121 | + | border-top: 1px solid var(--border); | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | .settings-cat-name { | |
| 125 | + | font-family: "Young Serif", serif; | |
| 126 | + | font-size: 1rem; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | .settings-cat-desc { | |
| 130 | + | display: block; | |
| 131 | + | font-size: 0.8rem; | |
| 132 | + | color: var(--text-muted); | |
| 133 | + | margin-top: 0.1rem; | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | .settings-mono { | |
| 137 | + | font-family: "IBM Plex Mono", monospace; | |
| 138 | + | font-size: 0.8rem; | |
| 139 | + | color: var(--text-muted); | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | .settings-actions { | |
| 143 | + | white-space: nowrap; | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | /* ACCESSIBILITY */ | |
| 147 | + | ||
| 148 | + | .skip-to-main { | |
| 149 | + | position: absolute; | |
| 150 | + | top: -100px; | |
| 151 | + | left: 0; | |
| 152 | + | background: var(--primary-dark); | |
| 153 | + | color: var(--primary-light); | |
| 154 | + | padding: 8px; | |
| 155 | + | z-index: 100; | |
| 156 | + | transition: top 0.2s ease; | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | .skip-to-main:focus { | |
| 160 | + | top: 0; | |
| 161 | + | } | |
| 162 | + | ||
| 163 | + | *:focus-visible { | |
| 164 | + | outline: 2px solid var(--focus-ring); | |
| 165 | + | outline-offset: 2px; | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | /* PAGINATION */ | |
| 169 | + | ||
| 170 | + | .pagination { | |
| 171 | + | display: flex; | |
| 172 | + | justify-content: center; | |
| 173 | + | align-items: center; | |
| 174 | + | gap: 1rem; | |
| 175 | + | padding: 0.75rem 0; | |
| 176 | + | font-family: "IBM Plex Mono", monospace; | |
| 177 | + | font-size: 0.85rem; | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | .pagination-link { | |
| 181 | + | color: var(--detail); | |
| 182 | + | text-decoration: none; | |
| 183 | + | padding: 0.3rem 0.6rem; | |
| 184 | + | border: 1px solid var(--border); | |
| 185 | + | background: var(--light-background); | |
| 186 | + | transition: background 0.2s ease; | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | .pagination-link:hover { | |
| 190 | + | background: var(--surface-muted); | |
| 191 | + | text-decoration: none; | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | .pagination-info { | |
| 195 | + | color: var(--text-muted); | |
| 196 | + | } | |
| 197 | + | ||
| 198 | + | /* ERROR PAGES */ | |
| 199 | + | ||
| 200 | + | .error-page { | |
| 201 | + | text-align: center; | |
| 202 | + | padding: 3rem 1rem; | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | .error-page h2 { | |
| 206 | + | font-family: "Young Serif", serif; | |
| 207 | + | font-size: 3rem; | |
| 208 | + | margin-bottom: 0.5rem; | |
| 209 | + | } | |
| 210 | + | ||
| 211 | + | .error-page p { | |
| 212 | + | color: var(--text-muted); | |
| 213 | + | margin-bottom: 1.5rem; | |
| 214 | + | } | |
| 215 | + | ||
| 216 | + | /* EMPTY STATE */ | |
| 217 | + | ||
| 218 | + | .empty-state { | |
| 219 | + | text-align: center; | |
| 220 | + | padding: 1rem; | |
| 221 | + | color: var(--text-muted); | |
| 222 | + | font-family: "IBM Plex Mono", monospace; | |
| 223 | + | } | |
| 224 | + | ||
| 225 | + | /* SITE FOOTER */ | |
| 226 | + | ||
| 227 | + | .site-footer { | |
| 228 | + | text-align: center; | |
| 229 | + | padding: 1rem; | |
| 230 | + | margin-top: 1rem; | |
| 231 | + | border-top: 1px solid var(--border); | |
| 232 | + | font-family: "IBM Plex Mono", monospace; | |
| 233 | + | font-size: 0.75rem; | |
| 234 | + | color: var(--text-muted); | |
| 235 | + | } | |
| 236 | + | ||
| 237 | + | .site-footer a { | |
| 238 | + | color: var(--detail); | |
| 239 | + | text-decoration: none; | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | .site-footer a:hover { | |
| 243 | + | text-decoration: underline; | |
| 244 | + | } | |
| 245 | + | ||
| 246 | + | .site-footer-links { | |
| 247 | + | margin-bottom: 0.5rem; | |
| 248 | + | } | |
| 249 | + | ||
| 250 | + | .site-footer-links a { | |
| 251 | + | margin: 0 0.75rem; | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | .footer-sep { | |
| 255 | + | margin: 0 0.4rem; | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | /* FOOTNOTES + IMMUTABLE POSTS */ | |
| 259 | + | ||
| 260 | + | .post-footnotes { | |
| 261 | + | margin-top: 0.75rem; | |
| 262 | + | padding-top: 0.5rem; | |
| 263 | + | border-top: 1px solid var(--border); | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | .footnote { | |
| 267 | + | font-size: 0.85rem; | |
| 268 | + | color: var(--text-muted); | |
| 269 | + | margin-bottom: 0.5rem; | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | .footnote p { | |
| 273 | + | margin: 0.15rem 0; | |
| 274 | + | } | |
| 275 | + | ||
| 276 | + | .footnote-prefix { | |
| 277 | + | font-family: "IBM Plex Mono", monospace; | |
| 278 | + | font-size: 0.75rem; | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | .footnote-form-toggle { | |
| 282 | + | margin-top: 0.5rem; | |
| 283 | + | } | |
| 284 | + | ||
| 285 | + | .footnote-form-toggle textarea { | |
| 286 | + | width: 100%; | |
| 287 | + | min-height: 60px; | |
| 288 | + | margin-top: 0.5rem; | |
| 289 | + | } | |
| 290 | + | ||
| 291 | + | .badge-unread { | |
| 292 | + | background: var(--highlight); | |
| 293 | + | color: var(--primary-light); | |
| 294 | + | font-size: 0.75rem; | |
| 295 | + | padding: 0.1rem 0.4rem; | |
| 296 | + | border-radius: 3px; | |
| 297 | + | font-family: "IBM Plex Mono", monospace; | |
| 298 | + | } | |
| 299 | + | ||
| 300 | + | tr.unread .thread-title a { | |
| 301 | + | font-weight: 700; | |
| 302 | + | } | |
| 303 | + | ||
| 304 | + | tr.unread .thread-title::before { | |
| 305 | + | content: ""; | |
| 306 | + | display: inline-block; | |
| 307 | + | width: 6px; | |
| 308 | + | height: 6px; | |
| 309 | + | background: var(--highlight); | |
| 310 | + | border-radius: 50%; | |
| 311 | + | margin-right: 0.35rem; | |
| 312 | + | vertical-align: middle; | |
| 313 | + | } | |
| 314 | + | ||
| 315 | + | /* Mention indicator on thread listings */ | |
| 316 | + | .badge-mention { | |
| 317 | + | color: var(--highlight); | |
| 318 | + | font-size: 0.75rem; | |
| 319 | + | font-weight: 700; | |
| 320 | + | font-family: "IBM Plex Mono", monospace; | |
| 321 | + | background: none; | |
| 322 | + | border: none; | |
| 323 | + | padding: 0; | |
| 324 | + | } | |
| 325 | + | ||
| 326 | + | tr.mentioned { | |
| 327 | + | border-left: 3px solid var(--highlight); | |
| 328 | + | } | |
| 329 | + | ||
| 330 | + | /* Tracking settings */ | |
| 331 | + | .tracking-settings { | |
| 332 | + | display: flex; | |
| 333 | + | align-items: center; | |
| 334 | + | gap: 0.75rem; | |
| 335 | + | margin-bottom: 1rem; | |
| 336 | + | font-size: 0.85rem; | |
| 337 | + | } | |
| 338 | + | ||
| 339 | + | .toggle-label { | |
| 340 | + | display: flex; | |
| 341 | + | align-items: center; | |
| 342 | + | gap: 0.4rem; | |
| 343 | + | cursor: pointer; | |
| 344 | + | } | |
| 345 | + | ||
| 346 | + | .toggle-checkbox { | |
| 347 | + | accent-color: var(--highlight); | |
| 348 | + | } | |
| 349 | + | ||
| 350 | + | /* Info/static page sections */ | |
| 351 | + | .info-section h3 { | |
| 352 | + | margin-top: 1.5rem; | |
| 353 | + | margin-bottom: 0.5rem; | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | .info-section ul { | |
| 357 | + | padding-left: 1.5rem; | |
| 358 | + | margin-bottom: 0.75rem; | |
| 359 | + | } | |
| 360 | + | ||
| 361 | + | .info-section li { | |
| 362 | + | margin-bottom: 0.3rem; | |
| 363 | + | line-height: 1.5; | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | .tag-filter { | |
| 367 | + | margin-bottom: 1rem; | |
| 368 | + | display: flex; | |
| 369 | + | flex-wrap: wrap; | |
| 370 | + | align-items: center; | |
| 371 | + | gap: 0.35rem; | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | .tag-filter-label { | |
| 375 | + | font-family: "IBM Plex Mono", monospace; | |
| 376 | + | font-size: 0.8rem; | |
| 377 | + | color: var(--text-muted); | |
| 378 | + | margin-right: 0.25rem; | |
| 379 | + | } | |
| 380 | + | ||
| 381 | + | .tag-chip { | |
| 382 | + | display: inline-block; | |
| 383 | + | padding: 0.15rem 0.5rem; | |
| 384 | + | font-size: 0.8rem; | |
| 385 | + | border: 1px solid var(--border); | |
| 386 | + | border-radius: 3px; | |
| 387 | + | text-decoration: none; | |
| 388 | + | color: var(--text-primary); | |
| 389 | + | } | |
| 390 | + | ||
| 391 | + | .tag-chip:hover { | |
| 392 | + | background: var(--surface-muted); | |
| 393 | + | } | |
| 394 | + | ||
| 395 | + | .tag-chip.tag-active { | |
| 396 | + | background: var(--primary-dark); | |
| 397 | + | color: var(--primary-light); | |
| 398 | + | border-color: var(--primary-dark); | |
| 399 | + | } | |
| 400 | + | ||
| 401 | + | .tag-badge { | |
| 402 | + | display: inline-block; | |
| 403 | + | font-size: 0.7rem; | |
| 404 | + | padding: 0.05rem 0.35rem; | |
| 405 | + | background: var(--surface-muted); | |
| 406 | + | border-radius: 2px; | |
| 407 | + | margin-left: 0.25rem; | |
| 408 | + | vertical-align: middle; | |
| 409 | + | font-family: "IBM Plex Mono", monospace; | |
| 410 | + | color: var(--text-muted); | |
| 411 | + | } | |
| 412 | + | ||
| 413 | + | .tag-fieldset { | |
| 414 | + | border: none; | |
| 415 | + | padding: 0; | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | .tag-fieldset legend { | |
| 419 | + | font-weight: 600; | |
| 420 | + | margin-bottom: 0.35rem; | |
| 421 | + | } | |
| 422 | + | ||
| 423 | + | .tag-checkbox { | |
| 424 | + | display: inline-block; | |
| 425 | + | margin-right: 1rem; | |
| 426 | + | font-size: 0.9rem; | |
| 427 | + | cursor: pointer; | |
| 428 | + | } | |
| 429 | + | ||
| 430 | + | .flag-form-toggle { | |
| 431 | + | margin-top: 0.5rem; | |
| 432 | + | } | |
| 433 | + | ||
| 434 | + | .flag-form { | |
| 435 | + | margin-top: 0.5rem; | |
| 436 | + | display: flex; | |
| 437 | + | flex-direction: column; | |
| 438 | + | gap: 0.35rem; | |
| 439 | + | } | |
| 440 | + | ||
| 441 | + | .flag-form label { | |
| 442 | + | font-size: 0.85rem; | |
| 443 | + | cursor: pointer; | |
| 444 | + | } | |
| 445 | + | ||
| 446 | + | .flag-form textarea { | |
| 447 | + | width: 100%; | |
| 448 | + | min-height: 40px; | |
| 449 | + | margin-top: 0.25rem; | |
| 450 | + | } | |
| 451 | + | ||
| 452 | + | .flag-btn { | |
| 453 | + | align-self: flex-start; | |
| 454 | + | font-size: 0.8rem; | |
| 455 | + | padding: 0.25rem 0.75rem; | |
| 456 | + | } | |
| 457 | + | ||
| 458 | + | .post-removed .post-body { | |
| 459 | + | color: var(--text-muted); | |
| 460 | + | font-style: italic; | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | .quote-attribution { | |
| 464 | + | display: block; | |
| 465 | + | font-size: 0.8rem; | |
| 466 | + | font-family: "IBM Plex Mono", monospace; | |
| 467 | + | color: var(--text-muted); | |
| 468 | + | margin-top: 0.25rem; | |
| 469 | + | font-style: normal; | |
| 470 | + | } | |
| 471 | + | ||
| 472 | + | .quote-attribution a { | |
| 473 | + | color: var(--text-muted); | |
| 474 | + | text-decoration: none; | |
| 475 | + | } | |
| 476 | + | ||
| 477 | + | .quote-attribution a:hover { | |
| 478 | + | text-decoration: underline; | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | .quote-btn { | |
| 482 | + | position: absolute; | |
| 483 | + | z-index: 100; | |
| 484 | + | background: var(--surface); | |
| 485 | + | border: 1px solid var(--border); | |
| 486 | + | padding: 0.25rem 0.5rem; | |
| 487 | + | font-size: 0.75rem; | |
| 488 | + | font-family: "IBM Plex Mono", monospace; | |
| 489 | + | cursor: pointer; | |
| 490 | + | } | |
| 491 | + | ||
| 492 | + | .quote-btn:hover { | |
| 493 | + | background: var(--light-background); | |
| 494 | + | } | |
| 495 | + | ||
| 496 | + | /* UTILITY CLASSES */ | |
| 497 | + | ||
| 498 | + | .form-inline { display: inline; } | |
| 499 | + | .form-inline-row { display: inline-flex; gap: 0.25rem; flex-direction: row; align-items: center; } | |
| 500 | + | .btn-secondary { text-decoration: none; } |
Lines truncated
| @@ -1,0 +1,195 @@ | |||
| 1 | + | /* LINK PREVIEWS */ | |
| 2 | + | ||
| 3 | + | .post-link-previews { | |
| 4 | + | margin-top: 0.75rem; | |
| 5 | + | display: flex; | |
| 6 | + | flex-direction: column; | |
| 7 | + | gap: 0.5rem; | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | .link-preview-card { | |
| 11 | + | display: block; | |
| 12 | + | padding: 0.5rem 0.75rem; | |
| 13 | + | border: 1px solid var(--border); | |
| 14 | + | background: var(--light-background); | |
| 15 | + | text-decoration: none; | |
| 16 | + | color: var(--detail); | |
| 17 | + | transition: background 0.2s ease; | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | .link-preview-card:hover { | |
| 21 | + | background: var(--surface-muted); | |
| 22 | + | text-decoration: none; | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | .lp-title { | |
| 26 | + | display: block; | |
| 27 | + | font-family: "IBM Plex Mono", monospace; | |
| 28 | + | font-size: 0.85rem; | |
| 29 | + | font-weight: bold; | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | .lp-desc { | |
| 33 | + | display: block; | |
| 34 | + | font-size: 0.8rem; | |
| 35 | + | color: var(--text-muted); | |
| 36 | + | margin-top: 0.15rem; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | .lp-url { | |
| 40 | + | display: block; | |
| 41 | + | font-size: 0.7rem; | |
| 42 | + | font-family: "IBM Plex Mono", monospace; | |
| 43 | + | color: var(--text-muted); | |
| 44 | + | margin-top: 0.15rem; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | /* SEARCH MODAL */ | |
| 48 | + | ||
| 49 | + | .search-toggle { | |
| 50 | + | font-family: "IBM Plex Mono", monospace; | |
| 51 | + | font-size: 0.85rem; | |
| 52 | + | color: var(--text-muted); | |
| 53 | + | background: none; | |
| 54 | + | border: 1px solid var(--border); | |
| 55 | + | padding: 0.25rem 0.75rem; | |
| 56 | + | cursor: pointer; | |
| 57 | + | transition: opacity 0.2s ease; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | .search-toggle:hover { | |
| 61 | + | opacity: 0.7; | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | .search-modal { | |
| 65 | + | position: fixed; | |
| 66 | + | top: 0; | |
| 67 | + | left: 0; | |
| 68 | + | right: 0; | |
| 69 | + | bottom: 0; | |
| 70 | + | z-index: 200; | |
| 71 | + | display: flex; | |
| 72 | + | justify-content: center; | |
| 73 | + | padding-top: 10vh; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | .search-modal[hidden] { | |
| 77 | + | display: none; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | .search-modal-backdrop { | |
| 81 | + | position: fixed; | |
| 82 | + | top: 0; | |
| 83 | + | left: 0; | |
| 84 | + | right: 0; | |
| 85 | + | bottom: 0; | |
| 86 | + | background: rgba(0, 0, 0, 0.3); | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | .search-modal-content { | |
| 90 | + | position: relative; | |
| 91 | + | width: 100%; | |
| 92 | + | max-width: 600px; | |
| 93 | + | max-height: 70vh; | |
| 94 | + | display: flex; | |
| 95 | + | flex-direction: column; | |
| 96 | + | background: var(--light-background); | |
| 97 | + | border: 1px solid var(--border); | |
| 98 | + | box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); | |
| 99 | + | z-index: 1; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | .search-input { | |
| 103 | + | width: 100%; | |
| 104 | + | padding: 0.75rem 1rem; | |
| 105 | + | font-size: 1rem; | |
| 106 | + | font-family: "IBM Plex Mono", monospace; | |
| 107 | + | background: var(--background); | |
| 108 | + | border: none; | |
| 109 | + | border-bottom: 1px solid var(--border); | |
| 110 | + | color: var(--detail); | |
| 111 | + | outline: none; | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | .search-input:focus { | |
| 115 | + | border-bottom-color: var(--highlight); | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | .search-results-container { | |
| 119 | + | overflow-y: auto; | |
| 120 | + | max-height: calc(70vh - 3rem); | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | .search-results { | |
| 124 | + | list-style: none; | |
| 125 | + | margin: 0; | |
| 126 | + | padding: 0; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | .search-result { | |
| 130 | + | border-bottom: 1px solid var(--border); | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | .search-result:last-child { | |
| 134 | + | border-bottom: none; | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | .search-result-link { | |
| 138 | + | display: block; | |
| 139 | + | padding: 0.6rem 1rem; | |
| 140 | + | text-decoration: none; | |
| 141 | + | color: var(--detail); | |
| 142 | + | transition: background 0.1s ease; | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | .search-result-link:hover, | |
| 146 | + | .search-result.search-active .search-result-link { | |
| 147 | + | background: var(--surface-muted); | |
| 148 | + | text-decoration: none; | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | .search-result-title { | |
| 152 | + | display: block; | |
| 153 | + | font-weight: bold; | |
| 154 | + | font-size: 0.9rem; | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | .search-result-meta { | |
| 158 | + | display: block; | |
| 159 | + | font-family: "IBM Plex Mono", monospace; | |
| 160 | + | font-size: 0.7rem; | |
| 161 | + | color: var(--text-muted); | |
| 162 | + | margin-top: 0.15rem; | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | .search-result-snippet { | |
| 166 | + | display: block; | |
| 167 | + | font-size: 0.8rem; | |
| 168 | + | color: var(--text-muted); | |
| 169 | + | margin-top: 0.15rem; | |
| 170 | + | overflow: hidden; | |
| 171 | + | text-overflow: ellipsis; | |
| 172 | + | white-space: nowrap; | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | .search-empty { | |
| 176 | + | padding: 1.5rem 1rem; | |
| 177 | + | text-align: center; | |
| 178 | + | color: var(--text-muted); | |
| 179 | + | font-family: "IBM Plex Mono", monospace; | |
| 180 | + | font-size: 0.85rem; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | /* POST ENDORSEMENTS */ | |
| 184 | + | ||
| 185 | + | .endorsed { | |
| 186 | + | color: var(--highlight); | |
| 187 | + | font-weight: bold; | |
| 188 | + | } | |
| 189 | + | ||
| 190 | + | .endorsement-count { | |
| 191 | + | color: var(--detail); | |
| 192 | + | font-size: 0.85rem; | |
| 193 | + | margin-left: 0.25rem; | |
| 194 | + | } | |
| 195 | + |
| @@ -1,0 +1,145 @@ | |||
| 1 | + | /* CHAT ROOM */ | |
| 2 | + | ||
| 3 | + | .chat-room { | |
| 4 | + | display: flex; | |
| 5 | + | flex-direction: column; | |
| 6 | + | border: 1px solid var(--border); | |
| 7 | + | background: var(--light-background); | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | /* The log scrolls, the composer does not. A fixed height rather than a growing | |
| 11 | + | page is what lets the island keep the newest message in view without moving | |
| 12 | + | the thing the reader is typing into. */ | |
| 13 | + | .chat-log { | |
| 14 | + | list-style: none; | |
| 15 | + | margin: 0; | |
| 16 | + | padding: 0.5rem; | |
| 17 | + | height: 60vh; | |
| 18 | + | min-height: 15rem; | |
| 19 | + | overflow-y: auto; | |
| 20 | + | overscroll-behavior: contain; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | .chat-message { | |
| 24 | + | display: flex; | |
| 25 | + | align-items: baseline; | |
| 26 | + | gap: 0.4rem; | |
| 27 | + | padding: 0.25rem 0.35rem; | |
| 28 | + | line-height: 1.45; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | .chat-message:hover { | |
| 32 | + | background: var(--surface-alt); | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | .chat-avatar { | |
| 36 | + | align-self: center; | |
| 37 | + | border-radius: 50%; | |
| 38 | + | flex-shrink: 0; | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | .chat-author { | |
| 42 | + | font-weight: bold; | |
| 43 | + | color: var(--detail); | |
| 44 | + | white-space: nowrap; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | .chat-time { | |
| 48 | + | font-family: "IBM Plex Mono", monospace; | |
| 49 | + | font-size: 0.75rem; | |
| 50 | + | color: var(--text-muted); | |
| 51 | + | white-space: nowrap; | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | .chat-body { | |
| 55 | + | flex: 1; | |
| 56 | + | min-width: 0; | |
| 57 | + | overflow-wrap: anywhere; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | /* Sent, not yet echoed back by the server. Dimmed rather than hidden: the | |
| 61 | + | message is on screen the instant it is typed, and the dimming is the only | |
| 62 | + | thing saying it is not confirmed. */ | |
| 63 | + | .chat-message.is-pending { | |
| 64 | + | opacity: 0.55; | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | .chat-message.is-failed { | |
| 68 | + | opacity: 1; | |
| 69 | + | border-left: 2px solid var(--danger); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | /* Someone named this reader. A tint and a rule rather than anything louder: | |
| 73 | + | chat scrolls, the highlight has to survive being one of forty messages, and | |
| 74 | + | there is deliberately no notification behind it. */ | |
| 75 | + | .chat-message.is-mention { | |
| 76 | + | background: var(--surface-alt); | |
| 77 | + | border-left: 2px solid var(--highlight); | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | .chat-error { | |
| 81 | + | color: var(--danger); | |
| 82 | + | font-size: 0.85rem; | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | /* Delete, and the retry on a failed send. Both stay out of the way until the | |
| 86 | + | message is under the pointer, and both are always reachable by keyboard. */ | |
| 87 | + | .chat-remove, | |
| 88 | + | .chat-retry { | |
| 89 | + | background: none; | |
| 90 | + | border: 1px solid transparent; | |
| 91 | + | color: var(--text-muted); | |
| 92 | + | cursor: pointer; | |
| 93 | + | font-family: "IBM Plex Mono", monospace; | |
| 94 | + | font-size: 0.75rem; | |
| 95 | + | padding: 0 0.3rem; | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | .chat-remove { | |
| 99 | + | opacity: 0; | |
| 100 | + | margin-left: auto; | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | .chat-message:hover .chat-remove, | |
| 104 | + | .chat-remove:focus-visible { | |
| 105 | + | opacity: 1; | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | .chat-remove:hover, | |
| 109 | + | .chat-retry:hover { | |
| 110 | + | color: var(--danger); | |
| 111 | + | border-color: var(--border); | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | .chat-status { | |
| 115 | + | margin: 0; | |
| 116 | + | padding: 0.35rem 0.6rem; | |
| 117 | + | border-top: 1px solid var(--border); | |
| 118 | + | background: var(--surface-alt); | |
| 119 | + | color: var(--text-muted); | |
| 120 | + | font-size: 0.85rem; | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | .chat-composer { | |
| 124 | + | display: flex; | |
| 125 | + | gap: 0.5rem; | |
| 126 | + | padding: 0.5rem; | |
| 127 | + | border-top: 1px solid var(--border); | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | .chat-composer input[type="text"] { | |
| 131 | + | flex: 1; | |
| 132 | + | min-width: 0; | |
| 133 | + | background: var(--input-background); | |
| 134 | + | border: 1px solid var(--border); | |
| 135 | + | color: var(--detail); | |
| 136 | + | padding: 0.45rem 0.6rem; | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | .chat-notice { | |
| 140 | + | padding: 0.6rem; | |
| 141 | + | border-top: 1px solid var(--border); | |
| 142 | + | color: var(--text-muted); | |
| 143 | + | font-size: 0.9rem; | |
| 144 | + | } | |
| 145 | + |
| @@ -1,0 +1,133 @@ | |||
| 1 | + | /* RESPONSIVE, 768px (tablet) */ | |
| 2 | + | ||
| 3 | + | @media (max-width: 768px) { | |
| 4 | + | .container { | |
| 5 | + | padding: 0.75rem; | |
| 6 | + | } | |
| 7 | + | ||
| 8 | + | .padded-page { | |
| 9 | + | padding: 0.75rem; | |
| 10 | + | } | |
| 11 | + | ||
| 12 | + | .form-row { | |
| 13 | + | grid-template-columns: 1fr; | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | .data-table th, | |
| 17 | + | .data-table td { | |
| 18 | + | padding: 0.35rem 0.5rem; | |
| 19 | + | font-size: 0.8rem; | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | .directory-table th, | |
| 23 | + | .directory-table td { | |
| 24 | + | padding: 0.35rem 0.5rem; | |
| 25 | + | font-size: 0.8rem; | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | .page-header { | |
| 29 | + | flex-direction: column; | |
| 30 | + | align-items: flex-start; | |
| 31 | + | gap: 0.5rem; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | /* Hamburger menu */ | |
| 35 | + | .nav-toggle-label { | |
| 36 | + | display: block; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | .site-header nav { | |
| 40 | + | display: none; | |
| 41 | + | position: absolute; | |
| 42 | + | top: 100%; | |
| 43 | + | left: 0; | |
| 44 | + | right: 0; | |
| 45 | + | background: var(--background); | |
| 46 | + | border-bottom: 1px solid var(--border); | |
| 47 | + | padding: 0.75rem 1.25rem; | |
| 48 | + | z-index: 10; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | .site-header { | |
| 52 | + | position: relative; | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | .nav-toggle-checkbox:checked ~ nav { | |
| 56 | + | display: block; | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | .site-header .nav-links { | |
| 60 | + | flex-direction: column; | |
| 61 | + | gap: 0.75rem; | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | /* Animate hamburger to X */ | |
| 65 | + | .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(1) { | |
| 66 | + | transform: rotate(45deg) translate(5px, 5px); | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(2) { | |
| 70 | + | opacity: 0; | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | .nav-toggle-checkbox:checked ~ .nav-toggle-label span:nth-child(3) { | |
| 74 | + | transform: rotate(-45deg) translate(5px, -5px); | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | /* Hide less important columns on mobile */ | |
| 78 | + | .data-table .col-author { | |
| 79 | + | display: none; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | .directory-table .col-type, | |
| 83 | + | .directory-table .col-items { | |
| 84 | + | display: none; | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | .col-replies { | |
| 88 | + | width: auto; | |
| 89 | + | } | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | /* RESPONSIVE, 480px (phone) */ | |
| 93 | + | ||
| 94 | + | @media (max-width: 480px) { | |
| 95 | + | .container { | |
| 96 | + | padding: 0.5rem; | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | .padded-page { | |
| 100 | + | padding: 0.5rem; | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | h1 { | |
| 104 | + | font-size: 1.6rem; | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | .toast-container { | |
| 108 | + | left: 0.75rem; | |
| 109 | + | right: 0.75rem; | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | .toast { | |
| 113 | + | max-width: 100%; | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | .post-header { | |
| 117 | + | flex-direction: column; | |
| 118 | + | gap: 0.2rem; | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | /* The composer and the on-screen keyboard together take most of a phone, so | |
| 122 | + | the log gives up height rather than pushing the input off the bottom. */ | |
| 123 | + | .chat-log { | |
| 124 | + | height: 55vh; | |
| 125 | + | padding: 0.35rem; | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | /* No hover on a touch screen, so a delete control that only appears on hover | |
| 129 | + | is a control that does not exist. */ | |
| 130 | + | .chat-remove { | |
| 131 | + | opacity: 1; | |
| 132 | + | } | |
| 133 | + | } |
| @@ -1,0 +1,48 @@ | |||
| 1 | + | # The site stylesheet | |
| 2 | + | ||
| 3 | + | `static/style.css` is generated. It is the join of the files here, in name | |
| 4 | + | order, produced by `build.rs` and committed. | |
| 5 | + | ||
| 6 | + | Committed rather than gitignored because 1,900 lines of annotated CSS keep their | |
| 7 | + | `git blame` that way, and because `src/static_assets.rs` embeds `static/` | |
| 8 | + | wholesale with `include_dir!`, so the built sheet has to exist on disk at compile | |
| 9 | + | time. Edit a part, never the output. | |
| 10 | + | ||
| 11 | + | The parts live here rather than under `static/` for the same `include_dir!` | |
| 12 | + | reason: anything under `static/` ships, so parts kept there would be served as | |
| 13 | + | well as embedded. | |
| 14 | + | ||
| 15 | + | This sheet uses no `@layer`, so the join is the whole of it and every file here | |
| 16 | + | is standalone-valid CSS on its own. | |
| 17 | + | ||
| 18 | + | ## The parts | |
| 19 | + | ||
| 20 | + | | Part | What it holds | | |
| 21 | + | |---|---| | |
| 22 | + | | `00-foundations.css` | The five `@font-face` blocks, the `:root` palette, reset, base, signature dot, layout. The makeover conversion's whole target: everything it replaces is in this file and nothing else is | | |
| 23 | + | | `10-chrome.css` | Site header and nav, hamburger toggle | | |
| 24 | + | | `20-controls.css` | Buttons, forms | | |
| 25 | + | | `30-listings.css` | Directory and data tables, thread and member columns, pinned rows, badges, breadcrumbs. Badges and breadcrumbs sit here because every one of them is worn by a row in those tables | | |
| 26 | + | | `40-threads.css` | Post item, post actions, deleted posts, OP indicator, draft indicator, reply form, page header | | |
| 27 | + | | `50-furniture.css` | Toast, settings, accessibility, pagination, error pages, empty state, footer, footnotes, immutable posts, mentions, tracking settings, info pages, utility classes. A grab bag of thirteen small sections, none worth its own file | | |
| 28 | + | | `60-search.css` | Link previews, search modal, post endorsements | | |
| 29 | + | | `70-chat.css` | The chat room: scrolling log against a non-growing composer, dimmed un-echoed send, named-reader tint, delete and failed-send retry | | |
| 30 | + | | `90-responsive.css` | The 768px and 480px blocks | | |
| 31 | + | ||
| 32 | + | `90-responsive.css` stays one file. Distributing the two blocks into the feature | |
| 33 | + | files is legal, but each reaches across six of the eight other parts, so it means | |
| 34 | + | splitting each into six fragments and re-proving order for every one, to end up | |
| 35 | + | with eight files instead of nine. Both breakpoints are also off the makeover size | |
| 36 | + | classes (768/480 against the server's 839/599), and this is where that gets | |
| 37 | + | reconciled when the conversion lands. | |
| 38 | + | ||
| 39 | + | ## Contiguity is the guarantee | |
| 40 | + | ||
| 41 | + | Every part is a contiguous line range of the single file this used to be, and | |
| 42 | + | they load in the original order, so the join is byte-identical and the split | |
| 43 | + | changed no rendering. | |
| 44 | + | ||
| 45 | + | That also says what a safe edit is. CSS resolves ties by source order, so moving | |
| 46 | + | a rule between parts can change which of two equal-specificity rules wins. A move | |
| 47 | + | earlier in source order needs its own tie check and its own commit; adding a rule | |
| 48 | + | to the part it belongs in needs neither. |