diff options
| -rw-r--r-- | assets/cgit.css | 54 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | assets/cgit.js | 56 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-shared.c | 3 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3 files changed, 108 insertions, 5 deletions
diff --git a/assets/cgit.css b/assets/cgit.css index 73b61f1..a65bc49 100644 --- a/assets/cgit.css +++ b/assets/cgit.css @@ -2,8 +2,9 @@ * cgit.css: styling for the cgit web interface * * Colours are driven by CSS custom properties via light-dark(), so the - * whole theme follows the reader's system preference. All rules stay - * scoped under div#cgit so cgit can be embedded in another page. + * whole theme follows the reader's system preference and can be flipped + * with the header toggle (which sets data-theme on div#cgit). All rules + * stay scoped under div#cgit so cgit can be embedded in another page. */ div#cgit { @@ -94,6 +95,10 @@ div#cgit { background: var(--bg); } +/* Explicit theme choices made through the header toggle. */ +div#cgit[data-theme="light"] { color-scheme: light; } +div#cgit[data-theme="dark"] { color-scheme: dark; } + /* On a standalone cgit page, paint the page background to match the app * and drop the default body margin so the interface is part of the page * rather than a card floating on a mismatched canvas. Scoped to the @@ -188,10 +193,10 @@ div#cgit button:focus-visible { div#cgit #header { display: grid; - grid-template-columns: auto 1fr auto; + grid-template-columns: auto 1fr auto auto; grid-template-areas: - "logo heading switch" - "logo desc owner"; + "logo heading switch theme" + "logo desc owner ."; align-items: center; column-gap: 10px; width: 100%; @@ -199,6 +204,45 @@ div#cgit #header { margin-bottom: 0; } +div#cgit #header .theme-toggle { + grid-area: theme; + justify-self: end; + align-self: center; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--control-h); + height: var(--control-h); + padding: 0; + color: var(--muted); + background-color: var(--surface); + border: solid 1px var(--border); + border-radius: 5px; + cursor: pointer; + line-height: 1; +} + +/* Keep the button hidden until the script reveals it, since the display + * rule above would otherwise override the hidden attribute. */ +div#cgit #header .theme-toggle[hidden] { + display: none; +} + +div#cgit #header .theme-toggle:hover { + color: var(--fg); + border-color: var(--border-mid); +} + +div#cgit #header .theme-toggle svg { + width: 1.1em; + height: 1.1em; + fill: none; + stroke: currentColor; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; +} + div#cgit #header .logo { grid-area: logo; align-self: start; diff --git a/assets/cgit.js b/assets/cgit.js index df3ad4e..85826b4 100644 --- a/assets/cgit.js +++ b/assets/cgit.js @@ -66,3 +66,59 @@ document.addEventListener("DOMContentLoaded", function() { }, false); })(); + +/* Colour theme toggle: cycles auto -> light -> dark and remembers the + * choice. "auto" leaves the page following the system preference via CSS. */ + +(function () { + +var KEY = "cgit-theme"; +var ORDER = [ "auto", "light", "dark" ]; +var ICONS = { + auto: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 0 0 18z" fill="currentColor" stroke="none"/></svg>', + light: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/><path d="M12 1v3M12 20v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M1 12h3M20 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"/></svg>', + dark: '<svg viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/></svg>' +}; + +function saved() { + try { return localStorage.getItem(KEY); } catch (e) { return null; } +} + +function persist(value) { + try { localStorage.setItem(KEY, value); } catch (e) { } +} + +function apply(theme, root, btn) { + if (theme === "auto") + root.removeAttribute("data-theme"); + else + root.setAttribute("data-theme", theme); + /* Keep the standalone page background in sync with the choice. */ + document.documentElement.style.colorScheme = (theme === "auto") ? "" : theme; + if (btn) { + btn.innerHTML = ICONS[theme]; + btn.title = "Colour theme: " + theme; + } +} + +document.addEventListener("DOMContentLoaded", function () { + var root = document.getElementById("cgit"); + if (!root) + return; + var btn = root.querySelector(".theme-toggle"); + var theme = saved(); + if (ORDER.indexOf(theme) < 0) + theme = "auto"; + + apply(theme, root, btn); + if (!btn) + return; + btn.hidden = false; + btn.addEventListener("click", function () { + theme = ORDER[(ORDER.indexOf(theme) + 1) % ORDER.length]; + persist(theme); + apply(theme, root, btn); + }); +}, false); + +})(); diff --git a/source/ui-shared.c b/source/ui-shared.c index 83454fe..88e5075 100644 --- a/source/ui-shared.c +++ b/source/ui-shared.c @@ -1082,6 +1082,9 @@ static void print_header(void) html_txt(ctx.cfg.root_desc); html("</p>\n"); } + + html("<button type='button' class='theme-toggle' hidden " + "aria-label='Toggle color theme'></button>\n"); html("</header>\n"); } |
