DSL

Elements, attributes, and children compose through one uniform apply. The Arg union auto-lifts: String → text, Squawk[String] → reactive text, attr key + Squawk → reactive attribute, Seq → flattened. Void elements (E.input, E.br) reject children at compile time.

Elements and attrs

{
  E.label(
    A.className("title"),
    A.htmlFor("name"),
    "Name",
  )
}
{
  E.div(
    E.input(A.`type`("text"), A.placeholder("draft")),
    E.br(),
    E.span("after break"),
  )
}

after break

Events

Ev.onClick hands you the typed platform event (dom.PointerEvent). For two-way binding there is the platform-neutral Events.onInput / Events.onKeyDown with targetValue / key. Handlers are ZIO effects. Use .sync (Ev.sync.onDragStart { … }) when the body must run on the dispatch stack (preventDefault, drag-and-drop).

{
  for draft <- sq("")
  yield E.input(
    A.value(draft),
    Events.onInput(e => draft.set(e.targetValue.getOrElse(""))),
  )
}

ARIA

{
  E.button(Aria.ariaLabel("Close"), Aria.role("button"), "×")
}