ref: b61213d33be35446f1fff9efcb83adbb3eaf4d54
parent: 35e034e341b0794b327eabb3f25392e3b27dd99b
author: Philip Silva <philip.silva@protonmail.com>
date: Sun Sep 19 09:24:07 EDT 2021
initial css var support
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@
consider turning on scroll since processing
waits for that...)
-`$font` is used to select the font.
+`$font` is used to select the font. Very large fonts will set dpi to 200.
## macOS
--- a/style/fonts_plan9.go
+++ b/style/fonts_plan9.go
@@ -18,6 +18,10 @@
)
func initFontserver() {
+ if dui == nil {
+ // unit test
+ return
+ }
if df := dui.Font(nil); df.Height >= 40 {
dui.Display.DPI = 200
}
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -110,7 +110,7 @@
}
func FetchNodeMap(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node]Map, err error) {
- mr, err := FetchNodeRules(doc, cssText, windowWidth)
+ mr, rv, err := FetchNodeRules(doc, cssText, windowWidth)
if err != nil {
return nil, fmt.Errorf("fetch rules: %w", err)
}
@@ -122,6 +122,13 @@
if exist, ok := ds[d.Property]; ok && smaller(*d, exist) {
continue
}
+ if strings.HasPrefix(d.Value, "var(") {
+ v := strings.TrimPrefix(d.Value, "var(")
+ v = strings.TrimSuffix(v, ")")
+ if vv, ok := rv[v]; ok {
+ d.Value = vv
+ }
+ }
ds[d.Property] = *d
}
}
@@ -147,14 +154,20 @@
return cascadia.Compile(v)
}
-func FetchNodeRules(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node][]*css.Rule, err error) {
+func FetchNodeRules(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node][]*css.Rule, rVars map[string]string, err error) {
m = make(map[*html.Node][]*css.Rule)
+ rVars = make(map[string]string)
s, err := parser.Parse(cssText)
if err != nil {
- return nil, fmt.Errorf("douceur parse: %w", err)
+ return nil, nil, fmt.Errorf("douceur parse: %w", err)
}
processRule := func(m map[*html.Node][]*css.Rule, r *css.Rule) (err error) {
for _, sel := range r.Selectors {
+ if sel.Value == ":root" {
+ for _, d := range r.Declarations {
+ rVars[d.Property] = d.Value
+ }
+ }
cs, err := compile(sel.Value)
if err != nil {
log.Printf("cssSel compile %v: %v", sel.Value, err)
@@ -173,7 +186,7 @@
}
for _, r := range s.Rules {
if err := processRule(m, r); err != nil {
- return nil, fmt.Errorf("process rule: %w", err)
+ return nil, nil, fmt.Errorf("process rule: %w", err)
}
// for media queries
@@ -185,7 +198,7 @@
l := m[1]+m[2]
maxWidth, _, err := length(nil, l)
if err != nil {
- return nil, fmt.Errorf("atoi: %w", err)
+ return nil, nil, fmt.Errorf("atoi: %w", err)
}
if float64(windowWidth) > maxWidth {
continue
@@ -196,7 +209,7 @@
l := m[1]+m[2]
minWidth, _, err := length(nil, l)
if err != nil {
- return nil, fmt.Errorf("atoi: %w", err)
+ return nil, nil, fmt.Errorf("atoi: %w", err)
}
if float64(windowWidth) < minWidth {
continue
@@ -204,7 +217,7 @@
}
for _, rr := range r.Rules {
if err := processRule(m, rr); err != nil {
- return nil, fmt.Errorf("process embedded rule: %w", err)
+ return nil, nil, fmt.Errorf("process embedded rule: %w", err)
}
}
}
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -67,7 +67,7 @@
`
for _, w := range []int{400, 800} {
t.Logf("w=%v", w)
- m, err := FetchNodeRules(doc, css, w)
+ m, _, err := FetchNodeRules(doc, css, w)
if err != nil {
t.Fail()
}
@@ -124,7 +124,7 @@
if err != nil {
t.Fail()
}
- m, err := FetchNodeRules(doc, AddOnCSS, 1024)
+ m, _, err := FetchNodeRules(doc, AddOnCSS, 1024)
if err != nil {
t.Fail()
}
@@ -354,3 +354,57 @@
}
}
}
+
+func TestCssVars(t *testing.T) {
+ data := `<body>
+ <h2 id="foo">a header</h2>
+ <h2 id="bar">another header</h2>
+ <p>Some text <b>in bold</b></p>
+ </body>`
+ doc, err := html.Parse(strings.NewReader(data))
+ if err != nil {
+ t.Fail()
+ }
+ css := AddOnCSS + `
+:root {
+ --emph: red;
+ --h: 10px;
+}
+
+b {
+ color: var(--emph);
+}
+ `
+
+ _, rv, err := FetchNodeRules(doc, css, 1280)
+ if err != nil {
+ t.Fail()
+ }
+
+ if len(rv) != 2 || rv["--emph"] != "red" || rv["--h"] != "10px" {
+ t.Fail()
+ }
+
+ var b *html.Node
+ var f func(n *html.Node)
+ f = func(n *html.Node) {
+ if n.Data == "b" {
+ b = n
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ f(c)
+ }
+ }
+ f(doc)
+
+ nm, err := FetchNodeMap(doc, css, 1280)
+ if err != nil {
+ t.Fail()
+ }
+ d := nm[b]
+ t.Logf("d=%+v", d)
+ if d.Declarations["color"].Value != "red" {
+ t.Fail()
+ }
+}
+