~gitpocket/gitpocket

1

feat(theme): default to light and make following the device opt in

commit
289561768541a261f75b2f304ae6fb03133dd142 browse tree
parent
34c1e1bf5b6e07f80c79ab72eac6627066c534e0
author
obscurejames <354576047+obscurejames@users.noreply.github.com>
signature
Signed by: ~james SHA256:KlzJ0P0gvJcnOYpaxD5yCUw52ImHttTKRjVAEnnYnKg
date
2026-11-13 16:06 UTC

6 files changed, +12 −7

README.md modified

@@ -23,7 +23,7 @@ The web interface is then on `127.0.0.1:3200` and wants a reverse proxy with TLS
2323 - Profiles with a pinned repository list, an activity graph and a README from your `~you/you` repository
2424 - SSH and PGP commit verification, TOTP, access tokens and Atom feeds for commits, issues and pull requests
2525 - Site search over repositories and people with language facets
26-- Light and dark themes that follow the device or a choice saved under settings
26+- Light and dark themes, light by default, with an option to follow the device
2727 - A notification bell for activity on pull requests and issues you take part in
2828 - Open, invite only or closed registration and an `/admin` page for accounts, invites, the front page and storage
2929

forge/admin.go modified

@@ -77,7 +77,7 @@ func (s *Store) IsAdmin(id int64) bool {
7777 return admin
7878 }
7979
80-var themes = []string{"", "light", "dark"}
80+var themes = []string{"", "light", "dark", "system"}
8181
8282 func (s *Store) Viewer(id int64) (admin bool, theme string, unread int) {
8383 s.db.QueryRow(`select admin and not banned, theme, (select count(*) from notifications where user = ?1 and unread = 1) from users where id = ?1`, id).Scan(&admin, &theme, &unread)

forge/flow_test.go modified

@@ -385,6 +385,11 @@ func TestAppearance(t *testing.T) {
385385 if !strings.Contains(get(h, "/", c).Body.String(), `data-theme="dark"`) {
386386 t.Error("dark theme not applied")
387387 }
388+ post(h, "/settings/appearance", url.Values{"theme": {"system"}}, c)
389+ if !strings.Contains(get(h, "/", c).Body.String(), `data-theme="system"`) {
390+ t.Error("system theme not applied")
391+ }
392+ post(h, "/settings/appearance", url.Values{"theme": {"dark"}}, c)
388393 post(h, "/settings/appearance", url.Values{"theme": {`x" onload="`}}, c)
389394 if !strings.Contains(get(h, "/", c).Body.String(), `data-theme="dark"`) {
390395 t.Error("unknown theme replaced the saved one")

forge/highlight.go modified

@@ -70,6 +70,6 @@ func highlightCSS() []byte {
7070 }
7171 rules = append(rules, strings.TrimSpace(rule))
7272 }
73- fmt.Fprintf(&buf, "%s\n@media (prefers-color-scheme:dark){\n%s}\n", scoped(rules, ":root[data-theme=dark] "), scoped(rules, ":root:not([data-theme=light]) "))
73+ fmt.Fprintf(&buf, "%s\n@media (prefers-color-scheme:dark){\n%s}\n", scoped(rules, ":root[data-theme=dark] "), scoped(rules, ":root[data-theme=system] "))
7474 return buf.Bytes()
7575 }

web/static/style.css modified

@@ -477,7 +477,7 @@ nav.site .me form{margin:0}
477477 label.radio input{width:auto;margin:0}
478478 :root[data-theme=dark]{--accent:#5b5fa8;--accentdark:#4a4d91;--accentsoft:#2c2e48;--focus:#4a4d91;--link:#a3a7ee;--heat0:#2f3034;--heat1:#2f3262;--heat2:#454991;--heat3:#666bc4;--heat4:#9ca0f0;--fg:#f4f4f4;--muted:#a8a8a8;--line:#3e3f45;--soft:#28292d;--bar:#303136;--add:#42be65;--del:#fa4d56;--addbg:#0e2a17;--delbg:#3a1518;--code:#28292d;--bg:#212225;--raised:#2e2f34;--raisedhover:#393a40;--edge:#57585f;--hl:#3d3412;--warnbg:#2d2510;--warnedge:#5c4b10;--warnfg:#f1c21b;--merged:#a56eff;--dotedge:rgba(255,255,255,.15);--tip:#f4f4f4;--tipfg:#212225;--fg2:#c2c4c9;color-scheme:dark}
479479 :root[data-theme=dark] nav.site .brand img{filter:brightness(2.2) saturate(.9)}
480-@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--accent:#5b5fa8;--accentdark:#4a4d91;--accentsoft:#2c2e48;--focus:#4a4d91;--link:#a3a7ee;--heat0:#2f3034;--heat1:#2f3262;--heat2:#454991;--heat3:#666bc4;--heat4:#9ca0f0;--fg:#f4f4f4;--muted:#a8a8a8;--line:#3e3f45;--soft:#28292d;--bar:#303136;--add:#42be65;--del:#fa4d56;--addbg:#0e2a17;--delbg:#3a1518;--code:#28292d;--bg:#212225;--raised:#2e2f34;--raisedhover:#393a40;--edge:#57585f;--hl:#3d3412;--warnbg:#2d2510;--warnedge:#5c4b10;--warnfg:#f1c21b;--merged:#a56eff;--dotedge:rgba(255,255,255,.15);--tip:#f4f4f4;--tipfg:#212225;--fg2:#c2c4c9;color-scheme:dark}:root:not([data-theme=light]) nav.site .brand img{filter:brightness(2.2) saturate(.9)}}
480+@media (prefers-color-scheme:dark){:root[data-theme=system]{--accent:#5b5fa8;--accentdark:#4a4d91;--accentsoft:#2c2e48;--focus:#4a4d91;--link:#a3a7ee;--heat0:#2f3034;--heat1:#2f3262;--heat2:#454991;--heat3:#666bc4;--heat4:#9ca0f0;--fg:#f4f4f4;--muted:#a8a8a8;--line:#3e3f45;--soft:#28292d;--bar:#303136;--add:#42be65;--del:#fa4d56;--addbg:#0e2a17;--delbg:#3a1518;--code:#28292d;--bg:#212225;--raised:#2e2f34;--raisedhover:#393a40;--edge:#57585f;--hl:#3d3412;--warnbg:#2d2510;--warnedge:#5c4b10;--warnfg:#f1c21b;--merged:#a56eff;--dotedge:rgba(255,255,255,.15);--tip:#f4f4f4;--tipfg:#212225;--fg2:#c2c4c9;color-scheme:dark}:root[data-theme=system] nav.site .brand img{filter:brightness(2.2) saturate(.9)}}
481481 .mdbar{display:flex;gap:.2rem;margin:.2rem 0 .3rem;font-size:.85rem}
482482 .mdbar button{font:inherit;background:none;border:1px solid transparent;color:var(--muted);padding:.1rem .55rem;cursor:pointer}
483483 .mdbar button:hover{color:var(--fg)}

web/templates/appearance.tmpl modified

@@ -2,11 +2,11 @@
22 <div class="panel"><h3>Theme</h3>
33 <form method="post" action="/settings/appearance" class="stack">
44 <label>Theme <select name="theme">
5-<option value=""{{if eq .Theme ""}} selected{{end}}>System</option>
6-<option value="light"{{if eq .Theme "light"}} selected{{end}}>Light</option>
5+<option value="light"{{if or (eq .Theme "") (eq .Theme "light")}} selected{{end}}>Light</option>
76 <option value="dark"{{if eq .Theme "dark"}} selected{{end}}>Dark</option>
7+<option value="system"{{if eq .Theme "system"}} selected{{end}}>System</option>
88 </select></label>
9-<small>System follows the light or dark setting of your device.</small>
9+<small>Light is the default. System follows the light or dark setting of your device.</small>
1010 <div class="actions left"><button class="btn primary">Save ▸</button></div>
1111 </form></div>
1212 {{end}}