Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all. WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files. CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support. Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers. New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec. Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
633 lines
24 KiB
Plaintext
633 lines
24 KiB
Plaintext
(defcomp
|
|
~reactive-islands/demo/reactive-islands-demo-content
|
|
()
|
|
(~docs/page
|
|
:title "Reactive Islands — Examples"
|
|
(~docs/section
|
|
:title "Live interactive islands"
|
|
:id "intro"
|
|
(p
|
|
(strong "Every example below is a live interactive island")
|
|
" — not a static code snippet. Click the buttons, type in the inputs. The signal runtime is defined in "
|
|
(code "signals.sx")
|
|
", bootstrapped to JavaScript. No hand-written signal logic.")
|
|
(p
|
|
"Each island uses "
|
|
(code "defisland")
|
|
" with signals ("
|
|
(code "signal")
|
|
", "
|
|
(code "deref")
|
|
", "
|
|
(code "reset!")
|
|
", "
|
|
(code "swap!")
|
|
"), derived values ("
|
|
(code "computed")
|
|
"), side effects ("
|
|
(code "effect")
|
|
"), and batch updates ("
|
|
(code "batch")
|
|
")."))
|
|
(~docs/section
|
|
:title "Examples"
|
|
:id "examples"
|
|
(ol
|
|
(~tw :tokens "space-y-1")
|
|
(map
|
|
(fn
|
|
(item)
|
|
(li
|
|
(a
|
|
:href (get item "href")
|
|
:sx-get (get item "href")
|
|
:sx-target "#sx-content"
|
|
:sx-select "#sx-content"
|
|
:sx-swap "outerHTML"
|
|
:sx-push-url "true"
|
|
(~tw :tokens "text-violet-600 hover:underline")
|
|
(get item "label"))))
|
|
reactive-examples-nav-items)))))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-counter
|
|
()
|
|
(~docs/page
|
|
:title "Signal + Computed + Effect"
|
|
(p
|
|
"A signal holds a value. A computed derives from it. Click the buttons — the counter and doubled value update instantly, no server round-trip.")
|
|
(~reactive-islands/index/demo-counter :initial 0)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-counter")
|
|
"lisp"))
|
|
(p
|
|
(code "(deref count)")
|
|
" in a text position creates a reactive text node. When "
|
|
(code "count")
|
|
" changes, "
|
|
(em "only that text node")
|
|
" updates. "
|
|
(code "doubled")
|
|
" recomputes automatically. No diffing.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-temperature
|
|
()
|
|
(~docs/page
|
|
:title "Temperature Converter"
|
|
(p
|
|
"Two derived values from one signal. Click to change Celsius — Fahrenheit updates reactively.")
|
|
(~reactive-islands/index/demo-temperature)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-temperature")
|
|
"lisp"))
|
|
(p
|
|
"The actual implementation uses "
|
|
(code "computed")
|
|
" for Fahrenheit: "
|
|
(code "(computed (fn () (+ (* (deref celsius) 1.8) 32)))")
|
|
". The "
|
|
(code "(deref fahrenheit)")
|
|
" in the span creates a reactive text node that updates when celsius changes.")
|
|
(div
|
|
(~tw :tokens "mt-6")
|
|
(~reactive-islands/test-runner-placeholder)
|
|
(script
|
|
:type "text/sx-test"
|
|
:data-for "temperature"
|
|
"(defsuite \"temperature converter\" (deftest \"initial celsius is 20\" (let ((celsius (signal 20))) (assert-signal-value celsius 20))) (deftest \"computed fahrenheit = celsius * 1.8 + 32\" (let ((celsius (signal 20)) (fahrenheit (computed (fn () (+ (* (deref celsius) 1.8) 32))))) (assert-signal-value fahrenheit 68) (assert-computed-depends-on fahrenheit celsius))) (deftest \"+5 increments celsius\" (let ((celsius (signal 20)) (btn (mock-element \"button\"))) (mock-add-listener! btn \"click\" (fn (e) (swap! celsius (fn (c) (+ c 5))))) (simulate-click btn) (assert-signal-value celsius 25))) (deftest \"fahrenheit updates on celsius change\" (let ((celsius (signal 20)) (fahrenheit (computed (fn () (+ (* (deref celsius) 1.8) 32))))) (reset! celsius 0) (assert-signal-value fahrenheit 32) (reset! celsius 100) (assert-signal-value fahrenheit 212))) (deftest \"multiple clicks accumulate\" (let ((celsius (signal 20)) (fahrenheit (computed (fn () (+ (* (deref celsius) 1.8) 32)))) (btn (mock-element \"button\"))) (mock-add-listener! btn \"click\" (fn (e) (swap! celsius (fn (c) (+ c 5))))) (simulate-click btn) (simulate-click btn) (simulate-click btn) (assert-signal-value celsius 35) (assert-signal-value fahrenheit 95))))"))))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-stopwatch
|
|
()
|
|
(~docs/page
|
|
:title "Effect + Cleanup: Stopwatch"
|
|
(p
|
|
"Effects can return cleanup functions. This stopwatch starts a "
|
|
(code "set-interval")
|
|
" — the cleanup clears it when the running signal toggles off.")
|
|
(~reactive-islands/index/demo-stopwatch)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-stopwatch")
|
|
"lisp"))
|
|
(p
|
|
"Three effects, each tracking different signals. The timer effect's cleanup fires before each re-run — toggling "
|
|
(code "running")
|
|
" off clears the interval. No hook rules: effects can appear anywhere, in any order.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-imperative
|
|
()
|
|
(~docs/page
|
|
:title "Imperative Pattern"
|
|
(p
|
|
"For complex reactivity (dynamic classes, conditional text), use the imperative pattern: "
|
|
(code "create-text-node")
|
|
" + "
|
|
(code "effect")
|
|
" + "
|
|
(code "dom-set-text-content")
|
|
".")
|
|
(~reactive-islands/index/demo-imperative)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-imperative")
|
|
"lisp"))
|
|
(p
|
|
"Two patterns exist: "
|
|
(strong "declarative")
|
|
" ("
|
|
(code "(span (deref sig))")
|
|
" — auto-reactive via "
|
|
(code "reactive-text")
|
|
") and "
|
|
(strong "imperative")
|
|
" ("
|
|
(code "create-text-node")
|
|
" + "
|
|
(code "effect")
|
|
" — explicit, full control). Use declarative for simple text, imperative for dynamic classes, conditional DOM, or complex updates.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-reactive-list
|
|
()
|
|
(~docs/page
|
|
:title "Reactive List"
|
|
(p
|
|
"When "
|
|
(code "map")
|
|
" is used with "
|
|
(code "(deref signal)")
|
|
" inside an island, it auto-upgrades to a reactive list. With "
|
|
(code ":key")
|
|
" attributes, existing DOM nodes are reused across updates — only additions, removals, and reorderings touch the DOM.")
|
|
(~reactive-islands/index/demo-reactive-list)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-reactive-list")
|
|
"lisp"))
|
|
(p
|
|
(code ":key")
|
|
" identifies each list item. When items change, the reconciler matches old and new keys — reusing existing DOM nodes, inserting new ones, and removing stale ones. Without keys, the list falls back to clear-and-rerender. "
|
|
(code "batch")
|
|
" groups the two signal writes into one update pass.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-input-binding
|
|
()
|
|
(~docs/page
|
|
:title "Input Binding"
|
|
(p
|
|
"The "
|
|
(code ":bind")
|
|
" attribute creates a two-way link between a signal and a form element. Type in the input — the signal updates. Change the signal — the input updates. Works with text inputs, checkboxes, radios, textareas, and selects.")
|
|
(~reactive-islands/index/demo-input-binding)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-input-binding")
|
|
"lisp"))
|
|
(p
|
|
(code ":bind")
|
|
" detects the element type automatically — text inputs use "
|
|
(code "value")
|
|
" + "
|
|
(code "input")
|
|
" event, checkboxes use "
|
|
(code "checked")
|
|
" + "
|
|
(code "change")
|
|
" event. The effect only updates the DOM when the value actually changed, preventing cursor jump.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-portal
|
|
()
|
|
(~docs/page
|
|
:title "Portals"
|
|
(p
|
|
"A "
|
|
(code "portal")
|
|
" renders children into a DOM node "
|
|
(em "outside")
|
|
" the island's subtree. Essential for modals, tooltips, and toasts — anything that must escape "
|
|
(code "overflow:hidden")
|
|
" or z-index stacking.")
|
|
(~reactive-islands/index/demo-portal)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-portal")
|
|
"lisp"))
|
|
(p
|
|
"The portal content lives in "
|
|
(code "#portal-root")
|
|
" (typically at the page body level), not inside the island. On island disposal, portal content is automatically removed from its target — the "
|
|
(code "register-in-scope")
|
|
" mechanism handles cleanup.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-error-boundary
|
|
()
|
|
(~docs/page
|
|
:title "Error Boundaries"
|
|
(p
|
|
"When an island's rendering or effect throws, "
|
|
(code "error-boundary")
|
|
" catches the error and renders a fallback. The fallback receives the error and a retry function. Partial effects created before the error are disposed automatically.")
|
|
(~reactive-islands/index/demo-error-boundary)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-error-boundary")
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "componentDidCatch")
|
|
" / "
|
|
(code "ErrorBoundary")
|
|
". SX's version is simpler — one form, not a class. The "
|
|
(code "error-boundary")
|
|
" form is a render-dom special form in "
|
|
(code "adapter-dom.sx")
|
|
".")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-refs
|
|
()
|
|
(~docs/page
|
|
:title "Refs — Imperative DOM Access"
|
|
(p
|
|
"The "
|
|
(code ":ref")
|
|
" attribute captures a DOM element handle into a dict. Use it for imperative operations: focusing, measuring, reading values.")
|
|
(~reactive-islands/index/demo-refs)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-refs")
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "useRef")
|
|
". In SX, a ref is just "
|
|
(code "(dict \"current\" nil)")
|
|
" — no special API. The "
|
|
(code ":ref")
|
|
" attribute sets "
|
|
(code "(dict-set! ref \"current\" el)")
|
|
" when the element is created. Read it with "
|
|
(code "(get ref \"current\")")
|
|
".")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-dynamic-class
|
|
()
|
|
(~docs/page
|
|
:title "Dynamic Class and Style"
|
|
(p
|
|
"React uses "
|
|
(code "className")
|
|
" and "
|
|
(code "style")
|
|
" props with state. SX does the same — "
|
|
(code "(deref signal)")
|
|
" inside a "
|
|
(code ":class")
|
|
" or "
|
|
(code ":style")
|
|
" attribute creates a reactive binding. The attribute updates when the signal changes.")
|
|
(~reactive-islands/index/demo-dynamic-class)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-dynamic-class")
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "className={danger ? 'red' : 'green'}")
|
|
" and "
|
|
(code "style={{fontSize: size}}")
|
|
". In SX the "
|
|
(code "str")
|
|
" + "
|
|
(code "if")
|
|
" + "
|
|
(code "deref")
|
|
" pattern handles it — no "
|
|
(code "classnames")
|
|
" library needed. For complex conditional classes, use a "
|
|
(code "computed")
|
|
" or a CSSX "
|
|
(code "defcomp")
|
|
" that returns a class string.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-resource
|
|
()
|
|
(~docs/page
|
|
:title "Resource + Suspense Pattern"
|
|
(p
|
|
(code "resource")
|
|
" wraps an async operation into a signal with "
|
|
(code "loading")
|
|
"/"
|
|
(code "data")
|
|
"/"
|
|
(code "error")
|
|
" states. Combined with "
|
|
(code "cond")
|
|
" + "
|
|
(code "deref")
|
|
", this is the suspense pattern — no special form needed.")
|
|
(~reactive-islands/index/demo-resource)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-resource")
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "Suspense")
|
|
" + "
|
|
(code "use()")
|
|
" or "
|
|
(code "useSWR")
|
|
". SX doesn't need a special "
|
|
(code "suspense")
|
|
" form because "
|
|
(code "resource")
|
|
" returns a signal and "
|
|
(code "cond")
|
|
" + "
|
|
(code "deref")
|
|
" creates reactive conditional rendering. When the promise resolves, the signal updates and the "
|
|
(code "cond")
|
|
" branch switches automatically.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-transition
|
|
()
|
|
(~docs/page
|
|
:title "Transition Pattern"
|
|
(p
|
|
"React's "
|
|
(code "startTransition")
|
|
" defers non-urgent updates so typing stays responsive. In SX: "
|
|
(code "schedule-idle")
|
|
" + "
|
|
(code "batch")
|
|
". The filter runs during idle time, not blocking the input event.")
|
|
(~reactive-islands/index/demo-transition)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-transition")
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "startTransition(() => setFiltered(...))")
|
|
". SX uses "
|
|
(code "schedule-idle")
|
|
" ("
|
|
(code "requestIdleCallback")
|
|
" under the hood) to defer the expensive "
|
|
(code "filter")
|
|
" operation, and "
|
|
(code "batch")
|
|
" to group the result into one update. Fine-grained signals already avoid the jank that makes transitions critical in React — this pattern is for truly expensive computations.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-stores
|
|
()
|
|
(~docs/page
|
|
:title "Shared Stores"
|
|
(p
|
|
"React uses "
|
|
(code "Context")
|
|
" or state management libraries for cross-component state. SX uses "
|
|
(code "def-store")
|
|
" / "
|
|
(code "use-store")
|
|
" — named signal containers that persist across island creation/destruction.")
|
|
(~reactive-islands/index/demo-store-writer)
|
|
(~reactive-islands/index/demo-store-reader)
|
|
(~docs/code
|
|
:src (highlight
|
|
(str
|
|
(component-source "~reactive-islands/index/demo-store-writer")
|
|
"\n\n"
|
|
(component-source "~reactive-islands/index/demo-store-reader"))
|
|
"lisp"))
|
|
(p
|
|
"React equivalent: "
|
|
(code "createContext")
|
|
" + "
|
|
(code "useContext")
|
|
" or Redux/Zustand. Stores are simpler — just named dicts of signals at page scope. "
|
|
(code "def-store")
|
|
" creates once, "
|
|
(code "use-store")
|
|
" retrieves. Stores survive island disposal but clear on full page navigation.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-event-bridge-demo
|
|
()
|
|
(~docs/page
|
|
:title "Event Bridge"
|
|
(p
|
|
"Server-rendered content inside an island (an htmx \"lake\") can communicate with island signals via DOM custom events. Buttons with "
|
|
(code "data-sx-emit")
|
|
" dispatch events that island effects catch.")
|
|
(~reactive-islands/index/demo-event-bridge)
|
|
(~docs/code
|
|
:src (highlight
|
|
(component-source "~reactive-islands/index/demo-event-bridge")
|
|
"lisp"))
|
|
(p
|
|
"The "
|
|
(code "data-sx-emit")
|
|
" attribute is processed by the client engine — it adds a click handler that dispatches a CustomEvent with the JSON from "
|
|
(code "data-sx-emit-detail")
|
|
". The event bubbles up to the island container where "
|
|
(code "bridge-event")
|
|
" catches it.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-defisland
|
|
()
|
|
(~docs/page
|
|
:title "How defisland Works"
|
|
(p
|
|
(code "defisland")
|
|
" creates a reactive component. Same calling convention as "
|
|
(code "defcomp")
|
|
" — keyword args, rest children — but with a reactive boundary. Inside an island, "
|
|
(code "deref")
|
|
" subscribes DOM nodes to signals.")
|
|
(~docs/code
|
|
:src (highlight
|
|
";; Definition — same syntax as defcomp\n(defisland ~reactive-islands/demo/counter (&key initial)\n (let ((count (signal (or initial 0))))\n (div\n (span (deref count)) ;; reactive text node\n (button :on-click (fn (e) (swap! count inc)) ;; event handler\n \"+\"))))\n\n;; Usage — same as any component\n(~reactive-islands/demo/counter :initial 42)\n\n;; Server-side rendering:\n;; <div data-sx-island=\"counter\" data-sx-state='{\"initial\":42}'>\n;; <span>42</span><button>+</button>\n;; </div>\n;;\n;; Client hydrates: signals + effects + event handlers attach"
|
|
"lisp"))
|
|
(p
|
|
"Each "
|
|
(code "deref")
|
|
" call registers the enclosing DOM node as a subscriber. Signal changes update "
|
|
(em "only")
|
|
" the subscribed nodes — no virtual DOM, no diffing, no component re-renders.")))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-tests
|
|
()
|
|
(~docs/page
|
|
:title "Test Suite"
|
|
(p
|
|
"17 tests verify the signal runtime against the spec. All pass in the Python test runner (which uses the hand-written evaluator with native platform primitives).")
|
|
(~docs/code
|
|
:src (highlight
|
|
";; Signal basics (6 tests)\n(assert-true (signal? (signal 42)))\n(assert-equal 42 (deref (signal 42)))\n(assert-equal 5 (deref 5)) ;; non-signal passthrough\n\n;; reset! changes value\n(let ((s (signal 0)))\n (reset! s 10)\n (assert-equal 10 (deref s)))\n\n;; reset! does NOT notify when value unchanged (identical? check)\n\n;; Computed (3 tests)\n(let ((a (signal 3)) (b (signal 4))\n (sum (computed (fn () (+ (deref a) (deref b))))))\n (assert-equal 7 (deref sum))\n (reset! a 10)\n (assert-equal 14 (deref sum)))\n\n;; Effects (4 tests) — immediate run, re-run on change, dispose, cleanup\n;; Batch (1 test) — defers notifications, deduplicates subscribers\n;; defisland (3 tests) — creates island, callable, accepts children"
|
|
"lisp"))
|
|
(p
|
|
(~tw :tokens "mt-2 text-sm text-stone-500")
|
|
"Run: "
|
|
(code "python3 shared/sx/tests/run.py signals"))))
|
|
|
|
(defcomp
|
|
~reactive-islands/demo/example-coverage
|
|
()
|
|
(~docs/page
|
|
:title "React Feature Coverage"
|
|
(p
|
|
"Every React feature has an SX equivalent — most are simpler because signals are fine-grained.")
|
|
(div
|
|
(~tw :tokens "overflow-x-auto rounded border border-stone-200")
|
|
(table
|
|
(~tw :tokens "w-full text-left text-sm")
|
|
(thead
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "React")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "SX")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Demo")))
|
|
(tbody
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "useState")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(signal value)")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#1"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "useMemo")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(computed (fn () ...))")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#1, #2"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "useEffect")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(effect (fn () ...))")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#3"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "useRef")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(dict \"current\" nil) + :ref")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#9"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "useCallback")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(fn (...) ...) — no dep arrays")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "N/A"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "className / style")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
":class (str ...) :style (str ...)")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#10"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Controlled inputs")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
":bind signal")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#6"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "key prop")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
":key value")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#5"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "createPortal")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(portal \"#target\" ...)")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#7"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "ErrorBoundary")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(error-boundary fallback ...)")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#8"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Suspense + use()")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"(resource fn) + cond/deref")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#11"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "startTransition")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"schedule-idle + batch")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#12"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Context / Redux")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-violet-700")
|
|
"def-store / use-store")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "#13"))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Virtual DOM / diffing")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 text-xs text-stone-500")
|
|
"N/A — fine-grained signals update exact DOM nodes")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") ""))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "JSX / build step")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 text-xs text-stone-500")
|
|
"N/A — s-expressions are the syntax")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") ""))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Server Components")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 text-xs text-stone-500")
|
|
"N/A — aser mode already expands server-side")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") ""))
|
|
(tr
|
|
(~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Concurrent rendering")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 text-xs text-stone-500")
|
|
"N/A — fine-grained updates are inherently incremental")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") ""))
|
|
(tr
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Hooks rules")
|
|
(td
|
|
(~tw :tokens "px-3 py-2 text-xs text-stone-500")
|
|
"N/A — signals are values, no ordering rules")
|
|
(td (~tw :tokens "px-3 py-2 text-xs text-stone-500") "")))))))
|