Skip to content

Commit 797d3fa

Browse files
committed
Add theme toggle functionality with light and dark mode support
1 parent 059fbe3 commit 797d3fa

6 files changed

Lines changed: 257 additions & 0 deletions

File tree

docs/_includes/head_custom.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{%- comment -%}
2+
Follow the reader's browser, and remember it when they disagree.
3+
4+
just-the-docs compiles one stylesheet per colour scheme and switches by
5+
swapping the href, so choosing a scheme is choosing a file. This runs in
6+
<head>, after that link and before first paint, which is what stops the page
7+
flashing light before turning dark.
8+
{%- endcomment -%}
9+
<script>
10+
(function () {
11+
var base = '{{ "/assets/css/just-the-docs-" | relative_url }}';
12+
var sheet = document.querySelector('link[rel="stylesheet"]');
13+
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
14+
15+
function chosen() {
16+
try { return localStorage.getItem('textui-theme'); } catch (e) { return null; }
17+
}
18+
19+
function resolve(choice) {
20+
if (choice === 'light' || choice === 'dark') return choice;
21+
return prefersDark.matches ? 'dark' : 'light';
22+
}
23+
24+
function apply(choice) {
25+
var scheme = resolve(choice);
26+
if (sheet) sheet.setAttribute('href', base + scheme + '.css');
27+
var root = document.documentElement;
28+
root.setAttribute('data-theme', scheme);
29+
root.setAttribute('data-theme-choice', choice || 'auto');
30+
}
31+
32+
window.textuiTheme = {
33+
get: function () { return chosen() || 'auto'; },
34+
set: function (choice) {
35+
try {
36+
if (choice === 'auto') localStorage.removeItem('textui-theme');
37+
else localStorage.setItem('textui-theme', choice);
38+
} catch (e) { /* private windows still get a working page */ }
39+
apply(choice === 'auto' ? null : choice);
40+
}
41+
};
42+
43+
apply(chosen());
44+
45+
// Keep following the system, but only while the reader has not overridden it.
46+
var follow = function () { if (!chosen()) apply(null); };
47+
if (prefersDark.addEventListener) prefersDark.addEventListener('change', follow);
48+
else if (prefersDark.addListener) prefersDark.addListener(follow);
49+
})();
50+
</script>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<button type="button" class="theme-toggle" id="theme-toggle" aria-live="polite">
2+
<span class="theme-toggle-dot" aria-hidden="true"></span>
3+
<span class="theme-toggle-label">Theme</span>
4+
</button>
5+
<script>
6+
(function () {
7+
var button = document.getElementById('theme-toggle');
8+
if (!button || !window.textuiTheme) return;
9+
10+
var order = ['auto', 'light', 'dark'];
11+
var labels = { auto: 'Auto', light: 'Light', dark: 'Dark' };
12+
var text = button.querySelector('.theme-toggle-label');
13+
14+
function paint() {
15+
var choice = window.textuiTheme.get();
16+
text.textContent = labels[choice];
17+
button.setAttribute('title', 'Colour scheme: ' + labels[choice].toLowerCase());
18+
}
19+
20+
button.addEventListener('click', function () {
21+
var next = order[(order.indexOf(window.textuiTheme.get()) + 1) % order.length];
22+
window.textuiTheme.set(next);
23+
paint();
24+
});
25+
26+
paint();
27+
})();
28+
</script>

docs/_sass/color_schemes/dark.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Plumbing only - see _sass/custom/setup.scss. Imported after light.scss, so
2+
// every value light sets must be restated here.
3+
$color-scheme: dark;
4+
5+
$body-background-color: $dark-canvas;
6+
$body-heading-color: $dark-ink;
7+
$body-text-color: $dark-ink-body;
8+
$link-color: $dark-accent;
9+
$nav-child-link-color: $dark-ink-body;
10+
$sidebar-color: $dark-surface;
11+
$base-button-color: $dark-surface-alt;
12+
$btn-primary-color: $dark-accent;
13+
$code-background-color: $dark-surface;
14+
$feedback-color: $dark-surface-alt;
15+
$table-background-color: $dark-surface;
16+
$search-background-color: $dark-surface;
17+
$search-result-preview-color: $dark-ink-muted;
18+
$border-color: $dark-rule;
19+
20+
@import "./vendor/accessible-pygments/github-dark";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Plumbing only. The colours live in _sass/custom/setup.scss, which is
2+
// imported before this file - assigning anything here would silently beat it.
3+
$color-scheme: light;
4+
5+
$body-background-color: $light-canvas;
6+
$body-heading-color: $light-ink;
7+
$body-text-color: $light-ink-body;
8+
$link-color: $light-accent;
9+
$nav-child-link-color: $light-ink-body;
10+
$sidebar-color: $light-surface;
11+
$base-button-color: $light-surface-alt;
12+
$btn-primary-color: $light-accent;
13+
$code-background-color: $light-surface;
14+
$feedback-color: $light-surface-alt;
15+
$table-background-color: $light-canvas;
16+
$search-background-color: $light-canvas;
17+
$search-result-preview-color: $light-ink-muted;
18+
$border-color: $light-rule;
19+
20+
@import "./vendor/accessible-pygments/github-light";

docs/_sass/custom/custom.scss

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Imported last, so this is where rules go rather than variables.
2+
3+
4+
// A rule under the masthead rather than a hard edge against the sidebar.
5+
.site-header,
6+
.site-nav,
7+
.side-bar {
8+
border-color: $border-color;
9+
}
10+
11+
// The current page gets a bar in the accent, not just a bolder weight. A
12+
// terminal library's docs can afford to say where you are.
13+
.site-nav .nav-list .nav-list-item .nav-list-link.active {
14+
box-shadow: inset 2px 0 0 0 $link-color;
15+
background-color: $feedback-color;
16+
}
17+
18+
// Code is the point of this site, so it gets a real container: the surface
19+
// tone behind it, one rule around it, and no rounded corners fighting the
20+
// box-drawing characters inside.
21+
div.highlighter-rouge,
22+
figure.highlight,
23+
pre.highlight {
24+
border-radius: $border-radius;
25+
}
26+
div.highlighter-rouge div.highlight {
27+
border: 1px solid rgba($border-color, 0.4);
28+
background-color: rgba($code-background-color, 0.1);
29+
}
30+
31+
code.language-plaintext.highlighter-rouge {
32+
border: 1px solid $border-color;
33+
padding: 0.12em 0.32em;
34+
}
35+
36+
// Tables carry a lot of this documentation - the token lists, the scope
37+
// tables, the capability matrix - so the header needs to separate.
38+
.main-content table thead th {
39+
background-color: $feedback-color;
40+
border-bottom: 1px solid $border-color;
41+
}
42+
43+
.main-content table td,
44+
.main-content table th {
45+
border-color: $border-color;
46+
}
47+
48+
// Headings a little tighter than the default, which is set for prose that is
49+
// mostly paragraphs rather than mostly reference.
50+
.main-content h1,
51+
.main-content h2,
52+
.main-content h3 {
53+
letter-spacing: -0.01em;
54+
}
55+
56+
// Blockquotes read as asides here (the "not a terminal adapter" notes), so
57+
// they get the accent rather than a grey bar.
58+
.main-content blockquote {
59+
border-left-width: 1px;
60+
}
61+
62+
p.warning, blockquote.warning {
63+
border-left: 1px solid $yellow-000;
64+
border-color: rgba($yellow-000, 0.3);
65+
background-color: rgba($yellow-000, 0.1);
66+
}
67+
// The scheme toggle, at the foot of the sidebar.
68+
.theme-toggle {
69+
display: flex;
70+
align-items: center;
71+
gap: 0.5rem;
72+
width: 100%;
73+
padding: 0.4rem 0.75rem;
74+
border: 1px solid $border-color;
75+
border-radius: $border-radius;
76+
background-color: transparent;
77+
color: $search-result-preview-color;
78+
font-size: 0.72rem;
79+
font-family: $mono-font-family;
80+
letter-spacing: 0.02em;
81+
cursor: pointer;
82+
}
83+
84+
.theme-toggle:hover {
85+
background-color: $feedback-color;
86+
color: $body-heading-color;
87+
}
88+
89+
.theme-toggle-dot {
90+
width: 0.55rem;
91+
height: 0.55rem;
92+
border-radius: 50%;
93+
border: 1px solid $link-color;
94+
background-color: transparent;
95+
}
96+
97+
// Filled when the reader has chosen; hollow while it follows the browser -
98+
// the same rest/selected distinction the component catalog uses.
99+
:root[data-theme-choice="light"] .theme-toggle-dot,
100+
:root[data-theme-choice="dark"] .theme-toggle-dot {
101+
background-color: $link-color;
102+
}

docs/_sass/custom/setup.scss

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Imported before the colour schemes, so this is the one file that can move
2+
// anything. Both schemes read their palette from here and define nothing of
3+
// their own - to recolour the site, edit below and nothing else.
4+
5+
// ── Type and metrics ────────────────────────────────────────────────────────
6+
//
7+
// A terminal UI library should not set its own documentation in a font that
8+
// cannot draw a box. The mono stack leads with the faces that ship with the
9+
// systems people read this on.
10+
$mono-font-family: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono",
11+
menlo, consolas, "Liberation Mono", monospace;
12+
13+
$body-font-family: system-ui, -apple-system, "Segoe UI", roboto,
14+
"Helvetica Neue", arial, sans-serif;
15+
16+
$border-radius: 6px;
17+
$content-width: 50rem;
18+
19+
// ── Light ───────────────────────────────────────────────────────────────────
20+
$light-canvas: #ffffff; // the page
21+
$light-surface: #f6f8fa; // sidebar, code blocks, table headers
22+
$light-surface-alt: #eaeef2; // buttons, hover
23+
$light-rule: #d0d7de; // every border
24+
$light-ink: #1f2328; // headings
25+
$light-ink-body: #414850; // paragraphs
26+
$light-ink-muted: #656d76; // captions, search previews
27+
$light-accent: #15803d; // links, active nav, blockquote rule, buttons
28+
29+
// ── Dark ────────────────────────────────────────────────────────────────────
30+
$dark-canvas: #0d1117;
31+
$dark-surface: #161b22;
32+
$dark-surface-alt: #1c2128;
33+
$dark-rule: #30363d;
34+
$dark-ink: #e6edf3;
35+
$dark-ink-body: #c3ccd6;
36+
$dark-ink-muted: #8b949e;
37+
$dark-accent: #22c55e;

0 commit comments

Comments
 (0)