sigildocs

UI

Server-driven UI components and real-time SSE updates.
(import (sigil web ui)
        (sigil web)
        (sigil http))

The (sigil web ui) module provides server-side rendering of interactive components. The server is the source of truth; the client is a thin rendering layer powered by a small JavaScript library.

Setup

Include the client library in your HTML head and add a route to serve it:

(define (layout title body)
  (sxml->html
    `(html
       (head
         ,@(sigil-web-ui-head)
         (title ,title))
       (body ,@body))))

(define app
  (router
    (route GET "/js/sigil-web-ui.js" (sigil-web-ui-handler))
    ...))

sigil-web-ui-head returns script tags for the idiomorph library and the Sigil UI script. Customize URLs with idiomorph-url: and script-url: keywords.

SSE Updates

Server-Sent Events allow pushing real-time updates from the server. Each helper produces an SSE-formatted string to send via chunked response.

sse-morph

Morph HTML content into a target element.

(sse-morph target: "#messages" mode: "append"
           content: `(div (@ (class "msg")) "New message!"))
ModeDescription
"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

The settle: keyword adds a delay in milliseconds after morphing, useful for CSS transitions.

sse-remove

Remove an element from the DOM.

(sse-remove target: "#notification")

sse-class

Add or remove CSS classes on a target element.

(sse-class target: "#panel" add: "visible active")
(sse-class target: "#btn" remove: "loading" add: "done")

sse-eval

Execute a limited client command (focus, scroll).

(sse-eval cmd: "focus" target: "#input")
(sse-eval cmd: "scroll-to" target: "#chat" position: "bottom")

sse-redirect

Redirect the browser to a new URL.

(sse-redirect url: "/login")

Response Helpers

sigil-ui-response

Create an HTML response with merge headers for single-target updates.

(sigil-ui-response target: "#results" mode: "inner"
                   content: `(ul ,@(map render-item items)))

sigil-ui-redirect

Trigger a client-side redirect (falls back to HTTP 302 for non-JS clients).

(sigil-ui-redirect url: "/dashboard")

sse-response-batch

Send multiple SSE events in one response for multi-target updates.

(sse-response-batch
  (sse-morph target: "#sidebar" content: new-sidebar)
  (sse-morph target: "#main" content: new-content)
  (sse-eval cmd: "focus" target: "#search"))

Form Fields

Form field helpers generate SXML with labels, error display, and standard HTML attributes.

HelperHTML type
sg-text-field<input type="text">
sg-email-field<input type="email">
sg-password-field<input type="password">
sg-hidden-field<input type="hidden">
sg-textarea-field<textarea>
sg-select-field<select>
sg-checkbox-field<input type="checkbox">
sg-submit-button<button type="submit">

Common keywords shared by most fields:

KeywordDescription
name:Input name attribute
value:Current value
label:Label text (wraps in <label>)
placeholder:Placeholder text
required:Add required attribute
disabled:Add disabled attribute
error:Error message (shown in <span>)
class:CSS class
id:Element ID
(sg-text-field name: "username" label: "Username"
               placeholder: "Enter name" required: #t)

(sg-select-field name: "role" label: "Role"
                 options: '(("admin" . "Admin")
                            ("user" . "User"))
                 value: "user")

(sg-submit-button label: "Save" loading: "opacity-50")

Interactive Components

sg-button

Create a button that triggers a server action via data-sg-* attributes.

(sg-button "Like" action: "/api/like" method: "post"
           target: "#count" loading: "opacity-50")

sg-link

Create a link that morphs content into a target (SPA-style navigation).

(sg-link "Introduction" action: "/lesson/1" target: "#content")

sg-form

Create a form with action handling and optional error targeting.

(sg-form (list
           (sg-email-field name: "email" label: "Email")
           (sg-password-field name: "password" label: "Password")
           (sg-submit-button label: "Login"))
         action: "/api/login" method: "post"
         target: "#result" error-target: "#errors")

sg-sse

Create an SSE-connected container that receives real-time updates.

(sg-sse '((div (@ (id "messages"))))
        url: "/events/chat" id: "chat-container")

Higher-Level Components

Modal Dialogs

;; Define a modal
(sg-modal (list
            (p "Are you sure you want to delete this item?")
            (sg-button "Delete" action: "/api/delete/42" method: "delete")
            (sg-modal-close "Cancel"))
          id: "confirm-modal" title: "Confirm Delete")

;; Trigger button
(sg-modal-trigger "Delete Item" target: "#confirm-modal")

Data Tables

Render tabular data with optional row actions. Column field values are extracted from dicts or alists. Action URLs support {field} placeholders interpolated from row data.

(sg-data-table
  columns: '((name "Name") (email "Email"))
  rows: users
  row-actions: (list
    (sg-table-action "Edit" action: "/users/{id}/edit"
                     method: "get")
    (sg-table-action "Delete" action: "/users/{id}"
                     method: "delete"
                     confirm: "Delete this user?"))
  empty-message: "No users found.")

Paginator

Render pagination navigation with prev/next links and page numbers. Uses sg-link internally for SSE morphing.

(sg-paginator current-page: 3 total-pages: 10
              base-url: "/users" target: "#user-list"
              params: '((sort . "name")))

Keywords: current-page:, total-pages:, base-url:, target:, params: (extra query params), window-size: (pages shown around current, default 2).

Flash Messages

Display temporary notifications with auto-removal.

;; Create a flash message element
(flash-message type: 'success message: "Record saved!"
               remove-after: 3000)

;; Send via SSE (appends to #sg-flash-container)
(sse-flash type: 'error message: "Validation failed")

Types: 'info, 'success, 'error, 'warning. Each gets a CSS class like sg-flash-success.

Loading Indicator

(sg-loading-indicator id: "spinner")
(sg-loading-indicator id: "spinner" active: #t)

Common Patterns

Form with Validation Errors

(define (login-form . errors)
  (sg-form (list
             (sg-email-field name: "email" label: "Email"
                             error: (assoc-ref 'email errors #f))
             (sg-password-field name: "password" label: "Password"
                                error: (assoc-ref 'password errors #f))
             (sg-submit-button label: "Sign In"))
           action: "/login" method: "post"
           target: "#login-form"))

(define (login-handler request)
  (let ((errors (validate-login request)))
    (if errors
        (sigil-ui-response target: "#login-form" mode: "morph"
                           content: (login-form errors))
        (sigil-ui-redirect url: "/dashboard"))))

Real-Time Updates

;; Page with SSE connection
(define (chat-page request)
  (http-response/html 200
    (layout "Chat"
      (list
        (sg-sse '((div (@ (id "messages"))))
                url: "/events/chat")
        '(div (@ (id "sg-flash-container")))))))

;; SSE handler pushes updates
(define (chat-event-handler request)
  (sse-response-batch
    (sse-morph target: "#messages" mode: "append"
               content: `(div (@ (class "msg")) ,new-message))
    (sse-eval cmd: "scroll-to" target: "#messages"
              position: "bottom")))