FEATURE: Dark mode alternative logos (#10441)
This commit is contained in:
parent
d5a4318ac1
commit
3745f2bb86
|
@ -82,6 +82,20 @@ export default {
|
|||
Session.currentProp("safe_mode", setupData.safeMode);
|
||||
}
|
||||
|
||||
Session.currentProp(
|
||||
"darkModeAvailable",
|
||||
document.head.querySelectorAll(
|
||||
'link[media="(prefers-color-scheme: dark)"]'
|
||||
).length > 0
|
||||
);
|
||||
|
||||
Session.currentProp(
|
||||
"darkColorScheme",
|
||||
getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--scheme-type")
|
||||
.trim() === "dark"
|
||||
);
|
||||
|
||||
app.HighlightJSPath = setupData.highlightJsPath;
|
||||
Session.currentProp("svgSpritePath", setupData.svgSpritePath);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { h } from "virtual-dom";
|
|||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import Session from "discourse/models/session";
|
||||
|
||||
export default createWidget("home-logo", {
|
||||
tagName: "div.title",
|
||||
|
@ -17,57 +18,110 @@ export default createWidget("home-logo", {
|
|||
return typeof href === "function" ? href() : href;
|
||||
},
|
||||
|
||||
logoUrl() {
|
||||
return this.siteSettings.site_logo_url || "";
|
||||
logoUrl(opts = {}) {
|
||||
return this.logoResolver("logo", opts);
|
||||
},
|
||||
|
||||
mobileLogoUrl() {
|
||||
return this.siteSettings.site_mobile_logo_url || "";
|
||||
mobileLogoUrl(opts = {}) {
|
||||
return this.logoResolver("mobile_logo", opts);
|
||||
},
|
||||
|
||||
smallLogoUrl() {
|
||||
return this.siteSettings.site_logo_small_url || "";
|
||||
smallLogoUrl(opts = {}) {
|
||||
return this.logoResolver("logo_small", opts);
|
||||
},
|
||||
|
||||
logo() {
|
||||
const { siteSettings } = this;
|
||||
const mobileView = this.site.mobileView;
|
||||
const { siteSettings } = this,
|
||||
mobileView = this.site.mobileView;
|
||||
|
||||
const darkModeOptions = Session.currentProp("darkModeAvailable")
|
||||
? { dark: true }
|
||||
: {};
|
||||
|
||||
const mobileLogoUrl = this.mobileLogoUrl(),
|
||||
mobileLogoUrlDark = this.mobileLogoUrl(darkModeOptions);
|
||||
|
||||
const mobileLogoUrl = this.mobileLogoUrl();
|
||||
const showMobileLogo = mobileView && mobileLogoUrl.length > 0;
|
||||
|
||||
const logoUrl = this.logoUrl();
|
||||
const logoUrl = this.logoUrl(),
|
||||
logoUrlDark = this.logoUrl(darkModeOptions);
|
||||
const title = siteSettings.title;
|
||||
|
||||
if (this.attrs.minimized) {
|
||||
const logoSmallUrl = this.smallLogoUrl();
|
||||
const logoSmallUrl = this.smallLogoUrl(),
|
||||
logoSmallUrlDark = this.smallLogoUrl(darkModeOptions);
|
||||
if (logoSmallUrl.length) {
|
||||
return h("img#site-logo.logo-small", {
|
||||
key: "logo-small",
|
||||
attributes: {
|
||||
src: getURL(logoSmallUrl),
|
||||
width: 36,
|
||||
alt: title
|
||||
}
|
||||
});
|
||||
return this.logoElement(
|
||||
"logo-small",
|
||||
logoSmallUrl,
|
||||
title,
|
||||
logoSmallUrlDark
|
||||
);
|
||||
} else {
|
||||
return iconNode("home");
|
||||
}
|
||||
} else if (showMobileLogo) {
|
||||
return h("img#site-logo.logo-big", {
|
||||
key: "logo-mobile",
|
||||
attributes: { src: getURL(mobileLogoUrl), alt: title }
|
||||
});
|
||||
return this.logoElement(
|
||||
"logo-mobile",
|
||||
mobileLogoUrl,
|
||||
title,
|
||||
mobileLogoUrlDark
|
||||
);
|
||||
} else if (logoUrl.length) {
|
||||
return h("img#site-logo.logo-big", {
|
||||
key: "logo-big",
|
||||
attributes: { src: getURL(logoUrl), alt: title }
|
||||
});
|
||||
return this.logoElement("logo-big", logoUrl, title, logoUrlDark);
|
||||
} else {
|
||||
return h("h1#site-text-logo.text-logo", { key: "logo-text" }, title);
|
||||
}
|
||||
},
|
||||
|
||||
logoResolver(name, opts = {}) {
|
||||
const { siteSettings } = this;
|
||||
|
||||
// get alternative logos for browser dark dark mode switching
|
||||
if (opts.dark) {
|
||||
return siteSettings[`site_${name}_dark_url`];
|
||||
}
|
||||
|
||||
// try dark logos first when color scheme is dark
|
||||
// this is independent of browser dark mode
|
||||
// hence the fallback to normal logos
|
||||
if (Session.currentProp("darkColorScheme")) {
|
||||
return (
|
||||
siteSettings[`site_${name}_dark_url`] ||
|
||||
siteSettings[`site_${name}_url`] ||
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
return siteSettings[`site_${name}_url`] || "";
|
||||
},
|
||||
|
||||
logoElement(key, url, title, darkUrl = null) {
|
||||
const attributes =
|
||||
key === "logo-small"
|
||||
? { src: getURL(url), width: 36, alt: title }
|
||||
: { src: getURL(url), alt: title };
|
||||
|
||||
const imgElement = h(`img#site-logo.${key}`, {
|
||||
key: key,
|
||||
attributes
|
||||
});
|
||||
|
||||
if (darkUrl && url !== darkUrl) {
|
||||
return h("picture", [
|
||||
h("source", {
|
||||
attributes: {
|
||||
srcset: getURL(darkUrl),
|
||||
media: "(prefers-color-scheme: dark)"
|
||||
}
|
||||
}),
|
||||
imgElement
|
||||
]);
|
||||
}
|
||||
|
||||
return imgElement;
|
||||
},
|
||||
|
||||
html() {
|
||||
return h(
|
||||
"a",
|
||||
|
|
|
@ -14,7 +14,16 @@
|
|||
@return red($hex), green($hex), blue($hex);
|
||||
}
|
||||
|
||||
@function schemeType() {
|
||||
@if is-light-color-scheme() {
|
||||
@return "light";
|
||||
} @else {
|
||||
@return "dark";
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--scheme-type: #{schemeType()};
|
||||
--primary: #{$primary};
|
||||
--secondary: #{$secondary};
|
||||
--tertiary: #{$tertiary};
|
||||
|
|
|
@ -183,6 +183,9 @@ class SiteSetting < ActiveRecord::Base
|
|||
site_logo_small_url
|
||||
site_mobile_logo_url
|
||||
site_favicon_url
|
||||
site_logo_dark_url
|
||||
site_logo_small_dark_url
|
||||
site_mobile_logo_dark_url
|
||||
}.each { |client_setting| client_settings << client_setting }
|
||||
|
||||
%i{
|
||||
|
@ -190,6 +193,9 @@ class SiteSetting < ActiveRecord::Base
|
|||
logo_small
|
||||
digest_logo
|
||||
mobile_logo
|
||||
logo_dark
|
||||
logo_small_dark
|
||||
mobile_logo_dark
|
||||
large_icon
|
||||
manifest_icon
|
||||
favicon
|
||||
|
|
|
@ -1481,6 +1481,9 @@ en:
|
|||
logo_small: "The small logo image at the top left of your site, seen when scrolling down. Use a square 120 × 120 image. If left blank, a home glyph will be shown."
|
||||
digest_logo: "The alternate logo image used at the top of your site's email summary. Use a wide rectangle image. Don't use an SVG image. If left blank, the image from the `logo` setting will be used."
|
||||
mobile_logo: "The logo used on mobile version of your site. Use a wide rectangular image with a height of 120 and an aspect ratio greater than 3:1. If left blank, the image from the `logo` setting will be used."
|
||||
logo_dark: "Dark scheme alternative for the 'logo' site setting."
|
||||
logo_small_dark: "Dark scheme alternative for the 'logo small' site setting."
|
||||
mobile_logo_dark: "Dark scheme alternative for the 'mobile logo' site setting."
|
||||
large_icon: "Image used as the base for other metadata icons. Should ideally be larger than 512 x 512. If left blank, logo_small will be used."
|
||||
manifest_icon: "Image used as logo/splash image on Android. Will be automatically resized to 512 × 512. If left blank, large_icon will be used."
|
||||
favicon: "A favicon for your site, see <a href='https://en.wikipedia.org/wiki/Favicon' target='_blank'>https://en.wikipedia.org/wiki/Favicon</a>. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, large_icon will be used."
|
||||
|
|
|
@ -74,6 +74,18 @@ branding:
|
|||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
logo_dark:
|
||||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
logo_small_dark:
|
||||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
mobile_logo_dark:
|
||||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
large_icon:
|
||||
default: ""
|
||||
client: true
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { moduleForWidget, widgetTest } from "helpers/widget-test";
|
||||
import Session from "discourse/models/session";
|
||||
moduleForWidget("home-logo");
|
||||
|
||||
const bigLogo = "/images/d-logo-sketch.png?test";
|
||||
const smallLogo = "/images/d-logo-sketch-small.png?test";
|
||||
const mobileLogo = "/images/d-logo-sketch.png?mobile";
|
||||
const darkLogo = "/images/d-logo-sketch.png?dark";
|
||||
const title = "Cool Forum";
|
||||
const prefersDark = "(prefers-color-scheme: dark)";
|
||||
|
||||
widgetTest("basics", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
|
@ -38,6 +41,7 @@ widgetTest("basics - minimized", {
|
|||
assert.ok(find("img.logo-small").length === 1);
|
||||
assert.equal(find("img.logo-small").attr("src"), smallLogo);
|
||||
assert.equal(find("img.logo-small").attr("alt"), title);
|
||||
assert.equal(find("img.logo-small").attr("width"), 36);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -79,7 +83,7 @@ widgetTest("mobile logo", {
|
|||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.ok(find("img#site-logo.logo-mobile").length === 1);
|
||||
assert.equal(find("#site-logo").attr("src"), mobileLogo);
|
||||
}
|
||||
});
|
||||
|
@ -96,3 +100,139 @@ widgetTest("mobile without logo", {
|
|||
assert.equal(find("#site-logo").attr("src"), bigLogo);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("logo with dark mode alternative", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
Session.currentProp("darkModeAvailable", true);
|
||||
},
|
||||
afterEach() {
|
||||
Session.currentProp("darkModeAvailable", null);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.equal(find("#site-logo").attr("src"), bigLogo);
|
||||
|
||||
assert.equal(
|
||||
find("picture source").attr("media"),
|
||||
prefersDark,
|
||||
"includes dark mode media attribute"
|
||||
);
|
||||
assert.equal(
|
||||
find("picture source").attr("srcset"),
|
||||
darkLogo,
|
||||
"includes dark mode alternative logo source"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("mobile logo with dark mode alternative", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_mobile_logo_url = mobileLogo;
|
||||
this.siteSettings.site_mobile_logo_dark_url = darkLogo;
|
||||
Session.currentProp("darkModeAvailable", true);
|
||||
|
||||
this.site.mobileView = true;
|
||||
},
|
||||
afterEach() {
|
||||
Session.currentProp("darkModeAvailable", null);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(find("#site-logo").attr("src"), mobileLogo);
|
||||
|
||||
assert.equal(
|
||||
find("picture source").attr("media"),
|
||||
prefersDark,
|
||||
"includes dark mode media attribute"
|
||||
);
|
||||
assert.equal(
|
||||
find("picture source").attr("srcset"),
|
||||
darkLogo,
|
||||
"includes dark mode alternative logo source"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("dark mode enabled but no dark logo set", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = "";
|
||||
Session.currentProp("darkModeAvailable", true);
|
||||
},
|
||||
afterEach() {
|
||||
Session.currentProp("darkModeAvailable", null);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.equal(find("#site-logo").attr("src"), bigLogo);
|
||||
assert.ok(
|
||||
find("picture").length === 0,
|
||||
"does not include alternative logo"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("dark logo set but no dark mode", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.equal(find("#site-logo").attr("src"), bigLogo);
|
||||
assert.ok(
|
||||
find("picture").length === 0,
|
||||
"does not include alternative logo"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("dark color scheme and dark logo set", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
Session.currentProp("darkColorScheme", true);
|
||||
},
|
||||
afterEach() {
|
||||
Session.currentProp("darkColorScheme", null);
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.equal(find("#site-logo").attr("src"), darkLogo, "uses dark logo");
|
||||
assert.ok(
|
||||
find("picture").length === 0,
|
||||
"does not add dark mode alternative"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("dark color scheme and dark logo not set", {
|
||||
template: '{{mount-widget widget="home-logo" args=args}}',
|
||||
beforeEach() {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = "";
|
||||
Session.currentProp("darkColorScheme", true);
|
||||
},
|
||||
afterEach() {
|
||||
Session.currentProp("darkColorScheme", null);
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(find("img#site-logo.logo-big").length === 1);
|
||||
assert.equal(
|
||||
find("#site-logo").attr("src"),
|
||||
bigLogo,
|
||||
"uses regular logo on dark scheme if no dark logo"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue