Elements and attributes
An element takes one list holding everything: children, attributes and event handlers, in any order.
Html.article
[
attr.className "card"
Html.h2 "Title"
Html.p "Body text."
]
Everything on this page follows one rule: a value is static, a function is reactive, and a signal passed directly is reactive too.
Text
Text-bearing elements are overloaded. A string is static. A Var or Signal passed directly updates with it. Html.text takes a function for text computed from several signals:
open Fable.Ripple
open Fable.Ripple.Dom
let app () =
let count = Var.create 0
Html.div
[
Html.button
[
on.click (fun _ -> count.Value <- count.Value + 1)
Html.text "Count"
]
Html.p "A static line."
Html.p [ Html.text (fun () -> $"A computed line: count is %d{count.Value}.") ]
Html.p count
]
Html.mount "app" (app ())
Attributes
The same three forms apply to attributes:
open Fable.Ripple
open Fable.Ripple.Dom
let app () =
let enabled = Var.create false
Html.div
[
Html.button
[
on.click (fun _ -> enabled.Value <- not enabled.Value)
Html.text "Toggle"
]
Html.p
[
attr.style (fun () ->
if enabled.Value then
"color: seagreen"
else
"color: gray"
)
Html.text (fun () -> if enabled.Value then "enabled" else "disabled")
]
]
Html.mount "app" (app ())
Classes
Beyond attr.className, three helpers cover the common shapes:
attr.classes [ "card"; "wide" ]- a list joined with spaces.attr.classList [ "card", true; "active", false ]- names paired with conditions. A function form re-evaluates them.attr.toggleClass ("active", fun () -> isActive.Value)- one name, one reactive condition.
className and classes own the attribute and write it whole. classList and toggleClass add and remove only their own names, so a static base class and a reactive toggle sit on the same element:
open Fable.Ripple
open Fable.Ripple.Dom
let app () =
let selected = Var.create false
Html.div
[
Html.button
[
on.click (fun _ -> selected.Value <- not selected.Value)
Html.text "Select"
]
Html.p
[
attr.className "card"
attr.toggleClass ("active", selected)
Html.text "card stays; active comes and goes"
]
]
Html.mount "app" (app ())
Form properties
attr.value, attr.checked', attr.disabled, attr.hidden, attr.selected, attr.readOnly, attr.required, attr.multiple and attr.isOpen set DOM properties rather than attributes - what a running page reads:
open Fable.Ripple
open Fable.Ripple.Dom
let app () =
let saving = Var.create false
Html.div
[
Html.label
[
Html.input
[
attr.type' "checkbox"
attr.bindChecked saving
]
Html.text "saving"
]
Html.button
[
attr.disabled saving
Html.text "Save"
]
]
Html.mount "app" (app ())
Custom attributes
attr.custom sets an attribute the DSL has no name for:
attr.custom ("aria-current", fun () -> if active.Value then "true" else "false")
It calls setAttribute, so it writes the markup rather than the live property. For a name that is a form property, use the named member above.
Refs
attr.ref hands you the raw HTMLElement - to attach an observer, mount a third-party widget, or read a measurement:
Html.div [ attr.ref (fun el -> el.textContent <- "reached directly") ]
It runs while the element is being built, inside the enclosing scope, so a Signal.onCleanup registered in it tears down with the element.
The element is not in the document yet. focus() does nothing, getBoundingClientRect() returns zeros, and dialog.showModal() throws. Defer those to an event or a timeout.
SVG
The Svg type mirrors Html for elements in the SVG namespace, with svgAttr for their attributes. See SVG.