Commit093650daRecorded29 Mar 2026Repositorysigil-web-styles
Initial implementation of sigil-web-styles design system
Message
Palette, typography, components, layout modules with default black+gold theme. Includes card, table, badge, status, button, nav, form, code block, log viewer, and ANSI color components. All styles use CSS custom properties for palette swapping.
Changed
demo/preview.sgl | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
dev-redirects.sgl | 6 +++
package.sgl | 29 ++++++++++++
src/sigil/web/styles.sgl | 88 +++++++++++++++++++++++++++++++++++
src/sigil/web/styles/components.sgl | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sigil/web/styles/layout.sgl | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sigil/web/styles/palette.sgl | 94 +++++++++++++++++++++++++++++++++++++
src/sigil/web/styles/typography.sgl | 70 ++++++++++++++++++++++++++++
test/test-styles.sgl | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1085 insertions(+)Diff
demo/preview.sgladded
@@ -0,0 +1,123 @@
+1
;;; Demo/preview page showing all sigil-web-styles components.+2
;;;+3
;;; Run with: sigil -L build/dev/lib eval -f demo/preview.sgl+4
+5
(import (sigil core)+6
(sigil io)+7
(sigil web styles))+8
+9
(define css (theme-stylesheet))+10
+11
(define html-1 (string-append+12
"<!DOCTYPE html><html><head>"+13
"<meta charset='utf-8'>"+14
"<meta name='viewport' content='width=device-width, initial-scale=1'>"+15
"<title>sigil-web-styles Preview</title>"+16
"<style>" css "</style>"+17
"</head><body>"+18
"<header class='header'>"+19
"<a href='/' class='header-logo'>sigil-web-styles</a>"+20
"<nav class='header-nav'><a href='#'>Dashboard</a></nav>"+21
"</header>"+22
"<main class='main'>"+23
"<h1 class='page-title'>Component Preview</h1>"))+24
+25
(define html-2 (string-append+26
"<section style='margin-bottom:2rem'><h2>Typography</h2>"+27
"<h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3>"+28
"<p>Body text with <a href='#'>a link</a> and <code>inline code</code>.</p>"+29
"<small>Small text</small>"+30
"<p class='text-caption'>Caption text</p>"+31
"<p class='mono'>Monospace text</p></section>"+32
"<section style='margin-bottom:2rem'><h2>Buttons</h2>"+33
"<a class='btn btn-primary' href='#'>Primary</a> "+34
"<a class='btn btn-secondary' href='#'>Secondary</a></section>"+35
"<section style='margin-bottom:2rem'><h2>Badges & Status</h2>"+36
"<p><span class='status status-up'>up</span> "+37
"<span class='status status-down'>down</span> "+38
"<span class='status status-degraded'>degraded</span> "+39
"<span class='status status-passed'>passed</span> "+40
"<span class='status status-failed'>failed</span> "+41
"<span class='status status-running'>running</span> "+42
"<span class='status status-pending'>pending</span></p>"+43
"<p style='margin-top:1rem'>Dots: "+44
"<span class='status-dot dot-up'></span> "+45
"<span class='status-dot dot-down'></span> "+46
"<span class='status-dot dot-degraded'></span> "+47
"<span class='status-dot'></span></p>"+48
"<p style='margin-top:1rem'>"+49
"<span class='status-badge status-up'>UP</span> "+50
"<span class='status-badge status-down'>DOWN</span> "+51
"<span class='status-badge status-degraded'>DEGRADED</span></p>"+52
"</section>"))+53
+54
(define html-3 (string-append+55
"<section style='margin-bottom:2rem'><h2>Cards</h2>"+56
"<div class='grid'>"+57
"<div class='card card-accent accent-success'>"+58
"<div style='font-weight:600'>Service A</div>"+59
"<div style='color:var(--color-text-muted)'><span class='status-dot dot-up'></span> up</div>"+60
"</div>"+61
"<div class='card card-accent accent-error'>"+62
"<div style='font-weight:600'>Service B</div>"+63
"<div style='color:var(--color-text-muted)'><span class='status-dot dot-down'></span> down</div>"+64
"</div>"+65
"<div class='card card-accent accent-warning'>"+66
"<div style='font-weight:600'>Service C</div>"+67
"<div style='color:var(--color-text-muted)'><span class='status-dot dot-degraded'></span> degraded</div>"+68
"</div></div></section>"+69
"<section style='margin-bottom:2rem'><h2>Status Panel</h2>"+70
"<div class='status-panel'>"+71
"<span class='status-badge status-up'>UP</span>"+72
"<div class='status-details'>"+73
"<div>Uptime: 99.9%</div><div>Latency: 12ms</div>"+74
"</div></div></section>"))+75
+76
(define html-4 (string-append+77
"<section style='margin-bottom:2rem'><h2>Table</h2>"+78
"<table class='table'><thead>"+79
"<tr><th>Job</th><th>Branch</th><th>Status</th><th>Duration</th></tr>"+80
"</thead><tbody>"+81
"<tr><td>build-sigil</td><td>master</td>"+82
"<td><span class='status status-passed'>passed</span></td><td>2m 15s</td></tr>"+83
"<tr><td>test-web</td><td>master</td>"+84
"<td><span class='status status-failed'>failed</span></td><td>1m 42s</td></tr>"+85
"<tr><td>deploy</td><td>master</td>"+86
"<td><span class='status status-running'>running</span></td><td>running...</td></tr>"+87
"</tbody></table></section>"+88
"<section style='margin-bottom:2rem'><h2>Form Inputs</h2>"+89
"<div style='max-width:400px'>"+90
"<label>Text Input</label>"+91
"<input type='text' placeholder='Enter value...' style='width:100%;margin-bottom:1rem'>"+92
"<label>Textarea</label>"+93
"<textarea rows='3' style='width:100%' placeholder='Enter text...'></textarea>"+94
"</div></section>"))+95
+96
(define html-5 (string-append+97
"<section style='margin-bottom:2rem'><h2>Log Viewer</h2>"+98
"<div class='log-viewer'>"+99
"<div class='log-line'><span class='log-seq'>1</span>"+100
"<span class='a-green'>==></span> Building...</div>"+101
"<div class='log-line'><span class='log-seq'>2</span>"+102
" <span class='a-cyan'>COMPILE</span> palette.sgl</div>"+103
"<div class='log-line'><span class='log-seq'>3</span>"+104
" <span class='a-cyan'>COMPILE</span> components.sgl</div>"+105
"<div class='log-line log-line-stderr'><span class='log-seq'>4</span>"+106
"<span class='a-red'>ERROR:</span> something went wrong</div>"+107
"<div class='log-line'><span class='log-seq'>5</span>"+108
"<span class='a-green'>Build complete!</span></div>"+109
"</div></section>"+110
"</main>"+111
"<footer class='footer'>"+112
"Crafted with <a href='https://usesigil.org'>Sigil</a>"+113
"</footer></body></html>"))+114
+115
(let ((port (open-output-file "demo/preview.html")))+116
(display html-1 port)+117
(display html-2 port)+118
(display html-3 port)+119
(display html-4 port)+120
(display html-5 port)+121
(close-port port))+122
+123
(display "Preview written to demo/preview.html\n")dev-redirects.sgladded
@@ -0,0 +1,6 @@
+1
;; Local development redirects+2
(redirects+3
repos: (list+4
(for-repo+5
url: "codeberg:sigil/sigil"+6
use: (from-path dir: "../sigil"))))package.sgladded
@@ -0,0 +1,29 @@
+1
;;; sigil-web-styles - Design system and theme library for Sigil web apps+2
;;;+3
;;; Provides palettes, typography, component styles, and layouts that give+4
;;; Sigil web apps a consistent look with customizable color themes.+5
+6
(define sigil-repo "codeberg:sigil/sigil#56f620e6")+7
+8
(package+9
name: "sigil-web-styles"+10
version: "0.1.0"+11
description: "Design system and theme library for Sigil web apps"+12
url: "https://codeberg.org/sigil/sigil-web-styles"+13
license: "BSD-3-Clause"+14
authors: (list "David Wilson <[email protected]>")+15
+16
dependencies: (list+17
(from-git url: sigil-repo package: "sigil-stdlib")+18
(from-git url: sigil-repo package: "sigil-css"))+19
+20
dev-dependencies: (list+21
(from-git url: sigil-repo package: "sigil-test"))+22
+23
tasks: (list+24
(task+25
name: 'build+26
description: "Compile sigil-web-styles modules"+27
steps: (list+28
(compile-sigil-modules sources: "src/**/*.sgl"+29
output-dir: (config-output-subdir "lib"))))))src/sigil/web/styles.sgladded
@@ -0,0 +1,88 @@
+1
;;; (sigil web styles) - Design system for Sigil web apps.+2
;;;+3
;;; Main entry point. Re-exports palette, typography, components, and layout.+4
;;; Use `theme-stylesheet` to generate a complete CSS string for an app.+5
;;;+6
;;; ## Basic Usage+7
;;;+8
;;; ```scheme+9
;;; (import (sigil web styles))+10
;;; (theme-stylesheet) ;; default black+gold+11
;;; (theme-stylesheet palette: my-custom-palette) ;; custom colors+12
;;; ```+13
+14
(define-library (sigil web styles)+15
(import (sigil core)+16
(sigil css)+17
(sigil web styles palette)+18
(sigil web styles typography)+19
(sigil web styles components)+20
(sigil web styles layout))+21
+22
(export+23
;; Main API+24
theme-stylesheet+25
+26
;; Palette+27
make-palette+28
default-palette+29
palette-background+30
palette-surface+31
palette-surface-hover+32
palette-primary+33
palette-primary-hover+34
palette-text+35
palette-text-muted+36
palette-border+37
palette-success+38
palette-warning+39
palette-error+40
palette->css-vars+41
+42
;; Typography+43
font-family-sans+44
font-family-mono+45
typography-styles+46
+47
;; Components+48
card-styles+49
table-styles+50
badge-styles+51
status-styles+52
button-styles+53
nav-styles+54
form-styles+55
code-styles+56
log-viewer-styles+57
empty-state-styles+58
ansi-color-styles+59
+60
;; Layout+61
reset-styles+62
layout-styles+63
footer-styles+64
grid-styles)+65
+66
(begin+67
+68
(define (theme-stylesheet (keys: (palette default-palette)))+69
(css->string+70
(palette->css-vars palette)+71
(reset-styles)+72
(typography-styles)+73
(nav-styles)+74
(layout-styles)+75
(footer-styles)+76
(grid-styles)+77
(card-styles)+78
(table-styles)+79
(badge-styles)+80
(status-styles)+81
(button-styles)+82
(form-styles)+83
(code-styles)+84
(log-viewer-styles)+85
(empty-state-styles)+86
(ansi-color-styles)))+87
+88
))src/sigil/web/styles/components.sgladded
@@ -0,0 +1,399 @@
+1
;;; (sigil web styles components) - Styled UI components.+2
;;;+3
;;; Each function returns sigil-css rules using CSS custom properties+4
;;; from the palette. Components include cards, tables, badges, buttons,+5
;;; nav bar, form inputs, code blocks, status indicators, and log viewer.+6
+7
(define-library (sigil web styles components)+8
(import (sigil core)+9
(sigil string)+10
(sigil css)+11
(sigil web styles typography))+12
+13
(export card-styles+14
table-styles+15
badge-styles+16
status-styles+17
button-styles+18
nav-styles+19
form-styles+20
code-styles+21
log-viewer-styles+22
empty-state-styles+23
ansi-color-styles)+24
+25
(begin+26
+27
;; ============================================================+28
;; Cards+29
;; ============================================================+30
+31
(define (card-styles)+32
(css-combine+33
(css ".card"+34
(background "linear-gradient(180deg, var(--color-surface) 0%, #141414 100%)")+35
(border "1px solid var(--color-border)")+36
(border-radius "0.5rem")+37
(padding "1.25rem")+38
(box-shadow "inset 0 1px 0 rgba(255,255,255,0.03)")+39
(transition "transform 0.15s, box-shadow 0.15s")+40
(color "var(--color-text)")+41
(text-decoration "none")+42
(display "block")+43
(& ":hover"+44
(transform "translateY(-2px)")+45
(box-shadow "0 4px 12px rgba(0,0,0,0.4)")))+46
+47
;; Card with left accent border (for status cards)+48
(css ".card-accent"+49
(border-left "4px solid var(--color-text-muted)"))+50
+51
(css ".card-accent.accent-success"+52
(border-left-color "var(--color-success)"))+53
(css ".card-accent.accent-error"+54
(border-left-color "var(--color-error)"))+55
(css ".card-accent.accent-warning"+56
(border-left-color "var(--color-warning)"))+57
(css ".card-accent.accent-primary"+58
(border-left-color "var(--color-primary)"))))+59
+60
;; ============================================================+61
;; Tables+62
;; ============================================================+63
+64
(define (table-styles)+65
(css-combine+66
(css ".table"+67
(width "100%")+68
(border-collapse "collapse"))+69
+70
(css ".table th"+71
(text-align "left")+72
(padding "0.5rem 0.75rem")+73
(border-bottom "1px solid var(--color-border)")+74
(color "var(--color-text-muted)")+75
(font-size "0.8rem")+76
(text-transform "uppercase")+77
(letter-spacing "0.05em")+78
(font-weight "500"))+79
+80
(css ".table td"+81
(padding "0.5rem 0.75rem")+82
(border-bottom "1px solid var(--color-border)"))+83
+84
(css ".table tr"+85
(cursor "pointer")+86
(& ":hover td"+87
(background "var(--color-surface-hover)")))+88
+89
(css ".table tr td a"+90
(color "inherit"))+91
+92
(css ".table tr td a.row-link"+93
(display "block")+94
(text-decoration "none"))))+95
+96
;; ============================================================+97
;; Badges / Status indicators+98
;; ============================================================+99
+100
(define (badge-styles)+101
(css-combine+102
(css ".badge"+103
(display "inline-block")+104
(padding "0.125rem 0.5rem")+105
(border-radius "4px")+106
(font-size "0.8rem")+107
(font-weight "500"))+108
+109
(css ".badge-success"+110
(background "#052e16")+111
(color "var(--color-success)"))+112
(css ".badge-error"+113
(background "#2d0a0a")+114
(color "var(--color-error)"))+115
(css ".badge-warning"+116
(background "#1a1500")+117
(color "var(--color-warning)"))+118
(css ".badge-primary"+119
(background "#1a1200")+120
(color "var(--color-primary)"))+121
(css ".badge-muted"+122
(background "var(--color-surface)")+123
(color "var(--color-text-muted)"))))+124
+125
;; Status-specific badges (for Vigil/Kiln status states)+126
(define (status-styles)+127
(css-combine+128
(css ".status"+129
(display "inline-block")+130
(padding "0.125rem 0.5rem")+131
(border-radius "4px")+132
(font-size "0.8rem")+133
(font-weight "500"))+134
+135
;; Vigil statuses+136
(css ".status-up"+137
(background "#052e16")+138
(color "var(--color-success)"))+139
(css ".status-down"+140
(background "#2d0a0a")+141
(color "var(--color-error)"))+142
(css ".status-degraded"+143
(background "#1a1500")+144
(color "var(--color-warning)"))+145
(css ".status-unknown"+146
(background "var(--color-surface)")+147
(color "var(--color-text-muted)"))+148
+149
;; Kiln statuses+150
(css ".status-passed"+151
(background "#052e16")+152
(color "var(--color-success)"))+153
(css ".status-failed"+154
(background "#2d0a0a")+155
(color "var(--color-error)"))+156
(css ".status-errored"+157
(background "#1a0d00")+158
(color "var(--color-warning)"))+159
(css ".status-running"+160
(background "#1a1200")+161
(color "var(--color-primary)")+162
(animation "pulse 2s infinite"))+163
(css ".status-starting"+164
(background "#0d1a1a")+165
(color "#5eead4")+166
(animation "pulse 2s infinite"))+167
(css (css-sel ".status-pending" ".status-waiting")+168
(background "var(--color-surface)")+169
(color "var(--color-text-muted)"))+170
(css (css-sel ".status-skipped" ".status-cancelled")+171
(background "var(--color-surface)")+172
(color "var(--color-text-muted)"))+173
+174
(css-keyframes "pulse"+175
(from (opacity "1"))+176
(|50%| (opacity "0.6"))+177
(to (opacity "1")))+178
+179
;; Status dot (small circular indicator)+180
(css ".status-dot"+181
(display "inline-block")+182
(width "8px")+183
(height "8px")+184
(border-radius "50%")+185
(background "var(--color-text-muted)"))+186
+187
(css ".dot-up, .status-up .status-dot"+188
(background "var(--color-success)"))+189
(css ".dot-down, .status-down .status-dot"+190
(background "var(--color-error)"))+191
(css ".dot-degraded, .status-degraded .status-dot"+192
(background "var(--color-warning)"))+193
+194
;; Status badge (larger, for detail pages)+195
(css ".status-badge"+196
(display "inline-block")+197
(padding "0.5rem 1.25rem")+198
(border-radius "0.25rem")+199
(font-weight "600")+200
(text-transform "uppercase")+201
(font-size "0.875rem"))+202
+203
(css ".status-badge.status-up"+204
(background "var(--color-success)")+205
(color "#000"))+206
(css ".status-badge.status-down"+207
(background "var(--color-error)")+208
(color "#fff"))+209
(css ".status-badge.status-degraded"+210
(background "var(--color-warning)")+211
(color "#000"))+212
(css ".status-badge.status-unknown"+213
(background "var(--color-text-muted)")+214
(color "#fff"))))+215
+216
;; ============================================================+217
;; Buttons+218
;; ============================================================+219
+220
(define (button-styles)+221
(css-combine+222
(css ".btn"+223
(display "inline-block")+224
(padding "0.5rem 1.25rem")+225
(border "none")+226
(border-radius "0.375rem")+227
(font-size "0.875rem")+228
(font-weight "600")+229
(cursor "pointer")+230
(text-decoration "none")+231
(transition "background 0.15s, transform 0.1s"))+232
+233
(css ".btn-primary"+234
(background "linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)")+235
(color "#000")+236
(box-shadow "inset 0 1px 0 rgba(255,255,255,0.15)")+237
(& ":hover"+238
(background "var(--color-primary-hover)"))+239
(& ":active"+240
(transform "translateY(1px)")))+241
+242
(css ".btn-secondary"+243
(background "var(--color-surface)")+244
(color "var(--color-text)")+245
(border "1px solid var(--color-border)")+246
(& ":hover"+247
(background "var(--color-surface-hover)")))))+248
+249
;; ============================================================+250
;; Navigation+251
;; ============================================================+252
+253
(define (nav-styles)+254
(css-combine+255
(css ".header"+256
(background "var(--color-surface)")+257
(border-bottom "1px solid var(--color-border)")+258
(padding "0.75rem 1.5rem")+259
(display "flex")+260
(align-items "center")+261
(gap "1.5rem"))+262
+263
(css ".header-logo"+264
(font-size "1.25rem")+265
(font-weight "700")+266
(letter-spacing "-0.02em")+267
(color "var(--color-primary)")+268
(text-decoration "none"))+269
+270
(css ".header-nav a"+271
(color "var(--color-text-muted)")+272
(text-decoration "none")+273
(& ":hover"+274
(color "var(--color-text)")))))+275
+276
;; ============================================================+277
;; Form Inputs+278
;; ============================================================+279
+280
(define (form-styles)+281
(css-combine+282
(css "input, textarea, select"+283
(background "var(--color-bg)")+284
(color "var(--color-text)")+285
(border "1px solid var(--color-border)")+286
(border-radius "0.375rem")+287
(padding "0.5rem 0.75rem")+288
(font-size "0.875rem")+289
(font-family "inherit")+290
(box-shadow "inset 0 1px 2px rgba(0,0,0,0.3)")+291
(transition "border-color 0.15s, box-shadow 0.15s")+292
(& ":focus"+293
(outline "none")+294
(border-color "var(--color-primary)")+295
(box-shadow "0 0 0 2px rgba(212,160,23,0.25)")))+296
+297
(css "label"+298
(display "block")+299
(font-size "0.875rem")+300
(font-weight "500")+301
(margin-bottom "0.25rem")+302
(color "var(--color-text-muted)"))))+303
+304
;; ============================================================+305
;; Code Blocks+306
;; ============================================================+307
+308
(define (code-styles)+309
(css-combine+310
(css "pre"+311
(background "#000")+312
(border "1px solid var(--color-border)")+313
(border-radius "6px")+314
(padding "1rem")+315
(overflow-x "auto")+316
(font-family font-family-mono)+317
(font-size "0.8rem")+318
(line-height "1.7"))+319
+320
(css "code"+321
(font-family font-family-mono)+322
(font-size "0.875rem"))+323
+324
(css ":not(pre) > code"+325
(background "var(--color-surface)")+326
(padding "0.125rem 0.375rem")+327
(border-radius "3px"))))+328
+329
;; ============================================================+330
;; Log Viewer (for Kiln-style build logs)+331
;; ============================================================+332
+333
(define (log-viewer-styles)+334
(css-combine+335
(css ".log-viewer"+336
(background "#000")+337
(border "1px solid var(--color-border)")+338
(border-radius "6px")+339
(padding "1rem")+340
(overflow-x "auto")+341
(font-family font-family-mono)+342
(font-size "0.8rem")+343
(line-height "1.7")+344
(max-height "70vh")+345
(overflow-y "auto"))+346
+347
(css ".log-line"+348
(white-space "pre")+349
(color "var(--color-text-muted)"))+350
+351
(css ".log-line-stderr"+352
(color "var(--color-error)"))+353
+354
(css ".log-seq"+355
(color "var(--color-text-muted)")+356
(user-select "none")+357
(margin-right "1rem")+358
(min-width "3ch")+359
(display "inline-block")+360
(text-align "right"))))+361
+362
;; ============================================================+363
;; Empty State+364
;; ============================================================+365
+366
(define (empty-state-styles)+367
(css ".empty"+368
(text-align "center")+369
(padding "3rem")+370
(color "var(--color-text-muted)")))+371
+372
;; ============================================================+373
;; ANSI Terminal Colors+374
;; ============================================================+375
+376
(define (ansi-color-styles)+377
(css-combine+378
(css ".a-bold" (font-weight "bold"))+379
(css ".a-dim" (opacity "0.6"))+380
(css ".a-italic" (font-style "italic"))+381
(css ".a-underline" (text-decoration "underline"))+382
(css ".a-black" (color "#555"))+383
(css ".a-red" (color "var(--color-error)"))+384
(css ".a-green" (color "var(--color-success)"))+385
(css ".a-yellow" (color "var(--color-primary)"))+386
(css ".a-blue" (color "#60a5fa"))+387
(css ".a-magenta" (color "#c084fc"))+388
(css ".a-cyan" (color "#22d3ee"))+389
(css ".a-white" (color "var(--color-text)"))+390
(css ".a-bright-black" (color "var(--color-text-muted)"))+391
(css ".a-bright-red" (color "#f87171"))+392
(css ".a-bright-green" (color "#4ade80"))+393
(css ".a-bright-yellow" (color "#fde047"))+394
(css ".a-bright-blue" (color "#93c5fd"))+395
(css ".a-bright-magenta" (color "#d8b4fe"))+396
(css ".a-bright-cyan" (color "#67e8f9"))+397
(css ".a-bright-white" (color "#fff"))))+398
+399
))src/sigil/web/styles/layout.sgladded
@@ -0,0 +1,143 @@
+1
;;; (sigil web styles layout) - Page structure and responsive layout.+2
;;;+3
;;; Page shell (header, main, footer), grid system for card layouts,+4
;;; responsive breakpoints, and container widths.+5
+6
(define-library (sigil web styles layout)+7
(import (sigil core)+8
(sigil css))+9
+10
(export reset-styles+11
layout-styles+12
footer-styles+13
grid-styles)+14
+15
(begin+16
+17
;; ============================================================+18
;; Reset+19
;; ============================================================+20
+21
(define (reset-styles)+22
(css "*"+23
(margin "0")+24
(padding "0")+25
(box-sizing "border-box")))+26
+27
;; ============================================================+28
;; Page Layout+29
;; ============================================================+30
+31
(define (layout-styles)+32
(css-combine+33
(css ".main"+34
(max-width "1200px")+35
(margin "0 auto")+36
(padding "1.5rem"))+37
+38
(css ".main-narrow"+39
(max-width "960px")+40
(margin "0 auto")+41
(padding "1.5rem"))+42
+43
(css ".page-title"+44
(font-size "1.5rem")+45
(margin-bottom "1rem")+46
(color "var(--color-text)"))+47
+48
;; Status panel (detail pages)+49
(css ".status-panel"+50
(display "flex")+51
(align-items "center")+52
(gap "2rem")+53
(margin-bottom "2rem")+54
(padding "1.5rem")+55
(background "linear-gradient(180deg, var(--color-surface) 0%, #141414 100%)")+56
(border-radius "0.5rem")+57
(box-shadow "inset 0 1px 0 rgba(255,255,255,0.03)"))+58
+59
(css ".status-details"+60
(color "var(--color-text-muted)")+61
(font-size "0.9rem")+62
(& " div"+63
(margin-bottom "0.25rem")))+64
+65
;; Run/status info line+66
(css ".run-status-line"+67
(margin-bottom "1rem")+68
(font-size "0.9rem")+69
(color "var(--color-text-muted)")+70
(display "flex")+71
(align-items "center")+72
(flex-wrap "wrap")+73
(gap "0.25rem"))+74
+75
(css ".run-status-line .mono"+76
(font-family "'JetBrains Mono', 'Fira Code', monospace")+77
(font-size "0.85rem"))+78
+79
(css ".dim"+80
(color "var(--color-text-muted)"))+81
+82
;; Check history section+83
(css ".check-history"+84
(margin-top "1rem"))+85
(css ".check-history h2"+86
(margin-bottom "1rem")+87
(font-size "1.25rem"))+88
+89
;; Responsive+90
(css-media "(max-width: 640px)"+91
(css ".main, .main-narrow"+92
(padding "1rem"))+93
(css ".status-panel"+94
(flex-direction "column")+95
(align-items "flex-start")+96
(gap "1rem")))))+97
+98
;; ============================================================+99
;; Footer+100
;; ============================================================+101
+102
(define (footer-styles)+103
(css ".footer"+104
(text-align "center")+105
(padding "2rem 1rem")+106
(color "var(--color-text-muted)")+107
(font-size "0.8rem")+108
(& " a"+109
(color "var(--color-text-muted)"))))+110
+111
;; ============================================================+112
;; Grid System+113
;; ============================================================+114
+115
(define (grid-styles)+116
(css-combine+117
(css ".grid"+118
(display "grid")+119
(grid-template-columns "repeat(auto-fill, minmax(280px, 1fr))")+120
(gap "1rem"))+121
+122
;; Service card grid (alias)+123
(css ".service-grid"+124
(display "grid")+125
(grid-template-columns "repeat(auto-fill, minmax(280px, 1fr))")+126
(gap "1rem"))+127
+128
;; Repo group (Kiln)+129
(css ".repo-group"+130
(margin-bottom "2rem"))+131
(css ".repo-heading"+132
(font-size "1rem")+133
(color "var(--color-text-muted)")+134
(font-family "'JetBrains Mono', 'Fira Code', monospace")+135
(margin-bottom "0.5rem")+136
(font-weight "normal"))+137
+138
;; Responsive+139
(css-media "(max-width: 640px)"+140
(css ".grid, .service-grid"+141
(grid-template-columns "1fr")))))+142
+143
))src/sigil/web/styles/palette.sgladded
@@ -0,0 +1,94 @@
+1
;;; (sigil web styles palette) - Color palettes for Sigil web apps.+2
;;;+3
;;; A palette is a record of named colors used by all component styles.+4
;;; The default palette is black and gold. Apps can create custom palettes+5
;;; with `make-palette` and pass them to component style functions.+6
+7
(define-library (sigil web styles palette)+8
(import (sigil core)+9
(sigil string)+10
(sigil css))+11
+12
(export make-palette+13
default-palette+14
palette-background+15
palette-surface+16
palette-surface-hover+17
palette-primary+18
palette-primary-hover+19
palette-text+20
palette-text-muted+21
palette-border+22
palette-success+23
palette-warning+24
palette-error+25
palette->css-vars)+26
+27
(begin+28
+29
;; A palette is a simple alist of named colors.+30
;; Using an alist allows easy extension and customization.+31
+32
(define (make-palette (keys: background surface surface-hover+33
primary primary-hover+34
text text-muted border+35
success warning error))+36
(list (cons 'background background)+37
(cons 'surface surface)+38
(cons 'surface-hover surface-hover)+39
(cons 'primary primary)+40
(cons 'primary-hover primary-hover)+41
(cons 'text text)+42
(cons 'text-muted text-muted)+43
(cons 'border border)+44
(cons 'success success)+45
(cons 'warning warning)+46
(cons 'error error)))+47
+48
(define default-palette+49
(make-palette+50
background: "#0d0d0d"+51
surface: "#1a1a1a"+52
surface-hover: "#222222"+53
primary: "#d4a017"+54
primary-hover: "#b8860b"+55
text: "#e8e8e8"+56
text-muted: "#888888"+57
border: "#2a2a2a"+58
success: "#22c55e"+59
warning: "#f59e0b"+60
error: "#ef4444"))+61
+62
;; Accessors+63
(define (palette-ref p key)+64
(let ((pair (assoc key p)))+65
(if pair (cdr pair) #f)))+66
+67
(define (palette-background p) (palette-ref p 'background))+68
(define (palette-surface p) (palette-ref p 'surface))+69
(define (palette-surface-hover p) (palette-ref p 'surface-hover))+70
(define (palette-primary p) (palette-ref p 'primary))+71
(define (palette-primary-hover p) (palette-ref p 'primary-hover))+72
(define (palette-text p) (palette-ref p 'text))+73
(define (palette-text-muted p) (palette-ref p 'text-muted))+74
(define (palette-border p) (palette-ref p 'border))+75
(define (palette-success p) (palette-ref p 'success))+76
(define (palette-warning p) (palette-ref p 'warning))+77
(define (palette-error p) (palette-ref p 'error))+78
+79
;; Convert palette to CSS custom properties (:root block)+80
(define (palette->css-vars p)+81
(css-vars+82
(--color-bg (palette-background p))+83
(--color-surface (palette-surface p))+84
(--color-surface-hover (palette-surface-hover p))+85
(--color-primary (palette-primary p))+86
(--color-primary-hover (palette-primary-hover p))+87
(--color-text (palette-text p))+88
(--color-text-muted (palette-text-muted p))+89
(--color-border (palette-border p))+90
(--color-success (palette-success p))+91
(--color-warning (palette-warning p))+92
(--color-error (palette-error p))))+93
+94
))src/sigil/web/styles/typography.sgladded
@@ -0,0 +1,70 @@
+1
;;; (sigil web styles typography) - Font stack, type scale, and text styles.+2
;;;+3
;;; System font stack with modular type scale for headings, body, and+4
;;; small text. Includes monospace stack for code elements.+5
+6
(define-library (sigil web styles typography)+7
(import (sigil core)+8
(sigil css))+9
+10
(export font-family-sans+11
font-family-mono+12
typography-styles)+13
+14
(begin+15
+16
(define font-family-sans+17
"system-ui, -apple-system, 'Segoe UI', sans-serif")+18
+19
(define font-family-mono+20
"'JetBrains Mono', 'Fira Code', 'Cascadia Code', ui-monospace, monospace")+21
+22
(define (typography-styles)+23
(css-combine+24
(css "body"+25
(font-family font-family-sans)+26
(font-size "16px")+27
(line-height "1.6")+28
(color "var(--color-text)")+29
(background "var(--color-bg)"))+30
+31
(css "h1"+32
(font-size "1.75rem")+33
(font-weight "700")+34
(line-height "1.2")+35
(margin-bottom "1rem"))+36
+37
(css "h2"+38
(font-size "1.375rem")+39
(font-weight "600")+40
(line-height "1.3")+41
(margin-bottom "0.75rem"))+42
+43
(css "h3"+44
(font-size "1.125rem")+45
(font-weight "600")+46
(line-height "1.4")+47
(margin-bottom "0.5rem"))+48
+49
(css "small, .text-small"+50
(font-size "0.875rem"))+51
+52
(css ".text-caption"+53
(font-size "0.8rem")+54
(color "var(--color-text-muted)"))+55
+56
(css ".mono"+57
(font-family font-family-mono)+58
(font-size "0.85rem"))+59
+60
(css "code, pre"+61
(font-family font-family-mono)+62
(font-size "0.875rem"))+63
+64
(css "a"+65
(color "var(--color-primary)")+66
(text-decoration "none")+67
(& ":hover"+68
(text-decoration "underline")))))+69
+70
))test/test-styles.sgladded
@@ -0,0 +1,133 @@
+1
;;; Test suite for sigil-web-styles+2
+3
(import (sigil test)+4
(sigil string)+5
(sigil web styles)+6
(sigil web styles palette)+7
(sigil web styles typography)+8
(sigil web styles components)+9
(sigil web styles layout)+10
(sigil css))+11
+12
(define (string-contains? haystack needle)+13
(let ((hlen (string-length haystack))+14
(nlen (string-length needle)))+15
(let loop ((i 0))+16
(cond+17
((> (+ i nlen) hlen) #f)+18
((string=? (substring haystack i (+ i nlen)) needle) #t)+19
(else (loop (+ i 1)))))))+20
+21
(test-group "palette"+22
(test "default-palette has all colors"+23
(assert-true (string? (palette-background default-palette)))+24
(assert-true (string? (palette-surface default-palette)))+25
(assert-true (string? (palette-primary default-palette)))+26
(assert-true (string? (palette-text default-palette)))+27
(assert-true (string? (palette-success default-palette)))+28
(assert-true (string? (palette-warning default-palette)))+29
(assert-true (string? (palette-error default-palette))))+30
+31
(test "default palette uses black+gold"+32
(assert-equal "#0d0d0d" (palette-background default-palette))+33
(assert-equal "#d4a017" (palette-primary default-palette)))+34
+35
(test "make-palette creates custom palette"+36
(let ((p (make-palette+37
background: "#fff" surface: "#f0f0f0" surface-hover: "#e0e0e0"+38
primary: "#0066cc" primary-hover: "#004c99"+39
text: "#111" text-muted: "#666" border: "#ccc"+40
success: "#0a0" warning: "#fa0" error: "#f00")))+41
(assert-equal "#fff" (palette-background p))+42
(assert-equal "#0066cc" (palette-primary p))))+43
+44
(test "palette->css-vars generates :root block"+45
(let ((css (css->string (palette->css-vars default-palette))))+46
(assert-true (string-contains? css ":root"))+47
(assert-true (string-contains? css "--color-bg: #0d0d0d"))+48
(assert-true (string-contains? css "--color-primary: #d4a017")))))+49
+50
(test-group "typography"+51
(test "typography-styles generates body styles"+52
(let ((css (css->string (typography-styles))))+53
(assert-true (string-contains? css "body"))+54
(assert-true (string-contains? css "font-family"))+55
(assert-true (string-contains? css "line-height: 1.6")))))+56
+57
(test-group "components"+58
(test "card-styles generates .card"+59
(let ((css (css->string (card-styles))))+60
(assert-true (string-contains? css ".card"))+61
(assert-true (string-contains? css "linear-gradient"))))+62
+63
(test "table-styles generates .table"+64
(let ((css (css->string (table-styles))))+65
(assert-true (string-contains? css ".table"))+66
(assert-true (string-contains? css "border-collapse"))))+67
+68
(test "status-styles generates status classes"+69
(let ((css (css->string (status-styles))))+70
(assert-true (string-contains? css ".status-up"))+71
(assert-true (string-contains? css ".status-down"))+72
(assert-true (string-contains? css ".status-passed"))+73
(assert-true (string-contains? css ".status-failed"))+74
(assert-true (string-contains? css "@keyframes pulse"))))+75
+76
(test "button-styles has gradient shine"+77
(let ((css (css->string (button-styles))))+78
(assert-true (string-contains? css ".btn-primary"))+79
(assert-true (string-contains? css "linear-gradient"))))+80
+81
(test "nav-styles generates header"+82
(let ((css (css->string (nav-styles))))+83
(assert-true (string-contains? css ".header"))+84
(assert-true (string-contains? css ".header-logo"))))+85
+86
(test "log-viewer-styles generates log classes"+87
(let ((css (css->string (log-viewer-styles))))+88
(assert-true (string-contains? css ".log-viewer"))+89
(assert-true (string-contains? css ".log-line"))))+90
+91
(test "ansi-color-styles generates ANSI classes"+92
(let ((css (css->string (ansi-color-styles))))+93
(assert-true (string-contains? css ".a-bold"))+94
(assert-true (string-contains? css ".a-red"))+95
(assert-true (string-contains? css ".a-green")))))+96
+97
(test-group "layout"+98
(test "reset-styles generates box-sizing"+99
(let ((css (css->string (reset-styles))))+100
(assert-true (string-contains? css "box-sizing: border-box"))))+101
+102
(test "layout-styles generates .main"+103
(let ((css (css->string (layout-styles))))+104
(assert-true (string-contains? css ".main"))+105
(assert-true (string-contains? css "max-width"))))+106
+107
(test "grid-styles generates grid"+108
(let ((css (css->string (grid-styles))))+109
(assert-true (string-contains? css ".grid"))+110
(assert-true (string-contains? css "grid-template-columns")))))+111
+112
(test-group "theme-stylesheet"+113
(test "generates complete stylesheet with default palette"+114
(let ((css (theme-stylesheet)))+115
(assert-true (string-contains? css ":root"))+116
(assert-true (string-contains? css "--color-bg: #0d0d0d"))+117
(assert-true (string-contains? css ".card"))+118
(assert-true (string-contains? css ".table"))+119
(assert-true (string-contains? css ".header"))+120
(assert-true (string-contains? css ".btn"))+121
(assert-true (string-contains? css "@keyframes pulse"))))+122
+123
(test "custom palette changes CSS variables"+124
(let* ((p (make-palette+125
background: "#ffffff" surface: "#f0f0f0" surface-hover: "#e0e0e0"+126
primary: "#0066cc" primary-hover: "#004c99"+127
text: "#111111" text-muted: "#666666" border: "#cccccc"+128
success: "#00aa00" warning: "#ffaa00" error: "#ff0000"))+129
(css (theme-stylesheet palette: p)))+130
(assert-true (string-contains? css "--color-bg: #ffffff"))+131
(assert-true (string-contains? css "--color-primary: #0066cc")))))+132
+133
(run-tests)