internal/httpd/theme.go

e6cd75b5f28bacf51620bb531320c30fd4e66bfd
gitbay/internal/httpd/theme.go history · blame · raw

61 lines · 1999 bytes

4 symbols in this file
 1package httpd
 2
 3import (
 4	"regexp"
 5	"strings"
 6
 7	"gitbay.org/gitbay/internal/web"
 8)
 9
10// themedCSS derives the served stylesheet from style.css. The source keeps
11// one dark token block under the media query, which the token tests read;
12// served, that block is guarded so a page stamped data-theme="light" keeps
13// the light tokens under a dark OS, and a copy of it applies, outside any
14// media query, when the page is stamped data-theme="dark" (#232).
15func themedCSS(src []byte) []byte {
16	const open = "@media (prefers-color-scheme: dark) {\n  :root {"
17	const close = "\n  }\n}"
18	s := string(src)
19	i := strings.Index(s, open)
20	if i < 0 {
21		return src
22	}
23	start := i + len(open)
24	n := strings.Index(s[start:], close)
25	if n < 0 {
26		return src
27	}
28	body := s[start : start+n]
29	var b strings.Builder
30	b.WriteString(s[:i])
31	b.WriteString("@media (prefers-color-scheme: dark) {\n  :root:not([data-theme=\"light\"]) {")
32	b.WriteString(body)
33	b.WriteString(close)
34	b.WriteString("\n:root[data-theme=\"dark\"] {")
35	b.WriteString(body)
36	b.WriteString("\n}")
37	b.WriteString(s[start+n+len(close):])
38	return []byte(b.String())
39}
40
41// styleCSS is what /static/style.css serves, minus the syntax palettes.
42var styleCSS = themedCSS(web.StyleCSS)
43
44// chromaRule matches the start of one rule in chroma's generated CSS: an
45// optional token-name comment, then the .chroma or .bg selector.
46var chromaRule = regexp.MustCompile(`(?m)^((?:/\*[^*]*\*/ )?)(\.chroma|\.bg)`)
47
48// scopeChroma prefixes every rule of a chroma palette with a selector, so
49// the palette applies only under that scheme. The LineLink rule is dropped:
50// it sets outline: none on the line-number links, which hid the focus ring;
51// style.css carries the rest of it.
52func scopeChroma(css, prefix string) string {
53	var out []string
54	for _, line := range strings.Split(css, "\n") {
55		if strings.Contains(line, ".lnlinks") {
56			continue
57		}
58		out = append(out, chromaRule.ReplaceAllString(line, "${1}"+prefix+" $2"))
59	}
60	return strings.Join(out, "\n")
61}