From f5bf714d9206a33e8b271b4cb1475cb0d7ded028 Mon Sep 17 00:00:00 2001 From: albertlast Date: Tue, 25 Aug 2026 20:47:15 +0200 Subject: [PATCH] Lets the colour mode into the editor itself The toolbar around the editor followed the colour mode from wave 5, but the surface you actually type on did not: it is an iframe with its own document, so nothing the page declares reaches it, and it stayed white text-on-white in the middle of a dark forum. The plugin already copies the theme's tokens into that document, so the mechanism was there and two things were missing from it. It matched `selectorText == ':root'`, and the colour mode blocks are `:root[data-mode="dark"]` and friends, so it walked straight past them. And nothing set data-mode inside the iframe, so even copied they would have selected nothing. 'system' is resolved to the mode it currently means rather than passed through. Outside, that mode works by asking the browser through a media attribute on the stylesheet, and there is no such attribute on anything in here; left as 'system', the rules would match the reader's own setting, so the editor would go dark on a forum deliberately kept light. A reader on 'system' can also change their mind while the editor is open, so the resolution is re-run on that. The sixteen literal colours in the iframe's own stylesheet become tokens, seeded with the values they replace. They are named for the editing surface rather than pointed at the tokens the rendered post uses, because the two are different contexts - what you type into has no post background behind it - even where the values happen to coincide today. Verified in all three modes on a running forum. Light is unchanged: computed colours, borders, outlines, shadows and sizes of every element on five pages *and inside the editor's own document*, before and after - 1805 records, 34 of them from inside the iframe, no differences. In dark the surface, quotes and code blocks now match the forum. On 'system' with the browser asking for dark, the editor resolves to dark alongside the page; on 'light' with the same browser still asking for dark, it correctly stays light. Signed-off-by: Mathias Alberts Signed-off-by: albertlast --- Themes/default/css/dark.css | 16 +++++++ .../default/css/jquery.sceditor.default.css | 32 ++++++------- Themes/default/css/variables.css | 18 ++++++++ .../default/scripts/sceditor.plugins.smf.js | 46 +++++++++++++++++-- 4 files changed, 93 insertions(+), 19 deletions(-) diff --git a/Themes/default/css/dark.css b/Themes/default/css/dark.css index 37740b8605..9d1f4de88c 100644 --- a/Themes/default/css/dark.css +++ b/Themes/default/css/dark.css @@ -538,6 +538,22 @@ /** Theme Picker **/ --picktheme-selected-bg: hsl(var(--primary-color-hue), 28%, 24%); + /** Editor Content **/ + --editor-content-bg: hsl(var(--primary-color-hue), 16%, 14%); + --editor-content-cite-border-color: var(--dark-line); + --editor-content-cite-color: var(--dark-ink-dim); + --editor-content-code-bg: hsl(var(--primary-color-hue), 14%, 11%); + --editor-content-code-border-color: var(--dark-line-strong); + --editor-content-color: var(--dark-ink); + --editor-content-details-border-color: var(--dark-line-strong); + --editor-content-float-border-color: var(--dark-line-strong); + --editor-content-quote-bg: hsl(var(--primary-color-hue), 20%, 17%); + --editor-content-quote-border-color: hsl(var(--primary-color-hue), 12%, 24%); + --editor-content-quote-border-color_side: hsl(var(--primary-color-hue), 12%, 32%); + --editor-content-spoiler-outline-color: hsl(var(--primary-color-hue), 45%, 55%); + --editor-content-table-border-color: var(--dark-line-strong); + --editor-content-tt-bg: rgba(255, 255, 255, 0.12); + /** Mentions **/ --mentions-bg: var(--dark-bg-3); --mentions-border-color: var(--dark-line-strong); diff --git a/Themes/default/css/jquery.sceditor.default.css b/Themes/default/css/jquery.sceditor.default.css index d0ef17ccfc..cb7f12ca16 100644 --- a/Themes/default/css/jquery.sceditor.default.css +++ b/Themes/default/css/jquery.sceditor.default.css @@ -10,8 +10,8 @@ html { } body { flex: 1; - color: #111; - background: #fff; + color: var(--editor-content-color); + background: var(--editor-content-bg); font: 13px / 1.5 Verdana, Arial, Helvetica, sans-serif; } @@ -23,7 +23,7 @@ ul, ol { } table, td { - border: 1px dotted #000; + border: 1px dotted var(--editor-content-table-border-color); empty-cells: show; min-width: 0.5ch; } @@ -46,15 +46,15 @@ table td { .bbc_code { margin-top: 1.5em; position: relative; - background: #eee; - border: 1px solid #aaa; + background: var(--editor-content-code-bg); + border: 1px solid var(--editor-content-code-border-color); white-space: pre-wrap; padding: .25em; display: block; } span.phpcode, font[face=monospace] { - background-color: rgba(127, 127, 127, 0.25); + background-color: var(--editor-content-tt-bg); padding: 0 0.2ch; display: inline; } @@ -63,20 +63,20 @@ blockquote { margin: 0 0 8px 0; padding: 6px 10px; font-size: small; - border: 1px solid #d6dfe2; - border-left: 2px solid #aaa; - border-right: 2px solid #aaa; - background-color: #e0e6f6; + border: 1px solid var(--editor-content-quote-border-color); + border-left: 2px solid var(--editor-content-quote-border-color_side); + border-right: 2px solid var(--editor-content-quote-border-color_side); + background-color: var(--editor-content-quote-bg); } blockquote cite { display: block; - border-bottom: 1px solid #aaa; + border-bottom: 1px solid var(--editor-content-cite-border-color); font-size: 0.9em; margin-bottom: 0.5em; } blockquote cite::before { - color: #aaa; + color: var(--editor-content-cite-color); font-size: 22px; font-style: normal; content: '\275D'; @@ -135,7 +135,7 @@ img { .floatleft, .floatright { max-width: 45%; - border: 1px dashed #aaa; + border: 1px dashed var(--editor-content-float-border-color); padding: 1px; min-height: 1em; } @@ -157,7 +157,7 @@ img { } .bbc_details { - border: 1px dotted #aaa; + border: 1px dotted var(--editor-content-details-border-color); } .bbc_summary, .bbc_details_content { @@ -165,8 +165,8 @@ img { } .bbc_details[open] .bbc_summary { padding-bottom: 5px; - border-bottom: 1px dotted #aaa; + border-bottom: 1px dotted var(--editor-content-details-border-color); } .bbc_inline_spoiler { - outline: 2px dashed #aaa; + outline: 2px dashed var(--editor-content-spoiler-outline-color); } \ No newline at end of file diff --git a/Themes/default/css/variables.css b/Themes/default/css/variables.css index 11ef5e503a..54178d739f 100644 --- a/Themes/default/css/variables.css +++ b/Themes/default/css/variables.css @@ -834,4 +834,22 @@ --mentions-item-font-weight: normal; --mentions-item-font-weight_current: normal; --mentions-width: 120px; + + /* The surface inside the editor, which is a document of its own. The + /* sceditor plugin copies these into it; see signalReady(). */ + /** Editor Content **/ + --editor-content-bg: #fff; + --editor-content-cite-border-color: #aaa; + --editor-content-cite-color: #aaa; + --editor-content-code-bg: #eee; + --editor-content-code-border-color: #aaa; + --editor-content-color: #111; + --editor-content-details-border-color: #aaa; + --editor-content-float-border-color: #aaa; + --editor-content-quote-bg: #e0e6f6; + --editor-content-quote-border-color: #d6dfe2; + --editor-content-quote-border-color_side: #aaa; + --editor-content-spoiler-outline-color: #aaa; + --editor-content-table-border-color: #000; + --editor-content-tt-bg: rgba(127, 127, 127, 0.25); } diff --git a/Themes/default/scripts/sceditor.plugins.smf.js b/Themes/default/scripts/sceditor.plugins.smf.js index d4a8b75c82..4c7fec3ad3 100644 --- a/Themes/default/scripts/sceditor.plugins.smf.js +++ b/Themes/default/scripts/sceditor.plugins.smf.js @@ -292,25 +292,65 @@ } } - // Copy variables from variants into ifrane. + /* + * Copy variables from variants into the iframe. + * + * What you type into is a document of its own, so nothing the page + * declares reaches it. The tokens are copied in here, and the dark + * ones have to come with them or the editing surface stays white on + * a dark forum. + */ const iframe = editor.getContentAreaContainer(); const el = iframe.contentDocument.createElement('style'); el.type = 'text/css'; let css = ''; + + // The colour mode blocks are ':root[data-mode="dark"]' and friends, + // so matching ':root' alone walks straight past them. + const wantedRoot = selector => selector === ':root' || selector?.startsWith(':root[data-mode'); + for (const sheet of document.styleSheets) { if (sheet.href?.includes('/index_') || sheet.href?.includes('/variables')) { for (const rule of sheet.cssRules) { css += rule.cssText; } - } else if (sheet.href?.includes('/minified_')) { + } else if (sheet.href?.includes('/minified_') || sheet.href?.includes('/dark')) { for (const rule of sheet.cssRules) { - if (rule.selectorText == ':root') { + if (wantedRoot(rule.selectorText)) { css += rule.cssText; } } } } el.innerHTML = css; + + /* + * Tell the iframe which mode it is in. + * + * 'system' is resolved to the mode it currently means rather than + * passed through: outside, the browser is asked by a media + * attribute on the stylesheet, and there is no such attribute on + * anything in here. Left as 'system' the rules would match whatever + * the reader's own setting is, so the editor would go dark on a + * forum deliberately kept light. + */ + const setMode = () => { + const mode = document.documentElement.dataset.mode; + + if (!mode) { + return; + } + + iframe.contentDocument.documentElement.dataset.mode = mode !== 'system' ? mode : + (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + }; + + setMode(); + + // A reader on 'system' can change their mind while the editor is open. + if (document.documentElement.dataset.mode === 'system') { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setMode); + } iframe.contentDocument.head.appendChild(el); // Override these functions in order to convince SCEditor not to