stagit My personal fork of a static git page generator
git clone https://git.muftee.net/stagit.git
Log | Files | Refs | README | LICENSE

commit c4e809c11b5ece2d40332db1647ab3c5a61efedd
parent ae3a6e08a8aa8ea8047ae6408a85086ddc7016db
Author: Ibrahim Muftee <ibrahim@muftee.net>
Date:   Thu,  3 Sep 2026 10:24:59 +0000

add dark mode toggle

Diffstat:
Mstagit-index.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mstagit.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mstyle.css | 156++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 179 insertions(+), 65 deletions(-)

diff --git a/stagit-index.c b/stagit-index.c @@ -93,6 +93,48 @@ printtimeshort(FILE *fp, const git_time *intime) } void +writejavascript(FILE *fp) +{ + fputs( + "<script>\n" + "(function() {\n" + " const toggle = document.getElementById('theme-toggle');\n" + " const icon = document.getElementById('theme-icon');\n" + " const label = document.getElementById('theme-label');\n" + " const body = document.body;\n" + "\n" + " function setTheme(theme) {\n" + " if (theme === 'dark') {\n" + " body.classList.add('dark-mode');\n" + " icon.textContent = '☀️';\n" + " label.textContent = 'Light';\n" + " } else {\n" + " body.classList.remove('dark-mode');\n" + " icon.textContent = '🌙';\n" + " label.textContent = 'Dark';\n" + " }\n" + " localStorage.setItem('theme', theme);\n" + " }\n" + "\n" + " // Check saved preference\n" + " const saved = localStorage.getItem('theme');\n" + " if (saved) {\n" + " setTheme(saved);\n" + " } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {\n" + " setTheme('dark');\n" + " }\n" + "\n" + " toggle.addEventListener('click', function() {\n" + " const isDark = body.classList.contains('dark-mode');\n" + " setTheme(isDark ? 'light' : 'dark');\n" + " });\n" + "})();\n" + "</script>\n", + fp + ); +} + +void writeheader(FILE *fp) { fputs("<!DOCTYPE html>\n" @@ -113,6 +155,8 @@ writeheader(FILE *fp) "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>" "<td><b>Last commit</b></td></tr>" "</thead><tbody>\n", fp); + + writejavascript(fp); } void diff --git a/stagit.c b/stagit.c @@ -497,6 +497,48 @@ printtimeshort(FILE *fp, const git_time *intime) } void +writejavascript(FILE *fp) +{ + fputs( + "<script>\n" + "(function() {\n" + " const toggle = document.getElementById('theme-toggle');\n" + " const icon = document.getElementById('theme-icon');\n" + " const label = document.getElementById('theme-label');\n" + " const body = document.body;\n" + "\n" + " function setTheme(theme) {\n" + " if (theme === 'dark') {\n" + " body.classList.add('dark-mode');\n" + " icon.textContent = '☀️';\n" + " label.textContent = 'Light';\n" + " } else {\n" + " body.classList.remove('dark-mode');\n" + " icon.textContent = '🌙';\n" + " label.textContent = 'Dark';\n" + " }\n" + " localStorage.setItem('theme', theme);\n" + " }\n" + "\n" + " // Check saved preference\n" + " const saved = localStorage.getItem('theme');\n" + " if (saved) {\n" + " setTheme(saved);\n" + " } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {\n" + " setTheme('dark');\n" + " }\n" + "\n" + " toggle.addEventListener('click', function() {\n" + " const isDark = body.classList.contains('dark-mode');\n" + " setTheme(isDark ? 'light' : 'dark');\n" + " });\n" + "})();\n" + "</script>\n", + fp + ); +} + +void writeheader(FILE *fp, const char *title) { fputs("<!DOCTYPE html>\n" @@ -548,6 +590,8 @@ writeheader(FILE *fp, const char *title) fprintf(fp, " | <a href=\"%sfile/%s.html\">LICENSE</a>", relpath, license); fputs("</td></tr></table>\n<hr/>\n<div id=\"content\">\n", fp); + + writejavascript(fp); } void diff --git a/style.css b/style.css @@ -1,6 +1,7 @@ +/* Light mode (default) */ body { - color: #000; - background-color: #fff; + color: #222; + background-color: #f8f8f8; font-family: monospace; } @@ -17,23 +18,22 @@ img { border: 0; } -a:target { - background-color: #ccc; +a { + color: #4a6fa5; + text-decoration: none; } -a.d, -a.h, -a.i, -a.line { - text-decoration: none; +a:hover { + color: #2a4a7a; + text-decoration: underline; } #blob a { - color: #555; + color: #888; } #blob a:hover { - color: blue; + color: #4a6fa5; text-decoration: none; } @@ -55,7 +55,7 @@ table td { #index tr:hover td, #log tr:hover td, #files tr:hover td { - background-color: #eee; + background-color: #f0f0f0; } #index tr td:nth-child(2), @@ -70,12 +70,12 @@ td.num { } .desc { - color: #555; + color: #888; } hr { border: 0; - border-top: 1px solid #555; + border-top: 1px solid #ccc; height: 1px; } @@ -84,19 +84,17 @@ pre { } pre a.h { - color: #00a; + color: #2a5a8a; } -.A, span.i, pre a.i { - color: #070; + color: #2d7a2d; } -.D, span.d, pre a.d { - color: #e00; + color: #cc3333; } pre a.h:hover, @@ -105,50 +103,78 @@ pre a.d:hover { text-decoration: none; } -@media (prefers-color-scheme: dark) { - body { - background-color: #000; - color: #bdbdbd; - } - hr { - border-color: #222; - } - a { - color: #56c8ff; - } - a:target { - background-color: #222; - } - .desc { - color: #aaa; - } - #blob a { - color: #555; - } - #blob a:target { - color: #eee; - } - #blob a:hover { - color: #56c8ff; - } - pre a.h { - color: #00cdcd; - } - .A, - span.i, - pre a.i { - color: #00cd00; - } - .D, - span.d, - pre a.d { - color: #cd0000; - } - #branches tr:hover td, - #tags tr:hover td, - #index tr:hover td, - #log tr:hover td, - #files tr:hover td { - background-color: #111; - } +/* Theme toggle button */ +#theme-toggle { + background: none; + border: 1px solid #888; + border-radius: 4px; + padding: 4px 8px; + cursor: pointer; + font-family: monospace; + color: #222; +} + +#theme-toggle:hover { + background-color: #f0f0f0; +} + +/* Dark mode */ +body.dark-mode { + color: #e0e0e0; + background-color: #1a1a1a; +} + +body.dark-mode a { + color: #6a9fd8; +} + +body.dark-mode a:hover { + color: #8ab8e8; +} + +body.dark-mode #blob a { + color: #888; +} + +body.dark-mode #blob a:hover { + color: #6a9fd8; +} + +body.dark-mode #branches tr:hover td, +body.dark-mode #tags tr:hover td, +body.dark-mode #index tr:hover td, +body.dark-mode #log tr:hover td, +body.dark-mode #files tr:hover td { + background-color: #2a2a2a; +} + +body.dark-mode .desc { + color: #888; +} + +body.dark-mode hr { + border-top: 1px solid #444; +} + +body.dark-mode pre a.h { + color: #6a9fd8; +} + +body.dark-mode span.i, +body.dark-mode pre a.i { + color: #5cb85c; +} + +body.dark-mode span.d, +body.dark-mode pre a.d { + color: #d9534f; +} + +body.dark-mode #theme-toggle { + color: #e0e0e0; + border-color: #666; +} + +body.dark-mode #theme-toggle:hover { + background-color: #2a2a2a; }