openvibers/openvibe.shared builtin-themes.js
builtin-themes.js
| 1 | ; |
| 2 | |
| 3 | const RAW_BUILTIN_THEMES = [ |
| 4 | { |
| 5 | name: 'Vibe', |
| 6 | slug: 'vibe', |
| 7 | mode: 'dark', |
| 8 | description: 'OpenVibe\'s default: deep blue night with an electric-blue glow.', |
| 9 | tags: ['default', 'cool', 'blue'], |
| 10 | variables: { |
| 11 | '--bg-primary': '#0a0f1c', |
| 12 | '--bg-secondary': '#101828', |
| 13 | '--bg-tertiary': '#172238', |
| 14 | '--bg-card': '#131c2e', |
| 15 | '--bg-hover': '#1c2a44', |
| 16 | '--bg-input': '#0d1424', |
| 17 | '--text-primary': '#e6edf7', |
| 18 | '--text-secondary': '#96a7c2', |
| 19 | '--text-muted': '#7386a3', |
| 20 | '--accent': '#3b82f6', |
| 21 | '--accent-light': '#60a5fa', |
| 22 | '--accent-dark': '#1d4ed8', |
| 23 | '--border': '#1f2d47', |
| 24 | '--border-light': '#2c3d5c', |
| 25 | '--live-red': '#ef4444', |
| 26 | '--success': '#22c55e', |
| 27 | '--warning': '#f59e0b', |
| 28 | '--danger': '#ef4444', |
| 29 | '--info': '#38bdf8', |
| 30 | '--shadow': '0 2px 12px rgba(0,0,0,0.45)', |
| 31 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.6)', |
| 32 | }, |
| 33 | }, |
| 34 | { |
| 35 | name: 'Violet Hour', |
| 36 | slug: 'violet-hour', |
| 37 | mode: 'dark', |
| 38 | description: 'Charcoal and violet: the colours OpenVibe started with.', |
| 39 | tags: ['classic', 'purple', 'warm'], |
| 40 | variables: { |
| 41 | '--bg-primary': '#0d0d0f', |
| 42 | '--bg-secondary': '#16161a', |
| 43 | '--bg-tertiary': '#1e1e24', |
| 44 | '--bg-card': '#1a1a20', |
| 45 | '--bg-hover': '#242430', |
| 46 | '--bg-input': '#12121a', |
| 47 | '--text-primary': '#e8e6e3', |
| 48 | '--text-secondary': '#9a9a9a', |
| 49 | '--text-muted': '#838282', |
| 50 | '--accent': '#8b5cf6', |
| 51 | '--accent-light': '#a78bfa', |
| 52 | '--accent-dark': '#6d28d9', |
| 53 | '--border': '#2a2a32', |
| 54 | '--border-light': '#3a3a44', |
| 55 | '--live-red': '#e74c3c', |
| 56 | '--success': '#2ecc71', |
| 57 | '--warning': '#f39c12', |
| 58 | '--danger': '#e74c3c', |
| 59 | '--info': '#3498db', |
| 60 | '--shadow': '0 2px 12px rgba(0,0,0,0.4)', |
| 61 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.6)', |
| 62 | }, |
| 63 | }, |
| 64 | { |
| 65 | name: 'Midnight', |
| 66 | slug: 'midnight', |
| 67 | mode: 'dark', |
| 68 | description: 'Blue-black with steel-indigo light.', |
| 69 | tags: ['cool', 'minimal', 'blue'], |
| 70 | variables: { |
| 71 | '--bg-primary': '#0a0e17', |
| 72 | '--bg-secondary': '#111827', |
| 73 | '--bg-tertiary': '#1a2235', |
| 74 | '--bg-card': '#151d2e', |
| 75 | '--bg-hover': '#1e2a42', |
| 76 | '--bg-input': '#0d1220', |
| 77 | '--text-primary': '#e2e8f0', |
| 78 | '--text-secondary': '#94a3b8', |
| 79 | '--text-muted': '#78879b', |
| 80 | '--accent': '#6366f1', |
| 81 | '--accent-light': '#818cf8', |
| 82 | '--accent-dark': '#4f46e5', |
| 83 | '--border': '#1e293b', |
| 84 | '--border-light': '#334155', |
| 85 | }, |
| 86 | }, |
| 87 | { |
| 88 | name: 'Understory', |
| 89 | slug: 'forest', |
| 90 | mode: 'dark', |
| 91 | description: 'Deep greens under the canopy.', |
| 92 | tags: ['nature', 'green', 'earthy'], |
| 93 | variables: { |
| 94 | '--bg-primary': '#0a120a', |
| 95 | '--bg-secondary': '#121e12', |
| 96 | '--bg-tertiary': '#1a2a1a', |
| 97 | '--bg-card': '#152015', |
| 98 | '--bg-hover': '#1e3420', |
| 99 | '--bg-input': '#0d160d', |
| 100 | '--text-primary': '#d4e8d4', |
| 101 | '--text-secondary': '#8aaa8a', |
| 102 | '--text-muted': '#708e70', |
| 103 | '--accent': '#4ade80', |
| 104 | '--accent-light': '#86efac', |
| 105 | '--accent-dark': '#22c55e', |
| 106 | '--border': '#1a3a1a', |
| 107 | '--border-light': '#2a4a2a', |
| 108 | }, |
| 109 | }, |
| 110 | { |
| 111 | name: 'Neon Tokyo', |
| 112 | slug: 'neon-tokyo', |
| 113 | mode: 'dark', |
| 114 | description: 'Hot pink neon on wet chrome.', |
| 115 | tags: ['cyberpunk', 'neon', 'vibrant'], |
| 116 | variables: { |
| 117 | '--bg-primary': '#0a0a0f', |
| 118 | '--bg-secondary': '#12101a', |
| 119 | '--bg-tertiary': '#1a1625', |
| 120 | '--bg-card': '#15111f', |
| 121 | '--bg-hover': '#221a32', |
| 122 | '--bg-input': '#0d0b14', |
| 123 | '--text-primary': '#f0e6ff', |
| 124 | '--text-secondary': '#b4a0d0', |
| 125 | '--text-muted': '#87799c', |
| 126 | '--accent': '#ff006e', |
| 127 | '--accent-light': '#ff4d94', |
| 128 | '--accent-dark': '#c9005a', |
| 129 | '--border': '#2a1a3a', |
| 130 | '--border-light': '#3a2a4a', |
| 131 | }, |
| 132 | }, |
| 133 | { |
| 134 | name: 'Dracula', |
| 135 | slug: 'dracula', |
| 136 | mode: 'dark', |
| 137 | description: 'The Dracula scheme, as its authors drew it.', |
| 138 | tags: ['popular', 'purple', 'classic'], |
| 139 | variables: { |
| 140 | '--bg-primary': '#282a36', |
| 141 | '--bg-secondary': '#1e1f29', |
| 142 | '--bg-tertiary': '#343746', |
| 143 | '--bg-card': '#2c2e3c', |
| 144 | '--bg-hover': '#3a3d50', |
| 145 | '--bg-input': '#21222c', |
| 146 | '--text-primary': '#f8f8f2', |
| 147 | '--text-secondary': '#bfbfb2', |
| 148 | '--text-muted': '#8995b8', |
| 149 | '--accent': '#bd93f9', |
| 150 | '--accent-light': '#d4b8ff', |
| 151 | '--accent-dark': '#9b6ddb', |
| 152 | '--border': '#44475a', |
| 153 | '--border-light': '#565970', |
| 154 | '--success': '#50fa7b', |
| 155 | '--danger': '#ff5555', |
| 156 | '--warning': '#f1fa8c', |
| 157 | '--info': '#8be9fd', |
| 158 | '--live-red': '#ff5555', |
| 159 | }, |
| 160 | }, |
| 161 | { |
| 162 | name: 'Monokai', |
| 163 | slug: 'monokai', |
| 164 | mode: 'dark', |
| 165 | description: 'The Monokai editor palette.', |
| 166 | tags: ['developer', 'classic', 'warm'], |
| 167 | variables: { |
| 168 | '--bg-primary': '#272822', |
| 169 | '--bg-secondary': '#1e1f1c', |
| 170 | '--bg-tertiary': '#3e3d32', |
| 171 | '--bg-card': '#2d2e27', |
| 172 | '--bg-hover': '#49483e', |
| 173 | '--bg-input': '#22231e', |
| 174 | '--text-primary': '#f8f8f2', |
| 175 | '--text-secondary': '#cfcfc2', |
| 176 | '--text-muted': '#979484', |
| 177 | '--accent': '#a6e22e', |
| 178 | '--accent-light': '#c4ff50', |
| 179 | '--accent-dark': '#7dac1c', |
| 180 | '--border': '#49483e', |
| 181 | '--border-light': '#5b5a50', |
| 182 | '--danger': '#f92672', |
| 183 | '--warning': '#e6db74', |
| 184 | '--info': '#66d9ef', |
| 185 | }, |
| 186 | }, |
| 187 | { |
| 188 | name: 'Trench', |
| 189 | slug: 'ocean-deep', |
| 190 | mode: 'dark', |
| 191 | description: 'Pressure-dark water with a teal glow.', |
| 192 | tags: ['ocean', 'teal', 'calm'], |
| 193 | variables: { |
| 194 | '--bg-primary': '#0a1215', |
| 195 | '--bg-secondary': '#0f1a1f', |
| 196 | '--bg-tertiary': '#162228', |
| 197 | '--bg-card': '#121c22', |
| 198 | '--bg-hover': '#1a2a32', |
| 199 | '--bg-input': '#0c1418', |
| 200 | '--text-primary': '#d0e8ef', |
| 201 | '--text-secondary': '#7aa8ba', |
| 202 | '--text-muted': '#658896', |
| 203 | '--accent': '#00bcd4', |
| 204 | '--accent-light': '#4dd0e1', |
| 205 | '--accent-dark': '#0097a7', |
| 206 | '--border': '#1a3040', |
| 207 | '--border-light': '#2a4050', |
| 208 | }, |
| 209 | }, |
| 210 | { |
| 211 | name: 'Dusk', |
| 212 | slug: 'sunset', |
| 213 | mode: 'dark', |
| 214 | description: 'Orange fading to rose over dark ground.', |
| 215 | tags: ['warm', 'gradient', 'vibrant'], |
| 216 | variables: { |
| 217 | '--bg-primary': '#120a0a', |
| 218 | '--bg-secondary': '#1a1010', |
| 219 | '--bg-tertiary': '#241616', |
| 220 | '--bg-card': '#1e1212', |
| 221 | '--bg-hover': '#2e1a1a', |
| 222 | '--bg-input': '#140c0c', |
| 223 | '--text-primary': '#ffe8e0', |
| 224 | '--text-secondary': '#c49080', |
| 225 | '--text-muted': '#977970', |
| 226 | '--accent': '#ff6b35', |
| 227 | '--accent-light': '#ff8f5e', |
| 228 | '--accent-dark': '#d04a1a', |
| 229 | '--border': '#3a2020', |
| 230 | '--border-light': '#4a3030', |
| 231 | }, |
| 232 | }, |
| 233 | { |
| 234 | name: 'Arctic', |
| 235 | slug: 'arctic', |
| 236 | mode: 'dark', |
| 237 | description: 'Frost surfaces, cyan light, crisp white text.', |
| 238 | tags: ['cool', 'ice', 'clean'], |
| 239 | variables: { |
| 240 | '--bg-primary': '#0b1620', |
| 241 | '--bg-secondary': '#0f1e2b', |
| 242 | '--bg-tertiary': '#163040', |
| 243 | '--bg-card': '#122534', |
| 244 | '--bg-hover': '#1a3a4c', |
| 245 | '--bg-input': '#0c1a26', |
| 246 | '--text-primary': '#eaf6ff', |
| 247 | '--text-secondary': '#9ec3d8', |
| 248 | '--text-muted': '#7ea3ba', |
| 249 | '--accent': '#22d3ee', |
| 250 | '--accent-light': '#a5f3fc', |
| 251 | '--accent-dark': '#0891b2', |
| 252 | '--border': '#1e3f52', |
| 253 | '--border-light': '#2a5268', |
| 254 | '--info': '#7dd3fc', |
| 255 | '--success': '#34d399', |
| 256 | '--shadow': '0 2px 12px rgba(2,12,20,0.5)', |
| 257 | '--shadow-lg': '0 8px 32px rgba(2,12,20,0.65)', |
| 258 | }, |
| 259 | }, |
| 260 | { |
| 261 | name: 'Ember', |
| 262 | slug: 'ember', |
| 263 | mode: 'dark', |
| 264 | description: 'Smouldering red on warm charcoal.', |
| 265 | tags: ['red', 'warm', 'intense'], |
| 266 | variables: { |
| 267 | '--bg-primary': '#120808', |
| 268 | '--bg-secondary': '#1a0e0e', |
| 269 | '--bg-tertiary': '#241414', |
| 270 | '--bg-card': '#1e1010', |
| 271 | '--bg-hover': '#2e1818', |
| 272 | '--bg-input': '#140a0a', |
| 273 | '--text-primary': '#f0dada', |
| 274 | '--text-secondary': '#b0808a', |
| 275 | '--text-muted': '#95787c', |
| 276 | '--accent': '#ef4444', |
| 277 | '--accent-light': '#f87171', |
| 278 | '--accent-dark': '#b91c1c', |
| 279 | '--border': '#3a1a20', |
| 280 | '--border-light': '#4a2a30', |
| 281 | }, |
| 282 | }, |
| 283 | { |
| 284 | name: 'Vapor', |
| 285 | slug: 'vapor', |
| 286 | mode: 'dark', |
| 287 | description: 'Magenta and teal, straight off a VHS tape.', |
| 288 | tags: ['retro', 'aesthetic', 'neon'], |
| 289 | variables: { |
| 290 | '--bg-primary': '#0e0a14', |
| 291 | '--bg-secondary': '#14101c', |
| 292 | '--bg-tertiary': '#1c1628', |
| 293 | '--bg-card': '#181220', |
| 294 | '--bg-hover': '#241a34', |
| 295 | '--bg-input': '#100c16', |
| 296 | '--text-primary': '#f0e0ff', |
| 297 | '--text-secondary': '#c090e0', |
| 298 | '--text-muted': '#8f73b7', |
| 299 | '--accent': '#e040fb', |
| 300 | '--accent-light': '#ea80fc', |
| 301 | '--accent-dark': '#aa00d4', |
| 302 | '--border': '#2a1a40', |
| 303 | '--border-light': '#3a2a50', |
| 304 | '--info': '#00e5ff', |
| 305 | '--success': '#69f0ae', |
| 306 | }, |
| 307 | }, |
| 308 | { |
| 309 | name: 'Slate', |
| 310 | slug: 'slate', |
| 311 | mode: 'dark', |
| 312 | description: 'Neutral grey with a cool violet accent.', |
| 313 | tags: ['neutral', 'minimal', 'professional'], |
| 314 | variables: { |
| 315 | '--bg-primary': '#0f1118', |
| 316 | '--bg-secondary': '#161820', |
| 317 | '--bg-tertiary': '#1e2028', |
| 318 | '--bg-card': '#1a1c24', |
| 319 | '--bg-hover': '#24262e', |
| 320 | '--bg-input': '#12141c', |
| 321 | '--text-primary': '#e4e6ea', |
| 322 | '--text-secondary': '#a0a4b0', |
| 323 | '--text-muted': '#828690', |
| 324 | '--accent': '#a78bfa', |
| 325 | '--accent-light': '#c4b5fd', |
| 326 | '--accent-dark': '#7c3aed', |
| 327 | '--border': '#2a2c38', |
| 328 | '--border-light': '#3a3c48', |
| 329 | }, |
| 330 | }, |
| 331 | { |
| 332 | name: 'Matrix', |
| 333 | slug: 'matrix', |
| 334 | mode: 'dark', |
| 335 | description: 'Green phosphor on black.', |
| 336 | tags: ['hacker', 'retro', 'terminal'], |
| 337 | variables: { |
| 338 | '--bg-primary': '#000000', |
| 339 | '--bg-secondary': '#050a05', |
| 340 | '--bg-tertiary': '#0a140a', |
| 341 | '--bg-card': '#071007', |
| 342 | '--bg-hover': '#0e1e0e', |
| 343 | '--bg-input': '#030803', |
| 344 | '--text-primary': '#00ff41', |
| 345 | '--text-secondary': '#00b030', |
| 346 | '--text-muted': '#008f25', |
| 347 | '--accent': '#00ff41', |
| 348 | '--accent-light': '#66ff8a', |
| 349 | '--accent-dark': '#00aa2a', |
| 350 | '--border': '#0a2a0a', |
| 351 | '--border-light': '#144014', |
| 352 | }, |
| 353 | }, |
| 354 | { |
| 355 | name: 'Nord', |
| 356 | slug: 'nord', |
| 357 | mode: 'dark', |
| 358 | description: 'The Nord palette: muted polar blues.', |
| 359 | tags: ['popular', 'blue', 'soft'], |
| 360 | variables: { |
| 361 | '--bg-primary': '#2e3440', |
| 362 | '--bg-secondary': '#272c36', |
| 363 | '--bg-tertiary': '#3b4252', |
| 364 | '--bg-card': '#333a48', |
| 365 | '--bg-hover': '#434c5e', |
| 366 | '--bg-input': '#2a303c', |
| 367 | '--text-primary': '#eceff4', |
| 368 | '--text-secondary': '#d8dee9', |
| 369 | '--text-muted': '#9ba5b8', |
| 370 | '--accent': '#88c0d0', |
| 371 | '--accent-light': '#8fbcbb', |
| 372 | '--accent-dark': '#5e81ac', |
| 373 | '--border': '#3b4252', |
| 374 | '--border-light': '#4c566a', |
| 375 | '--success': '#a3be8c', |
| 376 | '--danger': '#bf616a', |
| 377 | '--warning': '#ebcb8b', |
| 378 | '--info': '#81a1c1', |
| 379 | }, |
| 380 | }, |
| 381 | { |
| 382 | name: 'Gruvbox', |
| 383 | slug: 'gruvbox-dark', |
| 384 | mode: 'dark', |
| 385 | description: 'Warm retro contrast.', |
| 386 | tags: ['retro', 'warm', 'developer'], |
| 387 | variables: { |
| 388 | '--bg-primary': '#1d2021', |
| 389 | '--bg-secondary': '#282828', |
| 390 | '--bg-tertiary': '#3c3836', |
| 391 | '--bg-card': '#32302f', |
| 392 | '--bg-hover': '#504945', |
| 393 | '--bg-input': '#242424', |
| 394 | '--text-primary': '#ebdbb2', |
| 395 | '--text-secondary': '#d5c4a1', |
| 396 | '--text-muted': '#a69682', |
| 397 | '--accent': '#fabd2f', |
| 398 | '--accent-light': '#fce566', |
| 399 | '--accent-dark': '#d79921', |
| 400 | '--border': '#3c3836', |
| 401 | '--border-light': '#504945', |
| 402 | '--success': '#b8bb26', |
| 403 | '--danger': '#fb4934', |
| 404 | '--warning': '#fe8019', |
| 405 | '--info': '#83a598', |
| 406 | }, |
| 407 | }, |
| 408 | { |
| 409 | name: 'Abyss', |
| 410 | slug: 'abyss', |
| 411 | mode: 'dark', |
| 412 | description: 'Nearly black; the interface is the light.', |
| 413 | tags: ['minimal', 'ultra-dark', 'oled'], |
| 414 | variables: { |
| 415 | '--bg-primary': '#000000', |
| 416 | '--bg-secondary': '#080808', |
| 417 | '--bg-tertiary': '#111111', |
| 418 | '--bg-card': '#0a0a0a', |
| 419 | '--bg-hover': '#181818', |
| 420 | '--bg-input': '#050505', |
| 421 | '--text-primary': '#cccccc', |
| 422 | '--text-secondary': '#888888', |
| 423 | '--text-muted': '#7a7a7a', |
| 424 | '--accent': '#ffffff', |
| 425 | '--accent-light': '#ffffff', |
| 426 | '--accent-dark': '#aaaaaa', |
| 427 | '--border': '#1a1a1a', |
| 428 | '--border-light': '#252525', |
| 429 | }, |
| 430 | }, |
| 431 | { |
| 432 | name: 'Copper', |
| 433 | slug: 'copper', |
| 434 | mode: 'dark', |
| 435 | description: 'Bronze on dark walnut.', |
| 436 | tags: ['metallic', 'warm', 'premium'], |
| 437 | variables: { |
| 438 | '--bg-primary': '#110d09', |
| 439 | '--bg-secondary': '#1a140e', |
| 440 | '--bg-tertiary': '#231c14', |
| 441 | '--bg-card': '#1e1710', |
| 442 | '--bg-hover': '#2c2218', |
| 443 | '--bg-input': '#13100b', |
| 444 | '--text-primary': '#e8ddd0', |
| 445 | '--text-secondary': '#b49a80', |
| 446 | '--text-muted': '#8e7f6a', |
| 447 | '--accent': '#cd7f32', |
| 448 | '--accent-light': '#daa060', |
| 449 | '--accent-dark': '#a06020', |
| 450 | '--border': '#2a2016', |
| 451 | '--border-light': '#3a3020', |
| 452 | }, |
| 453 | }, |
| 454 | { |
| 455 | name: 'Sakura', |
| 456 | slug: 'sakura-night', |
| 457 | mode: 'dark', |
| 458 | description: 'Cherry blossom on indigo.', |
| 459 | tags: ['japanese', 'pink', 'soft'], |
| 460 | variables: { |
| 461 | '--bg-primary': '#0e0a14', |
| 462 | '--bg-secondary': '#150f1c', |
| 463 | '--bg-tertiary': '#1c1526', |
| 464 | '--bg-card': '#181020', |
| 465 | '--bg-hover': '#221830', |
| 466 | '--bg-input': '#100c16', |
| 467 | '--text-primary': '#f0e0f0', |
| 468 | '--text-secondary': '#c0a0c0', |
| 469 | '--text-muted': '#927592', |
| 470 | '--accent': '#f472b6', |
| 471 | '--accent-light': '#f9a8d4', |
| 472 | '--accent-dark': '#db2777', |
| 473 | '--border': '#2a1a30', |
| 474 | '--border-light': '#3a2a40', |
| 475 | }, |
| 476 | }, |
| 477 | { |
| 478 | name: 'Amber', |
| 479 | slug: 'hacker', |
| 480 | mode: 'dark', |
| 481 | description: 'Amber phosphor on black.', |
| 482 | tags: ['retro', 'terminal', 'nostalgia'], |
| 483 | variables: { |
| 484 | '--bg-primary': '#000000', |
| 485 | '--bg-secondary': '#0a0800', |
| 486 | '--bg-tertiary': '#141000', |
| 487 | '--bg-card': '#0e0c00', |
| 488 | '--bg-hover': '#1c1800', |
| 489 | '--bg-input': '#060400', |
| 490 | '--text-primary': '#ffb000', |
| 491 | '--text-secondary': '#cc8d00', |
| 492 | '--text-muted': '#a37100', |
| 493 | '--accent': '#ffb000', |
| 494 | '--accent-light': '#ffd050', |
| 495 | '--accent-dark': '#cc8d00', |
| 496 | '--border': '#1a1400', |
| 497 | '--border-light': '#2a2200', |
| 498 | }, |
| 499 | }, |
| 500 | { |
| 501 | name: 'Daylight', |
| 502 | slug: 'daylight', |
| 503 | mode: 'light', |
| 504 | description: 'Bright white with warm accents.', |
| 505 | tags: ['clean', 'bright', 'default-light'], |
| 506 | variables: { |
| 507 | '--bg-primary': '#ffffff', |
| 508 | '--bg-secondary': '#f7f7f8', |
| 509 | '--bg-tertiary': '#eeeef0', |
| 510 | '--bg-card': '#ffffff', |
| 511 | '--bg-hover': '#f0f0f2', |
| 512 | '--bg-input': '#f5f5f7', |
| 513 | '--text-primary': '#1a1a2e', |
| 514 | '--text-secondary': '#4a4a6a', |
| 515 | '--text-muted': '#747489', |
| 516 | '--accent': '#8756f3', |
| 517 | '--accent-light': '#a78bfa', |
| 518 | '--accent-dark': '#6d28d9', |
| 519 | '--border': '#e0e0e6', |
| 520 | '--border-light': '#d0d0d8', |
| 521 | '--shadow': '0 2px 12px rgba(0,0,0,0.08)', |
| 522 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.12)', |
| 523 | }, |
| 524 | }, |
| 525 | { |
| 526 | name: 'Paper', |
| 527 | slug: 'paper', |
| 528 | mode: 'light', |
| 529 | description: 'Aged paper and ink.', |
| 530 | tags: ['warm', 'soft', 'minimal'], |
| 531 | variables: { |
| 532 | '--bg-primary': '#faf8f5', |
| 533 | '--bg-secondary': '#f0ece6', |
| 534 | '--bg-tertiary': '#e8e2d8', |
| 535 | '--bg-card': '#faf8f5', |
| 536 | '--bg-hover': '#f0ece6', |
| 537 | '--bg-input': '#f5f2ed', |
| 538 | '--text-primary': '#2c2416', |
| 539 | '--text-secondary': '#5a4e3e', |
| 540 | '--text-muted': '#7b7061', |
| 541 | '--accent': '#8b6914', |
| 542 | '--accent-light': '#b08a30', |
| 543 | '--accent-dark': '#6a5010', |
| 544 | '--border': '#ddd6c8', |
| 545 | '--border-light': '#ccc4b0', |
| 546 | '--shadow': '0 2px 12px rgba(60,40,0,0.06)', |
| 547 | '--shadow-lg': '0 8px 32px rgba(60,40,0,0.10)', |
| 548 | }, |
| 549 | }, |
| 550 | { |
| 551 | name: 'Vibe Light', |
| 552 | slug: 'cloud', |
| 553 | mode: 'light', |
| 554 | description: 'OpenVibe\'s default in daylight: airy blue-white with the same blue.', |
| 555 | tags: ['blue', 'airy', 'fresh'], |
| 556 | variables: { |
| 557 | '--bg-primary': '#f0f5ff', |
| 558 | '--bg-secondary': '#e8eef8', |
| 559 | '--bg-tertiary': '#dde5f0', |
| 560 | '--bg-card': '#f5f8ff', |
| 561 | '--bg-hover': '#e4ecf8', |
| 562 | '--bg-input': '#edf2fa', |
| 563 | '--text-primary': '#1a2040', |
| 564 | '--text-secondary': '#4a5070', |
| 565 | '--text-muted': '#6b718c', |
| 566 | '--accent': '#2867ed', |
| 567 | '--accent-light': '#60a5fa', |
| 568 | '--accent-dark': '#2563eb', |
| 569 | '--border': '#d0d8ea', |
| 570 | '--border-light': '#bcc8e0', |
| 571 | '--shadow': '0 2px 12px rgba(0,20,60,0.06)', |
| 572 | '--shadow-lg': '0 8px 32px rgba(0,20,60,0.10)', |
| 573 | }, |
| 574 | }, |
| 575 | { |
| 576 | name: 'Meadow', |
| 577 | slug: 'meadow', |
| 578 | mode: 'light', |
| 579 | description: 'Spring greens in daylight.', |
| 580 | tags: ['nature', 'green', 'fresh'], |
| 581 | variables: { |
| 582 | '--bg-primary': '#f2f8f0', |
| 583 | '--bg-secondary': '#e8f0e4', |
| 584 | '--bg-tertiary': '#dce8d6', |
| 585 | '--bg-card': '#f5faf2', |
| 586 | '--bg-hover': '#e4f0de', |
| 587 | '--bg-input': '#edf5ea', |
| 588 | '--text-primary': '#1a2e16', |
| 589 | '--text-secondary': '#3a5a32', |
| 590 | '--text-muted': '#5d7a54', |
| 591 | '--accent': '#15823e', |
| 592 | '--accent-light': '#22c55e', |
| 593 | '--accent-dark': '#15803d', |
| 594 | '--border': '#c8dcc0', |
| 595 | '--border-light': '#b0ccaa', |
| 596 | '--shadow': '0 2px 12px rgba(0,40,0,0.06)', |
| 597 | '--shadow-lg': '0 8px 32px rgba(0,40,0,0.10)', |
| 598 | }, |
| 599 | }, |
| 600 | { |
| 601 | name: 'Peach', |
| 602 | slug: 'peach', |
| 603 | mode: 'light', |
| 604 | description: 'Peach and coral, soft and warm.', |
| 605 | tags: ['warm', 'pink', 'soft'], |
| 606 | variables: { |
| 607 | '--bg-primary': '#fff5f0', |
| 608 | '--bg-secondary': '#fbeae2', |
| 609 | '--bg-tertiary': '#f5ddd2', |
| 610 | '--bg-card': '#fff7f2', |
| 611 | '--bg-hover': '#fce8dc', |
| 612 | '--bg-input': '#fdf0ea', |
| 613 | '--text-primary': '#2e1a14', |
| 614 | '--text-secondary': '#6a4438', |
| 615 | '--text-muted': '#8f6a5c', |
| 616 | '--accent': '#c04808', |
| 617 | '--accent-light': '#fb923c', |
| 618 | '--accent-dark': '#ea580c', |
| 619 | '--border': '#f0d0c0', |
| 620 | '--border-light': '#e4bfab', |
| 621 | '--shadow': '0 2px 12px rgba(60,20,0,0.06)', |
| 622 | '--shadow-lg': '0 8px 32px rgba(60,20,0,0.10)', |
| 623 | }, |
| 624 | }, |
| 625 | { |
| 626 | name: 'Lavender', |
| 627 | slug: 'lavender', |
| 628 | mode: 'light', |
| 629 | description: 'Calm pale violet.', |
| 630 | tags: ['purple', 'calm', 'elegant'], |
| 631 | variables: { |
| 632 | '--bg-primary': '#f8f5ff', |
| 633 | '--bg-secondary': '#f0eaf8', |
| 634 | '--bg-tertiary': '#e6ddf0', |
| 635 | '--bg-card': '#faf7ff', |
| 636 | '--bg-hover': '#eee6f8', |
| 637 | '--bg-input': '#f4f0fa', |
| 638 | '--text-primary': '#1e1430', |
| 639 | '--text-secondary': '#4a3a60', |
| 640 | '--text-muted': '#796a92', |
| 641 | '--accent': '#844cf2', |
| 642 | '--accent-light': '#a78bfa', |
| 643 | '--accent-dark': '#7c3aed', |
| 644 | '--border': '#ddd0ee', |
| 645 | '--border-light': '#ccc0dd', |
| 646 | '--shadow': '0 2px 12px rgba(40,0,60,0.06)', |
| 647 | '--shadow-lg': '0 8px 32px rgba(40,0,60,0.10)', |
| 648 | }, |
| 649 | }, |
| 650 | { |
| 651 | name: 'Gruvbox Light', |
| 652 | slug: 'gruvbox-light', |
| 653 | mode: 'light', |
| 654 | description: 'Cream with bold retro accents.', |
| 655 | tags: ['retro', 'warm', 'developer'], |
| 656 | variables: { |
| 657 | '--bg-primary': '#fbf1c7', |
| 658 | '--bg-secondary': '#f2e5bc', |
| 659 | '--bg-tertiary': '#ebdbb2', |
| 660 | '--bg-card': '#fbf1c7', |
| 661 | '--bg-hover': '#f2e5bc', |
| 662 | '--bg-input': '#f5ecc4', |
| 663 | '--text-primary': '#3c3836', |
| 664 | '--text-secondary': '#504945', |
| 665 | '--text-muted': '#766b60', |
| 666 | '--accent': '#be4707', |
| 667 | '--accent-light': '#fe8019', |
| 668 | '--accent-dark': '#af3a03', |
| 669 | '--border': '#d5c4a1', |
| 670 | '--border-light': '#bdae93', |
| 671 | '--success': '#98971a', |
| 672 | '--danger': '#cc241d', |
| 673 | '--warning': '#d79921', |
| 674 | '--info': '#458588', |
| 675 | '--shadow': '0 2px 12px rgba(40,30,0,0.08)', |
| 676 | '--shadow-lg': '0 8px 32px rgba(40,30,0,0.12)', |
| 677 | }, |
| 678 | }, |
| 679 | { |
| 680 | name: 'Snow', |
| 681 | slug: 'snow', |
| 682 | mode: 'light', |
| 683 | description: 'Pure white with icy blue.', |
| 684 | tags: ['minimal', 'clean', 'blue'], |
| 685 | variables: { |
| 686 | '--bg-primary': '#ffffff', |
| 687 | '--bg-secondary': '#f8fafc', |
| 688 | '--bg-tertiary': '#f1f5f9', |
| 689 | '--bg-card': '#ffffff', |
| 690 | '--bg-hover': '#f1f5f9', |
| 691 | '--bg-input': '#f8fafc', |
| 692 | '--text-primary': '#0f172a', |
| 693 | '--text-secondary': '#334155', |
| 694 | '--text-muted': '#69768b', |
| 695 | '--accent': '#027cbb', |
| 696 | '--accent-light': '#38bdf8', |
| 697 | '--accent-dark': '#0284c7', |
| 698 | '--border': '#e2e8f0', |
| 699 | '--border-light': '#cbd5e1', |
| 700 | '--shadow': '0 2px 12px rgba(0,0,0,0.05)', |
| 701 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.08)', |
| 702 | }, |
| 703 | }, |
| 704 | { |
| 705 | name: 'Sand', |
| 706 | slug: 'sand', |
| 707 | mode: 'light', |
| 708 | description: 'Desert tan and warm stone.', |
| 709 | tags: ['warm', 'earthy', 'desert'], |
| 710 | variables: { |
| 711 | '--bg-primary': '#f8f4ef', |
| 712 | '--bg-secondary': '#f0e8de', |
| 713 | '--bg-tertiary': '#e8ddd0', |
| 714 | '--bg-card': '#faf6f0', |
| 715 | '--bg-hover': '#f0e8de', |
| 716 | '--bg-input': '#f5f0e8', |
| 717 | '--text-primary': '#2e2418', |
| 718 | '--text-secondary': '#5a4e3e', |
| 719 | '--text-muted': '#7a6e5f', |
| 720 | '--accent': '#8c6707', |
| 721 | '--accent-light': '#daa520', |
| 722 | '--accent-dark': '#8b6508', |
| 723 | '--border': '#ddd0be', |
| 724 | '--border-light': '#ccc0aa', |
| 725 | '--shadow': '0 2px 12px rgba(60,40,0,0.06)', |
| 726 | '--shadow-lg': '0 8px 32px rgba(60,40,0,0.10)', |
| 727 | }, |
| 728 | }, |
| 729 | { |
| 730 | name: 'Rose', |
| 731 | slug: 'rose', |
| 732 | mode: 'light', |
| 733 | description: 'Blush pink, light and delicate.', |
| 734 | tags: ['pink', 'romantic', 'soft'], |
| 735 | variables: { |
| 736 | '--bg-primary': '#fff5f7', |
| 737 | '--bg-secondary': '#fce7ec', |
| 738 | '--bg-tertiary': '#f5d5de', |
| 739 | '--bg-card': '#fff7f9', |
| 740 | '--bg-hover': '#fce0e8', |
| 741 | '--bg-input': '#fdeef2', |
| 742 | '--text-primary': '#2e1420', |
| 743 | '--text-secondary': '#6a3850', |
| 744 | '--text-muted': '#96617e', |
| 745 | '--accent': '#de1c47', |
| 746 | '--accent-light': '#f43f5e', |
| 747 | '--accent-dark': '#be123c', |
| 748 | '--border': '#f0c8d4', |
| 749 | '--border-light': '#e4b0c0', |
| 750 | '--shadow': '0 2px 12px rgba(60,0,20,0.06)', |
| 751 | '--shadow-lg': '0 8px 32px rgba(60,0,20,0.10)', |
| 752 | }, |
| 753 | }, |
| 754 | { |
| 755 | name: 'Solarized Dark', |
| 756 | slug: 'solarized-dark', |
| 757 | mode: 'dark', |
| 758 | description: 'Ethan Schoonover\'s precision palette, dark.', |
| 759 | tags: ['popular', 'developer', 'balanced'], |
| 760 | variables: { |
| 761 | '--bg-primary': '#002b36', |
| 762 | '--bg-secondary': '#073642', |
| 763 | '--bg-tertiary': '#0a3f4e', |
| 764 | '--bg-card': '#053542', |
| 765 | '--bg-hover': '#0d4a58', |
| 766 | '--bg-input': '#01303d', |
| 767 | '--text-primary': '#fdf6e3', |
| 768 | '--text-secondary': '#93a1a1', |
| 769 | '--text-muted': '#909c9a', |
| 770 | '--accent': '#b58900', |
| 771 | '--accent-light': '#d4a017', |
| 772 | '--accent-dark': '#8a6a00', |
| 773 | '--border': '#0a4050', |
| 774 | '--border-light': '#105060', |
| 775 | '--success': '#859900', |
| 776 | '--danger': '#dc322f', |
| 777 | '--warning': '#cb4b16', |
| 778 | '--info': '#268bd2', |
| 779 | }, |
| 780 | }, |
| 781 | { |
| 782 | name: 'Solarized Light', |
| 783 | slug: 'solarized-light', |
| 784 | mode: 'light', |
| 785 | description: 'Ethan Schoonover\'s precision palette, light.', |
| 786 | tags: ['popular', 'developer', 'balanced'], |
| 787 | variables: { |
| 788 | '--bg-primary': '#fdf6e3', |
| 789 | '--bg-secondary': '#eee8d5', |
| 790 | '--bg-tertiary': '#e4ddc6', |
| 791 | '--bg-card': '#fdf6e3', |
| 792 | '--bg-hover': '#eee8d5', |
| 793 | '--bg-input': '#f5eedb', |
| 794 | '--text-primary': '#002b36', |
| 795 | '--text-secondary': '#586e75', |
| 796 | '--text-muted': '#5b7478', |
| 797 | '--accent': '#8d6c00', |
| 798 | '--accent-light': '#d4a017', |
| 799 | '--accent-dark': '#8a6a00', |
| 800 | '--border': '#d6ceb5', |
| 801 | '--border-light': '#c8c0a5', |
| 802 | '--success': '#859900', |
| 803 | '--danger': '#dc322f', |
| 804 | '--warning': '#cb4b16', |
| 805 | '--info': '#268bd2', |
| 806 | '--shadow': '0 2px 12px rgba(0,30,40,0.08)', |
| 807 | '--shadow-lg': '0 8px 32px rgba(0,30,40,0.12)', |
| 808 | }, |
| 809 | }, |
| 810 | { |
| 811 | name: 'Catppuccin Mocha', |
| 812 | slug: 'catppuccin-mocha', |
| 813 | mode: 'dark', |
| 814 | description: 'Soothing pastels on mocha.', |
| 815 | tags: ['popular', 'pastel', 'soft'], |
| 816 | variables: { |
| 817 | '--bg-primary': '#1e1e2e', |
| 818 | '--bg-secondary': '#181825', |
| 819 | '--bg-tertiary': '#313244', |
| 820 | '--bg-card': '#1e1e2e', |
| 821 | '--bg-hover': '#313244', |
| 822 | '--bg-input': '#1a1a2c', |
| 823 | '--text-primary': '#cdd6f4', |
| 824 | '--text-secondary': '#bac2de', |
| 825 | '--text-muted': '#81869e', |
| 826 | '--accent': '#cba6f7', |
| 827 | '--accent-light': '#dcc0ff', |
| 828 | '--accent-dark': '#b07de0', |
| 829 | '--border': '#313244', |
| 830 | '--border-light': '#45475a', |
| 831 | '--success': '#a6e3a1', |
| 832 | '--danger': '#f38ba8', |
| 833 | '--warning': '#fab387', |
| 834 | '--info': '#89b4fa', |
| 835 | '--live-red': '#f38ba8', |
| 836 | }, |
| 837 | }, |
| 838 | { |
| 839 | name: 'Catppuccin Latte', |
| 840 | slug: 'catppuccin-latte', |
| 841 | mode: 'light', |
| 842 | description: 'Soothing pastels on latte.', |
| 843 | tags: ['popular', 'pastel', 'soft'], |
| 844 | variables: { |
| 845 | '--bg-primary': '#eff1f5', |
| 846 | '--bg-secondary': '#e6e9ef', |
| 847 | '--bg-tertiary': '#dce0e8', |
| 848 | '--bg-card': '#eff1f5', |
| 849 | '--bg-hover': '#dce0e8', |
| 850 | '--bg-input': '#e8ebf0', |
| 851 | '--text-primary': '#4c4f69', |
| 852 | '--text-secondary': '#5c5f77', |
| 853 | '--text-muted': '#696c83', |
| 854 | '--accent': '#8839ef', |
| 855 | '--accent-light': '#a05cff', |
| 856 | '--accent-dark': '#7028d4', |
| 857 | '--border': '#ccd0da', |
| 858 | '--border-light': '#bcc0cc', |
| 859 | '--success': '#40a02b', |
| 860 | '--danger': '#d20f39', |
| 861 | '--warning': '#fe640b', |
| 862 | '--info': '#1e66f5', |
| 863 | '--live-red': '#d20f39', |
| 864 | '--shadow': '0 2px 12px rgba(0,0,0,0.06)', |
| 865 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.10)', |
| 866 | }, |
| 867 | }, |
| 868 | { |
| 869 | name: 'High Contrast', |
| 870 | slug: 'high-contrast', |
| 871 | mode: 'dark', |
| 872 | description: 'Bold yellow on black for maximum legibility.', |
| 873 | tags: ['accessibility', 'high-contrast', 'bold'], |
| 874 | variables: { |
| 875 | '--bg-primary': '#000000', |
| 876 | '--bg-secondary': '#0a0a0a', |
| 877 | '--bg-tertiary': '#1a1a1a', |
| 878 | '--bg-card': '#0f0f0f', |
| 879 | '--bg-hover': '#222222', |
| 880 | '--bg-input': '#0a0a0a', |
| 881 | '--text-primary': '#ffffff', |
| 882 | '--text-secondary': '#f0f0f0', |
| 883 | '--text-muted': '#aaaaaa', |
| 884 | '--accent': '#ffd700', |
| 885 | '--accent-light': '#ffed4a', |
| 886 | '--accent-dark': '#ccac00', |
| 887 | '--border': '#444444', |
| 888 | '--border-light': '#666666', |
| 889 | '--live-red': '#ff0000', |
| 890 | '--success': '#00ff00', |
| 891 | '--warning': '#ffff00', |
| 892 | '--danger': '#ff0000', |
| 893 | '--info': '#00ffff', |
| 894 | }, |
| 895 | }, |
| 896 | // ── Seasonal themes ─────────────────────────────────────────────────────────────────────────── |
| 897 | // A theme with `season` is also the network's default between `from` and `to` (MM-DD, the visitor's own |
| 898 | // calendar, both days included) for everyone on the default theme who has not turned seasonal themes off |
| 899 | // (theme-loader.js; display preference `seasonal`). Anyone can also pick one like any other theme, any time. |
| 900 | { |
| 901 | name: 'Midnight Pumpkin', |
| 902 | slug: 'halloween', |
| 903 | mode: 'dark', |
| 904 | description: 'Halloween: pumpkin orange on OpenVibe\'s midnight navy. On by itself through October (Hayride in light).', |
| 905 | tags: ['seasonal', 'halloween', 'orange'], |
| 906 | season: { id: 'halloween', name: 'Halloween', from: '10-01', to: '10-31' }, |
| 907 | variables: { |
| 908 | '--bg-primary': '#0a0f1c', |
| 909 | '--bg-secondary': '#0f1626', |
| 910 | '--bg-tertiary': '#151e33', |
| 911 | '--bg-card': '#111a2b', |
| 912 | '--bg-hover': '#1a2540', |
| 913 | '--bg-input': '#0c1322', |
| 914 | '--text-primary': '#eef1f6', |
| 915 | '--text-secondary': '#a9b4c8', |
| 916 | '--text-muted': '#8c99b0', |
| 917 | '--accent': '#ff7a1a', |
| 918 | '--accent-light': '#ffa14f', |
| 919 | '--accent-dark': '#d65d00', |
| 920 | '--border': '#1c2840', |
| 921 | '--border-light': '#2a3858', |
| 922 | '--live-red': '#ef4444', |
| 923 | '--success': '#4ade80', |
| 924 | '--warning': '#fbbf24', |
| 925 | '--danger': '#f87171', |
| 926 | '--info': '#60a5fa', |
| 927 | '--shadow': '0 2px 12px rgba(0,4,14,0.5)', |
| 928 | '--shadow-lg': '0 8px 32px rgba(0,4,14,0.65)', |
| 929 | }, |
| 930 | }, |
| 931 | { |
| 932 | name: 'Frost', |
| 933 | slug: 'frost', |
| 934 | mode: 'dark', |
| 935 | description: 'Winter: frosted teal over a cold midnight sky. On by itself through December.', |
| 936 | tags: ['seasonal', 'winter', 'teal', 'cool'], |
| 937 | season: { id: 'winter', name: 'Winter', from: '12-01', to: '12-31' }, |
| 938 | variables: { |
| 939 | '--bg-primary': '#08111b', |
| 940 | '--bg-secondary': '#0d1926', |
| 941 | '--bg-tertiary': '#132334', |
| 942 | '--bg-card': '#0f1c2b', |
| 943 | '--bg-hover': '#18293d', |
| 944 | '--bg-input': '#0a1520', |
| 945 | '--text-primary': '#eaf4fb', |
| 946 | '--text-secondary': '#a3b9cc', |
| 947 | '--text-muted': '#7f97ad', |
| 948 | '--accent': '#2dd4bf', |
| 949 | '--accent-light': '#7ee8da', |
| 950 | '--accent-dark': '#0f9e8c', |
| 951 | '--border': '#1b2d41', |
| 952 | '--border-light': '#294057', |
| 953 | '--live-red': '#ef4444', |
| 954 | '--success': '#4ade80', |
| 955 | '--warning': '#fbbf24', |
| 956 | '--danger': '#f87171', |
| 957 | '--info': '#93c5fd', |
| 958 | '--shadow': '0 2px 12px rgba(0,6,14,0.5)', |
| 959 | '--shadow-lg': '0 8px 32px rgba(0,6,14,0.65)', |
| 960 | }, |
| 961 | }, |
| 962 | { |
| 963 | name: 'Dawn', |
| 964 | slug: 'dawn', |
| 965 | mode: 'light', |
| 966 | description: 'Midnight\'s indigo in the first light.', |
| 967 | tags: ['cool', 'minimal', 'indigo'], |
| 968 | variables: { |
| 969 | '--bg-primary': '#f5f6fc', |
| 970 | '--bg-secondary': '#eceefa', |
| 971 | '--bg-tertiary': '#e1e4f5', |
| 972 | '--bg-card': '#fafbff', |
| 973 | '--bg-hover': '#e6e9f8', |
| 974 | '--bg-input': '#f0f2fb', |
| 975 | '--text-primary': '#161a33', |
| 976 | '--text-secondary': '#474d6b', |
| 977 | '--text-muted': '#646a87', |
| 978 | '--accent': '#4f46e5', |
| 979 | '--accent-light': '#6366f1', |
| 980 | '--accent-dark': '#3730a3', |
| 981 | '--border': '#d6daef', |
| 982 | '--border-light': '#c3c8e6', |
| 983 | '--shadow': '0 2px 12px rgba(10,14,40,0.07)', |
| 984 | '--shadow-lg': '0 8px 32px rgba(10,14,40,0.12)', |
| 985 | }, |
| 986 | }, |
| 987 | { |
| 988 | name: 'Tokyo Day', |
| 989 | slug: 'tokyo-day', |
| 990 | mode: 'light', |
| 991 | description: 'Neon Tokyo by day: hot pink on soft white.', |
| 992 | tags: ['vibrant', 'pink', 'city'], |
| 993 | variables: { |
| 994 | '--bg-primary': '#fff6fa', |
| 995 | '--bg-secondary': '#ffedf4', |
| 996 | '--bg-tertiary': '#fde1ec', |
| 997 | '--bg-card': '#fffafc', |
| 998 | '--bg-hover': '#fde7f0', |
| 999 | '--bg-input': '#fff0f6', |
| 1000 | '--text-primary': '#24101a', |
| 1001 | '--text-secondary': '#5c3a4a', |
| 1002 | '--text-muted': '#7a5566', |
| 1003 | '--accent': '#d1005a', |
| 1004 | '--accent-light': '#ff2d7f', |
| 1005 | '--accent-dark': '#9c0043', |
| 1006 | '--border': '#f3d3e0', |
| 1007 | '--border-light': '#ebbfd1', |
| 1008 | '--shadow': '0 2px 12px rgba(40,0,16,0.07)', |
| 1009 | '--shadow-lg': '0 8px 32px rgba(40,0,16,0.12)', |
| 1010 | }, |
| 1011 | }, |
| 1012 | { |
| 1013 | name: 'Alucard', |
| 1014 | slug: 'alucard', |
| 1015 | mode: 'light', |
| 1016 | description: 'Dracula\'s daylight twin: purple ink on warm parchment.', |
| 1017 | tags: ['popular', 'purple', 'warm'], |
| 1018 | variables: { |
| 1019 | '--bg-primary': '#fffbeb', |
| 1020 | '--bg-secondary': '#f6f0d8', |
| 1021 | '--bg-tertiary': '#ece4c6', |
| 1022 | '--bg-card': '#fffdf3', |
| 1023 | '--bg-hover': '#f2ead0', |
| 1024 | '--bg-input': '#fbf6e2', |
| 1025 | '--text-primary': '#1f1f1f', |
| 1026 | '--text-secondary': '#4f4b38', |
| 1027 | '--text-muted': '#6c664b', |
| 1028 | '--accent': '#644ac9', |
| 1029 | '--accent-light': '#7f63e0', |
| 1030 | '--accent-dark': '#4a33a3', |
| 1031 | '--border': '#e3dab8', |
| 1032 | '--border-light': '#d6cba3', |
| 1033 | '--shadow': '0 2px 12px rgba(40,30,0,0.07)', |
| 1034 | '--shadow-lg': '0 8px 32px rgba(40,30,0,0.12)', |
| 1035 | }, |
| 1036 | }, |
| 1037 | { |
| 1038 | name: 'Monokai Light', |
| 1039 | slug: 'monokai-light', |
| 1040 | mode: 'light', |
| 1041 | description: 'Monokai\'s greens on a calm off-white page.', |
| 1042 | tags: ['developer', 'green', 'classic'], |
| 1043 | variables: { |
| 1044 | '--bg-primary': '#fafaf6', |
| 1045 | '--bg-secondary': '#f1f1ea', |
| 1046 | '--bg-tertiary': '#e7e7de', |
| 1047 | '--bg-card': '#fdfdfa', |
| 1048 | '--bg-hover': '#ecece3', |
| 1049 | '--bg-input': '#f6f6f0', |
| 1050 | '--text-primary': '#272822', |
| 1051 | '--text-secondary': '#4e4f44', |
| 1052 | '--text-muted': '#6e6f62', |
| 1053 | '--accent': '#5c8a00', |
| 1054 | '--accent-light': '#7aa61a', |
| 1055 | '--accent-dark': '#436600', |
| 1056 | '--border': '#dcdcd0', |
| 1057 | '--border-light': '#cdcdbf', |
| 1058 | '--shadow': '0 2px 12px rgba(20,20,10,0.07)', |
| 1059 | '--shadow-lg': '0 8px 32px rgba(20,20,10,0.12)', |
| 1060 | }, |
| 1061 | }, |
| 1062 | { |
| 1063 | name: 'Shallows', |
| 1064 | slug: 'shallows', |
| 1065 | mode: 'light', |
| 1066 | description: 'Trench in the sun: clear teal water over white sand.', |
| 1067 | tags: ['ocean', 'teal', 'calm'], |
| 1068 | variables: { |
| 1069 | '--bg-primary': '#f0fafb', |
| 1070 | '--bg-secondary': '#e4f4f6', |
| 1071 | '--bg-tertiary': '#d5ecef', |
| 1072 | '--bg-card': '#f7fcfd', |
| 1073 | '--bg-hover': '#dcf0f2', |
| 1074 | '--bg-input': '#ebf7f8', |
| 1075 | '--text-primary': '#0c2226', |
| 1076 | '--text-secondary': '#3a5458', |
| 1077 | '--text-muted': '#567277', |
| 1078 | '--accent': '#00788a', |
| 1079 | '--accent-light': '#0097a7', |
| 1080 | '--accent-dark': '#005662', |
| 1081 | '--border': '#c9e3e7', |
| 1082 | '--border-light': '#b4d6db', |
| 1083 | '--shadow': '0 2px 12px rgba(0,30,36,0.07)', |
| 1084 | '--shadow-lg': '0 8px 32px rgba(0,30,36,0.12)', |
| 1085 | }, |
| 1086 | }, |
| 1087 | { |
| 1088 | name: 'Ash', |
| 1089 | slug: 'ash', |
| 1090 | mode: 'light', |
| 1091 | description: 'Ember cooled to ash: warm greys with a red glow.', |
| 1092 | tags: ['red', 'warm', 'soft'], |
| 1093 | variables: { |
| 1094 | '--bg-primary': '#faf7f6', |
| 1095 | '--bg-secondary': '#f3eeec', |
| 1096 | '--bg-tertiary': '#eae2df', |
| 1097 | '--bg-card': '#fdfbfa', |
| 1098 | '--bg-hover': '#efe7e4', |
| 1099 | '--bg-input': '#f7f3f1', |
| 1100 | '--text-primary': '#231716', |
| 1101 | '--text-secondary': '#54403d', |
| 1102 | '--text-muted': '#725b57', |
| 1103 | '--accent': '#c62828', |
| 1104 | '--accent-light': '#e53935', |
| 1105 | '--accent-dark': '#8e1b1b', |
| 1106 | '--border': '#e2d6d2', |
| 1107 | '--border-light': '#d4c4bf', |
| 1108 | '--shadow': '0 2px 12px rgba(40,10,8,0.07)', |
| 1109 | '--shadow-lg': '0 8px 32px rgba(40,10,8,0.12)', |
| 1110 | }, |
| 1111 | }, |
| 1112 | { |
| 1113 | name: 'Vapor Day', |
| 1114 | slug: 'vapor-day', |
| 1115 | mode: 'light', |
| 1116 | description: 'Vapor in pastel daylight: magenta on lilac.', |
| 1117 | tags: ['retro', 'aesthetic', 'pink'], |
| 1118 | variables: { |
| 1119 | '--bg-primary': '#fcf6ff', |
| 1120 | '--bg-secondary': '#f6ecfc', |
| 1121 | '--bg-tertiary': '#eedff8', |
| 1122 | '--bg-card': '#fefaff', |
| 1123 | '--bg-hover': '#f2e5fa', |
| 1124 | '--bg-input': '#f9f1fe', |
| 1125 | '--text-primary': '#22122c', |
| 1126 | '--text-secondary': '#533f5f', |
| 1127 | '--text-muted': '#715c7e', |
| 1128 | '--accent': '#a21caf', |
| 1129 | '--accent-light': '#c026d3', |
| 1130 | '--accent-dark': '#701a75', |
| 1131 | '--border': '#e6d3f1', |
| 1132 | '--border-light': '#dbc0ea', |
| 1133 | '--shadow': '0 2px 12px rgba(30,0,40,0.07)', |
| 1134 | '--shadow-lg': '0 8px 32px rgba(30,0,40,0.12)', |
| 1135 | }, |
| 1136 | }, |
| 1137 | { |
| 1138 | name: 'Greenbar', |
| 1139 | slug: 'greenbar', |
| 1140 | mode: 'light', |
| 1141 | description: 'Matrix on paper: green-bar printout green on white.', |
| 1142 | tags: ['retro', 'terminal', 'green'], |
| 1143 | variables: { |
| 1144 | '--bg-primary': '#f4faf3', |
| 1145 | '--bg-secondary': '#e9f4e7', |
| 1146 | '--bg-tertiary': '#dcecd9', |
| 1147 | '--bg-card': '#f9fdf8', |
| 1148 | '--bg-hover': '#e2f0df', |
| 1149 | '--bg-input': '#eff7ed', |
| 1150 | '--text-primary': '#0d1f10', |
| 1151 | '--text-secondary': '#3b5240', |
| 1152 | '--text-muted': '#57705c', |
| 1153 | '--accent': '#137a2c', |
| 1154 | '--accent-light': '#1f9d3a', |
| 1155 | '--accent-dark': '#0b5a1f', |
| 1156 | '--border': '#cfe3cb', |
| 1157 | '--border-light': '#bcd7b7', |
| 1158 | '--shadow': '0 2px 12px rgba(0,30,6,0.07)', |
| 1159 | '--shadow-lg': '0 8px 32px rgba(0,30,6,0.12)', |
| 1160 | }, |
| 1161 | }, |
| 1162 | { |
| 1163 | name: 'Nord Light', |
| 1164 | slug: 'nord-light', |
| 1165 | mode: 'light', |
| 1166 | description: 'Nord\'s Snow Storm: frost blues on arctic white.', |
| 1167 | tags: ['popular', 'blue', 'soft'], |
| 1168 | variables: { |
| 1169 | '--bg-primary': '#eceff4', |
| 1170 | '--bg-secondary': '#e5e9f0', |
| 1171 | '--bg-tertiary': '#d8dee9', |
| 1172 | '--bg-card': '#f2f4f8', |
| 1173 | '--bg-hover': '#dfe4ec', |
| 1174 | '--bg-input': '#e8ecf2', |
| 1175 | '--text-primary': '#2e3440', |
| 1176 | '--text-secondary': '#434c5e', |
| 1177 | '--text-muted': '#4c566a', |
| 1178 | '--accent': '#5e81ac', |
| 1179 | '--accent-light': '#81a1c1', |
| 1180 | '--accent-dark': '#4c6a91', |
| 1181 | '--border': '#d0d7e2', |
| 1182 | '--border-light': '#c2cad8', |
| 1183 | '--shadow': '0 2px 12px rgba(20,26,40,0.07)', |
| 1184 | '--shadow-lg': '0 8px 32px rgba(20,26,40,0.12)', |
| 1185 | }, |
| 1186 | }, |
| 1187 | { |
| 1188 | name: 'Bright', |
| 1189 | slug: 'bright', |
| 1190 | mode: 'light', |
| 1191 | description: 'Abyss turned inside out: pure white, ink black, nothing else.', |
| 1192 | tags: ['minimal', 'monochrome', 'clean'], |
| 1193 | variables: { |
| 1194 | '--bg-primary': '#ffffff', |
| 1195 | '--bg-secondary': '#f6f6f6', |
| 1196 | '--bg-tertiary': '#ececec', |
| 1197 | '--bg-card': '#ffffff', |
| 1198 | '--bg-hover': '#f0f0f0', |
| 1199 | '--bg-input': '#fafafa', |
| 1200 | '--text-primary': '#0a0a0a', |
| 1201 | '--text-secondary': '#3d3d3d', |
| 1202 | '--text-muted': '#5c5c5c', |
| 1203 | '--accent': '#111111', |
| 1204 | '--accent-light': '#333333', |
| 1205 | '--accent-dark': '#000000', |
| 1206 | '--border': '#e2e2e2', |
| 1207 | '--border-light': '#d0d0d0', |
| 1208 | '--shadow': '0 2px 12px rgba(0,0,0,0.07)', |
| 1209 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.12)', |
| 1210 | }, |
| 1211 | }, |
| 1212 | { |
| 1213 | name: 'High Contrast Light', |
| 1214 | slug: 'high-contrast-light', |
| 1215 | mode: 'light', |
| 1216 | description: 'Black on white with strong edges and deep blue links.', |
| 1217 | tags: ['accessibility', 'high-contrast', 'bold'], |
| 1218 | variables: { |
| 1219 | '--bg-primary': '#ffffff', |
| 1220 | '--bg-secondary': '#ffffff', |
| 1221 | '--bg-tertiary': '#f2f2f2', |
| 1222 | '--bg-card': '#ffffff', |
| 1223 | '--bg-hover': '#e8e8e8', |
| 1224 | '--bg-input': '#ffffff', |
| 1225 | '--text-primary': '#000000', |
| 1226 | '--text-secondary': '#1a1a1a', |
| 1227 | '--text-muted': '#2b2b2b', |
| 1228 | '--accent': '#0000c8', |
| 1229 | '--accent-light': '#1a1aff', |
| 1230 | '--accent-dark': '#00008b', |
| 1231 | '--border': '#000000', |
| 1232 | '--border-light': '#333333', |
| 1233 | '--shadow': '0 2px 12px rgba(0,0,0,0.07)', |
| 1234 | '--shadow-lg': '0 8px 32px rgba(0,0,0,0.12)', |
| 1235 | }, |
| 1236 | }, |
| 1237 | { |
| 1238 | name: 'Hayride', |
| 1239 | slug: 'hayride', |
| 1240 | mode: 'light', |
| 1241 | description: 'Halloween in daylight: pumpkin orange on warm hay. Midnight Pumpkin\'s light half.', |
| 1242 | tags: ['seasonal', 'halloween', 'orange'], |
| 1243 | variables: { |
| 1244 | '--bg-primary': '#fbf6ee', |
| 1245 | '--bg-secondary': '#f6eee2', |
| 1246 | '--bg-tertiary': '#efe3d1', |
| 1247 | '--bg-card': '#ffffff', |
| 1248 | '--bg-hover': '#f3e9da', |
| 1249 | '--bg-input': '#fffaf2', |
| 1250 | '--text-primary': '#23180f', |
| 1251 | '--text-secondary': '#5a4838', |
| 1252 | '--text-muted': '#6b5a4a', |
| 1253 | '--accent': '#c2410c', |
| 1254 | '--accent-light': '#ea580c', |
| 1255 | '--accent-dark': '#9a3412', |
| 1256 | '--border': '#eadfcd', |
| 1257 | '--border-light': '#e0cfb6', |
| 1258 | '--shadow': '0 2px 12px rgba(40,24,8,0.07)', |
| 1259 | '--shadow-lg': '0 8px 32px rgba(40,24,8,0.12)', |
| 1260 | }, |
| 1261 | }, |
| 1262 | { |
| 1263 | name: 'Snowfall', |
| 1264 | slug: 'snowfall', |
| 1265 | mode: 'light', |
| 1266 | description: 'Winter in daylight: frosted teal on fresh snow. Frost\'s light half.', |
| 1267 | tags: ['seasonal', 'winter', 'teal'], |
| 1268 | variables: { |
| 1269 | '--bg-primary': '#f2f8fb', |
| 1270 | '--bg-secondary': '#e8f2f7', |
| 1271 | '--bg-tertiary': '#dbeaf1', |
| 1272 | '--bg-card': '#f8fbfd', |
| 1273 | '--bg-hover': '#e1eef4', |
| 1274 | '--bg-input': '#edf5f9', |
| 1275 | '--text-primary': '#0e1d26', |
| 1276 | '--text-secondary': '#3b5260', |
| 1277 | '--text-muted': '#57707e', |
| 1278 | '--accent': '#0f766e', |
| 1279 | '--accent-light': '#14b8a6', |
| 1280 | '--accent-dark': '#115e59', |
| 1281 | '--border': '#cfe2ea', |
| 1282 | '--border-light': '#bcd5df', |
| 1283 | '--shadow': '0 2px 12px rgba(0,20,30,0.07)', |
| 1284 | '--shadow-lg': '0 8px 32px rgba(0,20,30,0.12)', |
| 1285 | }, |
| 1286 | }, |
| 1287 | ]; |
| 1288 | |
| 1289 | // ── Derived tokens ────────────────────────────────────────────────────────────── |
| 1290 | // Every theme also carries tokens a site cannot guess from the palette alone: |
| 1291 | // --on-accent text/icon colour that reads on top of --accent (dark ink for light accents |
| 1292 | // such as cyan or lime, white for deep ones) — chosen by WCAG contrast |
| 1293 | // --accent-rgb "r,g,b" of the accent, for rgba(var(--accent-rgb), .3) in older stylesheets |
| 1294 | // --accent-glow the accent at 35% for glows and focus rings |
| 1295 | // --color-scheme dark|light, so native controls and scrollbars follow the theme |
| 1296 | // --accent-strong, --on-accent-strong |
| 1297 | // a filled button's background and its label at 4.5:1 or better (WCAG AA for |
| 1298 | // normal text): the accent, darkened under white or lightened under dark ink just |
| 1299 | // enough (the Sign In button; white on #3b82f6 was 3.67:1) |
| 1300 | // A theme may set any of them explicitly; otherwise they are computed here so all 37 themes, |
| 1301 | // and community themes that only define the base palette, stay consistent on every site. |
| 1302 | function hexToRgb(hex) { |
| 1303 | const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(String(hex || '').trim()); |
| 1304 | if (!m) return null; |
| 1305 | let h = m[1]; if (h.length === 3) h = h.split('').map((c) => c + c).join(''); |
| 1306 | return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)]; |
| 1307 | } |
| 1308 | function luminance(rgb) { |
| 1309 | const f = (v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }; |
| 1310 | return 0.2126 * f(rgb[0]) + 0.7152 * f(rgb[1]) + 0.0722 * f(rgb[2]); |
| 1311 | } |
| 1312 | function contrast(a, b) { const [x, y] = [luminance(a), luminance(b)]; return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05); } |
| 1313 | const toHex = (rgb) => '#' + rgb.map((v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, '0')).join(''); |
| 1314 | /** |
| 1315 | * The accent moved toward black (under white) or white (under dark ink) until the pair reads at 4.5:1. |
| 1316 | * The label keeps --on-accent's choice (white wherever it reads at 3:1), so a button keeps its look and |
| 1317 | * only its fill deepens. |
| 1318 | */ |
| 1319 | function strongPair(accent) { |
| 1320 | const WHITE = [255, 255, 255], INK = [11, 13, 16]; |
| 1321 | const ink = contrast(WHITE, accent) >= 3 ? WHITE : INK; |
| 1322 | const toward = ink === WHITE ? [0, 0, 0] : [255, 255, 255]; |
| 1323 | let bg = accent; |
| 1324 | for (let t = 0; t <= 1 && contrast(ink, bg) < 4.5; t += 0.02) bg = accent.map((c, i) => c + (toward[i] - c) * t); |
| 1325 | return [toHex(bg), toHex(ink)]; |
| 1326 | } |
| 1327 | /** |
| 1328 | * Text tokens read at 4.5:1 (WCAG AA for normal text) on every surface a theme defines. Sites write body text, |
| 1329 | * captions and links in --text-secondary, --text-muted, --accent and --accent-light on --bg-primary, --bg-secondary, |
| 1330 | * --bg-tertiary, --bg-card, --bg-input and --bg-hover; in the light themes --accent-light is a pale tint (snow's |
| 1331 | * #38bdf8 on white is 2.1:1) and --text-muted sat at 4.1 to 4.4 on the secondary surfaces, so axe failed 50 to 155 |
| 1332 | * nodes on every site's home in every light theme (2026-10-22). A token below the floor moves toward black (light |
| 1333 | * themes) or white (dark ones) just far enough; a token that already reads is left exactly as the theme set it. |
| 1334 | */ |
| 1335 | // In the dark themes the accents already read on their surfaces and are the theme's brand colour (the default Vibe |
| 1336 | // blue is #3b82f6), so only the two text tokens are held to the floor there. |
| 1337 | const TEXT_TOKENS = { light: ['--text-secondary', '--text-muted', '--accent', '--accent-light'], dark: ['--text-secondary', '--text-muted'] }; |
| 1338 | const SURFACES = ['--bg-primary', '--bg-secondary', '--bg-tertiary', '--bg-card', '--bg-input', '--bg-hover']; |
| 1339 | // The status colours stay exactly as each theme set them: they are backgrounds too (a live badge under white text, an |
| 1340 | // amber strip under dark text), and darkening them would spoil those. Each gets a `-text` twin held to the text floor |
| 1341 | // on every surface, for status words, counts and badges written in the colour (axe in snow, 2026-10-22: --success on |
| 1342 | // white read at 2.3:1). CSS writes var(--success-text, var(--success)). |
| 1343 | const STATUS_TOKENS = ['--success', '--warning', '--danger', '--error', '--info', '--live-red']; |
| 1344 | // A theme that sets no status colours carries the default theme's (theme-loader merges DEFAULT_VARS first), so its twin |
| 1345 | // is derived from those, on its own surfaces: snow inherits Vibe's #22c55e and needs a darker green for text. |
| 1346 | const _vibeRaw = (RAW_BUILTIN_THEMES.find((t) => t.slug === 'vibe') || RAW_BUILTIN_THEMES[0]).variables || {}; |
| 1347 | const STATUS_DEFAULTS = Object.fromEntries(STATUS_TOKENS.filter((k) => _vibeRaw[k]).map((k) => [k, _vibeRaw[k]])); |
| 1348 | function readableText(color, surfaces, mode, min = 4.5) { |
| 1349 | const c0 = hexToRgb(color); |
| 1350 | if (!c0 || !surfaces.length) return color; |
| 1351 | const worst = (c) => Math.min(...surfaces.map((b) => contrast(c, b))); |
| 1352 | if (worst(c0) >= min) return color; |
| 1353 | const toward = mode === 'light' ? [0, 0, 0] : [255, 255, 255]; |
| 1354 | let c = c0; |
| 1355 | // Measured on the rounded colour the theme will carry, so the hex itself meets the floor. |
| 1356 | for (let t = 0.02; t <= 1 && worst(c) < min; t += 0.02) c = hexToRgb(toHex(c0.map((v, i) => v + (toward[i] - v) * t))); |
| 1357 | return toHex(c); |
| 1358 | } |
| 1359 | |
| 1360 | function deriveTokens(vars, mode) { |
| 1361 | const out = { ...vars }; |
| 1362 | const surfaces = SURFACES.map((k) => hexToRgb(vars[k])).filter(Boolean); |
| 1363 | for (const k of TEXT_TOKENS[mode === 'light' ? 'light' : 'dark']) if (out[k]) out[k] = readableText(out[k], surfaces, mode); |
| 1364 | for (const k of STATUS_TOKENS) { |
| 1365 | const c = out[k] || STATUS_DEFAULTS[k]; |
| 1366 | if (c && !out[`${k}-text`]) out[`${k}-text`] = readableText(c, surfaces, mode); |
| 1367 | } |
| 1368 | const accent = hexToRgb(out['--accent']); |
| 1369 | if (accent) { |
| 1370 | if (!out['--accent-strong'] || !out['--on-accent-strong']) { |
| 1371 | const [bg, ink] = strongPair(accent); |
| 1372 | if (!out['--accent-strong']) out['--accent-strong'] = bg; |
| 1373 | if (!out['--on-accent-strong']) out['--on-accent-strong'] = ink; |
| 1374 | } |
| 1375 | // White wherever it reads (>= 3:1, the large-text floor a button label meets); dark ink only |
| 1376 | // on accents too light for it (cyan, lime, amber). |
| 1377 | if (!out['--on-accent']) out['--on-accent'] = contrast([255, 255, 255], accent) >= 3 ? '#ffffff' : '#0b0d10'; |
| 1378 | if (!out['--accent-rgb']) out['--accent-rgb'] = accent.join(','); |
| 1379 | if (!out['--accent-glow']) out['--accent-glow'] = `rgba(${accent.join(',')},0.35)`; |
| 1380 | } |
| 1381 | if (!out['--color-scheme']) out['--color-scheme'] = mode === 'light' ? 'light' : 'dark'; |
| 1382 | return out; |
| 1383 | } |
| 1384 | |
| 1385 | /** |
| 1386 | * Every theme has a light and a dark half (the owner, 2026-10-25: "each theme should have a light counterpart instead of |
| 1387 | * … a mixed bag"): `pair` names the other one. The account menu's Light / Dark switches to it, and Auto follows the |
| 1388 | * device (theme-loader.js). The seasons follow too: Midnight Pumpkin's light half is Hayride, Frost's is Snowfall. |
| 1389 | */ |
| 1390 | const PAIRS = [ |
| 1391 | ['vibe', 'cloud'], ['violet-hour', 'daylight'], ['midnight', 'dawn'], ['forest', 'meadow'], ['neon-tokyo', 'tokyo-day'], |
| 1392 | ['dracula', 'alucard'], ['monokai', 'monokai-light'], ['ocean-deep', 'shallows'], ['sunset', 'peach'], ['arctic', 'snow'], |
| 1393 | ['ember', 'ash'], ['vapor', 'vapor-day'], ['slate', 'lavender'], ['matrix', 'greenbar'], ['nord', 'nord-light'], |
| 1394 | ['gruvbox-dark', 'gruvbox-light'], ['abyss', 'bright'], ['copper', 'sand'], ['sakura-night', 'rose'], ['hacker', 'paper'], |
| 1395 | ['solarized-dark', 'solarized-light'], ['catppuccin-mocha', 'catppuccin-latte'], ['high-contrast', 'high-contrast-light'], |
| 1396 | ['halloween', 'hayride'], ['frost', 'snowfall'], |
| 1397 | ]; |
| 1398 | const PAIR_OF = Object.fromEntries(PAIRS.flatMap(([dark, light]) => [[dark, light], [light, dark]])); |
| 1399 | |
| 1400 | const BUILTIN_THEMES = RAW_BUILTIN_THEMES.map((theme) => ({ |
| 1401 | id: theme.slug, |
| 1402 | pair: PAIR_OF[theme.slug] || null, |
| 1403 | ...theme, |
| 1404 | tags: Array.isArray(theme.tags) ? [...theme.tags] : [], |
| 1405 | variables: deriveTokens(theme.variables || {}, theme.mode), |
| 1406 | })); |
| 1407 | |
| 1408 | const DEFAULT_THEME = BUILTIN_THEMES.find((theme) => theme.id === 'vibe') || BUILTIN_THEMES[0]; |
| 1409 | // The seasons, in calendar order: { id, name, theme, light, from: 'MM-DD', to: 'MM-DD' }: `theme` is the dark half and |
| 1410 | // `light` its light half (theme-loader.js carries a copy). |
| 1411 | const SEASONS = BUILTIN_THEMES.filter((theme) => theme.season).map((theme) => ({ id: theme.season.id, name: theme.season.name, theme: theme.id, light: theme.pair, from: theme.season.from, to: theme.season.to })) |
| 1412 | .sort((a, b) => a.from.localeCompare(b.from)); |
| 1413 | /** The season `date` falls in (the visitor's local calendar), or null. A season may run over New Year (from > to). */ |
| 1414 | function seasonOn(date = new Date()) { |
| 1415 | const md = `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; |
| 1416 | return SEASONS.find((s) => (s.from <= s.to ? md >= s.from && md <= s.to : md >= s.from || md <= s.to)) || null; |
| 1417 | } |
| 1418 | const DEFAULT_VARS = Object.freeze({ ...(DEFAULT_THEME?.variables || {}) }); |
| 1419 | |
| 1420 | module.exports = { |
| 1421 | BUILTIN_THEMES, |
| 1422 | PAIRS, |
| 1423 | SEASONS, |
| 1424 | seasonOn, |
| 1425 | DEFAULT_THEME, |
| 1426 | DEFAULT_VARS, |
| 1427 | deriveTokens, |
| 1428 | readableText, |
| 1429 | TEXT_TOKENS, |
| 1430 | STATUS_TOKENS, |
| 1431 | SURFACES, |
| 1432 | hexToRgb, |
| 1433 | contrast, |
| 1434 | }; |
| 1435 |