Fuaranfuaran

The source for generative UI

Components

Components

Every example below is a fuaran-wire block. At build time its wire JSON is decoded, pre-emit-validated, round-trip-checked and server-rendered by Fuaran.UI.Renderer.Server, then spliced into this page as a first-class node in the page's own Fuaran tree. The rendered component and the canonical JSON that produced it are emitted side by side, with a deep link that opens the same tree in the playground. A block that fails any gate fails the site build: an example on this page cannot be wrong, because publishing it runs it through the language's own gates.

Layout

Box

The unified container. A Box arranges its children and carries an optional heading and role (here, card chrome around a metric).

Insights
Revenue
GBP 1234.50
7.0%
vs last month
{"id":"card-1--card-1","kind":{"$type":"Box","children":[{"id":"card-1--metric-1","kind":{"$type":"Metric","format":{"$type":"Currency","code":"GBP"},"icon":"trending-up","label":"Revenue","subtext":"vs last month","tone":"Brand","trend":{"$type":"Static","value":0.07},"trendFormat":{"$type":"Percent","decimals":1},"value":{"$type":"Static","value":1234.5}}}],"heading":"Insights","layout":{"$type":"Flex","direction":"Vertical","wrap":false},"role":"Card"}}

Open in fuaran-live ↗

Grid

A Box in grid layout – explicit columns or a template, for dashboard-style tiling.

Revenue
GBP 1234.50
7.0%
vs last month
{"id":"glayout-1--glayout-1","kind":{"$type":"Box","children":[{"id":"glayout-1--metric-1","kind":{"$type":"Metric","format":{"$type":"Currency","code":"GBP"},"icon":"trending-up","label":"Revenue","subtext":"vs last month","tone":"Brand","trend":{"$type":"Static","value":0.07},"trendFormat":{"$type":"Percent","decimals":1},"value":{"$type":"Static","value":1234.5}}}],"layout":{"$type":"Grid","cols":12},"role":"Group"}}

Open in fuaran-live ↗

Tabs

A tabbed container; each tab owns a child subtree, with the active tab selected by binding. Tab labels come from tabHeaders – omit them and a host falls back to the child ids.

Revenue
GBP 1234.50
7.0%
vs last month
{"id":"tabs-1--tabs-1","kind":{"$type":"Tabs","activeIndex":{"$type":"Static","value":0},"children":[{"id":"tabs-1--metric-1","kind":{"$type":"Metric","format":{"$type":"Currency","code":"GBP"},"icon":"trending-up","label":"Revenue","subtext":"vs last month","tone":"Brand","trend":{"$type":"Static","value":0.07},"trendFormat":{"$type":"Percent","decimals":1},"value":{"$type":"Static","value":1234.5}}}],"onSelect":"<closure>","tabHeaders":[{"label":"Revenue"}]}}

Open in fuaran-live ↗

Switch

A state-bound conditional: the child whose match equals the bound state value renders; otherwise the default does. Branching is wire data, not host-language if.

Pick a view
No view selected
{"id":"switch-1--switch-1","kind":{"$type":"Switch","cases":[{"child":{"id":"switch-1--details","kind":{"$type":"Markdown","text":"Details view"}},"match":"details"},{"child":{"id":"switch-1--summary","kind":{"$type":"Markdown","text":"Summary view"}},"match":"summary"}],"default":{"id":"switch-1--default","kind":{"$type":"Callout","body":"No view selected","heading":"Pick a view","tone":"Info"}},"stateKey":"view"}}

Open in fuaran-live ↗

SplitPanel

Two panes split along one axis – a primary and secondary region.

Revenue
GBP 1234.50
7.0%
vs last month

Updated hourly.

{"id":"split-1--split-1","kind":{"$type":"SplitPanel","children":[{"id":"split-1--metric-1","kind":{"$type":"Metric","format":{"$type":"Currency","code":"GBP"},"icon":"trending-up","label":"Revenue","subtext":"vs last month","tone":"Brand","trend":{"$type":"Static","value":0.07},"trendFormat":{"$type":"Percent","decimals":1},"value":{"$type":"Static","value":1234.5}}},{"id":"split-1--markdown-1","kind":{"$type":"Markdown","text":"Updated hourly."}}],"weight":0.6}}

Open in fuaran-live ↗

SummaryList

A single card of label/value rows with divider rules – the "list of stats" container.

Stats
Total42.00
{"id":"summary-1--summary-1","kind":{"$type":"SummaryList","children":[{"id":"summary-1--lvr-1","kind":{"$type":"LabelValueRow","emphasis":true,"format":{"$type":"Number","decimals":2},"help":"Last 30 days","label":"Total","value":{"$type":"Static","value":42}}}],"heading":"Stats"}}

Open in fuaran-live ↗

Display

Heading

A section heading at a chosen level.

Channel performance

{"id":"heading-1--heading-1","kind":{"$type":"Heading","level":2,"text":"Channel performance","variant":"Standard"}}

Open in fuaran-live ↗

Metric

A formatted single value – currency / percent / number – with an optional label, icon, and subtext. The displayed scalar is the value slot; source is reserved for collection feeds.

Revenue
GBP 1234.50
7.0%
vs last month
{"id":"metric-1--metric-1","kind":{"$type":"Metric","format":{"$type":"Currency","code":"GBP"},"icon":"trending-up","label":"Revenue","subtext":"vs last month","tone":"Brand","trend":{"$type":"Static","value":0.07},"trendFormat":{"$type":"Percent","decimals":1},"value":{"$type":"Static","value":1234.5}}}

Open in fuaran-live ↗

Fact

The textual sibling of Metric – a labelled text fact with an optional icon, help, and emphasis.

Patient
Alice Smith
Primary insured
{"id":"fact-1--fact-1","kind":{"$type":"Fact","emphasis":true,"help":"Primary insured","icon":"user","label":"Patient","tone":"Brand","value":"Alice Smith"}}

Open in fuaran-live ↗

Badge

A small status pill with a tone.

Beta
{"id":"badge-1--badge-1","kind":{"$type":"Badge","label":"Beta","variant":"Info"}}

Open in fuaran-live ↗

Callout

A toned, optionally-dismissable notice with a heading, body, and icon.

Heads up
Live data is delayed.
{"id":"callout-1--callout-1","kind":{"$type":"Callout","body":"Live data is delayed.","dismissable":true,"heading":"Heads up","icon":"alert","tone":"Warning"}}

Open in fuaran-live ↗

Progress

A determinate progress indicator bound to a fraction.

Loading...
{"id":"progress-1--progress-1","kind":{"$type":"Progress","fraction":{"$type":"Static","value":0.42},"label":"Loading...","tone":"Brand"}}

Open in fuaran-live ↗

CodeBlock

First-class code display – deterministic <pre><code> with a language tag.

let x = 1
let y = 2
{"id":"code-1--code-1","kind":{"$type":"CodeBlock","code":"let x = 1\u000alet y = 2","copyable":true,"highlightLines":[1,2],"language":"fsharp","lineNumbers":true}}

Open in fuaran-live ↗

List

An ordered or unordered list of items.

  1. First
  2. Second
{"id":"list-1--list-1","kind":{"$type":"List","items":["First","Second"],"ordered":true}}

Open in fuaran-live ↗

Link

A hyperlink with a label and an href binding.

{"id":"link-1--link-1","kind":{"$type":"Link","download":false,"href":{"$type":"Static","value":"/about"},"label":"About us","rel":"noopener","target":"_blank"}}

Open in fuaran-live ↗

Image

An image with a source and alt text.

User avatar
{"id":"image-1--image-1","kind":{"$type":"Image","alt":"User avatar","src":{"$type":"Static","value":"/avatar.png"},"variant":"Avatar"}}

Open in fuaran-live ↗

Input

Button

An action button – its Action is data (dispatch / invoke / copy-link), not a code callback.

{"id":"btn-1--btn-1","kind":{"$type":"Button","disabled":{"$type":"State","defaultValue":false,"key":"loading"},"icon":"refresh","label":"Refresh","onClick":{"$type":"Chain","ops":[]},"variant":"Primary"}}

Open in fuaran-live ↗

Form

A typed form – fields, validation, and a submit action, all expressed as wire data.

{"id":"form-1--form-1","kind":{"$type":"Form","disabled":{"$type":"State","defaultValue":false,"key":"formBusy"},"fields":[{"help":"Full legal name","id":"form-1--name","kind":{"$type":"Text","onChange":"<closure>","value":{"$type":"Static","value":""}},"label":"Name","required":true},{"id":"form-1--age","kind":{"$type":"Number","onChange":"<closure>","value":{"$type":"Static","value":0}},"label":"Age","required":false},{"id":"form-1--agree","kind":{"$type":"Checkbox","onToggle":"<closure>","value":{"$type":"Static","value":false}},"label":"I agree","required":true},{"id":"form-1--tier","kind":{"$type":"Choice","onChange":"<closure>","options":{"$type":"Static","value":[{"label":"Basic","value":"basic"},{"label":"Pro","value":"pro"}]},"value":{"$type":"Static","value":"basic"}},"label":"Tier","required":false},{"id":"form-1--notes","kind":{"$type":"TextArea","onChange":"<closure>","rows":5,"value":{"$type":"Static","value":""}},"label":"Notes","required":false}],"onSubmit":{"$type":"Chain","ops":[]},"submitLabel":"Save"}}

Open in fuaran-live ↗

A field may omit its value entirely – it auto-binds to $state.<field id> with a typed placeholder default, so a minimal declarative form is just fields, labels, and a submit. The field vocabulary includes date pickers and the dual-thumb Range:

{"id":"form-2--form-2","kind":{"$type":"Form","fields":[{"id":"form-2--guest-name","kind":{"$type":"Text"},"label":"Name","required":true},{"id":"form-2--party-size","kind":{"$type":"Number"},"label":"Party size","required":false},{"id":"form-2--seating","kind":{"$type":"Choice","options":{"$type":"Static","value":[{"label":"Indoor","value":"indoor"},{"label":"Terrace","value":"terrace"}]}},"label":"Seating","required":false},{"id":"form-2--budget","kind":{"$type":"Range"},"label":"Budget","required":false},{"id":"form-2--visit-date","kind":{"$type":"Date","variant":"Date"},"label":"Date","required":true}],"onSubmit":{"$type":"Chain","ops":[]},"submitLabel":"Book"}}

Open in fuaran-live ↗

Select

A single- or multi-select control over a set of options.

{"id":"select-1--select-1","kind":{"$type":"Select","disabled":{"$type":"State","defaultValue":false,"key":"selectBusy"},"label":"Region","onChange":"<closure>","placeholder":"Choose one","source":{"$type":"Static","value":[{"label":"UK","value":"uk"}]},"value":{"$type":"Static","value":"uk"}}}

Open in fuaran-live ↗

Filters

A filter bar of chips wired to the data they filter. Each chip's control is an ordinary form-field kind, and a chip with no explicit value auto-binds to the named filter ($filters.<name>). The pre-emit validator rejects a decorative bar – every declared chip must be consumed downstream (here, as Transform params feeding the grid).

[Grid: 2 rows — hydrates client-side]
{"id":"filters-1--filters-1","kind":{"$type":"Box","children":[{"id":"filters-1--bar","kind":{"$type":"Filters","items":[{"kind":{"$type":"Text"},"label":"Search","name":"q"},{"kind":{"$type":"Choice","options":{"$type":"Static","value":[{"label":"Basic","value":"basic"},{"label":"Pro","value":"pro"}]}},"label":"Tier","name":"tier"}]}},{"id":"filters-1--grid","kind":{"$type":"DataGrid","columns":[{"field":"name","kind":{"$type":"Text"},"label":"Name"},{"field":"tier","kind":{"$type":"Text"},"label":"Tier"}],"rowKeyField":"name","source":{"$type":"Transform","params":[{"from":{"$type":"Filter","name":"q"},"name":"q"},{"from":{"$type":"Filter","name":"tier"},"name":"tier"}],"pipeline":[{"$type":"filter","pred":{"$type":"binary","left":{"$type":"col","name":"name"},"op":"contains","right":{"$type":"param","name":"q"}}},{"$type":"filter","pred":{"$type":"binary","left":{"$type":"col","name":"tier"},"op":"eq","right":{"$type":"param","name":"tier"}}}],"source":{"columns":{"name":{"validity":[true,true],"values":["Ada","Grace"]},"tier":{"validity":[true,true],"values":["pro","basic"]}},"schema":[{"name":"name","type":"string"},{"name":"tier","type":"string"}]}}}}],"layout":{"$type":"Auto"},"role":"Group"}}

Open in fuaran-live ↗

Visualisation

Chart

A chart lowered to the bounded drawing primitive – series and axes from wire data. Line, bar (grouped or stacked), area, pie/donut, and scatter arms all lower to the same Drawing vocabulary, with schema-grounded validation rejecting meaningless combinations at pre-emit time.

Channel mix00.20.40.60.81MonthValuerevenuecostChannel mix
{"id":"chart-1--chart-1","kind":{"$type":"Chart","kind":"Bar","source":{"$type":"Static","value":"<opaque>"},"stacked":true,"title":"Channel mix","xField":"month","yFields":["revenue","cost"]}}

Open in fuaran-live ↗

Drawing

The bounded vector-graphics primitive that charts lower to – a closed shape DU, no raw SVG.

The validation gateA flowchart of the validation gate: wire JSON is decoded and validated – a valid tree renders, an invalid one is rejected with typed errors and repaired.WireDecodeValid?yesRendernoReject + errorsrepair
{"id":"drawing-1--drawing-1","kind":{"$type":"Drawing","description":"A flowchart of the validation gate: wire JSON is decoded and validated – a valid tree renders, an invalid one is rejected with typed errors and repaired.","shapes":[{"$type":"Ellipse","cx":25,"cy":50,"rx":16,"ry":10,"style":{"fill":{"$type":"Static","value":"#3366cc"},"opacity":{"$type":"Static","value":0.15},"stroke":{"$type":"Static","value":"#3366cc"},"strokeWidth":{"$type":"Static","value":1}}},{"$type":"Line","style":{"stroke":{"$type":"Static","value":"#8a93a0"},"strokeWidth":{"$type":"Static","value":1}},"x1":41,"x2":58,"y1":50,"y2":50},{"$type":"Polygon","points":[{"x":58,"y":47},{"x":63,"y":50},{"x":58,"y":53}],"style":{"fill":{"$type":"Static","value":"#8a93a0"}}},{"$type":"Rectangle","cornerRadius":3,"height":18,"style":{"fill":{"$type":"Static","value":"#3366cc"},"opacity":{"$type":"Static","value":0.15},"stroke":{"$type":"Static","value":"#3366cc"},"strokeWidth":{"$type":"Static","value":1}},"width":30,"x":63,"y":41},{"$type":"Group","children":[{"$type":"Line","style":{},"x1":93,"x2":110,"y1":50,"y2":50},{"$type":"Polygon","points":[{"x":110,"y":47},{"x":115,"y":50},{"x":110,"y":53}],"style":{}}],"style":{"fill":{"$type":"Static","value":"#8a93a0"},"stroke":{"$type":"Static","value":"#8a93a0"},"strokeWidth":{"$type":"Static","value":1}}},{"$type":"Polygon","points":[{"x":115,"y":50},{"x":133,"y":36},{"x":151,"y":50},{"x":133,"y":64}],"style":{"fill":{"$type":"Static","value":"#cc6633"},"opacity":{"$type":"Static","value":0.15},"stroke":{"$type":"Static","value":"#cc6633"},"strokeWidth":{"$type":"Static","value":1}}},{"$type":"Line","style":{"stroke":{"$type":"Static","value":"#8a93a0"},"strokeWidth":{"$type":"Static","value":1}},"x1":151,"x2":161,"y1":50,"y2":50},{"$type":"Polygon","points":[{"x":161,"y":47},{"x":166,"y":50},{"x":161,"y":53}],"style":{"fill":{"$type":"Static","value":"#8a93a0"}}},{"$type":"Rectangle","cornerRadius":3,"height":18,"style":{"fill":{"$type":"Static","value":"#33aa55"},"opacity":{"$type":"Static","value":0.18},"stroke":{"$type":"Static","value":"#2f7a45"},"strokeWidth":{"$type":"Static","value":1}},"width":28,"x":166,"y":41},{"$type":"Circle","cx":194,"cy":39,"r":2.5,"style":{"fill":{"$type":"Static","value":"#33aa55"}}},{"$type":"Polyline","points":[{"x":133,"y":64},{"x":133,"y":82},{"x":112,"y":82}],"style":{"stroke":{"$type":"Static","value":"#8a93a0"},"strokeWidth":{"$type":"Static","value":1}}},{"$type":"Polygon","points":[{"x":112,"y":79},{"x":107,"y":82},{"x":112,"y":85}],"style":{"fill":{"$type":"Static","value":"#8a93a0"}}},{"$type":"Rectangle","cornerRadius":3,"height":16,"style":{"fill":{"$type":"Static","value":"#aa3344"},"opacity":{"$type":"Static","value":0.12},"stroke":{"$type":"Static","value":"#aa3344"},"strokeWidth":{"$type":"Static","value":1}},"width":44,"x":63,"y":74},{"$type":"Curve","commands":[{"$type":"MoveTo","to":{"x":63,"y":82}},{"$type":"CubicTo","control1":{"x":40,"y":82},"control2":{"x":25,"y":76},"to":{"x":25,"y":65}}],"style":{"stroke":{"$type":"Static","value":"#8a93a0"},"strokeWidth":{"$type":"Static","value":1}}},{"$type":"Polygon","points":[{"x":22,"y":65},{"x":25,"y":60},{"x":28,"y":65}],"style":{"fill":{"$type":"Static","value":"#8a93a0"}}},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#223344"},"fontFamily":"system-ui, sans-serif","fontSize":6,"textAnchor":"Middle"},"text":"Wire","x":25,"y":52},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#223344"},"fontFamily":"system-ui, sans-serif","fontSize":6,"textAnchor":"Middle"},"text":"Decode","x":78,"y":52},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#223344"},"fontFamily":"system-ui, sans-serif","fontSize":6,"textAnchor":"Middle"},"text":"Valid?","x":133,"y":52},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#55606c"},"fontFamily":"system-ui, sans-serif","fontSize":5,"textAnchor":"Middle"},"text":"yes","x":156,"y":45},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#223344"},"fontFamily":"system-ui, sans-serif","fontSize":6,"textAnchor":"Middle"},"text":"Render","x":180,"y":52},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#55606c"},"fontFamily":"system-ui, sans-serif","fontSize":5,"textAnchor":"Middle"},"text":"no","x":138,"y":72},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#223344"},"fontFamily":"system-ui, sans-serif","fontSize":5,"textAnchor":"Middle"},"text":"Reject + errors","x":85,"y":84},{"$type":"Label","style":{"fill":{"$type":"Static","value":"#55606c"},"fontFamily":"system-ui, sans-serif","fontSize":5,"textAnchor":"Middle"},"text":"repair","x":40,"y":78}],"style":{},"title":"The validation gate","viewBox":{"height":100,"minX":0,"minY":0,"width":200}}}

Open in fuaran-live ↗