MediaWiki:Gadget-dark-mode.css: Difference between revisions
From the Audiovisual Identity Database, the motion graphics museum
Content deleted Content added
Created page with "→* * Enables or disables the dark-mode gadget. * * Authors: [[User:SD0001]], [[User:Nardog]]: // 'Dark mode' and 'Light mode' messages must match the ::before content in // MediaWiki:Gadget-dark-mode-toggle-pagestyles.css and MediaWiki:Gadget-dark-mode.css, respectively. // Don't overwrite existing messages, if already set on a foreign wiki prior to loading this file if (!mw.messages.get('darkmode-turn-on-label')) { mw.messages.set({ 'darkmode-turn-on-..." |
No edit summary |
||
Line 1: | Line 1: | ||
/* WikimediaUI Dark Mode |
|||
/** |
|||
* |
|||
* Enables or disables the dark-mode gadget. |
|||
* Wikimedia Design Team 2019-2021 |
|||
* Original authors: |
|||
* - Volker E. – [[User:Volker_E._(WMF)]] |
|||
* - Alex Hollender |
|||
* - MusikAnimal |
|||
* - Carolyn Li-Madeo |
|||
* - Jdlrobson |
|||
* |
|||
* Original at https://en.wikipedia.org/wiki/User:Volker_E._(WMF)/dark-mode.css |
|||
* Version for Gadget CSS skin override usage only. |
|||
* Basically removed of all interaction element styles and |
|||
* set to `html` instead of JS injected `.client-dark-mode` class. |
|||
* |
|||
* Last updated: 2021-04-20 |
|||
* |
* |
||
* Authors: [[User:SD0001]], [[User:Nardog]] |
|||
*/ |
*/ |
||
/** To prevent 'jumping' effect within #p-personal in Vector/Monobook. Overrides [[MediaWiki:Gadget-dark-mode-toggle-pagestyles.css]] **/ |
|||
// 'Dark mode' and 'Light mode' messages must match the ::before content in |
|||
body.skin-vector-legacy :not(#pt-darkmode) + #pt-watchlist::before, |
|||
// [[MediaWiki:Gadget-dark-mode-toggle-pagestyles.css]] and [[MediaWiki:Gadget-dark-mode.css]], respectively. |
|||
body.skin-monobook :not(#pt-darkmode) + #pt-watchlist::before { |
|||
// Don't overwrite existing messages, if already set on a foreign wiki prior to loading this file |
|||
content: "Light mode"; |
|||
if (!mw.messages.get('darkmode-turn-on-label')) { |
|||
mw.messages.set({ |
|||
'darkmode-turn-on-label': 'Dark mode', |
|||
'darkmode-turn-on-tooltip': 'Turn dark mode on', |
|||
'darkmode-turn-off-label': 'Light mode', |
|||
'darkmode-turn-off-tooltip': 'Turn dark mode off', |
|||
}); |
|||
} |
} |
||
@media screen { |
|||
var isOn = mw.loader.getState('ext.gadget.dark-mode') === 'ready'; |
|||
/* set height for monobook and timeless, because the filter in FF needs dimensions to get it to apply */ |
|||
html { |
|||
height: 100%; |
|||
} |
|||
/* Filter needs to reside on `html`, see https://phabricator.wikimedia.org/T221425#5153917 */ |
|||
var broadcastChannel = new BroadcastChannel('gadget-dark-mode'); |
|||
html, |
|||
/* All other selectors have `filter` double-applied to turn back to “normal” by inheritance */ |
|||
html img, |
|||
html video, |
|||
html ogvjs, |
|||
html svg, |
|||
html iframe, |
|||
html .mw-no-invert, |
|||
html td .diffchange, |
|||
html .mwe-math-element, |
|||
html .wvui-typeahead-suggestion__thumbnail, |
|||
html .skin-minerva .mw-notification-visible .mw-notification-content, |
|||
/* Extensions */ |
|||
html .cdx-menu-item__thumbnail, /* T311835 */ |
|||
html .cx-slitem__image, |
|||
html .mw-mmv-overlay, |
|||
html .mw-mmv-pre-image, |
|||
html .media-viewer .image img, |
|||
html .media-viewer .mw-file-description img, |
|||
html .mw-kartographer-map, |
|||
html .mw-kartographer-mapDialog-map, |
|||
html .oo-ui-searchWidget-results .oo-ui-iconElement-icon, |
|||
html .list-thumb, |
|||
html .ext-related-articles-card-list .ext-related-articles-card-thumb { |
|||
filter: invert( 1 ) hue-rotate( 180deg ); |
|||
} |
|||
/* Reset overrides, needed where double application above isn't working. */ |
|||
function setThemeColor() { |
|||
/* Vector modern */ |
|||
// Update the theme-color used by some browsers for coloration of the tab headers and surrounding UI |
|||
html .skin-vector .mw-logo-wordmark, |
|||
$('meta[name="theme-color"]').attr('content', isOn ? '#000000' : '#eaecf0'); |
|||
html .skin-vector .mw-logo-tagline, |
|||
html .skin-timeless .mw-wiki-title > img, |
|||
html .wvui-icon svg, |
|||
html .mw-ext-score img, |
|||
html .mw-hiero-table img { |
|||
filter: none; |
|||
} |
} |
||
/* Backgrounds */ |
|||
function setHtmlClass() { |
|||
html table, |
|||
// CSS class for externally styling elements in dark mode via TemplateStyles (or CSS from other gadgets or common.css) |
|||
html table.ambox-content, |
|||
// A brief flash of the original styles will occur, so this is only suitable for style changes for which flashes are tolerable. |
|||
html table.toccolours, |
|||
// For others, update Gadget-dark-mode.css directly which is loaded without FOUCs |
|||
html .mw-notification, |
|||
$(document.documentElement).toggleClass('client-dark-mode', isOn); |
|||
html .mwe-popups, |
|||
html .infobox, |
|||
html .toc, |
|||
html .thumbinner, |
|||
html figure[typeof~='mw:File/Thumb'], |
|||
html figure[typeof~='mw:File/Frame'], |
|||
html figure[typeof~='mw:File/Thumb'] > figcaption, |
|||
html figure[typeof~='mw:File/Frame'] > figcaption, |
|||
html .wikitable, |
|||
html .cbnnr-main, |
|||
html .cx-callout, |
|||
html .overlay.media-viewer, |
|||
html #simpleSearch, |
|||
html #simpleSearch #searchInput, |
|||
html #siteNotice #centralNotice .cnotice { |
|||
background-color: #ddd; |
|||
} |
} |
||
/* Borders */ |
|||
function vectorStickyCallback() { |
|||
html body, |
|||
mw.hook('vector.page_title_scroll').remove(vectorStickyCallback); |
|||
html h1, |
|||
if (document.getElementById('pt-darkmode-sticky-header')) return; |
|||
html h2, |
|||
makePortletLink('p-personal-sticky-header', 'pt-darkmode-sticky-header', '#pt-watchlist-sticky-header'); |
|||
html h3, |
|||
html h4, |
|||
html h5, |
|||
html h6, |
|||
html table.ambox-content, |
|||
html table.toccolours, |
|||
html .mw-notification, |
|||
html .infobox, |
|||
html .toc, |
|||
html .thumbinner, |
|||
html figure[typeof~='mw:File/Thumb'], |
|||
html figure[typeof~='mw:File/Frame'], |
|||
html figure[typeof~='mw:File/Thumb'] > figcaption, |
|||
html figure[typeof~='mw:File/Frame'] > figcaption, |
|||
html #mw-head, |
|||
html #mw-panel, |
|||
/* Vector 2022 uses a transparent border for margin collapsing |
|||
(T312822) so don't apply this rule there */ |
|||
.skin-vector-legacy #content.mw-body, |
|||
html #simpleSearch, |
|||
html #simpleSearch #searchInput, |
|||
html #siteNotice #centralNotice .cnotice { |
|||
border-color: #cdcbc8; |
|||
} |
} |
||
/* Links */ |
|||
function addPortlets() { |
|||
/* Links: normal */ |
|||
makePortletLink('p-personal', 'pt-darkmode', '#pt-watchlist'); |
|||
html a, |
|||
html .vector-menu-tabs li a, |
|||
/* Backwards compatible VectorTabs, deprecated in MW v1.35. */ |
|||
html .vectorTabs li a, |
|||
html .toctogglelabel, |
|||
html .mw-parser-output a.external, |
|||
html .mw-parser-output a.extiw, |
|||
html .mw-parser-output a.extiw:active, |
|||
html #mw-panel .portal .body li a { |
|||
/* color: #69f; Proposal below for level AA conformance, see also https://phabricator.wikimedia.org/T233266 |
|||
`#36c` is transformed by :root `filter` to be closer to chosen `#69f`. */ |
|||
color: #36c; |
|||
} |
|||
/* Links: visited */ |
|||
if (mw.config.get('skin') === 'vector-2022') { |
|||
html a:visited, |
|||
mw.hook('vector.page_title_scroll').add(vectorStickyCallback); |
|||
html .mw-parser-output a.extiw:visited, |
|||
} |
|||
html #mw-panel .portal .body li a:visited { |
|||
/* color: #709bbd; Proposal below uses to-be-standardized color from https://phabricator.wikimedia.org/T213778 */ |
|||
color: #6b4ba1; |
|||
} |
} |
||
/* Links: red */ |
|||
function getMsg(suffix) { |
|||
html a.new, |
|||
var key = 'darkmode-turn-' + (isOn ? 'off' : 'on') + '-' + suffix; |
|||
html .vector-menu-tabs li.new a, |
|||
return mw.msg(key); |
|||
html .vectorTabs li.new a { |
|||
color: #ff6e6e; |
|||
} |
} |
||
/* ::: Special Element Treatments ::: */ |
|||
function makePortletLink(portletId, portletLinkId, nextnode) { |
|||
/* Image thumbnails */ |
|||
var label = getMsg('label'); |
|||
html .thumbimage, |
|||
var tooltip = getMsg('tooltip'); |
|||
html figure[typeof~='mw:File/Thumb'] > :not(figcaption) .mw-file-element, |
|||
$(mw.util.addPortletLink(portletId, '#', label, portletLinkId, tooltip, '', nextnode)) |
|||
html figure[typeof~='mw:File/Frame'] > :not(figcaption) .mw-file-element { |
|||
.children().on('click', function (e) { |
|||
border: 0; |
|||
e.preventDefault(); |
|||
toggleMode(); |
|||
}); |
|||
} |
} |
||
/* Content image (thumbnail) SVGs */ |
|||
function togglePortlets() { |
|||
/* `*not( .mbox-image )` exception doesn't work for unclear reasons */ |
|||
var labelSelector; |
|||
html .image img[ src*='svg' ], |
|||
switch (mw.config.get('skin')) { |
|||
html .mw-file-description img[ src*='svg' ], |
|||
case 'vector': |
|||
html img[ src*='Wiktionary-logo'] { |
|||
case 'vector-2022': |
|||
background-color: #fff; |
|||
case 'minerva': |
|||
border-radius: 1px; |
|||
labelSelector = '#pt-darkmode span:not(.mw-ui-icon), #pt-darkmode-sticky-header span:not(.mw-ui-icon)'; |
|||
break; |
|||
default: |
|||
labelSelector = '#pt-darkmode a'; |
|||
} |
|||
$(labelSelector).text(getMsg('label')); |
|||
$('#pt-darkmode a, #pt-darkmode-sticky-header a') |
|||
.attr('title', getMsg('tooltip')); |
|||
} |
} |
||
/* Dealing with false positives from selector above */ |
|||
function actuallyToggleDarkMode() { |
|||
html .mw-echo-ui-notificationItemWidget-icon img[ src*='svg' ], |
|||
// Modify the <link> element on the page to include/exclude dark-mode styles |
|||
html .mbox-image .image img[ src*='svg' ], |
|||
// We can't use mw.loader as it doesn't work both ways (see talk page) |
|||
html .mbox-image .mw-file-description img[ src*='svg' ], |
|||
var scriptPath = mw.util.wikiScript('load'); |
|||
/* Emoji generated by [[Template:Emoji]] */ |
|||
var $gadgetsLink = $('link[rel="stylesheet"][href^="' + scriptPath + '?"][href*="ext.gadget."]'); |
|||
html .emoji .image img, |
|||
if ($gadgetsLink.length) { |
|||
html .emoji .mw-file-description img, |
|||
var uri = new mw.Uri($gadgetsLink.prop('href')); |
|||
/* Vote symbols on Talk pages */ |
|||
if (isOn) { |
|||
html .image img[ alt^="Symbol" ], |
|||
uri.query.modules += ',dark-mode'; |
|||
html .mw-file-description img[ alt^="Symbol" ] { |
|||
} else { |
|||
background-color: transparent; |
|||
if (uri.query.modules === 'ext.gadget.dark-mode') { |
|||
// dark-mode is the only module in this link |
|||
$gadgetsLink.remove(); |
|||
return; |
|||
} |
|||
uri.query.modules = uri.query.modules |
|||
.replace('ext.gadget.dark-mode,', 'ext.gadget.') // dark-mode is first in the gadget list |
|||
.replace(/,dark-mode(,|$)/, '$1'); // dark-mode is in middle or end of the list |
|||
} |
|||
$gadgetsLink.prop('href', uri.getRelativePath()); |
|||
} else { |
|||
// No gadget-containing styles are enabled |
|||
$('<link>').attr({ |
|||
rel: 'stylesheet', |
|||
href: scriptPath + '?lang=' + mw.config.get('wgUserLanguage') + |
|||
'&modules=ext.gadget.dark-mode&only=styles&skin=' + mw.config.get('skin') |
|||
}).appendTo(document.head); |
|||
} |
|||
} |
} |
||
/* Page previews */ |
|||
function savePreference() { |
|||
html .mwe-popups { |
|||
new mw.Api().saveOption('gadget-dark-mode', isOn ? '1' : '0'); |
|||
box-shadow: 0 30px 90px -20px rgba( 0, 0, 0, 0.3 ), 0 0 1px #000; |
|||
} |
} |
||
html .mwe-popups.flipped-y:after, |
|||
function savePreferenceLocally() { |
|||
html .mwe-popups.flipped-x-y:after { |
|||
mw.user.options.set('gadget-dark-mode', Number(isOn)); |
|||
border-top: 11px solid #ddd; |
|||
} |
|||
html .mwe-popups.mwe-popups-no-image-pointer:after { |
|||
// In case the user navigates to another page too quickly |
|||
border-bottom: 11px solid #ddd; |
|||
mw.storage.session.set('dark-mode-toggled', isOn ? '1' : '0'); |
|||
} |
} |
||
/* Contributions menu */ |
|||
function notifyOtherTabs() { |
|||
html .cx-callout-1:after { |
|||
// Broadcast state change to other tabs |
|||
border-bottom-color: #ddd; |
|||
broadcastChannel.postMessage(isOn); |
|||
} |
} |
||
/* Mobile Wikipedia logo mobile header */ |
|||
function toggleMode(offline) { |
|||
html .branding-box img { |
|||
isOn = !isOn; |
|||
filter: brightness( 0 ); |
|||
if (!offline) { |
|||
savePreference(); |
|||
notifyOtherTabs(); |
|||
} |
|||
setHtmlClass(); |
|||
setThemeColor(); |
|||
savePreferenceLocally(); |
|||
togglePortlets(); |
|||
actuallyToggleDarkMode(); |
|||
} |
} |
||
} |
|||
function toggleBasedOnSystemColourScheme() { |
|||
var systemSchemeNow = matchMedia('(prefers-color-scheme: dark)').matches; |
|||
var systemSchemeLast = mw.storage.get('dark-mode-system-scheme') === '1'; |
|||
@-moz-document url-prefix() { |
|||
if (systemSchemeNow !== systemSchemeLast) { |
|||
body { |
|||
if (systemSchemeNow !== isOn) { |
|||
background: #000; |
|||
toggleMode(); |
|||
} |
|||
} |
|||
mw.requestIdleCallback(function () { |
|||
mw.storage.set('dark-mode-system-scheme', systemSchemeNow ? '1' : '0'); |
|||
}); |
|||
} |
|||
} |
} |
||
@-moz-document url-prefix() { |
|||
@supports (overflow-clip-margin: 1px) { |
|||
$.when($.ready, mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.Uri', 'mediawiki.storage'])).then(function () { |
|||
body { |
|||
setHtmlClass(); |
|||
background: #fff; |
|||
setThemeColor(); |
|||
} |
|||
addPortlets(); |
|||
// Recover state if the navigation was too quick |
|||
var storageState = mw.storage.session.get('dark-mode-toggled'); |
|||
if (storageState && Number(storageState) !== Number(isOn)) { |
|||
toggleMode(true); |
|||
} |
} |
||
} |
|||
// Listen to dark mode state change made on other tabs |
|||
broadcastChannel.onmessage = function (msg) { |
|||
if (msg.data !== isOn) { |
|||
toggleMode(true); |
|||
} |
|||
}; |
|||
if (window.wpDarkModeAutoToggle) { |
|||
toggleBasedOnSystemColourScheme(); |
|||
// If system colour scheme changes while user is viewing, toggle immediately |
|||
var mediaQuery = matchMedia('(prefers-color-scheme: dark)'); |
|||
if (mediaQuery.addEventListener) { |
|||
mediaQuery.addEventListener('change', toggleBasedOnSystemColourScheme); |
|||
} else if (mediaQuery.addListener) { // Safari 13 and older |
|||
mediaQuery.addListener(toggleBasedOnSystemColourScheme); |
|||
} |
|||
} |
|||
}); |
Revision as of 12:54, 7 October 2023
/* WikimediaUI Dark Mode
*
* Wikimedia Design Team 2019-2021
* Original authors:
* - Volker E. – [[User:Volker_E._(WMF)]]
* - Alex Hollender
* - MusikAnimal
* - Carolyn Li-Madeo
* - Jdlrobson
*
* Original at https://en.wikipedia.org/wiki/User:Volker_E._(WMF)/dark-mode.css
* Version for Gadget CSS skin override usage only.
* Basically removed of all interaction element styles and
* set to `html` instead of JS injected `.client-dark-mode` class.
*
* Last updated: 2021-04-20
*
*/
/** To prevent 'jumping' effect within #p-personal in Vector/Monobook. Overrides [[MediaWiki:Gadget-dark-mode-toggle-pagestyles.css]] **/
body.skin-vector-legacy :not(#pt-darkmode) + #pt-watchlist::before,
body.skin-monobook :not(#pt-darkmode) + #pt-watchlist::before {
content: "Light mode";
}
@media screen {
/* set height for monobook and timeless, because the filter in FF needs dimensions to get it to apply */
html {
height: 100%;
}
/* Filter needs to reside on `html`, see https://phabricator.wikimedia.org/T221425#5153917 */
html,
/* All other selectors have `filter` double-applied to turn back to “normal” by inheritance */
html img,
html video,
html ogvjs,
html svg,
html iframe,
html .mw-no-invert,
html td .diffchange,
html .mwe-math-element,
html .wvui-typeahead-suggestion__thumbnail,
html .skin-minerva .mw-notification-visible .mw-notification-content,
/* Extensions */
html .cdx-menu-item__thumbnail, /* T311835 */
html .cx-slitem__image,
html .mw-mmv-overlay,
html .mw-mmv-pre-image,
html .media-viewer .image img,
html .media-viewer .mw-file-description img,
html .mw-kartographer-map,
html .mw-kartographer-mapDialog-map,
html .oo-ui-searchWidget-results .oo-ui-iconElement-icon,
html .list-thumb,
html .ext-related-articles-card-list .ext-related-articles-card-thumb {
filter: invert( 1 ) hue-rotate( 180deg );
}
/* Reset overrides, needed where double application above isn't working. */
/* Vector modern */
html .skin-vector .mw-logo-wordmark,
html .skin-vector .mw-logo-tagline,
html .skin-timeless .mw-wiki-title > img,
html .wvui-icon svg,
html .mw-ext-score img,
html .mw-hiero-table img {
filter: none;
}
/* Backgrounds */
html table,
html table.ambox-content,
html table.toccolours,
html .mw-notification,
html .mwe-popups,
html .infobox,
html .toc,
html .thumbinner,
html figure[typeof~='mw:File/Thumb'],
html figure[typeof~='mw:File/Frame'],
html figure[typeof~='mw:File/Thumb'] > figcaption,
html figure[typeof~='mw:File/Frame'] > figcaption,
html .wikitable,
html .cbnnr-main,
html .cx-callout,
html .overlay.media-viewer,
html #simpleSearch,
html #simpleSearch #searchInput,
html #siteNotice #centralNotice .cnotice {
background-color: #ddd;
}
/* Borders */
html body,
html h1,
html h2,
html h3,
html h4,
html h5,
html h6,
html table.ambox-content,
html table.toccolours,
html .mw-notification,
html .infobox,
html .toc,
html .thumbinner,
html figure[typeof~='mw:File/Thumb'],
html figure[typeof~='mw:File/Frame'],
html figure[typeof~='mw:File/Thumb'] > figcaption,
html figure[typeof~='mw:File/Frame'] > figcaption,
html #mw-head,
html #mw-panel,
/* Vector 2022 uses a transparent border for margin collapsing
(T312822) so don't apply this rule there */
.skin-vector-legacy #content.mw-body,
html #simpleSearch,
html #simpleSearch #searchInput,
html #siteNotice #centralNotice .cnotice {
border-color: #cdcbc8;
}
/* Links */
/* Links: normal */
html a,
html .vector-menu-tabs li a,
/* Backwards compatible VectorTabs, deprecated in MW v1.35. */
html .vectorTabs li a,
html .toctogglelabel,
html .mw-parser-output a.external,
html .mw-parser-output a.extiw,
html .mw-parser-output a.extiw:active,
html #mw-panel .portal .body li a {
/* color: #69f; Proposal below for level AA conformance, see also https://phabricator.wikimedia.org/T233266
`#36c` is transformed by :root `filter` to be closer to chosen `#69f`. */
color: #36c;
}
/* Links: visited */
html a:visited,
html .mw-parser-output a.extiw:visited,
html #mw-panel .portal .body li a:visited {
/* color: #709bbd; Proposal below uses to-be-standardized color from https://phabricator.wikimedia.org/T213778 */
color: #6b4ba1;
}
/* Links: red */
html a.new,
html .vector-menu-tabs li.new a,
html .vectorTabs li.new a {
color: #ff6e6e;
}
/* ::: Special Element Treatments ::: */
/* Image thumbnails */
html .thumbimage,
html figure[typeof~='mw:File/Thumb'] > :not(figcaption) .mw-file-element,
html figure[typeof~='mw:File/Frame'] > :not(figcaption) .mw-file-element {
border: 0;
}
/* Content image (thumbnail) SVGs */
/* `*not( .mbox-image )` exception doesn't work for unclear reasons */
html .image img[ src*='svg' ],
html .mw-file-description img[ src*='svg' ],
html img[ src*='Wiktionary-logo'] {
background-color: #fff;
border-radius: 1px;
}
/* Dealing with false positives from selector above */
html .mw-echo-ui-notificationItemWidget-icon img[ src*='svg' ],
html .mbox-image .image img[ src*='svg' ],
html .mbox-image .mw-file-description img[ src*='svg' ],
/* Emoji generated by [[Template:Emoji]] */
html .emoji .image img,
html .emoji .mw-file-description img,
/* Vote symbols on Talk pages */
html .image img[ alt^="Symbol" ],
html .mw-file-description img[ alt^="Symbol" ] {
background-color: transparent;
}
/* Page previews */
html .mwe-popups {
box-shadow: 0 30px 90px -20px rgba( 0, 0, 0, 0.3 ), 0 0 1px #000;
}
html .mwe-popups.flipped-y:after,
html .mwe-popups.flipped-x-y:after {
border-top: 11px solid #ddd;
}
html .mwe-popups.mwe-popups-no-image-pointer:after {
border-bottom: 11px solid #ddd;
}
/* Contributions menu */
html .cx-callout-1:after {
border-bottom-color: #ddd;
}
/* Mobile Wikipedia logo mobile header */
html .branding-box img {
filter: brightness( 0 );
}
}
@-moz-document url-prefix() {
body {
background: #000;
}
}
@-moz-document url-prefix() {
@supports (overflow-clip-margin: 1px) {
body {
background: #fff;
}
}
}