sliemeobn / elementary

A modern and efficient HTML rendering library - inspired by SwiftUI, built for the web.
https://swiftpackageindex.com/sliemeobn/elementary
Apache License 2.0
57 stars 2 forks source link

Option to add trailing slash to void elements #15

Open christophhagen opened 1 month ago

christophhagen commented 1 month ago

First off, thanks for starting this library. I'm currently experimenting with it to generate HTML pages with swift, and ran across an issue:

I created a custom path element to write inline svg:

public typealias path = HTMLVoidElement<HTMLTag.path>

public extension HTMLTag {

    enum path: HTMLTrait.Unpaired, HTMLTrait.RenderedInline { public static let name = "path" }
}

public extension HTMLAttribute where Tag == HTMLTag.path {

    static func fill(_ value: String) -> HTMLAttribute<HTMLTag.path> {
        .init(name: "fill", value: value)
    }

    static func d(_ value: String) -> HTMLAttribute<HTMLTag.path> {
        .init(name: "d", value: value)
    }
}

I can then write simple svg content:

svg(.id("some")) {
    path(.fill("currentColor"), .d("..."))
    path(.fill("currentColor"), .d("..."))
}

Once I render this, it produces the following HTML:

<svg id="some">
    <path fill="currentColor" d="...">
    <path fill="currentColor" d="...">
</svg>

I think this is valid HTML, but if I try to display the svg in Safari, it doesn't render correctly. Apparently Safari needs a trailing slash for path, like so:

<svg id="some">
    <path fill="currentColor" d="..." />
    <path fill="currentColor" d="..." />
</svg>

Is there a way to force certain void elements to add a trailing slash when rendering? Or should I use a different approach?

I guess the alternative would be to use HTMLTrait.Paired instead of HTMLTrait.Unpaired, but that would make the HTML less compact.

sliemeobn commented 1 month ago

Hi, thanks for engaging!

That is the exact reason I did not include SVG stuff yet, because it behaves differently in subtle ways. HTML "prefers" no trailing slash for unpaired tags, but XML-like things like SVG require them....

So I wasn't sure if adding svg into the regular some HTML system is a good idea, or rather have a completely separate SVGBuilder approach. Also, the global namespace is littered enough as it is already by importing Elementary in a file, adding all the svg tags as well feels a bit much to me - especially since they can only be used within very narrow constraints.

In my projects, I just include things like

HTMLRaw("svg \(withParameters) goes here")

but that's obviously not the nicest.

Pro tip, for better (static) svg caching I use this trick actually (loads the actual svg from a cache-controlled file)

struct MyLogo: HTML {
    var size: Int = 60
    var basePath: String

    var content: some HTML {
        HTMLRaw("""
        <svg width="\(self.size)" height="\(self.size)" class="fill-green-500">
            <use xlink:href="\(self.basePath)/my-logo.svg#logo"></use>
        </svg>
        """)
    }
}

To summarize, I don't yet see a great way forward, but I am leaning towards having a completely separate SVG module.

christophhagen commented 1 month ago

Okay, good to know. I'll use HTMLTrait.Paired for now, until I find the time to work on a better system for me.