commit c4e809c11b5ece2d40332db1647ab3c5a61efedd
parent ae3a6e08a8aa8ea8047ae6408a85086ddc7016db
Author: Ibrahim Muftee <ibrahim@muftee.net>
Date: Thu, 3 Sep 2026 10:24:59 +0000
add dark mode toggle
Diffstat:
| M | stagit-index.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
| M | stagit.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
| M | style.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;
}