Skip to content

Commit b2b716f

Browse files
committed
Add theme selector
1 parent ee2f8cf commit b2b716f

File tree

8 files changed

+212
-27
lines changed

8 files changed

+212
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ site
55
static/styles/vendor.css
66
static/styles/app.css
77
static/styles/fonts.css
8+
static/styles/noscript.css

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl<'a> Generator<'a> {
8888
self.render_blog(blog)?;
8989
}
9090
self.compile_sass("app")?;
91+
self.compile_sass("noscript")?;
9192
self.compile_sass("fonts")?;
9293
self.concat_vendor_css(vec!["skeleton", "tachyons"])?;
9394
self.copy_static_files()?;

src/styles/app.scss

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
$body-font: 'Fira Sans', Helvetica, Arial, sans-serif;
44
$header-font: 'Alfa Slab One', serif;
55

6+
// Switching theme will only work if JavaScript is enabled as well
7+
68
// Default light theme
7-
:root {
9+
:root, :root:not([data-theme]) {
810
--gray: #2a3439;
911
--red: #a72145;
1012
--yellow: #ffc832;
@@ -23,27 +25,38 @@ $header-font: 'Alfa Slab One', serif;
2325
--nav-links-a: #2a3439;
2426
--publish-date-author: #2a3439;
2527
--section-header-h2-color: black;
28+
--theme-icon: #43484d;
29+
--theme-popup-border: #43484d;
30+
--theme-popup-bg: white;
31+
--theme-hover: #cacaca;
32+
--theme-choice-color: black;
2633
}
2734

2835
// Dark theme
29-
@media (prefers-color-scheme: dark) {
30-
:root {
31-
--code-color: white;
32-
--code-bg-color: rgba(213, 203, 198, 0.05);
33-
--code-border-color: rgba(213, 203, 198, 0.25);
34-
--blockquote-color: rgb(195, 205, 210);
35-
--blockquote-bg-color: rgba(213, 203, 198, 0.05);
36-
--blockquote-left-border-color: rgb(195, 205, 210);
37-
--body-background-color: #181a1b;
38-
--body-foreground-color: #e8e6e3;
39-
--body-color: white;
40-
--div-brand-a-color: white;
41-
--white-elem-color: white;
42-
--white-elem-a: #d5cbc6;
43-
--nav-links-a: #d5cbc6;
44-
--publish-date-author: #d5cbc6;
45-
--section-header-h2-color: white;
46-
}
36+
:root[data-theme='dark'] {
37+
--gray: #2a3439;
38+
--red: #a72145;
39+
--yellow: #ffc832;
40+
--code-color: white;
41+
--code-bg-color: rgba(213, 203, 198, 0.05);
42+
--code-border-color: rgba(213, 203, 198, 0.25);
43+
--blockquote-color: rgb(195, 205, 210);
44+
--blockquote-bg-color: rgba(213, 203, 198, 0.05);
45+
--blockquote-left-border-color: rgb(195, 205, 210);
46+
--body-background-color: #181a1b;
47+
--body-foreground-color: #e8e6e3;
48+
--body-color: white;
49+
--div-brand-a-color: white;
50+
--white-elem-color: white;
51+
--white-elem-a: #d5cbc6;
52+
--nav-links-a: #d5cbc6;
53+
--publish-date-author: #d5cbc6;
54+
--section-header-h2-color: white;
55+
--theme-icon: #43484d;
56+
--theme-popup-border: #43484d;
57+
--theme-popup-bg: #141617;
58+
--theme-hover: #474c51;
59+
--theme-choice-color: #d5cbc6;
4760
}
4861

4962
html {
@@ -363,3 +376,45 @@ header h1 {
363376
border-top-left-radius: 0;
364377
}
365378
}
379+
380+
// Theme switch popup
381+
// theme selector visible only if JavaScript is available
382+
383+
.theme-icon {
384+
display: none;
385+
line-height: 2em;
386+
border: 2px solid var(--theme-icon);
387+
border-radius: 5px;
388+
padding: 0px 5px;
389+
color: var(--theme-choice-color);
390+
&:hover {
391+
color: var(--theme-choice-color);
392+
}
393+
}
394+
395+
#theme-choice {
396+
display: none;
397+
border: 2px solid var(--theme-popup-border);
398+
border-radius: 5px;
399+
color: var(--theme-choice-color);
400+
background: var(--theme-popup-bg);
401+
position: absolute;
402+
list-style: none;
403+
font-weight: 400;
404+
padding: 0px;
405+
// counterbalance vendored skeleton.css
406+
margin: 0.2em -0.5em;
407+
}
408+
409+
.theme-item {
410+
padding: 0px 5px;
411+
}
412+
413+
#theme-choice.showThemeDropdown {
414+
display: block;
415+
z-index: 1;
416+
}
417+
418+
li.theme-item:hover {
419+
background-color: var(--theme-hover);
420+
}

src/styles/noscript.scss

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This stylesheet is used when the user agent has no JavaScript capabilities (or has it disabled)
2+
// Sets dark theme according to user agent preferences
3+
4+
// Default light theme
5+
:root, :root:not([data-theme]) {
6+
--gray: #2a3439;
7+
--red: #a72145;
8+
--yellow: #ffc832;
9+
--code-color: black;
10+
--code-bg-color: rgba(42, 52, 57, 0.05);
11+
--code-border-color: rgba(42, 52, 57, 0.25);
12+
--blockquote-color: black;
13+
--blockquote-bg-color: rgb(247, 249, 249);
14+
--blockquote-left-border-color: rgb(195, 205, 210);
15+
--body-background-color: white;
16+
--body-foreground-color: white;
17+
--body-color: rgb(34,34,34);
18+
--div-brand-a-color: black;
19+
--white-elem-color: black;
20+
--white-a: #2a3439;
21+
--nav-links-a: #2a3439;
22+
--publish-date-author: #2a3439;
23+
--section-header-h2-color: black;
24+
}
25+
26+
// Dark theme (probed from user prefs)
27+
@media (prefers-color-scheme: dark) {
28+
:root, :root:not([data-theme]) {
29+
--gray: #2a3439;
30+
--red: #a72145;
31+
--yellow: #ffc832;
32+
--code-color: white;
33+
--code-bg-color: rgba(213, 203, 198, 0.05);
34+
--code-border-color: rgba(213, 203, 198, 0.25);
35+
--blockquote-color: rgb(195, 205, 210);
36+
--blockquote-bg-color: rgba(213, 203, 198, 0.05);
37+
--blockquote-left-border-color: rgb(195, 205, 210);
38+
--body-background-color: #181a1b;
39+
--body-foreground-color: #e8e6e3;
40+
--body-color: white;
41+
--div-brand-a-color: white;
42+
--white-elem-color: white;
43+
--white-elem-a: #d5cbc6;
44+
--nav-links-a: #d5cbc6;
45+
--publish-date-author: #d5cbc6;
46+
--section-header-h2-color: white;
47+
}
48+
}
49+
50+
// Don't show the theme selector button when JavaScript is disabled
51+
#theme-icon {
52+
display: none !important;
53+
}

static/scripts/theme-switch.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"use strict";
2+
3+
function changeThemeTo(val) {
4+
document.documentElement.setAttribute("data-theme", val);
5+
// save theme prefs in the browser
6+
if (storageAvailable("localStorage")) {
7+
localStorage.setItem("blog-rust-lang-org-theme", val);
8+
}
9+
// the theme dropdown will close itself when returning to the dropdown() caller
10+
}
11+
12+
function dropdown () {
13+
document.getElementById("theme-choice").classList.toggle("showThemeDropdown");
14+
}
15+
16+
// courtesy MDN
17+
function storageAvailable(type) {
18+
let storage;
19+
try {
20+
storage = window[type];
21+
const x = "__storage_test__";
22+
storage.setItem(x, x);
23+
storage.removeItem(x);
24+
return true;
25+
} catch (e) {
26+
return (
27+
e instanceof DOMException &&
28+
e.name === "QuotaExceededError" &&
29+
// acknowledge QuotaExceededError only if there's something already stored
30+
storage &&
31+
storage.length !== 0
32+
);
33+
}
34+
}
35+
36+
function handleBlur(event) {
37+
const parent = document.getElementById("theme-choice");
38+
if (!parent.contains(document.activeElement) &&
39+
!parent.contains(event.relatedTarget) &&
40+
parent.classList.contains("showThemeDropdown")
41+
) {
42+
console.debug('Closing the dropdown');
43+
parent.classList.remove("showThemeDropdown");
44+
}
45+
}
46+
47+
// close the theme dropdown if clicking somewhere else
48+
document.querySelector('.theme-icon').onblur = handleBlur;
49+
50+
// Check for saved user preference on load, else check and save user agent prefs
51+
let savedTheme = null;
52+
if (storageAvailable("localStorage")) {
53+
savedTheme = localStorage.getItem("blog-rust-lang-org-theme");
54+
}
55+
if (savedTheme) {
56+
document.documentElement.setAttribute("data-theme", savedTheme);
57+
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
58+
document.documentElement.setAttribute("data-theme", "dark");
59+
localStorage.setItem("blog-rust-lang-org-theme", "dark");
60+
}
61+
62+
// show the theme selector only if JavaScript is enabled/available
63+
document.querySelector('.theme-icon').style.display = 'block';

templates/footer.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@
4444

4545
<!-- scripts -->
4646
<script src="{{root}}scripts/highlight.js"></script>
47+
<script src="{{root}}scripts/theme-switch.js"></script>

templates/headers.hbs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
<!-- Twitter card -->
2-
<meta name="twitter:card" content="summary">
3-
<meta name="twitter:site" content="@rustlang">
4-
<meta name="twitter:creator" content="@rustlang">
5-
<meta name="twitter:title" content="{{title}}">
6-
<meta name="twitter:description" content="{{blog.description}}">
1+
<!-- Twitter card -->
2+
<meta name="twitter:card" content="summary">
3+
<meta name="twitter:site" content="@rustlang">
4+
<meta name="twitter:creator" content="@rustlang">
5+
<meta name="twitter:title" content="{{title}}">
6+
<meta name="twitter:description" content="{{blog.description}}">
77
<meta name="twitter:image" content="https://www.rust-lang.org/static/images/rust-social.jpg">
88

99
<!-- Facebook OpenGraph -->
@@ -19,6 +19,11 @@
1919
<link rel="stylesheet" href="{{root}}styles/app.css"/>
2020
<link rel="stylesheet" href="{{root}}styles/highlight.css"/>
2121

22+
<!-- stylesheet for user agents without js -->
23+
<noscript>
24+
<link rel="stylesheet" href="{{root}}styles/noscript.css">
25+
</noscript>
26+
2227
<!-- favicon -->
2328
<link rel="apple-touch-icon" sizes="180x180" href="{{root}}images/apple-touch-icon.png">
2429
<link rel="icon" type="image/png" sizes="16x16" href="{{root}}images/favicon-16x16.png">
@@ -29,5 +34,5 @@
2934
<meta name="msapplication-TileColor" content="#00aba9">
3035
<meta name="theme-color" content="#ffffff">
3136

32-
<!-- atom -->
33-
<link type="application/atom+xml" rel="alternate" href="https://blog.rust-lang.org/{{blog.prefix}}feed.xml" title="{{blog.title}}" />
37+
<!-- atom -->
38+
<link type="application/atom+xml" rel="alternate" href="https://blog.rust-lang.org/{{blog.prefix}}feed.xml" title="{{blog.title}}" />

templates/nav.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,11 @@
1313
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/tools">Tools</a></li>
1414
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/governance">Governance</a></li>
1515
<li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/community">Community</a></li>
16+
<button class="theme-icon" onclick="dropdown();">🖌
17+
<ul id="theme-choice">
18+
<li class="theme-item" onclick="changeThemeTo('light');">Light</li>
19+
<li class="theme-item" onclick="changeThemeTo('dark');">Dark</li>
20+
</ul>
21+
</button>
1622
</ul>
1723
</nav>

0 commit comments

Comments
 (0)