diff --git a/randogoth.com/bbs/static/bbs.css b/randogoth.com/bbs/static/bbs.css index 8c46c1d..c21f5dd 100644 --- a/randogoth.com/bbs/static/bbs.css +++ b/randogoth.com/bbs/static/bbs.css @@ -26,6 +26,42 @@ --lred: #ff5555; --lmagenta: #ff55ff; --yellow: #ffff55; --white: #ffffff; } +/* ── paper ────────────────────────────────────────────────────────────── + * Light mode is a remap of the sixteen slots above, not a second stylesheet. + * Every rule in this file goes through those variables, so the whole board + * inverts by redefining them and nothing below needs to know. + * + * Warm greys on sepia, monochrome: this is not an EGA palette and does not + * pretend to be one. It is the amber-on-paper end of the same machine. The + * tones are picked by contrast ratio against the ground rather than by eye, + * and the ordering of the sixteen is preserved, so what was recessive in the + * dark theme stays recessive here: --dgrey is quiet, --blue is chrome, + * --yellow carries the hotkeys, --lred is the loudest thing on the screen. + * + * Unlike the dark palette these are allowed to depart from EGA fidelity, so + * --dgrey is lifted to 4.5:1 rather than left at the 2.7:1 its counterpart + * sits at on black. + */ +[data-theme="light"] { + color-scheme: light; + --black: #f4ecdc; /* 1.0 the paper */ + --blue: #a0917c; /* 2.6 frames and rules */ + --brown: #887a64; /* 3.6 section marks */ + --lblue: #a0917c; /* 2.6 */ + --dgrey: #756b5b; /* 4.5 dates, numbers, the sysline */ + --green: #6a6053; /* 5.2 quote rule */ + --cyan: #655c50; /* 5.6 list bullets */ + --magenta: #655c50; /* 5.6 */ + --red: #534d43; /* 7.1 */ + --lgreen: #534d43; /* 7.1 code, counts, quoted text */ + --grey: #47423a; /* 8.5 body */ + --lcyan: #38342e; /* 10.5 links and the lightbar */ + --lmagenta:#38342e; /* 10.5 */ + --yellow: #2b2824; /* 12.5 headings, hotkeys, the address */ + --white: #1e1c19; /* 14.5 emphasis */ + --lred: #121210; /* 16.0 new */ +} + html { background: var(--black); } body { margin: 0; padding: 0; @@ -183,7 +219,7 @@ a:visited { color: var(--lcyan); } .gb-row label { display: inline-block; width: 10ch; color: var(--dgrey); } .gb-row label.second { width: auto; padding: 0 1ch 0 3ch; } .gb-form input, .gb-form textarea { - font: inherit; background: #000; color: var(--white); + font: inherit; background: var(--black); color: var(--white); border: 1px solid var(--blue); outline: 0; padding: 0 1ch; } .gb-form input { width: 22ch; } .gb-form textarea { width: 58ch; vertical-align: top; resize: vertical; line-height: 1.4; } diff --git a/randogoth.com/bbs/static/bbs.js b/randogoth.com/bbs/static/bbs.js index ece3cae..cc9c8c7 100644 --- a/randogoth.com/bbs/static/bbs.js +++ b/randogoth.com/bbs/static/bbs.js @@ -1,12 +1,25 @@ -/* One ANSI ramp per session. +var BBS = {}; + +/* One ANSI ramp per session, and the dark/paper switch. * * Boards rotated their welcome screens; this draws a palette instead. It is * held in sessionStorage so it stays put while you read and changes when you * come back, rather than flickering on every page load. * - * Every ramp is three of the sixteen EGA indices, ordered dark to bright, and - * none of them bottom out on the frame blue, which would dissolve the logo's - * feet into the chrome. Without JS the CSS defaults apply and nothing is lost. + * Every dark ramp is three of the sixteen EGA indices, ordered dark to bright, + * and none of them bottom out on the frame blue, which would dissolve the + * logo's feet into the chrome. Without JS the CSS defaults apply and nothing + * is lost. + * + * On paper the ordering reverses: presence is ink rather than light, so the + * feet are the palest step and the crown the darkest, which keeps the weight + * where the dark version puts it. None of them start at the frame tone, for + * the same reason as above. + * + * This runs from , before first paint, so neither the ramp nor the + * theme flashes. Dark is the default and the no-JS result; paper is opt in + * and remembered in localStorage rather than per session, because it is a + * preference and not a welcome screen. */ (function () { var RAMPS = [ @@ -26,6 +39,18 @@ ['#aa00aa', '#ff55ff', '#ffffff'], // violet ['#aa0000', '#aa00aa', '#ff55ff'] // sunset ]; + /* The paper ramps, pale to dark, off the same warm grey scale as bbs.css. */ + var PAPER = [ + ['#887a64', '#655c50', '#38342e'], // paper + ['#887a64', '#534d43', '#1e1c19'], // ink + ['#756b5b', '#534d43', '#2b2824'], // umber + ['#756b5b', '#47423a', '#1e1c19'], // strong + ['#655c50', '#47423a', '#2b2824'], // close + ['#887a64', '#756b5b', '#534d43'], // soft + ['#534d43', '#38342e', '#1e1c19'], // heavy + ['#38342e', '#655c50', '#887a64'], // sunlit, the one that runs the other way + ['#a0917c', '#756b5b', '#2b2824'] // wide + ]; var i; try { i = sessionStorage.getItem('ramp'); @@ -36,11 +61,20 @@ } catch (e) { i = Math.floor(Math.random() * RAMPS.length); } - var r = RAMPS[i % RAMPS.length]; - var s = document.documentElement.style; - s.setProperty('--l1', r[0]); - s.setProperty('--l2', r[1]); - s.setProperty('--l3', r[2]); + + BBS.ramp = function (theme) { + var set = theme === 'light' ? PAPER : RAMPS; + var r = set[i % set.length]; + var s = document.documentElement.style; + s.setProperty('--l1', r[0]); + s.setProperty('--l2', r[1]); + s.setProperty('--l3', r[2]); + }; + + var t; + try { t = localStorage.getItem('theme'); } catch (e) { /* private mode */ } + if (t === 'light') document.documentElement.setAttribute('data-theme', 'light'); + BBS.ramp(t === 'light' ? 'light' : 'dark'); })(); /* The prompt accepts commands. @@ -73,6 +107,7 @@ [].forEach.call(document.querySelectorAll('[data-key]'), function (a) { m[String(a.dataset.key).trim().toUpperCase()] = { href: a.getAttribute('href'), + cmd: a.dataset.cmd, label: a.textContent.trim().replace(/\s+/g, ' ').slice(0, 44) }; }); @@ -89,10 +124,45 @@ } function go(k) { var hit = MAP[k]; - if (hit) { location.href = hit.href; return true; } - return false; + if (!hit) return false; + if (hit.cmd === 'theme') { flip(); return true; } + location.href = hit.href; + return true; } + /* Dark or paper, as a command rather than a button: a board had no widgets, + it had a letter you typed. The entry names where you are going, so it is + [L]ight on a dark screen and [D]ark on a light one, and the key moves with + the label. Injected rather than written into the template, so a reader + without JS is not offered a switch that cannot work. */ + var root = document.documentElement; + var slot = document.getElementById('themecmd'); + + function theme() { + return root.getAttribute('data-theme') === 'light' ? 'light' : 'dark'; + } + function draw() { + if (!slot) return; + var next = theme() === 'light' ? 'dark' : 'light'; + var k = next.charAt(0).toUpperCase(); + slot.innerHTML = '[' + k + ']' + next.slice(1) + ''; + slot.firstChild.addEventListener('click', function (e) { + e.preventDefault(); + flip(); + }); + MAP = keys(); + } + function flip() { + var next = theme() === 'light' ? 'dark' : 'light'; + if (next === 'light') root.setAttribute('data-theme', 'light'); + else root.removeAttribute('data-theme'); + try { localStorage.setItem('theme', next); } catch (e) { /* private mode */ } + BBS.ramp(next); + draw(); + } + draw(); + addEventListener('keydown', function (e) { if (e.metaKey || e.ctrlKey || e.altKey) return; var k = e.key; diff --git a/randogoth.com/bbs/templates/base.html b/randogoth.com/bbs/templates/base.html index 35174a4..4977ed3 100644 --- a/randogoth.com/bbs/templates/base.html +++ b/randogoth.com/bbs/templates/base.html @@ -29,7 +29,7 @@ {% block content %}{% endblock %}
────────────────────────────────────────────────────────────────────────────────
-
 [I]ndex   [K]eywords   [F]eed   Main [M]enu
+
 [I]ndex   [K]eywords   [F]eed   Main [M]enu   
  Command: