From 68e7727fdebd1f8e6e734914074cd9714f2e279f Mon Sep 17 00:00:00 2001 From: albertlast Date: Tue, 11 Aug 2026 09:17:16 +0200 Subject: [PATCH 1/2] Lets the colour mode reach the post editor The editor's own stylesheet was never tokenised, so with dark mode on, the toolbar stayed a light grey island in the middle of a dark page. This gives its twenty colours tokens and a dark value each. One of them is not a colour. Every toolbar icon is cut from a single raster sprite, editor_sprite.png, so none of them can be recoloured the way an icon font or an SVG could, and in dark mode the near-black line art all but disappeared. A filter is the only lever a colour mode has over a raster, so the sprite gets one: `none` here, and an inversion in dark mode. The sprite is mixed - most icons are line art but a few carry their own colour, the YouTube mark among them - so the inversion is paired with a hue rotation, which puts the coloured ones back roughly where they started while the greys, having no hue to rotate, stay inverted. Only the chrome is in scope. The surface you type on is an iframe with its own document, and Editor.php hands it jquery.sceditor.default.css as its single stylesheet. No token is in scope inside that document, so tokenising that file would leave every var() unresolved and break the light theme as well; feeding the iframe the tokens is a change to how the editor is given its stylesheets and belongs on its own. Worth knowing that the surface is only shown to members who have turned the rich editor on - rich_active needs the wysiwyg_default option, which is off by default - so what everybody sees first is the source view, an ordinary textarea on the page itself, which already follows the mode. Light mode is unchanged: the computed colours of every element over twelve pages, on this branch and its parent, are identical. Signed-off-by: Mathias Alberts Signed-off-by: albertlast --- Themes/default/css/dark.css | 34 ++++++++++++++++++++ Themes/default/css/jquery.sceditor.css | 44 ++++++++++++++------------ Themes/default/css/variables.css | 23 ++++++++++++++ 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/Themes/default/css/dark.css b/Themes/default/css/dark.css index 2aeb3fcef76..0ab51882ddc 100644 --- a/Themes/default/css/dark.css +++ b/Themes/default/css/dark.css @@ -464,6 +464,40 @@ --overlay-bg: var(--dark-bg-3); --overlay-bg-image: linear-gradient(to bottom, hsl(var(--primary-color-hue), 14%, 20%) 0%, hsl(var(--primary-color-hue), 14%, 17%) 95%); + /* The editor's chrome only. The surface you actually type on lives in an + /* iframe with its own document and its own stylesheet, which has no tokens + /* in scope, so it is not reachable from here and is left for its own change. + /* That surface is only shown to members who have turned the rich editor on; + /* the default is the source view, which is an ordinary textarea on this + /* page and does follow the mode. */ + /** Editor **/ + --editor-border-color: var(--dark-line-strong); + --editor-button-bg_active: hsl(var(--primary-color-hue), 20%, 26%); + --editor-button-bg_pressed: hsl(var(--primary-color-hue), 20%, 30%); + --editor-button-box-shadow_active: inset 1px 1px 0 rgba(0, 0, 0, 0.5), inset -1px 0 rgba(0, 0, 0, 0.5), inset 0 -1px 0 rgba(0, 0, 0, 0.4); + --editor-button-box-shadow_pressed: inset 1px 1px 0 rgba(0, 0, 0, 0.5), inset -1px 0 rgba(0, 0, 0, 0.5), inset 0 -1px 0 rgba(0, 0, 0, 0.4), inset 0 0 8px rgba(0, 0, 0, 0.5); + --editor-dnd-bg: rgba(255, 255, 255, 0.08); + --editor-dnd-border-color: var(--dark-line-strong); + --editor-dnd-color: var(--dark-ink-dim); + --editor-dropdown-bg: var(--dark-bg-3); + --editor-dropdown-border-color: var(--dark-line-strong); + --editor-dropdown-box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); + --editor-dropdown-label-color: var(--dark-ink); + --editor-dropdown-link-bg_hover: hsl(var(--primary-color-hue), 25%, 26%); + --editor-dropdown-link-color: var(--dark-ink-bright); + --editor-grip-bg: linear-gradient(#0000 0, hsl(var(--primary-color-hue), 10%, 40%) 2px, #0000 3px, hsl(var(--primary-color-hue), 10%, 40%) 5px, #0000 6px, hsl(var(--primary-color-hue), 10%, 40%) 8px, #0000 9px); + /* The sprite is a mix: most icons are near-black line art, but a few carry + /* their own colour - the YouTube mark, the coloured letters. Inverting alone + /* would light the line art and wreck those, so the hue rotation puts the + /* coloured ones back roughly where they started while the greys, which have + /* no hue to rotate, stay inverted. */ + --editor-icon-filter: invert(1) hue-rotate(180deg); + --editor-group-bg: var(--dark-bg-3); + --editor-popup-bg: rgba(0, 0, 0, 0.65); + --editor-popup-container-bg: var(--dark-bg-3); + --editor-tablegrid-bg_active: hsl(var(--primary-color-hue), 40%, 45%); + --editor-tablegrid-border-color: var(--dark-line-strong); + /** Auto Suggest **/ --autosuggest-border-color: var(--dark-line-strong); --autosuggest-item-bg: var(--dark-bg-3); diff --git a/Themes/default/css/jquery.sceditor.css b/Themes/default/css/jquery.sceditor.css index 1e269cff059..bbfe59fe38f 100644 --- a/Themes/default/css/jquery.sceditor.css +++ b/Themes/default/css/jquery.sceditor.css @@ -15,17 +15,21 @@ line-height: 0; } .sceditor-grip::before { - background: linear-gradient(#0000 0, #bbb 2px, #0000 3px, #bbb 5px, #0000 6px, #bbb 8px, #0000 9px); + background: var(--editor-grip-bg); content: ""; display: inline-block; height: 9px; width: 64px; } +/* Every toolbar icon is cut from one raster sprite, so none of them can be + recoloured the way an icon font or an SVG could. The filter is the only lever + a colour mode has over them; it is `none` here and stays that way. */ .sceditor-button div { background-image: url("../images/icons/editor_sprite.png"); background-repeat: no-repeat; width: 16px; height: 16px; + filter: var(--editor-icon-filter); } .sceditor-button-youtube div { background-position: 0px 0px; @@ -152,7 +156,7 @@ position: relative; display: flex; flex-direction: column; - border: 1px solid #bbb; + border: 1px solid var(--editor-border-color); } .sceditor-container iframe, .sceditor-container textarea { @@ -171,12 +175,12 @@ div.sceditor-dnd-cover { left: 0; bottom: 0; right: 0; - background: rgba(255, 255, 255, 0.2); - border: 5px dashed #aaa; + background: var(--editor-dnd-bg); + border: 5px dashed var(--editor-dnd-border-color); z-index: 200; font-size: 2em; text-align: center; - color: #aaa; + color: var(--editor-dnd-color); } div.sceditor-dnd-cover p { position: relative; @@ -215,11 +219,11 @@ body.sceditor-maximize { } .sceditor-dropdown { position: absolute; - border: 1px solid #ccc; - background: #fff; + border: 1px solid var(--editor-dropdown-border-color); + background: var(--editor-dropdown-bg); z-index: 4000; border-radius: 2px; - box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.2); + box-shadow: var(--editor-dropdown-box-shadow); } .sceditor-dropdown:not(.sceditor-font-picker, .sceditor-item-picker) > div { display: grid; @@ -238,15 +242,15 @@ body.sceditor-maximize { } .sceditor-dropdown label { display: block; - color: #3c3c3c; + color: var(--editor-dropdown-label-color); padding: 4px 0; } .sceditor-dropdown a:hover { - background: #eee; + background: var(--editor-dropdown-link-bg_hover); text-decoration: none; } .sceditor-dropdown a { - color: #222; + color: var(--editor-dropdown-link-color); display: block; line-height: 1; padding: 7px 10px; @@ -269,7 +273,7 @@ body.sceditor-maximize { } .sceditor-group { display: inline-block; - background: #ddd; + background: var(--editor-group-bg); padding: 1px 2px; margin: 2px 4px; border-bottom: 1px solid #aaa; @@ -287,13 +291,13 @@ body.sceditor-maximize { .sceditor-button:not(.disabled):hover, .sceditor-button:active, .sceditor-button.active { - background: #fff; - box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.3), inset -1px 0 rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2); + background: var(--editor-button-bg_active); + box-shadow: var(--editor-button-box-shadow_active); } .sceditor-button:active { - background: #fff; - box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.3), inset -1px 0 rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2), inset 0 0 8px rgba(0, 0, 0, 0.3); + background: var(--editor-button-bg_pressed); + box-shadow: var(--editor-button-box-shadow_pressed); } .sceditor-button.disabled { cursor: default; @@ -401,14 +405,14 @@ body.sceditor-maximize { } .sceditor-insert-table-grid span { opacity: 0.5; - border: 1px solid #85a1b7; + border: 1px solid var(--editor-tablegrid-border-color); } .sceditor-insert-table-grid .active { - background: #85a1b7; + background: var(--editor-tablegrid-bg_active); } #popup { - background: #568d; + background: var(--editor-popup-bg); height: 100%; width: 100%; position: fixed; @@ -426,7 +430,7 @@ body.sceditor-maximize { transition: opacity 1s ease-in-out, visibility 1s ease-in-out; } #popup-container { - background: #f5f5f5; + background: var(--editor-popup-container-bg); border-radius: 0.4em; margin: 0 auto; padding: 1rem; diff --git a/Themes/default/css/variables.css b/Themes/default/css/variables.css index dd7b204ee9d..531980e7bcb 100644 --- a/Themes/default/css/variables.css +++ b/Themes/default/css/variables.css @@ -765,4 +765,27 @@ /** Embedded Video **/ --video-bg: black; + + /** Editor **/ + --editor-border-color: #bbb; + --editor-button-bg_active: #fff; + --editor-button-bg_pressed: #fff; + --editor-button-box-shadow_active: inset 1px 1px 0 rgba(0, 0, 0, 0.3), inset -1px 0 rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2); + --editor-button-box-shadow_pressed: inset 1px 1px 0 rgba(0, 0, 0, 0.3), inset -1px 0 rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2), inset 0 0 8px rgba(0, 0, 0, 0.3); + --editor-dnd-bg: rgba(255, 255, 255, 0.2); + --editor-dnd-border-color: #aaa; + --editor-dnd-color: #aaa; + --editor-dropdown-bg: #fff; + --editor-dropdown-border-color: #ccc; + --editor-dropdown-box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.2); + --editor-dropdown-label-color: #3c3c3c; + --editor-dropdown-link-bg_hover: #eee; + --editor-dropdown-link-color: #222; + --editor-icon-filter: none; + --editor-grip-bg: linear-gradient(#0000 0, #bbb 2px, #0000 3px, #bbb 5px, #0000 6px, #bbb 8px, #0000 9px); + --editor-group-bg: #ddd; + --editor-popup-bg: #568d; + --editor-popup-container-bg: #f5f5f5; + --editor-tablegrid-bg_active: #85a1b7; + --editor-tablegrid-border-color: #85a1b7; } From 0ae132e7891234feeef0e94a62f81ffe28fed4d3 Mon Sep 17 00:00:00 2001 From: albertlast Date: Tue, 11 Aug 2026 09:20:42 +0200 Subject: [PATCH 2/2] Tokenises the editor colours a property-name regex missed The first pass matched `border`, `border-*-color` and the usual paint properties, which left four behind: the grip's `border-top`, the toolbar group's `border-bottom`, and the `fill` on the SVG icon variant and its disabled state. All four are light-theme values, so in dark mode the toolbar group kept a pale hairline under it. jquery.sceditor.css now has no literal colour left. Light mode is unchanged, checked the same way as the first pass and with `fill` added to the properties recorded. Signed-off-by: Mathias Alberts Signed-off-by: albertlast --- Themes/default/css/dark.css | 4 ++++ Themes/default/css/jquery.sceditor.css | 8 ++++---- Themes/default/css/variables.css | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Themes/default/css/dark.css b/Themes/default/css/dark.css index 0ab51882ddc..48b84a7d1a0 100644 --- a/Themes/default/css/dark.css +++ b/Themes/default/css/dark.css @@ -485,6 +485,10 @@ --editor-dropdown-label-color: var(--dark-ink); --editor-dropdown-link-bg_hover: hsl(var(--primary-color-hue), 25%, 26%); --editor-dropdown-link-color: var(--dark-ink-bright); + --editor-grip-border-color: var(--dark-line); + --editor-group-border-color: var(--dark-line); + --editor-icon-fill: var(--dark-ink); + --editor-icon-fill_disabled: var(--dark-ink-dim); --editor-grip-bg: linear-gradient(#0000 0, hsl(var(--primary-color-hue), 10%, 40%) 2px, #0000 3px, hsl(var(--primary-color-hue), 10%, 40%) 5px, #0000 6px, hsl(var(--primary-color-hue), 10%, 40%) 8px, #0000 9px); /* The sprite is a mix: most icons are near-black line art, but a few carry /* their own colour - the YouTube mark, the coloured letters. Inverting alone diff --git a/Themes/default/css/jquery.sceditor.css b/Themes/default/css/jquery.sceditor.css index bbfe59fe38f..be681551b76 100644 --- a/Themes/default/css/jquery.sceditor.css +++ b/Themes/default/css/jquery.sceditor.css @@ -8,7 +8,7 @@ * https://opensource.org/licenses/mit-license.php */ .sceditor-grip { - border-top: 1px solid #bbb; + border-top: 1px solid var(--editor-grip-border-color); cursor: s-resize; padding-block: 4px; text-align: center; @@ -276,7 +276,7 @@ body.sceditor-maximize { background: var(--editor-group-bg); padding: 1px 2px; margin: 2px 4px; - border-bottom: 1px solid #aaa; + border-bottom: 1px solid var(--editor-group-border-color); border-radius: 3px; } .sceditor-button { @@ -307,12 +307,12 @@ body.sceditor-maximize { height: 16px; width: 16px; margin: 2px 0; - fill: #111; + fill: var(--editor-icon-fill); pointer-events: none; line-height: 1; } .sceditor-button.disabled svg { - fill: #888; + fill: var(--editor-icon-fill_disabled); } .sceditor-button div { margin: 2px 0; diff --git a/Themes/default/css/variables.css b/Themes/default/css/variables.css index 531980e7bcb..a7051b0d44a 100644 --- a/Themes/default/css/variables.css +++ b/Themes/default/css/variables.css @@ -782,6 +782,10 @@ --editor-dropdown-link-bg_hover: #eee; --editor-dropdown-link-color: #222; --editor-icon-filter: none; + --editor-grip-border-color: #bbb; + --editor-group-border-color: #aaa; + --editor-icon-fill: #111; + --editor-icon-fill_disabled: #888; --editor-grip-bg: linear-gradient(#0000 0, #bbb 2px, #0000 3px, #bbb 5px, #0000 6px, #bbb 8px, #0000 9px); --editor-group-bg: #ddd; --editor-popup-bg: #568d;