(sigil web ui)
(sigil web ui) - Server-Driven Hypermedia Library
Enables dynamic, real-time web interfaces driven entirely from Sigil. The server is the source of truth; the client is a thin rendering layer.
Core concepts:
- SSE events for real-time updates (sigil:morph, sigil:eval, sigil:redirect)
- HTTP headers for single-target responses (Sigil-UI-Merge-*)
- Declarative HTML attributes (data-sg-*) for client interactions
Example:
(import (sigil web ui)
(sigil http))
;; Send an SSE event to update a target
(write-chunk (sse-morph target: "#chat" mode: "append"
content: `(div (@ (class "msg")) "Hello!")))
;; Return HTML with merge headers
(sigil-ui-response target: "#results" mode: "inner"
content: `(ul ,@(map render-item items)))Exports
sse-morphprocedureFormat an SSE morph event.
Morphs HTML content into a target element. The content can be SXML (converted to HTML) or a string.
Parameters: target: CSS selector for the target element (e.g., "#chat") content: SXML or HTML string to morph mode: Morph mode (default: "morph") settle: Milliseconds to wait after morph (for CSS transitions)
Modes: "morph" - Intelligent diff/patch via idiomorph (default) "replace" - Replace target's outerHTML "inner" - Replace target's innerHTML "append" - Append to target's children "prepend" - Prepend to target's children "before" - Insert before target "after" - Insert after target "remove" - Remove target element (no content needed)
Example:
(sse-morph target: "#messages" mode: "append"
content: `(div (@ (class "msg")) "Hello!"))sse-removeprocedureFormat an SSE remove event.
Removes the target element from the DOM. Shorthand for (sse-morph target: TARGET mode: "remove").
Example:
(sse-remove target: "#notification")sse-classprocedureFormat an SSE class event.
Adds or removes CSS classes on the target element.
Parameters: target: CSS selector for the target element add: Space-separated class names to add remove: Space-separated class names to remove
Example:
(sse-class target: "#panel" add: "visible active")
(sse-class target: "#btn" remove: "loading" add: "done")sse-evalprocedureFormat an SSE eval event.
Executes a limited command on the client. Only use for things that can't be expressed as HTML state (focus, scroll).
Commands: "focus" - Focus the target element "scroll-to" - Scroll target into view (or to bottom with position: "bottom")
Example:
(sse-eval cmd: "focus" target: "#input")
(sse-eval cmd: "scroll-to" target: "#chat" position: "bottom")sse-redirectprocedureFormat an SSE redirect event.
Redirects the browser to a new URL.
Example:
(sse-redirect url: "/login")sse-reloadprocedureFormat an SSE reload event.
Tells the browser to re-fetch the current page and morph the result into the DOM using Idiomorph. Preserves scroll position, form state, and focus. Useful for live-coding: redefine a view function, then send a reload to see the update immediately.
Parameters: target: CSS selector for the element to reload (default: "body")
Example:
(sse-reload) ; reload full body
(sse-reload target: "#sidebar") ; reload specific elementsse-css-reloadprocedureFormat an SSE css-reload event.
Reloads CSS stylesheets in the browser without a full page reload. If href: is provided, only stylesheets matching that substring are reloaded. Otherwise all stylesheets are reloaded.
Example:
(sse-css-reload) ; reload all stylesheets
(sse-css-reload href: "styles.css") ; reload specific stylesheetsse-jsprocedureFormat an SSE js event.
Executes JavaScript code in all connected browsers.
Example:
(sse-js code: "console.log('hello')")sigil-ui-headersprocedureBuild Sigil-UI headers for merge operations.
Returns a dict of headers to include in an HTTP response.
Example:
(http-response
status: 200
headers: (dict-merge (sigil-ui-headers target: "#results" mode: "append")
(dict content-type: "text/html"))
body: "<div>New item</div>")sigil-ui-responseprocedureCreate an HTML response with Sigil-UI merge headers.
Convenience helper that combines headers and body.
Example:
(sigil-ui-response target: "#chat" mode: "append"
content: `(div (@ (class "msg")) "Hello!"))sigil-ui-redirectprocedureCreate a redirect response using Sigil-UI header.
For JavaScript-enhanced clients, this triggers a client-side redirect. For non-JS clients, falls back to standard HTTP redirect.
Example:
(sigil-ui-redirect url: "/dashboard")sse-response-batchprocedureCreate a batch SSE response for multi-target updates.
Returns SSE-formatted events in a single response. Useful when one action needs to update multiple targets.
Example:
(sse-response-batch
(sse-morph target: "#sidebar" content: new-sidebar)
(sse-morph target: "#main" content: new-main)
(sse-eval cmd: "focus" target: "#input"))optional-attrsprocedureBuild an SXML attribute list, filtering out #f values.
Takes alternating name/value pairs and returns a list of (name value) entries suitable for splicing into SXML (@...) forms. Pairs where the value is #f are omitted.
Example:
`(div (@ (class "main")
,@(optional-attrs
'id my-id
'data-target target
'disabled (and disabled "disabled"))))sg-buttonprocedureCreate a button that triggers an action.
Example:
(sg-button "Like" action: "/api/like" method: "post"
loading: "opacity-50")sg-linkprocedureCreate a link that morphs content into a target.
Provides SPA-style navigation while preserving standard link behavior for non-JS clients. Children can be a string or a list of SXML nodes.
Example:
(sg-link '("Introduction") action: "/lesson/1" target: "#content")
(sg-link "Click here" action: "/page")sg-formprocedureCreate a form with action handling.
Example:
(sg-form (list
(sg-email-field name: "email")
(sg-password-field name: "password")
(sg-submit-button "Login"))
action: "/api/login" method: "post"
target: "#result" error-target: "#errors")sg-sseprocedureCreate an SSE-connected container.
Example:
(sg-sse '((div (@ (id "messages")))) url: "/events/chat" id: "chat")sg-input-fieldprocedureCreate an input field of any type.
Handles label wrapping, error display, and value coercion. Non-string values are auto-coerced: numbers become strings, #f omits the value attribute.
Example:
(sg-input-field type: "text" name: "title" label: "Title"
value: some-value required: #t)sg-text-fieldprocedureCreate a text input field.
sg-email-fieldprocedureCreate an email input field.
sg-password-fieldprocedureCreate a password input field.
sg-textarea-fieldprocedureCreate a textarea field.
Value is auto-coerced: numbers become strings, #f becomes "".
sg-select-fieldprocedureCreate a select dropdown field.
Options can be:
- List of strings: ("a" "b" "c")
- List of (value . label) pairs: (("a" . "Option A") ("b" . "Option B"))
Value is auto-coerced for comparison: numbers become strings.
sg-checkbox-fieldprocedureCreate a checkbox field.
sg-submit-buttonprocedureCreate a submit button.
flash-messageprocedureCreate a flash message notification.
Returns a div with role="alert" for accessibility. The type: controls the CSS class (sg-flash-success, sg-flash-error, etc.). When remove-after: is set, the element auto-removes after that many milliseconds.
Example:
(flash-message type: 'success message: "Saved!" remove-after: 3000)
(flash-message type: 'error message: "Failed to save")sse-flashprocedureSend a flash message via SSE.
Wraps sse-morph with mode "append" to add a flash message to a container element. Defaults to targeting "#sg-flash-container".
Example:
(sse-flash type: 'success message: "Record updated" remove-after: 3000)sg-loading-indicatorprocedureCreate a loading indicator element.
Returns a div with class "sg-loading". When active: is #t, adds the "active" class. Uses aria-hidden for accessibility.
Example:
(sg-loading-indicator id: "spinner")
(sg-loading-indicator id: "spinner" active: #t)sg-modalprocedureCreate a modal dialog using the HTML <dialog> element.
Example:
(sg-modal (list (p "Are you sure?")
(sg-modal-close "Cancel"))
id: "confirm" title: "Confirm")sg-modal-triggerprocedureCreate a button that opens a modal dialog.
Example:
(sg-modal-trigger "Open Settings" target: "#settings-modal")sg-modal-closeprocedureCreate a button that closes the nearest modal dialog.
Example:
(sg-modal-close "Cancel")sg-table-actionprocedureCreate a table action descriptor for use with sg-data-table.
Example:
(sg-table-action "Edit" action: "/users/{id}/edit")
(sg-table-action "Delete" action: "/users/{id}"
method: "delete" confirm: "Delete this user?")sg-data-tableprocedureCreate a data table with headers, rows, and optional actions.
Example:
(sg-data-table columns: '((name "Name") (email "Email"))
rows: users
row-actions: (list
(sg-table-action "Edit" action: "/users/{id}/edit")
(sg-table-action "Delete" action: "/users/{id}"
method: "delete"
confirm: "Delete?")))sg-paginatorprocedureCreate a pagination navigator.
Renders prev/next links and page numbers. Links use sg-link internally for SSE morphing. Current page is displayed as a <span>. Ellipsis shown when page window doesn't cover full range.
Example:
(sg-paginator current-page: 3 total-pages: 10
base-url: "/users" target: "#user-list"
params: '((sort . "name")))sigil-web-ui-scriptprocedureReturn the Sigil Web UI JavaScript library as a string.
Use this to include the script in static builds or serve it from a custom route.
Example:
(http-response/text 200 (sigil-web-ui-script))sigil-web-ui-handlerprocedureCreate an HTTP handler that serves the Sigil Web UI script.
Example:
(router
(route 'GET "/js/sigil-web-ui.js" (sigil-web-ui-handler))
...)sigil-web-ui-headprocedureGenerate SXML for including Sigil Web UI in a page head.
Parameters: idiomorph-url: URL for idiomorph library (default: CDN) script-url: URL for sigil-web-ui.js (default: /js/sigil-web-ui.js)
Example:
(html
(head
,@(sigil-web-ui-head)
(title "My App"))
(body ...))