Commitfcb436c2Recorded19 Feb 2026Repositorysigil-css
feat: Add css-sel for compound selectors, eliminate css-raw from demo
Message
Add css-sel function to join multiple selectors with ", " for shared properties. Update css macro to correctly apply & suffixes to each part of compound selectors via %css-sel-append. Replace all 7 css-raw calls in the demo with proper DSL: css-sel for selector lists, & for pseudo- elements, and unquoted attribute selectors.
Changed
src/sigil/css.sgl | 33 +++++++++++++++++++++++++++++++--
test/test-css.sgl | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 2 deletions(-)Diff
src/sigil/css.sglmodified
@@ -70,6 +70,9 @@
70
css-import ; (css-import url) - @import for fonts, etc. 71
css-raw ; (css-raw "...") - raw CSS string 72
+73
;; Selector helpers+74
css-sel ; (css-sel ".a" ".b") - join selectors with ", "+75
76
;; Combinators 77
css-combine ; combine multiple css fragments 78
@@ -96,6 +99,7 @@
99
%value->string 100
%values->string 101
%css-keyframe-step+102
%css-sel-append 103
) 104
105
(begin@@ -177,6 +181,31 @@
181
(string-join (map %value->string vals) " ")) 182
183
+184
;; ========== Selector Helpers ==========+185
+186
;;; Build a compound CSS selector from multiple selectors.+187
;;;+188
;;; Joins selectors with ", " for rules that share the same properties.+189
;;;+190
;;; ```scheme+191
;;; (css (css-sel ".btn" "button[type=submit]")+192
;;; (padding "0.5rem 1rem")+193
;;; (background "#007bff"))+194
;;; ```+195
(define (css-sel . selectors)+196
(string-join selectors ", "))+197
+198
;; Append a suffix to each selector in a (possibly compound) selector.+199
;; Handles both simple selectors and comma-separated lists correctly.+200
;; % prefix = exported for macro hygiene+201
(define (%css-sel-append base suffix)+202
(let ((parts (string-split base ", ")))+203
(if (null? (cdr parts))+204
(string-append base suffix)+205
(string-join (map (lambda (s) (string-append s suffix)) parts)+206
", "))))+207
+208
209
;; ========== Rule Constructors ========== 210
211
;;; Main CSS rule constructor (macro version)@@ -196,14 +225,14 @@
225
;; Nested rule with & suffix 226
((css selector (& suffix item ...) rest ...) 227
(let ((parent (css selector rest ...))−199
(nested (css (string-append selector suffix) item ...)))+228
(nested (css (%css-sel-append selector suffix) item ...))) 229
(%make-rule (%rule-selector parent) 230
(%rule-props parent) 231
(append (%rule-children parent) (list nested))))) 232
;; Nested css rule (child selector) 233
((css selector (css child-sel item ...) rest ...) 234
(let ((parent (css selector rest ...))−206
(nested (css (string-append selector " " child-sel) item ...)))+235
(nested (css (%css-sel-append selector (string-append " " child-sel)) item ...))) 236
(%make-rule (%rule-selector parent) 237
(%rule-props parent) 238
(append (%rule-children parent) (list nested)))))test/test-css.sglmodified
@@ -173,6 +173,46 @@
173
(assert-true (string-contains? result ".button:hover {")) 174
(assert-true (string-contains? result "@media (max-width: 600px)"))))) 175
+176
(test-group "css-sel"+177
(test "joins selectors with comma"+178
(assert-equal (css-sel ".a" ".b") ".a, .b"))+179
+180
(test "single selector passes through"+181
(assert-equal (css-sel ".a") ".a"))+182
+183
(test "three selectors"+184
(assert-equal (css-sel ".a" ".b" ".c") ".a, .b, .c"))+185
+186
(test "compound selector in css rule"+187
(let ((result (css->string+188
(css (css-sel ".btn" "button[type=submit]")+189
(padding "0.5rem")))))+190
(assert-true (string-contains? result ".btn, button[type=submit] {"))))+191
+192
(test "compound selector with & nesting"+193
(let ((result (css->string+194
(css (css-sel ".btn" "button[type=submit]")+195
(color "white")+196
(& ":hover"+197
(background "blue"))))))+198
(assert-true (string-contains? result ".btn, button[type=submit] {"))+199
(assert-true (string-contains? result ".btn:hover, button[type=submit]:hover {"))))+200
+201
(test "pseudo-element with &"+202
(let ((result (css->string+203
(css "dialog"+204
(border "none")+205
(& "::backdrop"+206
(background "rgba(0,0,0,0.5)"))))))+207
(assert-true (string-contains? result "dialog {"))+208
(assert-true (string-contains? result "dialog::backdrop {"))))+209
+210
(test "attribute selector without quotes"+211
(let ((result (css->string+212
(css "input[type=text]"+213
(padding "0.5rem")))))+214
(assert-true (string-contains? result "input[type=text] {")))))+215
216
;;; Helper for string-contains? if not available 217
(define (string-contains? str substr) 218
(let ((str-len (string-length str))