| @@ -0,0 +1,308 @@ | |||
| 1 | + | /* | |
| 2 | + | * Dark theme for the Zammad public Knowledge Base. | |
| 3 | + | * | |
| 4 | + | * Install per https://next.zammad.org/en/tutorials/custom-kb-css.html: | |
| 5 | + | * 1. Copy this file into app/assets/stylesheets/custom_knowledge_base_public/ | |
| 6 | + | * 2. rake assets:precompile | |
| 7 | + | * 3. Restart Zammad | |
| 8 | + | * 4. View the *published* KB (not the admin editor) to see the result. | |
| 9 | + | * | |
| 10 | + | * Built against the markup/CSS served at /help/ (Zammad's default | |
| 11 | + | * "knowledge_base" stylesheet). No !important needed: Zammad emits | |
| 12 | + | * app/assets/stylesheets/custom_knowledge_base_public/* as its own | |
| 13 | + | * <link> (knowledge_base_custom-*.css), placed after both the base | |
| 14 | + | * knowledge_base-*.css and the inline branding <style> block in | |
| 15 | + | * <head> — verified on the live instance. Plain cascade order wins, | |
| 16 | + | * which also means more specific rules Zammad already ships (e.g. | |
| 17 | + | * ".article-nav-adjacent a") still take precedence over this file's | |
| 18 | + | * broader selectors, as intended. | |
| 19 | + | * | |
| 20 | + | * This stylesheet does NOT overwrite your custom colors from the KB management. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | :root { | |
| 24 | + | --kb-bg: hsl(220, 16%, 9%); /* page background */ | |
| 25 | + | --kb-bg-alt: hsl(220, 15%, 13%); /* header, footer, hover surfaces */ | |
| 26 | + | --kb-surface: hsl(220, 13%, 16%); /* cards, inputs, dropdowns, code blocks */ | |
| 27 | + | --kb-surface-hover: hsl(220, 12%, 21%); | |
| 28 | + | --kb-border: hsl(220, 10%, 24%); | |
| 29 | + | --kb-text: hsl(210, 20%, 92%); /* primary text / headings */ | |
| 30 | + | --kb-text-secondary: hsl(210, 12%, 75%); | |
| 31 | + | --kb-text-muted: hsl(210, 10%, 58%); | |
| 32 | + | --kb-accent: #3ecb82; /* text-selection highlight only; the real link/icon accent is admin-configured */ | |
| 33 | + | --kb-shadow: rgba(0, 0, 0, 0.55); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | /* ---------- Base ---------- */ | |
| 37 | + | ||
| 38 | + | html { | |
| 39 | + | background: var(--kb-bg); | |
| 40 | + | color: var(--kb-text-secondary); | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | body { | |
| 44 | + | background: var(--kb-bg); | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | ::selection { | |
| 48 | + | background: var(--kb-accent); | |
| 49 | + | color: hsl(220, 16%, 9%); | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | /* ---------- Header & search ---------- */ | |
| 53 | + | ||
| 54 | + | .header { | |
| 55 | + | border-bottom: 1px solid var(--kb-border); | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | .search-field input[type="search"] { | |
| 59 | + | background: var(--kb-surface); | |
| 60 | + | border: 1px solid var(--kb-border); | |
| 61 | + | color: var(--kb-text); | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | .search-field input[type="search"]::placeholder { | |
| 65 | + | color: var(--kb-text-muted); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | .search-field .icon { | |
| 69 | + | fill: var(--kb-text-muted); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | /* :not(:empty) matters: the base theme never boxes this <ul> — it's | |
| 73 | + | present but invisible until JS populates it with results/a message. | |
| 74 | + | An unconditional border/background here would show as a stray bar | |
| 75 | + | under the search field on every page load, even with no query. */ | |
| 76 | + | .search-results:not(:empty) { | |
| 77 | + | background: var(--kb-surface); | |
| 78 | + | border: 1px solid var(--kb-border); | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | .search-message { | |
| 82 | + | color: var(--kb-text-muted); | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | .result a:hover { | |
| 86 | + | border-color: var(--kb-border); | |
| 87 | + | background: var(--kb-surface-hover); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | .result-title { | |
| 91 | + | color: var(--kb-text); | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | .result-category, | |
| 95 | + | .result-preview { | |
| 96 | + | color: var(--kb-text-muted); | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | /* ---------- Breadcrumbs ---------- */ | |
| 100 | + | ||
| 101 | + | .breadcrumbs .container { | |
| 102 | + | border-bottom: 1px solid var(--kb-border); | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | .breadcrumb { | |
| 106 | + | color: var(--kb-text-secondary); | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | .breadcrumb[href]:hover { | |
| 110 | + | color: var(--kb-text); | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | .breadcrumb::after, | |
| 114 | + | .breadcrumb::before { | |
| 115 | + | background: var(--kb-text-muted); | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | /* ---------- Category / article listing cards ---------- */ | |
| 119 | + | ||
| 120 | + | .main--categories h1, | |
| 121 | + | .main--article h1, | |
| 122 | + | h1, h2, h3, h4, h5, h6 { | |
| 123 | + | color: var(--kb-text); | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | .main--categories h1 .icon-hashtag, | |
| 127 | + | .article-accessories .icon { | |
| 128 | + | fill: var(--kb-text-muted); | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | .feedback, | |
| 132 | + | .button, | |
| 133 | + | .sections--grid .section-inner, | |
| 134 | + | .sections--list .section-inner, | |
| 135 | + | .sections--compact-text .section-inner, | |
| 136 | + | .box { | |
| 137 | + | background: var(--kb-surface); | |
| 138 | + | color: var(--kb-text-secondary); | |
| 139 | + | box-shadow: 0 2px 10px var(--kb-shadow); | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | .sections--list .section-inner:hover, | |
| 143 | + | .sections--grid .section-inner:hover, | |
| 144 | + | .sections--compact-text .section-inner:hover { | |
| 145 | + | background: var(--kb-surface-hover); | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | .section-category { | |
| 149 | + | color: var(--kb-text-muted); | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | .sections-empty { | |
| 153 | + | color: var(--kb-text-muted); | |
| 154 | + | border: 1px dashed var(--kb-border); | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | .sections + hr { | |
| 158 | + | border-top: 1px solid var(--kb-border); | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | .not-published-note { | |
| 162 | + | color: var(--kb-text-muted); | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | /* ---------- Article content ---------- */ | |
| 166 | + | ||
| 167 | + | .article-content { | |
| 168 | + | color: var(--kb-text-secondary); | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | .article-meta { | |
| 172 | + | color: var(--kb-text-muted); | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | .article-content table { | |
| 176 | + | box-shadow: 0 0 0 1px var(--kb-border), 0 2px var(--kb-border); | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | .article-content td, | |
| 180 | + | .article-content th { | |
| 181 | + | border: 1px solid var(--kb-border); | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | .article-content th { | |
| 185 | + | background: var(--kb-surface); | |
| 186 | + | color: var(--kb-text); | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | .article-content blockquote { | |
| 190 | + | border-left: 5px solid var(--kb-border); | |
| 191 | + | color: var(--kb-text-secondary); | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | .article-content code, | |
| 195 | + | .article-content pre { | |
| 196 | + | background: var(--kb-surface); | |
| 197 | + | color: var(--kb-text); | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | .article-content hr { | |
| 201 | + | border-top: 1px solid var(--kb-border); | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | /* Zammad's own .article-nav-adjacent a is already a subtle gray — left alone. | |
| 205 | + | Its :hover darkens toward near-black for a light page, which would go | |
| 206 | + | near-invisible on a dark one, so that part alone needs a dark-mode value. */ | |
| 207 | + | .article-nav-adjacent a:hover { | |
| 208 | + | color: var(--kb-text); | |
| 209 | + | fill: var(--kb-text); | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | /* Photos/screenshots keep their real colors; just soften pure-white glare a touch */ | |
| 213 | + | .article-content img { | |
| 214 | + | filter: brightness(0.92); | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | /* ---------- Attachments & tags ---------- */ | |
| 218 | + | ||
| 219 | + | .attachment { | |
| 220 | + | border-bottom: 1px solid var(--kb-border); | |
| 221 | + | color: var(--kb-text-secondary); | |
| 222 | + | } | |
| 223 | + | ||
| 224 | + | .attachment:hover { | |
| 225 | + | background: var(--kb-surface-hover); | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | .attachment-size { | |
| 229 | + | color: var(--kb-text-muted); | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | /* ---------- Image lightbox preview ---------- */ | |
| 233 | + | ||
| 234 | + | .kb-image-preview { | |
| 235 | + | background: hsla(220, 16%, 9%, 0.94); | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | .kb-image-preview-image { | |
| 239 | + | background: var(--kb-surface); | |
| 240 | + | border: 1px solid var(--kb-border); | |
| 241 | + | box-shadow: 0 2px 10px var(--kb-shadow); | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | /* ---------- Footer & language picker ---------- */ | |
| 245 | + | ||
| 246 | + | .footer { | |
| 247 | + | border-top: 1px solid var(--kb-border); | |
| 248 | + | background: var(--kb-bg-alt); | |
| 249 | + | color: var(--kb-text-muted); | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | .footer .menu-item { | |
| 253 | + | color: var(--kb-text-secondary); | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | /* ---------- Buttons & dropdowns ---------- */ | |
| 257 | + | ||
| 258 | + | .btn { | |
| 259 | + | border-color: var(--kb-border); | |
| 260 | + | color: var(--kb-text); | |
| 261 | + | } | |
| 262 | + | ||
| 263 | + | .dropdown-menu { | |
| 264 | + | background: var(--kb-surface); | |
| 265 | + | box-shadow: 0 15px 35px var(--kb-shadow), 0 5px 15px var(--kb-shadow); | |
| 266 | + | } | |
| 267 | + | ||
| 268 | + | .dropdown-menu::before { | |
| 269 | + | background: var(--kb-surface); | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | .dropdown-menu a { | |
| 273 | + | color: var(--kb-text); | |
| 274 | + | } | |
| 275 | + | ||
| 276 | + | /* Checkmark on the selected entry: dim it relative to the bright label text */ | |
| 277 | + | .dropdown-menu .icon { | |
| 278 | + | fill: var(--kb-text-secondary); | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | .dropdown-menu a:hover, | |
| 282 | + | .dropdown-menu a:focus { | |
| 283 | + | background: var(--kb-surface-hover); | |
| 284 | + | } | |
| 285 | + | ||
| 286 | + | .dropdown-menu li.is-disabled a { | |
| 287 | + | color: var(--kb-text-muted); | |
| 288 | + | } | |
| 289 | + | ||
| 290 | + | /* ---------- Scrollbar (WebKit/Blink) ---------- */ | |
| 291 | + | ||
| 292 | + | ::-webkit-scrollbar { | |
| 293 | + | width: 12px; | |
| 294 | + | height: 12px; | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | ::-webkit-scrollbar-track { | |
| 298 | + | background: var(--kb-bg); | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | ::-webkit-scrollbar-thumb { | |
| 302 | + | background: var(--kb-border); | |
| 303 | + | border-radius: 6px; | |
| 304 | + | } | |
| 305 | + | ||
| 306 | + | ::-webkit-scrollbar-thumb:hover { | |
| 307 | + | background: var(--kb-surface-hover); | |
| 308 | + | } | |
MrGeneration / Dark Zammad Knowledge Base Stylesheet
Last active 15 hours ago
MrGeneration
revised this gist 15 hours ago
·
cecf6ff
1 file changed, 308 insertions