Rebrand sexp → sx across web platform (173 files)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s

Rename all sexp directories, files, identifiers, and references to sx.
artdag/ excluded (separate media processing DSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 11:06:57 +00:00
parent 17cebe07e7
commit e8bc228c7f
174 changed files with 3126 additions and 2952 deletions

View File

@@ -0,0 +1,44 @@
(defcomp ~post-card (&key title slug href feature-image excerpt
status published-at updated-at publish-requested
hx-select like widgets at-bar)
(article :class "border-b pb-6 last:border-b-0 relative"
(when like like)
(a :href href
:sx-get href
:sx-target "#main-panel"
:sx-select hx-select
:sx-swap "outerHTML"
:sx-push-url "true"
:class "block rounded-xl bg-white shadow hover:shadow-md transition overflow-hidden"
(header :class "mb-2 text-center"
(h2 :class "text-4xl font-bold text-stone-900" title)
(cond
(= status "draft")
(begin
(div :class "flex justify-center gap-2 mt-1"
(span :class "inline-block px-2 py-0.5 rounded-full text-xs font-semibold bg-amber-100 text-amber-800" "Draft")
(when publish-requested
(span :class "inline-block px-2 py-0.5 rounded-full text-xs font-semibold bg-blue-100 text-blue-800" "Publish requested")))
(when updated-at
(p :class "text-sm text-stone-500" (str "Updated: " updated-at))))
published-at
(p :class "text-sm text-stone-500" (str "Published: " published-at))))
(when feature-image
(div :class "mb-4"
(img :src feature-image :alt "" :class "rounded-lg w-full object-cover")))
(when excerpt
(p :class "text-stone-700 text-lg leading-relaxed text-center" excerpt)))
(when widgets widgets)
(when at-bar at-bar)))
(defcomp ~order-summary-card (&key order-id created-at description status currency total-amount)
(div :class "rounded-2xl border border-stone-200 bg-white/80 p-4 sm:p-6 space-y-2 text-xs sm:text-sm text-stone-800"
(p (span :class "font-medium" "Order ID:") " " (span :class "font-mono" (str "#" order-id)))
(p (span :class "font-medium" "Created:") " " (or created-at "\u2014"))
(p (span :class "font-medium" "Description:") " " (or description "\u2013"))
(p (span :class "font-medium" "Status:") " " (~status-pill :status (or status "pending") :size "[11px]"))
(p (span :class "font-medium" "Currency:") " " (or currency "GBP"))
(p (span :class "font-medium" "Total:") " "
(if total-amount
(str (or currency "GBP") " " total-amount)
"\u2013"))))

View File

@@ -0,0 +1,98 @@
(defcomp ~search-mobile (&key current-local-href search search-count hx-select search-headers-mobile)
(div :id "search-mobile-wrapper"
:class "flex flex-row gap-2 items-center flex-1 min-w-0 pr-2"
(input :id "search-mobile"
:type "text" :name "search" :aria-label "search"
:class "text-base md:text-sm col-span-5 rounded-md px-3 py-2 mb-2 w-full min-w-0 max-w-full border-2 border-stone-200 placeholder-shown:border-stone-200 [&:not(:placeholder-shown)]:border-yellow-200"
:sx-preserve true
:value (or search "")
:placeholder "search"
:sx-trigger "input changed delay:300ms"
:sx-target "#main-panel"
:sx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper")
:sx-get current-local-href
:sx-swap "outerHTML"
:sx-push-url "true"
:sx-headers search-headers-mobile
:sx-sync "this:replace"
:autocomplete "off")
(div :id "search-count-mobile" :aria-label "search count"
:class (if (not search-count) "text-xl text-red-500" "")
(when search (str search-count)))))
(defcomp ~search-desktop (&key current-local-href search search-count hx-select search-headers-desktop)
(div :id "search-desktop-wrapper"
:class "flex flex-row gap-2 items-center"
(input :id "search-desktop"
:type "text" :name "search" :aria-label "search"
:class "w-full mx-1 my-3 px-3 py-2 text-md rounded-xl border-2 shadow-sm border-white placeholder-shown:border-white [&:not(:placeholder-shown)]:border-yellow-200"
:sx-preserve true
:value (or search "")
:placeholder "search"
:sx-trigger "input changed delay:300ms"
:sx-target "#main-panel"
:sx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper")
:sx-get current-local-href
:sx-swap "outerHTML"
:sx-push-url "true"
:sx-headers search-headers-desktop
:sx-sync "this:replace"
:autocomplete "off")
(div :id "search-count-desktop" :aria-label "search count"
:class (if (not search-count) "text-xl text-red-500" "")
(when search (str search-count)))))
(defcomp ~mobile-filter (&key filter-summary action-buttons filter-details)
(details :class "group/filter p-2 md:hidden" :data-toggle-group "mobile-panels"
(summary :class "bg-white/90"
(div :class "flex flex-row items-start"
(div
(div :class "md:hidden mx-2 bg-stone-200 rounded"
(span :class "flex items-center justify-center text-stone-600 text-lg h-12 w-12 transition-transform group-open/filter:hidden self-start"
(i :class "fa-solid fa-filter"))
(span
(svg :aria-hidden "true" :viewBox "0 0 24 24"
:class "w-12 h-12 rotate-180 transition-transform group-open/filter:block hidden self-start"
(path :d "M6 9l6 6 6-6" :fill "currentColor")))))
(div :id "filter-summary-mobile"
:class "flex-1 md:hidden grid grid-cols-12 items-center gap-3"
(div :class "flex flex-col items-start gap-2"
(when filter-summary filter-summary)))))
(when action-buttons action-buttons)
(div :id "filter-details-mobile" :style "display:contents"
(when filter-details filter-details))))
(defcomp ~infinite-scroll (&key url page total-pages id-prefix colspan)
(if (< page total-pages)
(tr :id (str id-prefix "-sentinel-" page)
:sx-get url
:sx-trigger "intersect once delay:250ms"
:sx-swap "outerHTML"
:sx-retry "exponential:1000:30000"
:role "status" :aria-live "polite" :aria-hidden "true"
(td :colspan colspan :class "px-3 py-4"
(div :class "block md:hidden h-[60vh] js-mobile-sentinel"
(div :class "sx-indicator js-loading text-center text-xs text-stone-400"
(str "loading\u2026 " page " / " total-pages))
(div :class "js-neterr hidden flex h-full items-center justify-center"))
(div :class "hidden md:block h-[30vh] js-desktop-sentinel"
(div :class "sx-indicator js-loading text-center text-xs text-stone-400"
(str "loading\u2026 " page " / " total-pages))
(div :class "js-neterr hidden inset-0 grid place-items-center p-4"))))
(tr (td :colspan colspan :class "px-3 py-4 text-center text-xs text-stone-400"
"End of results"))))
(defcomp ~status-pill (&key status size)
(let* ((s (or status "pending"))
(lower (lower s))
(sz (or size "xs"))
(colours (cond
(= lower "paid") "border-emerald-300 bg-emerald-50 text-emerald-700"
(= lower "confirmed") "border-emerald-300 bg-emerald-50 text-emerald-700"
(= lower "checked_in") "border-blue-300 bg-blue-50 text-blue-700"
(or (= lower "failed") (= lower "cancelled")) "border-rose-300 bg-rose-50 text-rose-700"
(= lower "provisional") "border-amber-300 bg-amber-50 text-amber-700"
(= lower "ordered") "border-blue-300 bg-blue-50 text-blue-700"
true "border-stone-300 bg-stone-50 text-stone-700")))
(span :class (str "inline-flex items-center rounded-full border px-2 py-0.5 text-" sz " font-medium " colours)
s)))

View File

@@ -0,0 +1,73 @@
(defcomp ~link-card (&key link title image icon subtitle detail data-app)
(a :href link
:class "block rounded border border-stone-200 bg-white hover:bg-stone-50 transition-colors no-underline"
:data-fragment "link-card"
:data-app data-app
:sx-disable true
(div :class "flex flex-row items-start gap-3 p-3"
(if image
(img :src image :alt "" :class "flex-shrink-0 w-16 h-16 rounded object-cover")
(div :class "flex-shrink-0 w-16 h-16 rounded bg-stone-100 flex items-center justify-center text-stone-400"
(i :class icon)))
(div :class "flex-1 min-w-0"
(div :class "font-medium text-stone-900 text-sm clamp-2" title)
(when subtitle
(div :class "text-xs text-stone-500 mt-0.5" subtitle))
(when detail
(div :class "text-xs text-stone-400 mt-1" detail))))))
(defcomp ~cart-mini (&key cart-count blog-url cart-url oob)
(div :id "cart-mini"
:sx-swap-oob oob
(if (= cart-count 0)
(div :class "h-12 w-12 rounded-full overflow-hidden border border-stone-300 flex-shrink-0"
(a :href blog-url
:class "h-full w-full font-bold text-5xl flex-shrink-0 flex flex-row items-center gap-1"
(img :src (str blog-url "static/img/logo.jpg")
:class "h-full w-full rounded-full object-cover border border-stone-300 flex-shrink-0")))
(a :href cart-url
:sx-get cart-url
:sx-target "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"
:class "relative inline-flex items-center justify-center text-stone-700 hover:text-emerald-700"
(i :class "fa fa-shopping-cart text-5xl" :aria-hidden "true")
(span :class "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 inline-flex items-center justify-center rounded-full bg-emerald-600 text-white text-sm w-5 h-5"
cart-count)))))
(defcomp ~auth-menu (&key user-email account-url)
(<>
(span :id "auth-menu-desktop" :class "hidden md:inline-flex"
(if user-email
(a :href account-url
:sx-get account-url
:sx-target "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"
:class "justify-center cursor-pointer flex flex-row items-center p-3 gap-2 rounded bg-stone-200 text-black"
:data-close-details true
(i :class "fa-solid fa-user")
(span user-email))
(a :href account-url
:sx-get account-url
:sx-target "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"
:class "justify-center cursor-pointer flex flex-row items-center p-3 gap-2 rounded bg-stone-200 text-black"
:data-close-details true
(i :class "fa-solid fa-key")
(span "sign in or register"))))
(span :id "auth-menu-mobile" :class "block md:hidden text-md font-bold"
(if user-email
(a :href account-url
:sx-get account-url
:sx-target "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"
:data-close-details true
(i :class "fa-solid fa-user")
(span user-email))
(a :href account-url
:sx-get account-url
:sx-target "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"
(i :class "fa-solid fa-key")
(span "sign in or register"))))))
(defcomp ~account-nav-item (&key href label)
(div :class "relative nav-group"
(a :href href
:class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3"
:sx-disable true
label)))

View File

@@ -0,0 +1,139 @@
(defcomp ~app-body (&key header-rows filter aside menu content)
(div :class "max-w-screen-2xl mx-auto py-1 px-1"
(div :class "w-full"
(details :class "group/root p-2" :data-toggle-group "mobile-panels"
(summary
(header :class "z-50"
(div :id "root-header-summary"
:class "flex items-start gap-2 p-1 bg-sky-500"
(div :class "flex flex-col w-full items-center"
(when header-rows header-rows)))))
(div :id "root-menu" :sx-swap-oob "outerHTML" :class "md:hidden"
(when menu menu))))
(div :id "filter"
(when filter filter))
(main :id "root-panel" :class "max-w-full"
(div :class "md:min-h-0"
(div :class "flex flex-row md:h-full md:min-h-0"
(aside :id "aside"
:class "hidden md:flex md:flex-col max-w-xs md:h-full md:min-h-0 mr-3"
(when aside aside))
(section :id "main-panel"
:class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"
(when content content)
(div :class "pb-8")))))))
(defcomp ~oob-sx (&key oobs filter aside menu content)
(<>
(when oobs oobs)
(div :id "filter" :sx-swap-oob "outerHTML"
(when filter filter))
(aside :id "aside" :sx-swap-oob "outerHTML"
:class "hidden md:flex md:flex-col max-w-xs md:h-full md:min-h-0 mr-3"
(when aside aside))
(div :id "root-menu" :sx-swap-oob "outerHTML" :class "md:hidden"
(when menu menu))
(section :id "main-panel"
:class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"
(when content content))))
(defcomp ~hamburger ()
(div :class "md:hidden bg-stone-200 rounded"
(svg :class "h-12 w-12 transition-transform group-open/root:hidden block self-start"
:viewBox "0 0 24 24" :fill "none" :stroke "currentColor"
(path :stroke-linecap "round" :stroke-linejoin "round" :stroke-width "2"
:d "M4 6h16M4 12h16M4 18h16"))
(svg :aria-hidden "true" :viewBox "0 0 24 24"
:class "w-12 h-12 rotate-180 transition-transform group-open/root:block hidden self-start"
(path :d "M6 9l6 6 6-6" :fill "currentColor"))))
(defcomp ~post-label (&key feature-image title)
(<> (when feature-image
(img :src feature-image :class "h-8 w-8 rounded-full object-cover border border-stone-300 flex-shrink-0"))
(span title)))
(defcomp ~page-cart-badge (&key href count)
(a :href href :class "relative inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-full border border-emerald-300 bg-emerald-50 text-emerald-800 hover:bg-emerald-100 transition"
(i :class "fa fa-shopping-cart" :aria-hidden "true")
(span count)))
(defcomp ~header-row-sx (&key cart-mini blog-url site-title app-label
nav-tree auth-menu nav-panel
settings-url is-admin oob)
(<>
(div :id "root-row"
:sx-swap-oob (if oob "outerHTML" nil)
:class "flex flex-col items-center md:flex-row justify-center md:justify-between w-full p-1 bg-sky-500"
(div :class "w-full flex flex-row items-top"
(when cart-mini cart-mini)
(div :class "font-bold text-5xl flex-1"
(a :href (or blog-url "/") :class "flex justify-center md:justify-start items-baseline gap-2"
(h1 (or site-title ""))))
(nav :class "hidden md:flex gap-4 text-sm ml-2 justify-end items-center flex-0"
(when nav-tree nav-tree)
(when auth-menu auth-menu)
(when nav-panel nav-panel)
(when (and is-admin settings-url)
(a :href settings-url :class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3"
(i :class "fa fa-cog" :aria-hidden "true"))))
(~hamburger)))
(div :class "block md:hidden text-md font-bold"
(when auth-menu auth-menu))))
(defcomp ~menu-row-sx (&key id level colour link-href link-label link-label-content icon
hx-select nav child-id child oob external)
(let* ((c (or colour "sky"))
(lv (or level 1))
(shade (str (- 500 (* lv 100)))))
(<>
(div :id id
:sx-swap-oob (if oob "outerHTML" nil)
:class (str "flex flex-col items-center md:flex-row justify-center md:justify-between w-full p-1 bg-" c "-" shade)
(div :class "relative nav-group"
(a :href link-href
:sx-get (if external nil link-href)
:sx-target (if external nil "#main-panel")
:sx-select (if external nil (or hx-select "#main-panel"))
:sx-swap (if external nil "outerHTML")
:sx-push-url (if external nil "true")
:class "w-full whitespace-normal flex items-center gap-2 font-bold text-2xl px-3 py-2"
(when icon (i :class icon :aria-hidden "true"))
(if link-label-content link-label-content
(when link-label (div link-label)))))
(when nav
(nav :class "hidden md:flex gap-4 text-sm ml-2 justify-end items-center flex-0"
nav)))
(when (and child-id (not oob))
(div :id child-id :class "flex flex-col w-full items-center"
(when child child))))))
(defcomp ~oob-header-sx (&key parent-id child-id row)
(div :id parent-id :sx-swap-oob "outerHTML" :class "w-full"
(div :class "w-full" row
(div :id child-id))))
(defcomp ~header-child-sx (&key id inner)
(div :id (or id "root-header-child") :class "w-full" inner))
(defcomp ~error-content (&key errnum message image)
(div :class "text-center p-8 max-w-lg mx-auto"
(div :class "font-bold text-2xl md:text-4xl text-red-500 mb-4" errnum)
(div :class "text-stone-600 mb-4" message)
(when image
(div :class "flex justify-center"
(img :src image :width "300" :height "300")))))
(defcomp ~nav-link (&key href hx-select label icon aclass select-colours is-selected)
(div :class "relative nav-group"
(a :href href
:sx-get href
:sx-target "#main-panel"
:sx-select (or hx-select "#main-panel")
:sx-swap "outerHTML"
:sx-push-url "true"
:aria-selected (when is-selected "true")
:class (or aclass
(str "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3 "
(or select-colours "")))
(when icon (i :class icon :aria-hidden "true"))
(when label (span label)))))

View File

@@ -0,0 +1,35 @@
;; Miscellaneous shared components for Phase 3 conversion
;; The single place where raw! lives — for CMS content (Ghost post body,
;; product descriptions, etc.) that arrives as pre-rendered HTML.
(defcomp ~rich-text (&key html)
(raw! html))
(defcomp ~error-inline (&key message)
(div :class "text-red-600 text-sm" message))
(defcomp ~notification-badge (&key count)
(span :class "bg-red-500 text-white text-xs rounded-full px-1.5 py-0.5" count))
(defcomp ~cache-cleared (&key time-str)
(span :class "text-green-600 font-bold" "Cache cleared at " time-str))
(defcomp ~error-list (&key items)
(ul :class "list-disc pl-5 space-y-1 text-sm text-red-600"
(when items items)))
(defcomp ~error-list-item (&key message)
(li message))
(defcomp ~fragment-error (&key service)
(p :class "text-sm text-red-600" "Service " (b service) " is unavailable."))
(defcomp ~htmx-sentinel (&key id hx-get hx-trigger hx-swap class extra-attrs)
(div :id id :sx-get hx-get :sx-trigger hx-trigger :sx-swap hx-swap :class class))
(defcomp ~nav-group-link (&key href hx-select nav-class label)
(div :class "relative nav-group"
(a :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML"
:sx-push-url "true" :class nav-class
label)))

View File

@@ -0,0 +1,24 @@
(defcomp ~calendar-entry-nav (&key href name date-str nav-class)
(a :href href :class nav-class
(div :class "w-8 h-8 rounded bg-stone-200 flex-shrink-0")
(div :class "flex-1 min-w-0"
(div :class "font-medium truncate" name)
(div :class "text-xs text-stone-600 truncate" date-str))))
(defcomp ~calendar-link-nav (&key href name nav-class)
(a :href href :class nav-class
(i :class "fa fa-calendar" :aria-hidden "true")
(div name)))
(defcomp ~market-link-nav (&key href name nav-class)
(a :href href :class nav-class
(i :class "fa fa-shopping-bag" :aria-hidden "true")
(div name)))
(defcomp ~relation-nav (&key href name icon nav-class relation-type)
(a :href href :class (or nav-class "flex items-center gap-3 rounded-lg py-2 px-3 text-sm text-stone-700 hover:bg-stone-100 transition-colors")
(when icon
(div :class "w-8 h-8 rounded bg-stone-200 flex items-center justify-center flex-shrink-0"
(i :class icon :aria-hidden "true")))
(div :class "flex-1 min-w-0"
(div :class "font-medium truncate" name))))

View File

@@ -0,0 +1,25 @@
(defcomp ~base-shell (&key title asset-url &rest children)
(<>
(raw! "<!doctype html>")
(html :lang "en"
(head
(meta :charset "utf-8")
(meta :name "viewport" :content "width=device-width, initial-scale=1")
(title title)
(style
"body{margin:0;min-height:100vh;display:flex;align-items:center;"
"justify-content:center;font-family:system-ui,sans-serif;"
"background:#fafaf9;color:#1c1917}")
(script :src "https://cdn.tailwindcss.com")
(link :rel "stylesheet" :href (str asset-url "/fontawesome/css/all.min.css")))
(body :class "bg-stone-50 text-stone-900"
children))))
(defcomp ~error-page (&key title message image asset-url)
(~base-shell :title title :asset-url asset-url
(div :class "text-center p-8 max-w-lg mx-auto"
(div :class "font-bold text-2xl md:text-4xl text-red-500 mb-4"
(div message))
(when image
(div :class "flex justify-center"
(img :src image :width "300" :height "300"))))))

View File

@@ -0,0 +1,15 @@
(defcomp ~relation-attach (&key create-url label icon)
(a :href create-url
:sx-get create-url
:sx-target "#main-panel"
:sx-swap "outerHTML"
:sx-push-url "true"
:class "flex items-center gap-2 text-sm p-2 rounded hover:bg-stone-100 text-stone-500 hover:text-stone-700 transition-colors"
(when icon (i :class icon))
(span (or label "Add"))))
(defcomp ~relation-detach (&key detach-url name)
(button :sx-delete detach-url
:sx-confirm (str "Remove " (or name "this item") "?")
:class "text-red-500 hover:text-red-700 text-sm p-1 rounded hover:bg-red-50 transition-colors"
(i :class "fa fa-times" :aria-hidden "true")))