@zio.dev/zio-blocks 0.0.51 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/adr/2026-07-18-data-migration.md +123 -0
  2. package/guides/async-getting-started.md +687 -0
  3. package/guides/compile-time-resource-safety-with-scope.md +6 -0
  4. package/guides/getting-started-with-mux.md +0 -112
  5. package/guides/query-dsl-extending.md +1 -1
  6. package/guides/query-dsl-fluent-builder.md +1 -1
  7. package/guides/query-dsl-reified-optics.md +1 -1
  8. package/guides/query-dsl-sql.md +395 -1
  9. package/guides/sql-checked-interpolation.md +173 -0
  10. package/guides/sql-transactions.md +286 -0
  11. package/guides/telemetry-guide.md +131 -70
  12. package/guides/zio-schema-migration.md +6 -6
  13. package/index.md +200 -583
  14. package/package.json +1 -1
  15. package/reference/async.md +1379 -531
  16. package/reference/chunk.md +3 -3
  17. package/reference/codegen/index.md +1 -1
  18. package/reference/combinators.md +4 -4
  19. package/reference/config/config-decoder.md +460 -0
  20. package/reference/config/config-source.md +489 -0
  21. package/reference/config/errors.md +278 -0
  22. package/reference/config/flags.md +369 -0
  23. package/reference/config/formats.md +314 -0
  24. package/reference/config/index.md +304 -0
  25. package/reference/config/rollout.md +336 -0
  26. package/reference/context.md +6 -49
  27. package/reference/data-migration.md +269 -0
  28. package/reference/datastar/attributes.md +302 -0
  29. package/reference/datastar/events.md +234 -0
  30. package/reference/datastar/index.md +256 -0
  31. package/reference/datastar/signals.md +230 -0
  32. package/reference/datastar/sse.md +295 -0
  33. package/reference/datastar.md +2 -2
  34. package/reference/docs.md +2 -2
  35. package/reference/endpoint/bulk-creation.md +96 -0
  36. package/reference/endpoint/index.md +9 -89
  37. package/reference/endpoint/path-codec.md +12 -24
  38. package/reference/endpoint/route-pattern.md +4 -6
  39. package/reference/endpoint/segment-codec.md +19 -32
  40. package/reference/html.md +313 -9
  41. package/reference/htmx/index.md +4 -52
  42. package/reference/htmx/response-headers.md +240 -0
  43. package/reference/http-model/headers.md +735 -0
  44. package/reference/http-model/index.md +3 -1
  45. package/reference/http-model/model.md +107 -71
  46. package/reference/http-model/schema-codecs.md +522 -0
  47. package/reference/http-model/schema.md +6 -3
  48. package/reference/http-model/server-sent-event.md +341 -0
  49. package/reference/jwt.md +195 -0
  50. package/reference/maybe.md +128 -11
  51. package/reference/media-type.md +2 -2
  52. package/reference/mux.md +254 -0
  53. package/reference/mux.mdx +7 -2
  54. package/reference/openapi.md +3 -3
  55. package/reference/projection.md +654 -0
  56. package/reference/resource-management/resource.md +2 -98
  57. package/reference/resource-management/scope.md +1 -209
  58. package/reference/resource-management/wire.md +4 -50
  59. package/reference/ringbuffer/advanced.mdx +1 -1
  60. package/reference/ringbuffer/index.mdx +3 -3
  61. package/reference/ringbuffer/mpmc.mdx +38 -4
  62. package/reference/ringbuffer/mpsc.mdx +36 -4
  63. package/reference/ringbuffer/spmc.mdx +1 -1
  64. package/reference/ringbuffer/spsc.mdx +87 -15
  65. package/reference/schema/allows.md +0 -96
  66. package/reference/schema/binding.md +2 -2
  67. package/reference/schema/built-in-codecs/avro.md +2 -2
  68. package/reference/schema/built-in-codecs/bson.md +50 -20
  69. package/reference/schema/built-in-codecs/csv.md +2 -2
  70. package/reference/schema/built-in-codecs/index.md +3 -3
  71. package/reference/schema/built-in-codecs/json/index.md +2 -2
  72. package/reference/schema/built-in-codecs/messagepack.md +3 -3
  73. package/reference/schema/built-in-codecs/thrift.md +2 -2
  74. package/reference/schema/built-in-codecs/toon.md +3 -3
  75. package/reference/schema/built-in-codecs/yaml.md +2 -2
  76. package/reference/schema/codec.md +11 -11
  77. package/reference/schema/dynamic-optic.md +48 -3
  78. package/reference/schema/dynamic-schema.md +3 -3
  79. package/reference/schema/index.md +2 -0
  80. package/reference/schema/path-interpolator.md +2 -0
  81. package/reference/schema/reflect-transformer.md +140 -0
  82. package/reference/schema/schema-evolution/as.md +4 -4
  83. package/reference/schema/schema-evolution/into.md +2 -2
  84. package/reference/schema/schema-expr.md +2 -2
  85. package/reference/schema/schema-search.md +263 -0
  86. package/reference/schema/schema.md +10 -2
  87. package/reference/schema/type-class-derivation.md +1 -1
  88. package/reference/smithy.md +502 -3
  89. package/reference/sql/db-codec-deriver.md +3 -3
  90. package/reference/sql/db-codec.md +22 -22
  91. package/reference/sql/db-con.md +4 -4
  92. package/reference/sql/db-connection.md +1 -1
  93. package/reference/sql/db-param.md +1 -1
  94. package/reference/sql/db-result-reader.md +4 -2
  95. package/reference/sql/db-tx.md +46 -14
  96. package/reference/sql/ddl.md +1 -1
  97. package/reference/sql/frag.md +44 -10
  98. package/reference/sql/index.md +7 -7
  99. package/reference/sql/repo.md +15 -15
  100. package/reference/sql/sql-dialect.md +1 -1
  101. package/reference/sql/sql-logger.md +1 -1
  102. package/reference/sql/sql-name-mapper.md +3 -3
  103. package/reference/sql/table-metadata.md +3 -3
  104. package/reference/sql/table.md +10 -10
  105. package/reference/sql/transactor-zio.md +1 -1
  106. package/reference/sql/transactor.md +21 -11
  107. package/reference/sql-zio.md +1 -1
  108. package/reference/streams/core/index.md +32 -0
  109. package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
  110. package/reference/streams/{sink.md → core/sink.md} +331 -353
  111. package/reference/streams/{stream.md → core/stream.md} +919 -209
  112. package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
  113. package/reference/streams/execution-and-compatibility/index.md +35 -0
  114. package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
  115. package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
  116. package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
  117. package/reference/streams/index.md +140 -67
  118. package/reference/streams/primitives/index.md +30 -0
  119. package/reference/streams/primitives/reader.md +1992 -0
  120. package/reference/streams/{writer.md → primitives/writer.md} +254 -98
  121. package/reference/telemetry/common/any-value.md +90 -0
  122. package/reference/telemetry/common/attribute-key.md +87 -0
  123. package/reference/telemetry/common/attributes.md +118 -0
  124. package/reference/telemetry/common/index.md +39 -0
  125. package/reference/telemetry/common/instrumentation-scope.md +24 -0
  126. package/reference/telemetry/common/resource.md +34 -0
  127. package/reference/telemetry/index.md +311 -0
  128. package/reference/telemetry/logging/index.md +197 -0
  129. package/reference/telemetry/logging/log-enrichment.md +72 -0
  130. package/reference/telemetry/logging/log-formatter.md +100 -0
  131. package/reference/telemetry/logging/log-record-processor.md +56 -0
  132. package/reference/telemetry/logging/log-record.md +44 -0
  133. package/reference/telemetry/logging/log-writer.md +64 -0
  134. package/reference/telemetry/logging/logger-provider.md +142 -0
  135. package/reference/telemetry/logging/logger.md +83 -0
  136. package/reference/telemetry/logging/severity.md +62 -0
  137. package/reference/telemetry/metrics/index.md +150 -0
  138. package/reference/telemetry/metrics/instruments.md +183 -0
  139. package/reference/telemetry/metrics/labeled-instruments.md +74 -0
  140. package/reference/telemetry/metrics/meter-provider.md +76 -0
  141. package/reference/telemetry/metrics/meter.md +98 -0
  142. package/reference/telemetry/metrics/metric-data.md +57 -0
  143. package/reference/telemetry/otel/custom-exporter.md +216 -0
  144. package/reference/telemetry/otel/index.md +212 -0
  145. package/reference/telemetry/tracing/index.md +155 -0
  146. package/reference/telemetry/tracing/sampler.md +89 -0
  147. package/reference/telemetry/tracing/span-builder.md +57 -0
  148. package/reference/telemetry/tracing/span-context.md +39 -0
  149. package/reference/telemetry/tracing/span-data.md +32 -0
  150. package/reference/telemetry/tracing/span-kind.md +55 -0
  151. package/reference/telemetry/tracing/span-processor.md +53 -0
  152. package/reference/telemetry/tracing/span-status.md +47 -0
  153. package/reference/telemetry/tracing/span.md +117 -0
  154. package/reference/telemetry/tracing/tracer-provider.md +91 -0
  155. package/reference/telemetry/tracing/tracer.md +52 -0
  156. package/reference/typeid.md +0 -64
  157. package/sidebars.js +150 -12
  158. package/undocumented-report.md +528 -270
  159. package/reference/config.md +0 -158
  160. package/reference/streams/concurrent-operators.md +0 -106
  161. package/reference/streams/reader.md +0 -1284
  162. package/reference/streams/scala-2-compatibility.md +0 -55
  163. package/reference/streams/zero-boxing.md +0 -275
  164. package/reference/telemetry.md +0 -693
package/reference/html.md CHANGED
@@ -20,6 +20,18 @@ sealed trait Dom.Element extends Dom
20
20
  case class Dom.Element.Generic(tag: String, attributes: Chunk[Dom.Attribute], children: Chunk[Dom]) extends Dom.Element
21
21
  case class Dom.Element.Script(attributes: Chunk[Dom.Attribute], children: Chunk[Dom]) extends Dom.Element
22
22
  case class Dom.Element.Style(attributes: Chunk[Dom.Attribute], children: Chunk[Dom]) extends Dom.Element
23
+ sealed trait Dom.Element.Void extends Dom // void elements (self-closing, no children)
24
+ case class Dom.Element.VoidGeneric(tag: String, attributes: Chunk[Dom.Attribute]) extends Dom.Element.Void
25
+
26
+ // Sealed content-model markers (implemented by dedicated element classes)
27
+ sealed trait Dom.Element.Li extends Dom.Element // <li>, child of ul/ol
28
+ sealed trait Dom.Element.Cell extends Dom.Element // <th> or <td>, child of tr
29
+ sealed trait Dom.Element.Th extends Dom.Element.Cell
30
+ sealed trait Dom.Element.Td extends Dom.Element.Cell
31
+ sealed trait Dom.Element.Tr extends Dom.Element // <tr>, child of table
32
+ sealed trait Dom.Element.SelectChild extends Dom.Element // <option> or <optgroup>, child of select
33
+ sealed trait Dom.Element.Opt extends Dom.Element.SelectChild
34
+ sealed trait Dom.Element.Optgroup extends Dom.Element.SelectChild
23
35
 
24
36
  // Attribute variants
25
37
  sealed trait Dom.Attribute
@@ -65,13 +77,13 @@ Template engines with runtime parsing add overhead and complexity:
65
77
  Add to `build.sbt`:
66
78
 
67
79
  ```
68
- libraryDependencies += "dev.zio" %% "zio-blocks-html" % "0.0.51"
80
+ libraryDependencies += "dev.zio" %% "zio-blocks-html" % "0.0.55"
69
81
  ```
70
82
 
71
83
  For Scala.js projects, use `%%%`:
72
84
 
73
85
  ```
74
- libraryDependencies += "dev.zio" %%% "zio-blocks-html" % "0.0.51"
86
+ libraryDependencies += "dev.zio" %%% "zio-blocks-html" % "0.0.55"
75
87
  ```
76
88
 
77
89
  Supported Scala versions: 2.13.x and 3.x
@@ -88,7 +100,7 @@ The module is organized around five core subsystems that compose together:
88
100
  ┌─────────────────────────────────────────────────────┐
89
101
  │ DSL Functions (div, p, span, ...) │
90
102
  │ + Attribute Builders (id :=, className +=) │
91
- │ └─> Produces: Dom.Element.Generic │
103
+ │ └─> Produces: Generic / Void element nodes │
92
104
  │ │
93
105
  ├─────────────────────────────────────────────────────┤
94
106
  │ String Interpolators (html"", css"", js"") │
@@ -101,6 +113,7 @@ The module is organized around five core subsystems that compose together:
101
113
  │ Dom ADT (sealed trait Dom) │
102
114
  │ ├─> Dom.Text (HTML-escaped text content) │
103
115
  │ ├─> Dom.Element (Generic, Script, Style) │
116
+ │ ├─> Dom.Element.Void (void/self-closing elements) │
104
117
  │ ├─> Dom.Doctype (<!DOCTYPE html>) │
105
118
  │ └─> Dom.Empty (renders to nothing) │
106
119
  │ │
@@ -131,9 +144,9 @@ The module is organized around five core subsystems that compose together:
131
144
 
132
145
  The `Dom` sealed trait is the core data model. Everything in the module works with or produces `Dom` nodes.
133
146
 
134
- ### The Four Node Types
147
+ ### The Five Node Types
135
148
 
136
- A `Dom` tree is composed of four node types:
149
+ A `Dom` tree is composed of five node types:
137
150
 
138
151
  #### Dom.Text
139
152
 
@@ -447,9 +460,64 @@ val card = div(
447
460
  ))
448
461
  ```
449
462
 
450
- ### Void Elements
463
+ ### Dom.Element.Void
451
464
 
452
- Void elements (self-closing tags) automatically render with the correct syntax:
465
+ Void HTML elements (`br`, `hr`, `img`, `input`, `meta`, `link`, `area`, `base`, `col`, `embed`, `param`, `source`, `track`, `wbr`) are a **separate type** from `Dom.Element` — they extend `Dom.Element.Void` which extends `Dom` directly (not `Dom.Element`). This means they **structurally cannot have children** — passing a modifier like a string or another element to a void element factory is a **compile-time error**:
466
+
467
+ ```scala
468
+ import zio.blocks.html._
469
+
470
+ img("oops") // ❌ does not compile — Void elements don't accept children
471
+ input(div("child")) // ❌ does not compile — Void elements don't accept children
472
+ // error:
473
+ // Found: ("oops" : String)
474
+ // Required: zio.blocks.html.Dom.Attribute
475
+ // error:
476
+ // Found: zio.blocks.html.Dom.Element
477
+ // Required: zio.blocks.html.Dom.Attribute
478
+ // error:
479
+ // Conflicting definitions:
480
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 17 and
481
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 20
482
+ //
483
+ // error:
484
+ // Conflicting definitions:
485
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 20 and
486
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 26
487
+ //
488
+ // error:
489
+ // Conflicting definitions:
490
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 26 and
491
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 29
492
+ //
493
+ // error:
494
+ // Conflicting definitions:
495
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 50 and
496
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 74
497
+ //
498
+ // error:
499
+ // Conflicting definitions:
500
+ // val link: zio.blocks.html.Dom.Element in object MdocApp at line 43 and
501
+ // val link: zio.blocks.html.Dom.Element in object MdocApp at line 76
502
+ //
503
+ // error:
504
+ // Conflicting definitions:
505
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 74 and
506
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 79
507
+ //
508
+ ```
509
+
510
+ Void elements only accept `Dom.Attribute` arguments:
511
+
512
+ ```scala
513
+ import zio.blocks.html._
514
+
515
+ val image = img(src := "photo.jpg", alt := "A photo") // ✅ compiles — only attributes
516
+ val lineBreak = br // ✅ compiles — no args
517
+ val textField = input(`type` := "text", placeholder := "Enter text") // ✅ compiles
518
+ ```
519
+
520
+ Void elements render as self-closing tags:
453
521
 
454
522
  ```scala
455
523
  import zio.blocks.html._
@@ -462,6 +530,150 @@ val voidElements = div(
462
530
  )
463
531
  ```
464
532
 
533
+ ## Typed Content Models
534
+
535
+ Several container elements enforce the HTML content model of their children at
536
+ compile time. Instead of accepting arbitrary modifiers, these factories only
537
+ accept attributes plus children whose type matches the content model:
538
+
539
+ | Factory | Accepted element children | Marker type |
540
+ |---|---|---|
541
+ | `ul(...)`, `ol(...)` | `<li>` | `Dom.Element.Li` |
542
+ | `tr(...)` | `<th>`, `<td>` | `Dom.Element.Cell` (`Th \| Td`) |
543
+ | `table(...)` | `<tr>` | `Dom.Element.Tr` |
544
+ | `select(...)` | `<option>`, `<optgroup>` | `Dom.Element.SelectChild` (`Opt \| Optgroup`) |
545
+ | `optgroup(...)` | `<option>` | `Dom.Element.Opt` |
546
+
547
+ Invalid children are rejected by the compiler:
548
+
549
+ ```scala
550
+ import zio.blocks.html._
551
+
552
+ ul(div("not a list item")) // ❌ does not compile — ul only accepts Li children
553
+ tr(p("not a cell")) // ❌ does not compile — tr only accepts Th/Td cells
554
+ select(div("not an option")) // ❌ does not compile — select only accepts option/optgroup
555
+ // error:
556
+ // None of the overloaded alternatives of method ul in trait HtmlElements with types
557
+ // (children: Iterable[zio.blocks.html.Dom.Element.Li]):
558
+ // zio.blocks.html.Dom.Element
559
+ // (effect: zio.blocks.html.Dom.Attribute | zio.blocks.html.Dom.Element.Li, effects
560
+ // : (zio.blocks.html.Dom.Attribute | zio.blocks.html.Dom.Element.Li)*):
561
+ // zio.blocks.html.Dom.Element
562
+ // (): zio.blocks.html.Dom.Element
563
+ // match arguments (zio.blocks.html.Dom.Element)
564
+ // error:
565
+ // None of the overloaded alternatives of method tr in trait HtmlElements with types
566
+ // (children: Iterable[zio.blocks.html.Dom.Element.Cell]):
567
+ // zio.blocks.html.Dom.Element.Tr
568
+ // (effect: zio.blocks.html.Dom.Attribute | zio.blocks.html.Dom.Element.Cell,
569
+ // effects: (zio.blocks.html.Dom.Attribute | zio.blocks.html.Dom.Element.Cell)*)
570
+ // : zio.blocks.html.Dom.Element.Tr
571
+ // (): zio.blocks.html.Dom.Element.Tr
572
+ // match arguments (zio.blocks.html.Dom.Element)
573
+ // error:
574
+ // None of the overloaded alternatives of method select in trait HtmlElements with types
575
+ // (children: Iterable[zio.blocks.html.Dom.Element.SelectChild]):
576
+ // zio.blocks.html.Dom.Element
577
+ // (
578
+ // effect: zio.blocks.html.Dom.Attribute |
579
+ // zio.blocks.html.Dom.Element.SelectChild,
580
+ // effects: (zio.blocks.html.Dom.Attribute |
581
+ // zio.blocks.html.Dom.Element.SelectChild)*): zio.blocks.html.Dom.Element
582
+ // (): zio.blocks.html.Dom.Element
583
+ // match arguments (zio.blocks.html.Dom.Element)
584
+ // select(div("not an option")) // ❌ does not compile — select only accepts option/optgroup
585
+ // ^^^^^^
586
+ // error:
587
+ // Conflicting definitions:
588
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 17 and
589
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 20
590
+ //
591
+ // error:
592
+ // Conflicting definitions:
593
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 20 and
594
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 26
595
+ //
596
+ // error:
597
+ // Conflicting definitions:
598
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 26 and
599
+ // val tree: zio.blocks.html.Dom.Element in object MdocApp at line 29
600
+ //
601
+ // error:
602
+ // Conflicting definitions:
603
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 50 and
604
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 74
605
+ //
606
+ // error:
607
+ // Conflicting definitions:
608
+ // val link: zio.blocks.html.Dom.Element in object MdocApp at line 43 and
609
+ // val link: zio.blocks.html.Dom.Element in object MdocApp at line 76
610
+ //
611
+ // error:
612
+ // Conflicting definitions:
613
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 74 and
614
+ // val div1: zio.blocks.html.Dom.Element in object MdocApp at line 79
615
+ //
616
+ // error:
617
+ // Conflicting definitions:
618
+ // val image: zio.blocks.html.Dom.Element.Void in object MdocApp at line 38 and
619
+ // val image: zio.blocks.html.Dom.Element.Void in object MdocApp at line 103
620
+ //
621
+ // error:
622
+ // Conflicting definitions:
623
+ // val lineBreak: zio.blocks.html.Dom.Element.Void in object MdocApp at line 39 and
624
+ // val lineBreak: zio.blocks.html.Dom.Element.Void in object MdocApp at line 104
625
+ //
626
+ ```
627
+
628
+ Attributes are accepted alongside the constrained children:
629
+
630
+ ```scala
631
+ import zio.blocks.html._
632
+
633
+ val list = ul(id := "menu", className := "nav", li("Home"), li("About"))
634
+ val row = tr(className := "header-row", th("Name"), td("Value"))
635
+ val picker = select(id := "color", name := "color", option("Red"), optgroup(option("Light"), option("Dark")))
636
+ ```
637
+
638
+ Each factory has three forms: empty (`ul()`), mixed attributes and children
639
+ (`ul(id := "x", li("a"))`), and collections of children
640
+ (`ul(items.map(item => li(item)))`).
641
+
642
+ The typed factories produce elements carrying a sealed marker type (`Li`,
643
+ `Cell`, `Tr`, `SelectChild`, ...). The markers are sealed — their
644
+ implementations live inside the module — so a value typed `Dom.Element.Li` is
645
+ guaranteed to render as `<li>`; the DSL factories are the intended
646
+ construction path. Elements with permissive content models (`li`, `th`, `td`,
647
+ `option`) keep accepting any flow content through the regular modifier API.
648
+
649
+ :::note
650
+ `caption`, `colgroup`, `thead`, `tbody`, and `tfoot` do not have typed markers
651
+ yet, so tables containing them cannot be built with the `table(...)`
652
+ factory. Compose those tables with the `html""` interpolator or generic
653
+ elements.
654
+ :::
655
+
656
+ ### Equality Across Element Classes
657
+
658
+ Element equality is structural (tag + attributes + children + text-escaping
659
+ semantics), independent of the concrete class. A factory-produced `li(...)`
660
+ equals the structurally identical `Generic("li", ...)`, and vice versa; equal
661
+ elements also have equal hash codes:
662
+
663
+ ```scala
664
+ import zio.blocks.html._
665
+ import zio.blocks.chunk.Chunk
666
+
667
+ val fromFactory = li("a")
668
+ val generic = Dom.Element.Generic("li", Chunk.empty, Chunk(Dom.Text("a")))
669
+ fromFactory == generic // true — structural equality across classes
670
+ ```
671
+
672
+ Elements that render differently are never equal, even with identical fields:
673
+ `script`/`style` render their children **unescaped** while `Generic` escapes
674
+ them, so a `Script` never equals an equally-shaped `Generic("script", ...)` —
675
+ equal elements always render identically.
676
+
465
677
  ## String Interpolators
466
678
 
467
679
  The module provides four string interpolators: `html""`, `css""`, `js""`, and `selector""`. All offer compile-time safety (on Scala 3) and automatic escaping appropriate to their context.
@@ -622,8 +834,8 @@ Pseudo-classes match elements by their state, and pseudo-elements create dynamic
622
834
  import zio.blocks.html._
623
835
 
624
836
  val hoverSel = a.hover // a:hover
625
- val firstChild = li.firstChild // li:first-child
626
- val nthChild = tr.nthChild(2) // tr:nth-child(2)
837
+ val firstChild = li().firstChild // li:first-child
838
+ val nthChild = tr().nthChild(2) // tr:nth-child(2)
627
839
  val before = div.before // div::before
628
840
  val after = span.after // span::after
629
841
  ```
@@ -774,6 +986,98 @@ All `Css` subtypes support `.render()` for minified output and `.render(indent:
774
986
  - `Css.Raw` — raw CSS string
775
987
  - `Css.Comment` — CSS comment
776
988
 
989
+ ### Typed Values: Lengths and Colors
990
+
991
+ A `Css.Declaration` takes its value as a `String`, so nothing stops `"10pixels"` or `"#gggggg"` from reaching a stylesheet. `CssLength` and `CssColor` are the typed alternatives: each validates on construction and renders itself, so an invalid value cannot be built rather than being caught downstream.
992
+
993
+ `CssLength` pairs a numeric value with a unit, and rejects units outside the CSS set — `px`, `em`, `rem`, `%`, `vh`, `vw`, `ch`, `ex`, `vmin`, `vmax`, `cm`, `mm`, `in`, `pt`, `pc`:
994
+
995
+ ```scala
996
+ import zio.blocks.html._
997
+ import zio.blocks.html.CssLength._
998
+ ```
999
+
1000
+ The second import is what brings the numeric extension methods into scope. They live in `object CssLength` rather than the package, so `import zio.blocks.html._` alone gives you the `CssLength` constructor but not `300.px`.
1001
+
1002
+ Writing the constructor directly works, but the extension methods on `Int` and `Double` are shorter and produce the same value:
1003
+
1004
+ ```scala
1005
+ CssLength(300.0, "px").render
1006
+ // res45: String = "300px"
1007
+ 300.px.render
1008
+ // res46: String = "300px"
1009
+ 1.5.rem.render
1010
+ // res47: String = "1.5rem"
1011
+ ```
1012
+
1013
+ `CssLengthIntOps` and `CssLengthDoubleOps` provide `px`, `em`, `rem`, `pct`, `vh`, and `vw` on numeric literals. `pct` is spelled out because `%` is not a legal method name:
1014
+
1015
+ ```scala
1016
+ 50.pct.render
1017
+ // res48: String = "50%"
1018
+ 100.vh.render
1019
+ // res49: String = "100vh"
1020
+ ```
1021
+
1022
+ A whole-number `Double` renders without its decimal point, so `2.0.em` and `2.em` produce identical CSS:
1023
+
1024
+ ```scala
1025
+ 2.0.em.render
1026
+ // res50: String = "2em"
1027
+ 2.em.render
1028
+ // res51: String = "2em"
1029
+ ```
1030
+
1031
+ `CssColor` is a sealed ADT with five cases, covering the notations CSS accepts:
1032
+
1033
+ | Case | Constructor | Renders as |
1034
+ | ---------------------- | --------------------------------- | ----------------------- |
1035
+ | `CssColor.Hex` | `Hex(value)` / `Hex.unsafe(value)` | `#ff0000` |
1036
+ | `CssColor.Rgb` | `Rgb(r, g, b)` | `rgb(255,0,0)` |
1037
+ | `CssColor.Rgba` | `Rgba(r, g, b, a)` | `rgba(255,0,0,0.5)` |
1038
+ | `CssColor.Hsl` | `Hsl(h, s, l)` | `hsl(0,100%,50%)` |
1039
+ | `CssColor.Named` | `Named(name)` | `red` |
1040
+
1041
+ The numeric cases are plain case classes, so they need no validation step:
1042
+
1043
+ ```scala
1044
+ CssColor.Rgb(255, 0, 0).render
1045
+ // res52: String = "rgb(255,0,0)"
1046
+ CssColor.Rgba(255, 0, 0, 0.5).render
1047
+ // res53: String = "rgba(255,0,0,0.5)"
1048
+ CssColor.Hsl(0, 100, 50).render
1049
+ // res54: String = "hsl(0,100%,50%)"
1050
+ ```
1051
+
1052
+ `Hsl` renders its saturation and lightness with percent signs while taking them as bare `Int`s, so pass `100` rather than `1.0` for full saturation.
1053
+
1054
+ `CssColor.Hex` and `CssColor.Named` are the two validated cases, and both return an `Option` from `CssColor.Hex.apply` rather than throwing:
1055
+
1056
+ ```scala
1057
+ CssColor.Hex("ff0000")
1058
+ // res55: Option[CssColor] = Some(Hex("ff0000"))
1059
+ CssColor.Hex("gggggg")
1060
+ // res56: Option[CssColor] = None
1061
+ ```
1062
+
1063
+ A leading `#` is optional and the value is lowercased, so the same colour written four ways yields one representation:
1064
+
1065
+ ```scala
1066
+ CssColor.Hex("#FF0000").map(_.render)
1067
+ // res57: Option[String] = Some("#ff0000")
1068
+ ```
1069
+
1070
+ `CssColor.Hex.unsafe` skips validation for literals you know are well-formed. It returns a `CssColor` directly rather than an `Option`, which is what makes it convenient inside an interpolator — and what makes it unsuitable for anything user-supplied:
1071
+
1072
+ ```scala
1073
+ CssColor.Hex.unsafe("ff0000").render
1074
+ // res58: String = "#ff0000"
1075
+ ```
1076
+
1077
+ :::warning[`unsafe` renders whatever it is given]
1078
+ `CssColor.Hex.unsafe` performs no checking, so `Hex.unsafe("not-a-color")` renders as `#not-a-color` and produces a stylesheet the browser silently ignores. Use it only for compile-time literals; route anything dynamic through `CssColor.Hex.apply` and handle the `None`.
1079
+ :::
1080
+
777
1081
  ### Declarations and Rules
778
1082
 
779
1083
  A `Css.Declaration` is a property-value pair:
@@ -52,10 +52,10 @@ Add the HTMX module to your project dependencies:
52
52
 
53
53
  ```scala
54
54
  // JVM
55
- libraryDependencies += "dev.zio" %% "zio-blocks-http-htmx" % "0.0.51"
55
+ libraryDependencies += "dev.zio" %% "zio-blocks-http-htmx" % "0.0.55"
56
56
 
57
57
  // Scala.js
58
- libraryDependencies += "dev.zio" %%% "zio-blocks-http-htmx" % "0.0.51"
58
+ libraryDependencies += "dev.zio" %%% "zio-blocks-http-htmx" % "0.0.55"
59
59
  ```
60
60
 
61
61
  Supported Scala versions: Scala 3.x. The module is cross-compiled for JVM and Scala.js.
@@ -210,7 +210,7 @@ import zio.http.htmx._
210
210
  input(
211
211
  hxPost := "/search",
212
212
  hxTrigger := HxTrigger.input.filter(Js("event.target.value.length > 2")),
213
- "Only POST if search has 3+ characters"
213
+ placeholder := "Only POST if search has 3+ characters"
214
214
  )
215
215
  ```
216
216
 
@@ -228,7 +228,7 @@ The `Js` type is intentionally raw—do not build it from unsanitized user input
228
228
 
229
229
  **With JavaScript:** Attributes that accept raw JavaScript expressions (`hxOn:*`, `hxTrigger.filter()`) accept the `Js` type, making it explicit that you're writing unescaped JavaScript code. This prevents accidental XSS while allowing intentional dynamic behavior.
230
230
 
231
- **With headers:** The `zio.http.htmx.headers` submodule provides typed HTMX request/response headers (HX-Request, HX-Trigger, HX-Redirect, etc.), letting you inspect and build headers with the same type safety as attributes.
231
+ **With headers:** The [`zio.http.htmx.headers`](./response-headers.md) submodule provides typed HTMX request/response headers (HX-Request, HX-Trigger, HX-Redirect, etc.), letting you inspect and build headers with the same type safety as attributes.
232
232
 
233
233
  **Extending with custom types:** Implement `ToHtmxValue[MyType]` to let your domain types render themselves in the DSL. For example, a custom `enum Status { Active, Inactive }` defines `implicit val statusToHtmx: ToHtmxValue[Status] = ...` and renders directly in HTMX attributes.
234
234
 
@@ -252,22 +252,6 @@ cd zio-blocks
252
252
  Demonstrates fundamental HTMX attribute construction: triggering requests (hxPost, hxGet), swapping strategies (InnerHTML, OuterHTML), and target selection (This, closest, find). Shows how the typed DSL ensures correct HTMX syntax at compile time. Here is the source code:
253
253
 
254
254
  ```scala title="zio-blocks-htmx-examples/src/main/scala/zioBlocksHtmx/BasicUsage.scala"
255
- /*
256
- * Copyright 2024-2026 John A. De Goes and the ZIO Contributors
257
- *
258
- * Licensed under the Apache License, Version 2.0 (the "License");
259
- * you may not use this file except in compliance with the License.
260
- * You may obtain a copy of the License at
261
- *
262
- * http://www.apache.org/licenses/LICENSE-2.0
263
- *
264
- * Unless required by applicable law or agreed to in writing, software
265
- * distributed under the License is distributed on an "AS IS" BASIS,
266
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
267
- * See the License for the specific language governing permissions and
268
- * limitations under the License.
269
- */
270
-
271
255
  package zioBlocksHtmx
272
256
 
273
257
  import zio.blocks.html._
@@ -409,22 +393,6 @@ sbt "zio-blocks-htmx-examples/runMain zioBlocksHtmx.BasicUsage"
409
393
  Demonstrates complex HTMX interactions: combining multiple triggers, chaining modifiers, controlling request queuing, animation with transitions, and JavaScript-based filtering. Shows how modifiers compose to create sophisticated client-side behaviors. Here is the source code:
410
394
 
411
395
  ```scala title="zio-blocks-htmx-examples/src/main/scala/zioBlocksHtmx/AdvancedPatterns.scala"
412
- /*
413
- * Copyright 2024-2026 John A. De Goes and the ZIO Contributors
414
- *
415
- * Licensed under the Apache License, Version 2.0 (the "License");
416
- * you may not use this file except in compliance with the License.
417
- * You may obtain a copy of the License at
418
- *
419
- * http://www.apache.org/licenses/LICENSE-2.0
420
- *
421
- * Unless required by applicable law or agreed to in writing, software
422
- * distributed under the License is distributed on an "AS IS" BASIS,
423
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
424
- * See the License for the specific language governing permissions and
425
- * limitations under the License.
426
- */
427
-
428
396
  package zioBlocksHtmx
429
397
 
430
398
  import zio.blocks.html._
@@ -629,22 +597,6 @@ sbt "zio-blocks-htmx-examples/runMain zioBlocksHtmx.AdvancedPatterns"
629
597
  A realistic e-commerce search and filtering interface combining multiple HTMX attributes: debounced search input, live category filtering, paginated results, out-of-band notifications, and dynamic UI updates. Demonstrates how types compose to create a type-safe, interactive UI. Here is the source code:
630
598
 
631
599
  ```scala title="zio-blocks-htmx-examples/src/main/scala/zioBlocksHtmx/CompleteExample.scala"
632
- /*
633
- * Copyright 2024-2026 John A. De Goes and the ZIO Contributors
634
- *
635
- * Licensed under the Apache License, Version 2.0 (the "License");
636
- * you may not use this file except in compliance with the License.
637
- * You may obtain a copy of the License at
638
- *
639
- * http://www.apache.org/licenses/LICENSE-2.0
640
- *
641
- * Unless required by applicable law or agreed to in writing, software
642
- * distributed under the License is distributed on an "AS IS" BASIS,
643
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
644
- * See the License for the specific language governing permissions and
645
- * limitations under the License.
646
- */
647
-
648
600
  package zioBlocksHtmx
649
601
 
650
602
  import zio.blocks.html._