Hi Simon, thanks for the comprehensive and candid reply. Let me address your specific concerns based on my experience with swift-html-css-pointfree
.
On HTML Types & Storage
You're right that perfect generalization across all rendering engines might not be achievable. The goal is to provide a solid foundation that frameworks can extend or wrap as needed.
Regarding child element storage - PointFree's solution uses callAsFunction
to return types conforming to their HTML protocol:
public struct ContentDivision: Element {
public static var tag: String { "div" }
public init() {}
}
extension ContentDivision {
public func callAsFunction(@HTMLBuilder _ content: () -> some HTML) -> some HTML {
HTMLElement(tag: Self.tag) { content() }
}
}
This enables intuitive syntax while only requiring rendering logic for HTMLElement
, not each element type. It however does't limit what kinds of elements can be child elements for a particular element, only that it can have child elements (or not).
On Extending Attributes
Custom attributes (htmx, data-*, etc.) are handled through PointFreeHTML's .attribute
method on the HTML
protocol. This provides complete flexibility while maintaining type safety for standard attributes. Because HTMLElement conforms to the HTML
protocol, each element can access the .attribute
method. You can also extend any HTML element type or the HTML protocol with more type-safe custom attributes, such as for htmx.
On CSS: Beyond "50% of the way"
Static stylesheets in Swift offer real benefits: type safety, IDE autocomplete, and compile-time validation.
Ideally you write inline styles-as in SwiftUI-but generate a stylesheet. Rather than inline styles, PointFreeHTML generates optimized CSS classes. Consider the following test in swift-html-css-pointfree
:
@Test("Labeled Input renders correctly in a div")
func labeledInputInDiv() {
assertInlineSnapshot(
of: HTMLDocument {
div {
label {
"label-text"
input.text
.color(.red)
}
.color(.red)
}
},
as: .html
) {
"""
<!doctype html>
<html lang="en">
<head>
<style>
.color-dMYaj4{color:red}
</style>
</head>
<body>
<div><label class="color-dMYaj4">label-text<input class="color-dMYaj4" type="text"></label>
</div>
</body>
</html>
"""
}
}
Note that assertInlineSnapshot
takes a type and then renders it in the closure. I.e. the lines between """...""" are generated by the HTMLDocument instantiation.
In release mode, the class name is minified. This provides a production-ready solution with proper stylesheet generation and efficient class deduplication - hopefully addressing your concern about going beyond inline styles.
On WASM and Dynamic CSS
You're absolutely right that WASM presents additional challenges. This is an area that needs more exploration, and I don't have all the answers yet.
Moving Forward
I appreciate your focus on "How can we best create web apps in Swift?" Perhaps these shared types could be one piece of that larger puzzle - providing a foundation that frameworks like Elementary can build upon in their own unique ways. However, some frameworks might be too tightly coupled to their element definitions for this to be feasible. The goal isn't to force frameworks a particular way, but to eliminate redundant work on type definitions where possible, so we can focus on the harder problems you've identified.