Component vocabulary
TL;DR. This is the foundational vocabulary that ships with the framework. Every entry has a native renderer on web, iOS, and Android, plus a server-side HTML mapping so the WebFrame fallback is never empty.
The framework registers these via wun.foundation.components. Each
has a native renderer in wun-web, wun-ios, and wun-android,
plus a server-side HTML mapping for WebFrame fallback.
| keyword | role |
|---|---|
:wun/Stack | Vertical or horizontal flex container. Props: :direction :gap :padding. |
:wun/Text | Body text with variants :h1 :h2 :body. |
:wun/Heading | Semantic heading. :level 1-4. |
:wun/Button | Tap target. :on-press {:intent :ns/name :params {…}}. |
:wun/Link | Hyperlink-styled tap target with optional :href + :on-press. |
:wun/Switch | Toggle. :value boolean, :on-toggle <intent-ref>. |
:wun/Input | Single-line text input. :value :placeholder :on-change. |
:wun/Image | :src URL, :alt, :size pixels. |
:wun/Avatar | Circle with image or initials. :src :initials :size. |
:wun/Card | Padded panel with optional :title. |
:wun/Badge | Inline pill. :tone :info/:success/:warning/:danger. |
:wun/Divider | Horizontal rule. :thickness px. |
:wun/Spacer | Empty space. :size px. |
:wun/List | List container. |
:wun/ScrollView | Scrollable region. :direction :vertical/:horizontal. |
:wun/Skeleton | Loading-state placeholder. :width :height (CSS values). |
:wun/Form | <form> wrapper. :id (form id used by :wun.forms/* intents). |
:wun/Field | Bound input. :form :name :type :label :placeholder. |
:wun/FileInput | File picker that triggers a /upload POST. :form :field :accept :multiple. |
:wun/ErrorBoundary | Surfaced when a screen render throws. :reason (string). |
:wun/WebFrame | Capability fallback. :src :missing :reason. |
A foundational binding
Here’s how :wun/Button is bound on each platform — the same
keyword, three thin renderers around the platform’s native tap
target. The platform choice you make below persists across the
site.
(r/register! :wun/Button (fn [{:keys [on-press tone]} children] [:button {:class (str "wun-btn wun-btn-" (name (or tone :default))) :on-click #(bus/dispatch! on-press)} children]))public enum Button { public static let render: WunComponent = { props, children in AnyView( SwiftUI.Button(action: { if let intent = props["on-press"] { Bus.dispatch(intent) } }) { ForEach(Array(children.enumerated()), id: \.offset) { _, kid in WunView(kid) } } .buttonStyle(.borderedProminent) ) }}object ButtonRenderer { val render: WunComponent = { props, children -> FilledTonalButton( onClick = { (props["on-press"] as? JsonElement)?.let(Bus::dispatch) } ) { children.forEach { WunView(it) } } }}Adding more
Define one in wun-shared/src/wun/foundation/components.cljc,
register a renderer in each platform, ship server HTML in
wun.server.html, and bump its :since whenever the schema
changes.
For app-specific components, use wun add component <ns>/<Name>
which scaffolds the cljc declaration plus iOS Swift + Android
Kotlin renderers and splices both registry files.