Remove click buffer, add CSS cursor:pointer for island controls
The click buffer (capture + stopPropagation + replay) caused more harm than good: synchronous XHR blocks the main thread during kernel load, so there's no window where clicks can be captured. The buffer was eating clicks after hydration due to property name mismatches. Replace with pure CSS: buttons/links/[role=button] inside islands get cursor:pointer from SSR. No JS needed, works immediately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -492,27 +492,7 @@
|
||||
(process-elements nil)
|
||||
;; Wire up popstate for back/forward navigation
|
||||
(dom-listen (dom-window) "popstate"
|
||||
(fn (e) (handle-popstate 0)))
|
||||
;; Replay buffered clicks from hydration gap
|
||||
(let ((queued (host-get (dom-window) "_sxQ")))
|
||||
(when queued
|
||||
(let ((arr (host-call (host-global "Array") "from" queued)))
|
||||
(let ((n (host-get arr "length")))
|
||||
(when (> n 0)
|
||||
(log-info (str "replaying " n " buffered click(s)"))
|
||||
(let loop ((i 0))
|
||||
(when (< i n)
|
||||
(let ((entry (host-call arr "at" i)))
|
||||
(when entry
|
||||
(let ((target (host-get entry "t")))
|
||||
(when (and target (host-get target "isConnected"))
|
||||
(dom-remove-class target "sx-pending")
|
||||
(host-call target "click")))))
|
||||
(loop (+ i 1)))))))
|
||||
(host-set! (dom-window) "_sxQ" nil)
|
||||
;; Clear any remaining pending indicators
|
||||
(for-each (fn (el) (dom-remove-class el "sx-pending"))
|
||||
(dom-query-all (dom-body) ".sx-pending")))))))
|
||||
(fn (e) (handle-popstate 0))))))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -76,9 +76,8 @@ details.group{overflow:hidden}details.group>summary{list-style:none}details.grou
|
||||
(body :class "bg-stone-50 text-stone-900"
|
||||
;; Server-rendered HTML — visible immediately before JS loads
|
||||
(div :id "sx-root" (raw! (or body-html "")))
|
||||
;; Event buffer — captures clicks during hydration gap, replayed after boot
|
||||
(style (raw! "@keyframes sx-pending-pulse{0%,100%{opacity:1}50%{opacity:.5}}.sx-pending{animation:sx-pending-pulse .8s ease-in-out infinite}"))
|
||||
(script (raw! "document.addEventListener('click',function(e){var i=e.target.closest('[data-sx-island]');if(i&&!i['_sxBoundisland-hydrated']){e.stopPropagation();var t=e.target.closest('button,a,[role=button]')||e.target;t.classList.add('sx-pending');(window._sxQ=window._sxQ||[]).push({t:t,ts:Date.now()})}},true)"))
|
||||
;; Island interactive elements — cursor pointer from SSR, no JS needed
|
||||
(style (raw! "[data-sx-island] button,[data-sx-island] a,[data-sx-island] [role=button]{cursor:pointer}"))
|
||||
(script :type "text/sx" :data-components true :data-hash component-hash
|
||||
(raw! (or component-defs "")))
|
||||
(when init-sx
|
||||
|
||||
22
web/boot.sx
22
web/boot.sx
@@ -492,27 +492,7 @@
|
||||
(process-elements nil)
|
||||
;; Wire up popstate for back/forward navigation
|
||||
(dom-listen (dom-window) "popstate"
|
||||
(fn (e) (handle-popstate 0)))
|
||||
;; Replay buffered clicks from hydration gap
|
||||
(let ((queued (host-get (dom-window) "_sxQ")))
|
||||
(when queued
|
||||
(let ((arr (host-call (host-global "Array") "from" queued)))
|
||||
(let ((n (host-get arr "length")))
|
||||
(when (> n 0)
|
||||
(log-info (str "replaying " n " buffered click(s)"))
|
||||
(let loop ((i 0))
|
||||
(when (< i n)
|
||||
(let ((entry (host-call arr "at" i)))
|
||||
(when entry
|
||||
(let ((target (host-get entry "t")))
|
||||
(when (and target (host-get target "isConnected"))
|
||||
(dom-remove-class target "sx-pending")
|
||||
(host-call target "click")))))
|
||||
(loop (+ i 1)))))))
|
||||
(host-set! (dom-window) "_sxQ" nil)
|
||||
;; Clear any remaining pending indicators
|
||||
(for-each (fn (el) (dom-remove-class el "sx-pending"))
|
||||
(dom-query-all (dom-body) ".sx-pending")))))))
|
||||
(fn (e) (handle-popstate 0))))))
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user