| @@ -1,4 +1,22 @@ | ||
| 1 | +/* | |
| 2 | + * The colour rules below set `background-color`, never the `background` | |
| 3 | + * shorthand — and that distinction is load-bearing, not a style preference. | |
| 4 | + * | |
| 5 | + * `background` resets every background property it does not mention, so a rule | |
| 6 | + * of the form `background: <colour> !important` does not just repaint an | |
| 7 | + * element: it sets `background-image: none` on it. Everything a design paints | |
| 8 | + * through that property disappears — a select's dropdown chevron, an icon on a | |
| 9 | + * button, a sprite, a texture, a pattern, a gradient. And because the rule is | |
| 10 | + * `!important`, it outranks even an inline `background-image`, so nothing the | |
| 11 | + * engine writes inline can put the image back. | |
| 12 | + * | |
| 13 | + * The pairing was invisible while every rule also declared `background-color` | |
| 14 | + * on the next line with the same value: the shorthand contributed nothing to | |
| 15 | + * the colour and only did the damage. Dropping it changes no colour anywhere | |
| 16 | + * and lets the design's own imagery survive into dark mode. | |
| 17 | + */ | |
| 18 | + | |
| 1 | 19 | .darkify_dark_mode_enabled |
| 2 | 20 | .darkify_style_all:not( |
| 3 | 21 | .darkify_switch, |
| 4 | 22 | .darkify_switch *, |
| @@ -4,12 +22,14 @@ | ||
| 4 | 22 | .darkify_switch *, |
| 5 | 23 | .darkify_ignore, |
| 6 | 24 | .darkify_ignore * |
| 7 | 25 | ) { |
| 8 | - background: var(--darkify_dark_mode_bg) !important; | |
| 9 | 26 | background-color: var(--darkify_dark_mode_bg) !important; |
| 10 | 27 | color: var(--darkify_dark_mode_text_color) !important; |
| 11 | - border-color: var(--darkify_dark_mode_border_color) !important; | |
| 28 | + border-right-color: var(--darkify_dark_mode_border_color) !important; | |
| 29 | + border-left-color: var(--darkify_dark_mode_border_color) !important; | |
| 30 | + border-top-color: var(--darkify_dark_mode_border_color) !important; | |
| 31 | + border-bottom-color: var(--darkify_dark_mode_border_color) !important; | |
| 12 | 32 | } |
| 13 | 33 | .darkify_dark_mode_enabled |
| 14 | 34 | .darkify_style_bg_txt:not( |
| 15 | 35 | .darkify_switch, |
| @@ -16,9 +36,8 @@ | ||
| 16 | 36 | .darkify_switch *, |
| 17 | 37 | .darkify_ignore, |
| 18 | 38 | .darkify_ignore * |
| 19 | 39 | ) { |
| 20 | - background: var(--darkify_dark_mode_bg) !important; | |
| 21 | 40 | background-color: var(--darkify_dark_mode_bg) !important; |
| 22 | 41 | color: var(--darkify_dark_mode_text_color) !important; |
| 23 | 42 | } |
| 24 | 43 | .darkify_dark_mode_enabled |
| @@ -27,11 +46,13 @@ | ||
| 27 | 46 | .darkify_switch *, |
| 28 | 47 | .darkify_ignore, |
| 29 | 48 | .darkify_ignore * |
| 30 | 49 | ) { |
| 31 | - background: var(--darkify_dark_mode_bg) !important; | |
| 32 | 50 | background-color: var(--darkify_dark_mode_bg) !important; |
| 33 | - border-color: var(--darkify_dark_mode_border_color) !important; | |
| 51 | + border-right-color: var(--darkify_dark_mode_border_color) !important; | |
| 52 | + border-left-color: var(--darkify_dark_mode_border_color) !important; | |
| 53 | + border-top-color: var(--darkify_dark_mode_border_color) !important; | |
| 54 | + border-bottom-color: var(--darkify_dark_mode_border_color) !important; | |
| 34 | 55 | } |
| 35 | 56 | .darkify_dark_mode_enabled |
| 36 | 57 | .darkify_style_txt_border:not( |
| 37 | 58 | .darkify_switch, |
| @@ -39,9 +60,12 @@ | ||
| 39 | 60 | .darkify_ignore, |
| 40 | 61 | .darkify_ignore * |
| 41 | 62 | ) { |
| 42 | 63 | color: var(--darkify_dark_mode_text_color) !important; |
| 43 | - border-color: var(--darkify_dark_mode_border_color) !important; | |
| 64 | + border-right-color: var(--darkify_dark_mode_border_color) !important; | |
| 65 | + border-left-color: var(--darkify_dark_mode_border_color) !important; | |
| 66 | + border-top-color: var(--darkify_dark_mode_border_color) !important; | |
| 67 | + border-bottom-color: var(--darkify_dark_mode_border_color) !important; | |
| 44 | 68 | } |
| 45 | 69 | .darkify_dark_mode_enabled |
| 46 | 70 | .darkify_style_bg:not( |
| 47 | 71 | .darkify_switch, |
| @@ -48,9 +72,8 @@ | ||
| 48 | 72 | .darkify_switch *, |
| 49 | 73 | .darkify_ignore, |
| 50 | 74 | .darkify_ignore * |
| 51 | 75 | ) { |
| 52 | - background: var(--darkify_dark_mode_bg) !important; | |
| 53 | 76 | background-color: var(--darkify_dark_mode_bg) !important; |
| 54 | 77 | } |
| 55 | 78 | .darkify_dark_mode_enabled |
| 56 | 79 | .darkify_style_txt:not( |
| @@ -67,9 +90,12 @@ | ||
| 67 | 90 | .darkify_switch *, |
| 68 | 91 | .darkify_ignore, |
| 69 | 92 | .darkify_ignore * |
| 70 | 93 | ) { |
| 71 | - border-color: var(--darkify_dark_mode_border_color) !important; | |
| 94 | + border-right-color: var(--darkify_dark_mode_border_color) !important; | |
| 95 | + border-left-color: var(--darkify_dark_mode_border_color) !important; | |
| 96 | + border-top-color: var(--darkify_dark_mode_border_color) !important; | |
| 97 | + border-bottom-color: var(--darkify_dark_mode_border_color) !important; | |
| 72 | 98 | } |
| 73 | 99 | .darkify_dark_mode_enabled |
| 74 | 100 | .darkify_style_secondary_bg:not( |
| 75 | 101 | .darkify_switch, |
| @@ -76,9 +102,8 @@ | ||
| 76 | 102 | .darkify_switch *, |
| 77 | 103 | .darkify_ignore, |
| 78 | 104 | .darkify_ignore * |
| 79 | 105 | ) { |
| 80 | - background: var(--darkify_dark_mode_secondary_bg) !important; | |
| 81 | 106 | background-color: var(--darkify_dark_mode_secondary_bg) !important; |
| 82 | 107 | } |
| 83 | 108 | |
| 84 | 109 | /*Handle Link Style*/ |
| @@ -101,8 +126,58 @@ | ||
| 101 | 126 | ):hover { |
| 102 | 127 | color: var(--darkify_dark_mode_link_hover_color) !important; |
| 103 | 128 | } |
| 104 | 129 | |
| 130 | +/* | |
| 131 | + * Colour overrides ("Replace Individual Colors"). | |
| 132 | + * | |
| 133 | + * The value arrives as a custom property set inline by | |
| 134 | + * darkify_process_color_overrides(); these rules are what actually apply it. | |
| 135 | + * It used to be written straight onto the property as an inline `!important`, | |
| 136 | + * which outranks every author rule in the cascade — so an overridden element | |
| 137 | + * could never show its dark-mode `:hover` colour. Moving the application into | |
| 138 | + * the stylesheet puts it back into normal specificity competition. | |
| 139 | + * | |
| 140 | + * The selector shape is deliberate, load-bearing, and measured against the | |
| 141 | + * rules it has to sit between: | |
| 142 | + * | |
| 143 | + * .darkify_dark_mode_enabled .darkify_style_x:not(...) = (0,3,0) | |
| 144 | + * .darkify_dark_mode_enabled .darkify_style_link:not(...) = (0,3,3) | |
| 145 | + * ^ higher than its siblings because its :not() list carries | |
| 146 | + * `.darkify-nav ul li a`, and :not() takes the specificity of its most | |
| 147 | + * specific argument — three type selectors' worth. | |
| 148 | + * .darkify_dark_mode_enabled .darkify_style_x:not(...):hover = (0,4,0) | |
| 149 | + * | |
| 150 | + * So the override must be ABOVE (0,3,3) and BELOW (0,4,0): that means exactly | |
| 151 | + * three classes and at least four types. The repeated class buys the classes; | |
| 152 | + * `html:is(html):is(html):is(html)` buys the types without changing what the | |
| 153 | + * selector matches, since :is() takes its argument's specificity and every | |
| 154 | + * argument here is the element the rule already starts from. | |
| 155 | + * | |
| 156 | + * Do NOT add a `:not()` or another class — a fourth class ties the hover rules | |
| 157 | + * on class count and the override starts winning hover again, which is the | |
| 158 | + * exact bug this replaced. Elements inside `.darkify_switch` / | |
| 159 | + * `.darkify_ignore` are never processed, so they never carry these classes. | |
| 160 | + */ | |
| 161 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_bg.darkify_ovr_bg { | |
| 162 | + background-color: var(--darkify-ovr-bg) !important; | |
| 163 | +} | |
| 164 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_color.darkify_ovr_color { | |
| 165 | + color: var(--darkify-ovr-color) !important; | |
| 166 | +} | |
| 167 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_bt.darkify_ovr_bt { | |
| 168 | + border-top-color: var(--darkify-ovr-bt) !important; | |
| 169 | +} | |
| 170 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_br.darkify_ovr_br { | |
| 171 | + border-right-color: var(--darkify-ovr-br) !important; | |
| 172 | +} | |
| 173 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_bb.darkify_ovr_bb { | |
| 174 | + border-bottom-color: var(--darkify-ovr-bb) !important; | |
| 175 | +} | |
| 176 | +html:is(html):is(html):is(html).darkify_dark_mode_enabled .darkify_ovr_bl.darkify_ovr_bl { | |
| 177 | + border-left-color: var(--darkify-ovr-bl) !important; | |
| 178 | +} | |
| 179 | + | |
| 105 | 180 | /*Handle Form Element Style*/ |
| 106 | 181 | .darkify_dark_mode_enabled |
| 107 | 182 | .darkify_style_form_element:not( |
| 108 | 183 | .darkify_switch, |
| @@ -109,9 +184,8 @@ | ||
| 109 | 184 | .darkify_switch *, |
| 110 | 185 | .darkify_ignore, |
| 111 | 186 | .darkify_ignore * |
| 112 | 187 | ) { |
| 113 | - background: var(--darkify_dark_mode_input_bg) !important; | |
| 114 | 188 | background-color: var(--darkify_dark_mode_input_bg) !important; |
| 115 | 189 | color: var(--darkify_dark_mode_input_text_color) !important; |
| 116 | 190 | } |
| 117 | 191 | .darkify_dark_mode_enabled |
| @@ -131,14 +205,160 @@ | ||
| 131 | 205 | .darkify_switch *, |
| 132 | 206 | .darkify_ignore, |
| 133 | 207 | .darkify_ignore * |
| 134 | 208 | ) { |
| 135 | - background: var(--darkify_dark_mode_btn_bg) !important; | |
| 136 | 209 | background-color: var(--darkify_dark_mode_btn_bg) !important; |
| 137 | 210 | color: var(--darkify_dark_mode_btn_text_color) !important; |
| 211 | + /* | |
| 212 | + * Buttons carry their own border token rather than the page's. | |
| 213 | + * | |
| 214 | + * The classifier takes `darkify_style_all` off an element the moment it calls | |
| 215 | + * it a button, so until these four lines existed a button's border was the | |
| 216 | + * one colour on the page dark mode never touched: an outline button kept the | |
| 217 | + * light design's brand edge — a teal ring around a grey fill — and no setting | |
| 218 | + * could reach it. A button also cannot simply borrow the generic border | |
| 219 | + * colour, because its border belongs to the button's own colour set: it is | |
| 220 | + * usually the fill or a shade of it, which is why the default for these | |
| 221 | + * tokens mirrors the button background. | |
| 222 | + */ | |
| 223 | + border-right-color: var(--darkify_dark_mode_btn_border_color) !important; | |
| 224 | + border-left-color: var(--darkify_dark_mode_btn_border_color) !important; | |
| 225 | + border-top-color: var(--darkify_dark_mode_btn_border_color) !important; | |
| 226 | + border-bottom-color: var(--darkify_dark_mode_btn_border_color) !important; | |
| 138 | 227 | } |
| 228 | +/* the hover state */ | |
| 229 | +.darkify_dark_mode_enabled | |
| 230 | + .darkify_style_button:not( | |
| 231 | + .darkify_switch, | |
| 232 | + .darkify_switch *, | |
| 233 | + .darkify_ignore, | |
| 234 | + .darkify_ignore * | |
| 235 | + ):hover { | |
| 236 | + background-color: var(--darkify_dark_mode_btn_hover_bg) !important; | |
| 237 | + color: var(--darkify_dark_mode_btn_text_hover_color) !important; | |
| 238 | + border-right-color: var(--darkify_dark_mode_btn_hover_border_color) !important; | |
| 239 | + border-left-color: var(--darkify_dark_mode_btn_hover_border_color) !important; | |
| 240 | + border-top-color: var(--darkify_dark_mode_btn_hover_border_color) !important; | |
| 241 | + border-bottom-color: var(--darkify_dark_mode_btn_hover_border_color) !important; | |
| 242 | +} | |
| 139 | 243 | |
| 244 | + | |
| 140 | 245 | /* ======================== |
| 246 | + Borders the design does not draw | |
| 247 | +===========================*/ | |
| 248 | +/* | |
| 249 | + * A side marked here had a width in the light design but no colour — the | |
| 250 | + * `border: 8px solid transparent` idiom themes and block grids use for gutters | |
| 251 | + * and alignment. It occupies space and paints nothing. | |
| 252 | + * | |
| 253 | + * The rules above cannot tell those sides apart from real ones: they set all | |
| 254 | + * four border colours at once, because a class cannot carry per-side | |
| 255 | + * information. Painting a gutter turns invisible spacing into a visible line, | |
| 256 | + * and an element ringed by four of them grows a box that the light design never | |
| 257 | + * had — the effect that made WooCommerce product cards appear boxed in dark | |
| 258 | + * mode while looking open in light. | |
| 259 | + * | |
| 260 | + * The classifier stamps these markers on exactly the sides it measured as | |
| 261 | + * transparent, so each one is pinned back to transparent regardless of which | |
| 262 | + * colour rule matched. They repeat the `:not(...)` shape of the rules they | |
| 263 | + * override so the specificity matches, and sit after them so they win the tie. | |
| 264 | + */ | |
| 265 | +.darkify_dark_mode_enabled | |
| 266 | + .darkify_border_keep_top:not( | |
| 267 | + .darkify_switch, | |
| 268 | + .darkify_switch *, | |
| 269 | + .darkify_ignore, | |
| 270 | + .darkify_ignore * | |
| 271 | + ) { | |
| 272 | + border-top-color: transparent !important; | |
| 273 | +} | |
| 274 | +.darkify_dark_mode_enabled | |
| 275 | + .darkify_border_keep_right:not( | |
| 276 | + .darkify_switch, | |
| 277 | + .darkify_switch *, | |
| 278 | + .darkify_ignore, | |
| 279 | + .darkify_ignore * | |
| 280 | + ) { | |
| 281 | + border-right-color: transparent !important; | |
| 282 | +} | |
| 283 | +.darkify_dark_mode_enabled | |
| 284 | + .darkify_border_keep_bottom:not( | |
| 285 | + .darkify_switch, | |
| 286 | + .darkify_switch *, | |
| 287 | + .darkify_ignore, | |
| 288 | + .darkify_ignore * | |
| 289 | + ) { | |
| 290 | + border-bottom-color: transparent !important; | |
| 291 | +} | |
| 292 | +.darkify_dark_mode_enabled | |
| 293 | + .darkify_border_keep_left:not( | |
| 294 | + .darkify_switch, | |
| 295 | + .darkify_switch *, | |
| 296 | + .darkify_ignore, | |
| 297 | + .darkify_ignore * | |
| 298 | + ) { | |
| 299 | + border-left-color: transparent !important; | |
| 300 | +} | |
| 301 | + | |
| 302 | +/* ======================== | |
| 303 | + Pre-paint baseline (FOUC) | |
| 304 | +===========================*/ | |
| 305 | +/* | |
| 306 | + * Dark mode is decided in the <head>, but the colours come from a DOM walk that | |
| 307 | + * cannot start until the DOM exists. Something has to cover that gap. | |
| 308 | + * | |
| 309 | + * It cannot be a colour wash on the elements themselves. The engine derives | |
| 310 | + * each dark colour by *reading* the element's computed light colour, so any | |
| 311 | + * rule that restyles elements beforehand destroys its input — everything reads | |
| 312 | + * back as the wash and nothing can be derived. | |
| 313 | + * | |
| 314 | + * A `brightness()` filter on <html> used to fill the gap. It painted only, so | |
| 315 | + * `getComputedStyle` still reported the design's real colours, but it dimmed | |
| 316 | + * the *whole viewport* — on a slow page that reads as a grey overlay sitting | |
| 317 | + * over the site for the entire load, and it repeats on every navigation. The | |
| 318 | + * filter is gone for that reason. | |
| 319 | + * | |
| 320 | + * What is left is the page backdrop alone: the root paints the configured dark | |
| 321 | + * background from the first frame, so the gap before the walk is a dark page | |
| 322 | + * rather than a white one. Content that has already painted keeps its light | |
| 323 | + * colours for those few frames — a much smaller artefact than dimming the | |
| 324 | + * entire viewport. | |
| 325 | + * | |
| 326 | + * Deliberately temporary: the class comes off the moment the first walk has | |
| 327 | + * assigned real colours. | |
| 328 | + */ | |
| 329 | +html.darkify_dark_mode_enabled.darkify_prepaint { | |
| 330 | + background-color: var(--darkify_dark_mode_bg) !important; | |
| 331 | +} | |
| 332 | + | |
| 333 | +/* | |
| 334 | + * The gap the backdrop above cannot cover. | |
| 335 | + * | |
| 336 | + * `html`'s dark background is only visible while nothing paints over it, and | |
| 337 | + * on essentially every theme `body` carries its own opaque light background, | |
| 338 | + * which does. So the intent of the rule above — "the gap before the walk is a | |
| 339 | + * dark page rather than a white one" — was defeated in practice: reloading with | |
| 340 | + * dark mode on showed the site's full light design for as long as the walk took, | |
| 341 | + * then flipped. That flip is the flicker. | |
| 342 | + * | |
| 343 | + * It cannot be fixed by washing the elements: the engine derives every dark | |
| 344 | + * colour by reading the element's computed light colour, so a colour applied | |
| 345 | + * beforehand becomes the input and the derivation collapses. `visibility` is | |
| 346 | + * the one property that hides the artefact without being read — it does not | |
| 347 | + * change any computed colour, and unlike `display` it keeps layout, so the walk | |
| 348 | + * measures exactly what it would have measured anyway. | |
| 349 | + * | |
| 350 | + * Scoped to its own class rather than `darkify_prepaint` so it can be dropped | |
| 351 | + * the instant the first walk has assigned colours, which is earlier than the | |
| 352 | + * backdrop is dropped. The inline script in the page head clears it on a | |
| 353 | + * failsafe timer regardless, so a failed or missing engine can never leave the | |
| 354 | + * page hidden. | |
| 355 | + */ | |
| 356 | +html.darkify_dark_mode_enabled.darkify_fouc_guard body { | |
| 357 | + visibility: hidden !important; | |
| 358 | +} | |
| 359 | + | |
| 360 | +/* ======================== | |
| 141 | 361 | Admin Bar Menu Design |
| 142 | 362 | ===========================*/ |
| 143 | 363 | .darkify_admin_bar_switch_container { |
| 144 | 364 | cursor: pointer !important; |
| @@ -147,18 +367,25 @@ | ||
| 147 | 367 | justify-content: center; |
| 148 | 368 | width: 40px !important; |
| 149 | 369 | height: 32px !important; |
| 150 | 370 | } |
| 371 | +/* On Darkify's own settings/help screens the admin-bar node is always rendered | |
| 372 | + so the React admin can reveal it the instant "Admin Panel Dark Mode" is | |
| 373 | + switched on (see WpAdminBarBridge) rather than only after a reload. It starts | |
| 374 | + hidden whenever the option is off. */ | |
| 375 | +.darkify_admin_bar_switch_container.darkify_admin_bar_hidden { | |
| 376 | + display: none !important; | |
| 377 | +} | |
| 151 | 378 | .darkify_admin_bar_switch_container .ab-item { |
| 152 | 379 | height: 18px !important; |
| 153 | 380 | cursor: pointer !important; |
| 154 | 381 | background-color: #ffffff !important; |
| 155 | - -webkit-mask: url(../img/switch_icons/switch_apple_light.svg) no-repeat center; | |
| 156 | - mask: url(../img/switch_icons/switch_apple_light.svg) no-repeat center; | |
| 382 | + -webkit-mask: url(../../Admin/assets/images/switch_apple_light.svg) no-repeat center; | |
| 383 | + mask: url(../../Admin/assets/images/switch_apple_light.svg) no-repeat center; | |
| 157 | 384 | } |
| 158 | 385 | .darkify_dark_mode_enabled .darkify_admin_bar_switch_container .ab-item { |
| 159 | - -webkit-mask: url(../img/switch_icons/switch_apple_dark.svg) no-repeat center; | |
| 160 | - mask: url(../img/switch_icons/switch_apple_dark.svg) no-repeat center; | |
| 386 | + -webkit-mask: url(../../Admin/assets/images/switch_apple_dark.svg) no-repeat center; | |
| 387 | + mask: url(../../Admin/assets/images/switch_apple_dark.svg) no-repeat center; | |
| 161 | 388 | } |
| 162 | 389 | |
| 163 | 390 | /* ======================== |
| 164 | 391 | Block Editor Menu Design |
| @@ -172,8 +399,9 @@ | ||
| 172 | 399 | border: 0; |
| 173 | 400 | background: #121116; |
| 174 | 401 | border-radius: 3px; |
| 175 | 402 | padding: 0 12px; |
| 403 | + margin-right: 8px; | |
| 176 | 404 | } |
| 177 | 405 | .darkify_dark_mode_enabled .darkify_block_editor_switch { |
| 178 | 406 | background: #ffffff; |
| 179 | 407 | } |
| @@ -180,34 +408,22 @@ | ||
| 180 | 408 | .darkify_block_editor_switch .icon { |
| 181 | 409 | width: 20px; |
| 182 | 410 | height: 20px; |
| 183 | 411 | background-color: #ffffff !important; |
| 412 | + -webkit-mask: url(../../Admin/assets/images/switch_apple_light.svg) no-repeat center; | |
| 413 | + mask: url(../../Admin/assets/images/switch_apple_light.svg) no-repeat center; | |
| 184 | 414 | } |
| 185 | 415 | |
| 416 | +.darkify_dark_mode_enabled .darkify_block_editor_switch .icon { | |
| 417 | + background-color: #171717 !important; | |
| 418 | + -webkit-mask: url(../../Admin/assets/images/switch_apple_dark.svg) no-repeat center; | |
| 419 | + mask: url(../../Admin/assets/images/switch_apple_dark.svg) no-repeat center; | |
| 420 | +} | |
| 421 | + | |
| 186 | 422 | /* ======================== |
| 187 | 423 | Switch Design |
| 188 | 424 | ===========================*/ |
| 189 | 425 | |
| 190 | -.darkify_switch { | |
| 191 | - --darkify_switch_position_from_top_right_top: 40px; | |
| 192 | - --darkify_switch_position_from_top_right_right: 40px; | |
| 193 | - --darkify_switch_position_from_bottom_right_bottom: 40px; | |
| 194 | - --darkify_switch_position_from_bottom_right_right: 40px; | |
| 195 | - --darkify_switch_position_from_top_left_top: 40px; | |
| 196 | - --darkify_switch_position_from_top_left_left: 40px; | |
| 197 | - --darkify_switch_position_from_bottom_left_bottom: 40px; | |
| 198 | - --darkify_switch_position_from_bottom_left_left: 40px; | |
| 199 | - --darkify_switch_position_from_top_right_top_in_mobile: 40px; | |
| 200 | - --darkify_switch_position_from_top_right_right_in_mobile: 40px; | |
| 201 | - --darkify_switch_position_from_bottom_right_bottom_in_mobile: 40px; | |
| 202 | - --darkify_switch_position_from_bottom_right_right_in_mobile: 40px; | |
| 203 | - --darkify_switch_position_from_top_left_top_in_mobile: 40px; | |
| 204 | - --darkify_switch_position_from_top_left_left_in_mobile: 40px; | |
| 205 | - --darkify_switch_position_from_bottom_left_bottom_in_mobile: 40px; | |
| 206 | - --darkify_switch_position_from_bottom_left_left_in_mobile: 40px; | |
| 207 | - --darkify_switch_tooltip_bg_color: #142434; | |
| 208 | - --darkify_switch_tooltip_text_color: #b0cbe7; | |
| 209 | -} | |
| 210 | 426 | .darkify_switch, |
| 211 | 427 | .darkify_switch * { |
| 212 | 428 | box-sizing: border-box; |
| 213 | 429 | } |
| @@ -218,14 +434,14 @@ | ||
| 218 | 434 | position: fixed; |
| 219 | 435 | z-index: 99999; |
| 220 | 436 | } |
| 221 | 437 | .darkify_switch.darkify_top_left { |
| 222 | - top: var(--darkify_switch_position_from_top_left_left_in_mobile); | |
| 223 | - left: var(--darkify_switch_position_from_top_left_left_in_mobile); | |
| 438 | + top: var(--darkify_switch_position_from_top_left_top); | |
| 439 | + left: var(--darkify_switch_position_from_top_left_left); | |
| 224 | 440 | } |
| 225 | 441 | .darkify_switch.darkify_top_right { |
| 226 | - top: var(--darkify_switch_position_from_top_right_top_in_mobile); | |
| 227 | - right: var(--darkify_switch_position_from_top_right_right_in_mobile); | |
| 442 | + top: var(--darkify_switch_position_from_top_right_top); | |
| 443 | + right: var(--darkify_switch_position_from_top_right_right); | |
| 228 | 444 | } |
| 229 | 445 | .darkify_switch.darkify_bottom_left { |
| 230 | 446 | bottom: var(--darkify_switch_position_from_bottom_left_bottom); |
| 231 | 447 | left: var(--darkify_switch_position_from_bottom_left_left); |
| @@ -235,9 +451,8 @@ | ||
| 235 | 451 | right: var(--darkify_switch_position_from_bottom_right_right); |
| 236 | 452 | } |
| 237 | 453 | .darkify_switch.darkify_absolute_position { |
| 238 | 454 | position: absolute; |
| 239 | - z-index: unset; | |
| 240 | 455 | } |
| 241 | 456 | |
| 242 | 457 | @media only screen and (max-width: 600px) { |
| 243 | 458 | .darkify_switch.darkify_hide_on_mobile { |
| @@ -285,11 +500,12 @@ | ||
| 285 | 500 | color: var(--darkify_switch_tooltip_text_color); |
| 286 | 501 | font-size: 12px; |
| 287 | 502 | text-align: center; |
| 288 | 503 | border-radius: 0; |
| 289 | - padding: 5px 0; | |
| 504 | + padding: 5px 5px; | |
| 290 | 505 | position: absolute; |
| 291 | 506 | z-index: 1; |
| 507 | + border-radius: 3px; | |
| 292 | 508 | } |
| 293 | 509 | .darkify_tooltip .darkify_tooltiptext::after { |
| 294 | 510 | content: ""; |
| 295 | 511 | position: absolute; |
| @@ -348,44 +564,337 @@ | ||
| 348 | 564 | |
| 349 | 565 | /* ============== |
| 350 | 566 | Switch Style |
| 351 | 567 | ============== */ |
| 568 | + | |
| 352 | 569 | .darkify_switch_style { |
| 353 | - --darkify_switch_width_height: 60px; | |
| 354 | - --darkify_switch_icon_size: 50px; | |
| 355 | - --darkify_switch_border_radius: 7px; | |
| 356 | - --darkify_switch_light_mode_bg: #121116; | |
| 357 | - --darkify_switch_dark_mode_bg: #ffffff; | |
| 358 | - --darkify_switch_light_mode_color: #ffffff; | |
| 359 | - --darkify_switch_dark_mode_color: #121116; | |
| 570 | + cursor: pointer; | |
| 571 | + z-index: 9999; | |
| 572 | + width: calc(60px * var(--darkify-switch-scale, 1)); | |
| 573 | + height: calc(60px * var(--darkify-switch-scale, 1)); | |
| 574 | + border-radius: var(--darkify_switch_border_radius); | |
| 575 | + background: var(--darkify_switch_light_mode_bg); | |
| 576 | + display: flex; | |
| 577 | + align-items: center; | |
| 578 | + justify-content: center; | |
| 579 | + border-width: var(--darkify_switch_border); | |
| 580 | + border-style: solid; | |
| 581 | + border-color: var(--darkify_switch_border_light_color); | |
| 360 | 582 | } |
| 361 | -.darkify_switch_style{ | |
| 362 | - cursor: pointer; | |
| 363 | - z-index: 9999; | |
| 364 | - width: var(--darkify_switch_width_height); | |
| 365 | - height: var(--darkify_switch_width_height); | |
| 366 | - border-radius: var(--darkify_switch_border_radius); | |
| 367 | - background: var(--darkify_switch_light_mode_bg); | |
| 368 | - display: flex; | |
| 369 | - align-items: center; | |
| 370 | - justify-content: center; | |
| 371 | - fill: var(--darkify_switch_light_mode_icon_color); | |
| 372 | - stroke: var(--darkify_switch_light_mode_icon_color); | |
| 583 | + | |
| 584 | +.darkify_dark_mode_enabled .darkify_switch_style { | |
| 585 | + border-color: var(--darkify_switch_border_dark_color); | |
| 373 | 586 | } |
| 374 | -.darkify_switch_style .theme-toggle{ | |
| 375 | - display: flex; | |
| 376 | - align-items: center; | |
| 377 | - justify-content: center; | |
| 587 | +.darkify_switch_style .theme-toggle { | |
| 588 | + display: flex; | |
| 589 | + align-items: center; | |
| 590 | + justify-content: center; | |
| 378 | 591 | } |
| 379 | 592 | .darkify_switch_style svg { |
| 380 | - width: var(--darkify_switch_icon_size); | |
| 381 | - height: var(--darkify_switch_icon_size); | |
| 382 | - color: var(--darkify_switch_light_mode_color); | |
| 383 | - stroke: var(--darkify_switch_light_mode_color); | |
| 593 | + width: calc(40px * var(--darkify-switch-scale, 1)); | |
| 594 | + height: calc(40px * var(--darkify-switch-scale, 1)); | |
| 595 | + color: var(--darkify_switch_light_mode_color); | |
| 596 | + stroke: var(--darkify_switch_light_mode_color); | |
| 384 | 597 | } |
| 385 | -.darkify_dark_mode_enabled .darkify_switch_style{ | |
| 386 | - background: var(--darkify_switch_dark_mode_bg); | |
| 598 | +.darkify_dark_mode_enabled .darkify_switch_style { | |
| 599 | + background: var(--darkify_switch_dark_mode_bg); | |
| 387 | 600 | } |
| 388 | 601 | .darkify_dark_mode_enabled .darkify_switch_style svg { |
| 389 | - color: var(--darkify_switch_dark_mode_color); | |
| 390 | - stroke: var(--darkify_switch_dark_mode_color); | |
| 391 | -} | |
| 602 | + color: var(--darkify_switch_dark_mode_color); | |
| 603 | + stroke: var(--darkify_switch_dark_mode_color); | |
| 604 | +} | |
| 605 | +.switch-orbit, | |
| 606 | +.switch-duality, | |
| 607 | +.switch-dual, | |
| 608 | +.switch-shift { | |
| 609 | + box-sizing: content-box; | |
| 610 | +} | |
| 611 | +.darkify_dark_mode_enabled | |
| 612 | + .block-editor-page.post-content-style-boxed | |
| 613 | + .editor-styles-wrapper:before { | |
| 614 | + background-color: unset; | |
| 615 | +} | |
| 616 | +.darkify_dark_mode_enabled h3#color-palette-1-0, | |
| 617 | +.darkify_dark_mode_enabled .components-color-palette__custom-color-name { | |
| 618 | + color: var(--darkify_dark_mode_text_color)!important; | |
| 619 | +} | |
| 620 | +.darkify-dropdown { | |
| 621 | + position: relative; | |
| 622 | + width: 160px; | |
| 623 | + margin-left: 8px; | |
| 624 | +} | |
| 625 | + | |
| 626 | +.darkify-dropdown .darkify-selected { | |
| 627 | + padding: 5px; | |
| 628 | + background: #fff; | |
| 629 | + color: #000; | |
| 630 | + cursor: pointer; | |
| 631 | + border-radius: 3px; | |
| 632 | + border: 1px solid #c0c0c0; | |
| 633 | +} | |
| 634 | + | |
| 635 | +.darkify-dropdown .darkify-options { | |
| 636 | + display: none; | |
| 637 | + position: absolute; | |
| 638 | + width: 100%; | |
| 639 | + background: #fff; | |
| 640 | + border-radius: 6px; | |
| 641 | + margin-top: 5px; | |
| 642 | + overflow: hidden; | |
| 643 | + z-index: 1000; | |
| 644 | + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | |
| 645 | + color: #000; | |
| 646 | + top: 30px; | |
| 647 | +} | |
| 648 | + | |
| 649 | +.darkify-dropdown .darkify-options.show { | |
| 650 | + display: block; | |
| 651 | +} | |
| 652 | + | |
| 653 | +.darkify-dropdown .darkify-option { | |
| 654 | + padding: 8px; | |
| 655 | + cursor: pointer; | |
| 656 | + display: flex; | |
| 657 | + align-items: center; | |
| 658 | + gap: 4px; | |
| 659 | +} | |
| 660 | +.darkify-dropdown .darkify-pro-option { | |
| 661 | + cursor: not-allowed; | |
| 662 | + opacity: 0.55; | |
| 663 | + pointer-events: none; | |
| 664 | + gap: 4px; | |
| 665 | +} | |
| 666 | +.darkify-option-pro-badge { | |
| 667 | + margin-left: auto; | |
| 668 | + font-size: 10px; | |
| 669 | + font-weight: 600; | |
| 670 | + background: #cbd6df; | |
| 671 | + color: #5e6e7d; | |
| 672 | + padding: 2px 5px; | |
| 673 | + border-radius: 3px; | |
| 674 | + white-space: nowrap; | |
| 675 | + flex-shrink: 0; | |
| 676 | +} | |
| 677 | +.darkify-dropdown .darkify-selected { | |
| 678 | + display: flex; | |
| 679 | + align-items: center; | |
| 680 | + width: 100%; | |
| 681 | + gap: 4px; | |
| 682 | +} | |
| 683 | +.darkify-dropdown .theme_color { | |
| 684 | + display: inline-block; | |
| 685 | + width: 20px; | |
| 686 | + height: 20px; | |
| 687 | + border-radius: 50%; | |
| 688 | + border: 1px solid gray; | |
| 689 | +} | |
| 690 | +.darkify-dropdown .default { | |
| 691 | + background-color: #fff; | |
| 692 | +} | |
| 693 | +/* | |
| 694 | + * "Auto" has no single colour to show — Dark Reader derives one per element — | |
| 695 | + * so the swatch shows a gradient rather than claiming a flat value the way the | |
| 696 | + * named presets legitimately can. | |
| 697 | + */ | |
| 698 | +.darkify-dropdown .auto_palette { | |
| 699 | + background-image: linear-gradient(135deg, #0f0f0f 0%, #3a3a3a 50%, #8f8f8f 100%); | |
| 700 | +} | |
| 701 | +.darkify-dropdown .carbon_mist { | |
| 702 | + background-color: #171717; | |
| 703 | +} | |
| 704 | +.darkify-dropdown .midnight_reverie { | |
| 705 | + background-color: #4e478d; | |
| 706 | +} | |
| 707 | +.darkify-dropdown .verdant_depths { | |
| 708 | + background-color: #073d2f; | |
| 709 | +} | |
| 710 | +.darkify-dropdown .celestial_tide { | |
| 711 | + background-color: #144e78; | |
| 712 | +} | |
| 713 | +.darkify-dropdown .emberwood { | |
| 714 | + background-color: #372911; | |
| 715 | +} | |
| 716 | +.darkify-dropdown .glacier_drift { | |
| 717 | + background-color: #123d52; | |
| 718 | +} | |
| 719 | +.darkify-dropdown .indigo_veil { | |
| 720 | + background-color: #46598c; | |
| 721 | +} | |
| 722 | +.darkify-dropdown .duskmire { | |
| 723 | + background-color: #403953; | |
| 724 | +} | |
| 725 | +.darkify-dropdown .tidal_frost { | |
| 726 | + background-color: #286f8d; | |
| 727 | +} | |
| 728 | +.darkify-dropdown .blue_bastion { | |
| 729 | + background-color: #244892; | |
| 730 | +} | |
| 731 | +.darkify-dropdown .crimson_veil { | |
| 732 | + background-color: #440649; | |
| 733 | +} | |
| 734 | + | |
| 735 | +.darkify_dark_mode_enabled .darkify-dropdown, | |
| 736 | +.darkify_dark_mode_enabled .darkify-options.show { | |
| 737 | + background-color: var(--darkify_dark_mode_bg) !important; | |
| 738 | +} | |
| 739 | + | |
| 740 | +/* Switch Attention Effects */ | |
| 741 | +@keyframes darkify_attention_pulse { | |
| 742 | + 0%, 100% { transform: scale(1); } | |
| 743 | + 50% { transform: scale(1.1); } | |
| 744 | +} | |
| 745 | +@keyframes darkify_attention_radiance { | |
| 746 | + 0%, 100% { box-shadow: 0 0 0 0 rgba(100, 100, 100, 0.45); } | |
| 747 | + 50% { box-shadow: 0 0 0 14px rgba(100, 100, 100, 0); } | |
| 748 | +} | |
| 749 | +@keyframes darkify_attention_leap { | |
| 750 | + 0%, 55%, 100% { transform: translateY(0); } | |
| 751 | + 30% { transform: translateY(-14px); } | |
| 752 | + 45% { transform: translateY(-8px); } | |
| 753 | +} | |
| 754 | +@keyframes darkify_attention_swing { | |
| 755 | + 0%, 100% { transform: rotate(0deg); transform-origin: center bottom; } | |
| 756 | + 20% { transform: rotate(-9deg); transform-origin: center bottom; } | |
| 757 | + 40% { transform: rotate(7deg); transform-origin: center bottom; } | |
| 758 | + 60% { transform: rotate(-5deg); transform-origin: center bottom; } | |
| 759 | + 80% { transform: rotate(3deg); transform-origin: center bottom; } | |
| 760 | +} | |
| 761 | +@keyframes darkify_attention_drift { | |
| 762 | + 0%, 100% { transform: translateY(0); } | |
| 763 | + 50% { transform: translateY(-8px); } | |
| 764 | +} | |
| 765 | +@keyframes darkify_attention_nudge { | |
| 766 | + 0%, 65%, 100% { transform: translateX(0); } | |
| 767 | + 10% { transform: translateX(-6px); } | |
| 768 | + 20% { transform: translateX(6px); } | |
| 769 | + 30% { transform: translateX(-4px); } | |
| 770 | + 40% { transform: translateX(4px); } | |
| 771 | + 50% { transform: translateX(-2px); } | |
| 772 | + 57% { transform: translateX(2px); } | |
| 773 | +} | |
| 774 | +.darkify_switch.darkify_attention_pulse { | |
| 775 | + animation: darkify_attention_pulse 2s ease-in-out 2s infinite; | |
| 776 | +} | |
| 777 | +.darkify_switch.darkify_attention_radiance { | |
| 778 | + animation: darkify_attention_radiance 2s ease-in-out 2s infinite; | |
| 779 | +} | |
| 780 | +.darkify_switch.darkify_attention_leap { | |
| 781 | + animation: darkify_attention_leap 2.5s ease-in-out 2s infinite; | |
| 782 | +} | |
| 783 | +.darkify_switch.darkify_attention_swing { | |
| 784 | + animation: darkify_attention_swing 1.6s ease-in-out 2s infinite; | |
| 785 | +} | |
| 786 | +.darkify_switch.darkify_attention_drift { | |
| 787 | + animation: darkify_attention_drift 3s ease-in-out 2s infinite; | |
| 788 | +} | |
| 789 | +.darkify_switch.darkify_attention_nudge { | |
| 790 | + animation: darkify_attention_nudge 2.5s ease-in-out 2s infinite; | |
| 791 | +} | |
| 792 | +.darkify_switch.darkify_attention_pulse:hover, | |
| 793 | +.darkify_switch.darkify_attention_radiance:hover, | |
| 794 | +.darkify_switch.darkify_attention_leap:hover, | |
| 795 | +.darkify_switch.darkify_attention_swing:hover, | |
| 796 | +.darkify_switch.darkify_attention_drift:hover, | |
| 797 | +.darkify_switch.darkify_attention_nudge:hover { | |
| 798 | + animation-play-state: paused; | |
| 799 | +} | |
| 800 | +/* | |
| 801 | + * Transition suppression for the duration of an engine pass. | |
| 802 | + * | |
| 803 | + * The engine classifies an element from its computed colours, and a colour | |
| 804 | + * mid-transition computes to an interpolated value that belongs to neither the | |
| 805 | + * start nor the end state — so a pass that runs while the page is animating | |
| 806 | + * reads colours the design never actually paints. Suppressing transitions for | |
| 807 | + * the length of the pass makes every read a settled one. | |
| 808 | + * | |
| 809 | + * This replaces an inline `transition-property: none !important` that was | |
| 810 | + * written to, and then removed from, every element individually. Writing a | |
| 811 | + * style and then reading a computed style off the same element forces a | |
| 812 | + * synchronous style recalculation, so that pattern cost one full recalc per | |
| 813 | + * element; as one class on the root it costs one invalidation per pass. | |
| 814 | + * | |
| 815 | + * `!important` is warranted here and is the reason the declaration can stay a | |
| 816 | + * single rule: it has to beat inline transitions written by builders and | |
| 817 | + * animation libraries, which is precisely the case it exists to neutralise. It | |
| 818 | + * is present only while a pass is running — never in the page's resting state. | |
| 819 | + */ | |
| 820 | +/* | |
| 821 | + * The switcher is deliberately outside this suppression, and leaving it in was | |
| 822 | + * a real bug: it silently removed the toggle's animation. | |
| 823 | + * | |
| 824 | + * The switcher is the one animated thing on the page that the engine does not | |
| 825 | + * manage — it never receives `darkify_processed`, its colours are never read to | |
| 826 | + * classify it, and its whole visual job is to animate between the two states. | |
| 827 | + * Suppressing transitions on it therefore protects nothing and costs the | |
| 828 | + * feature. | |
| 829 | + * | |
| 830 | + * The damage is not obvious from reading the rule, because the class is added | |
| 831 | + * and removed inside a single synchronous task, so it is never painted. What | |
| 832 | + * matters is that the pass calls `getComputedStyle` thousands of times while | |
| 833 | + * the class is on, and every one of those forces a style recalculation. A | |
| 834 | + * recalculation that sees `transition-property: none` *cancels the transitions | |
| 835 | + * already running* — and the switcher's transitions started moments earlier, | |
| 836 | + * when the state class flipped. Removing the class afterwards does not revive | |
| 837 | + * them; the values have already been committed to their end state. Measured on | |
| 838 | + * the stress page: 17 transitions created on `d`, `opacity` and `transform`, | |
| 839 | + * all with `currentTime` still 0, gone by the next frame — the toggle snapped | |
| 840 | + * instead of animating. | |
| 841 | + * | |
| 842 | + * `.darkify_ignore` is excluded for the same reason it is excluded from every | |
| 843 | + * other rule in this file: it marks a subtree the plugin has been told to keep | |
| 844 | + * its hands off, and cancelling its animations is exactly the interference the | |
| 845 | + * marker exists to prevent. | |
| 846 | + * | |
| 847 | + * This costs nothing. The exclusion is evaluated only while a pass is running, | |
| 848 | + * and it removes work rather than adding it — the excluded subtrees are the | |
| 849 | + * ones the engine was never going to read. | |
| 850 | + */ | |
| 851 | +html.darkify_suspend_transitions, | |
| 852 | +html.darkify_suspend_transitions | |
| 853 | + *:not( | |
| 854 | + .darkify_switch, | |
| 855 | + .darkify_switch *, | |
| 856 | + .darkify_ignore, | |
| 857 | + .darkify_ignore * | |
| 858 | + ) { | |
| 859 | + transition-property: none !important; | |
| 860 | +} | |
| 861 | + | |
| 862 | +/* | |
| 863 | + * The same suppression, scoped to a single element — and now to a single case. | |
| 864 | + * | |
| 865 | + * The rule above is correct for the two passes that read the whole document — | |
| 866 | + * the first walk and the dark-mode state sweep — because at those moments the | |
| 867 | + * page is either not yet interactive or is being repainted wholesale anyway. | |
| 868 | + * This rule covers the incremental passes, where exactly one of the two things | |
| 869 | + * that reach them wants it: | |
| 870 | + * | |
| 871 | + * - An element being coloured for the FIRST time (a node that arrived after | |
| 872 | + * the initial walk) gets the class. Without it, dark mode *animates* onto | |
| 873 | + * the element and it fades from its light colour to its dark one in full | |
| 874 | + * view. On a hard reload, with nothing in cache, far more of the page | |
| 875 | + * arrives late and is coloured here rather than by the walk the FOUC guard | |
| 876 | + * hides — which is what made backgrounds visibly wash from white to dark. | |
| 877 | + * | |
| 878 | + * - An element being RE-READ because its class list changed (an accordion | |
| 879 | + * opening, a dropdown, a hover state) must NOT get it, so the animation | |
| 880 | + * that triggered the pass still eases. Applying it here was what made those | |
| 881 | + * snap to their end state. | |
| 882 | + * | |
| 883 | + * The class is applied around the read and released on the next frame rather | |
| 884 | + * than synchronously (see darkify_release_no_transition in client_main.js): a | |
| 885 | + * same-task removal means the browser only ever computes the finished state — | |
| 886 | + * new colour with transitions live — and eases into it, which is the very thing | |
| 887 | + * being prevented. | |
| 888 | + * | |
| 889 | + * `transition-property` is not inherited, so this cancels the element's own | |
| 890 | + * transitions and nothing below it. It is also a reasonable thing to put on an | |
| 891 | + * element by hand. | |
| 892 | + */ | |
| 893 | +.darkify_no_transition:not( | |
| 894 | + .darkify_switch, | |
| 895 | + .darkify_switch *, | |
| 896 | + .darkify_ignore, | |
| 897 | + .darkify_ignore * | |
| 898 | + ) { | |
| 899 | + transition-property: none !important; | |
| 900 | +} | |