Alex Styl
10/16/2025, 2:59 AMsvg tag doesn't support path ? if that's the case how do you do svgs?Tim Schraepen
10/18/2025, 10:21 PMfun SVG.path(block: Tag.() -> Unit = {}): Unit {
PATH(emptyMap, consumer).visit(block)
}
@Suppress("unused")
open class PATH(
initialAttributes: Map<String, String>,
override val consumer: TagConsumer<*>
) : HTMLTag(
tagName = "path",
consumer = consumer,
initialAttributes = initialAttributes,
namespace = null,
inlineTag = false,
emptyTag = false
), HtmlBlockInlineTag
fun FlowContent.icon() {
svg {
attributes["width"] = "120"
attributes["height"] = "120"
attributes["viewBox"] = "0 0 120 120"
path {
attributes["d"] = "M10 10 H110 V110 H10 Z"
attributes["fill"] = "none"
attributes["stroke"] = "black"
attributes["stroke-width"] = "4"
}
}
}Tim Schraepen
10/18/2025, 10:26 PMTim Schraepen
10/18/2025, 10:44 PMprivate const val SVG_NS = "<http://www.w3.org/2000/svg>"
fun SVG.path(block: PATH.() -> Unit = {}): Unit {
PATH(emptyMap(), consumer).visit(block)
}
@Suppress("unused")
open class PATH(
initialAttributes: Map<String, String>,
override val consumer: TagConsumer<*>
) : HTMLTag(
tagName = "path",
consumer = consumer,
initialAttributes = initialAttributes,
namespace = SVG_NS,
inlineTag = false,
emptyTag = true,
)
inline var PATH.d: String
get() = attributes["d"].orEmpty()
set(value) { attributes["d"] = value }
inline var PATH.fill: String
get() = attributes["fill"].orEmpty()
set(value) { attributes["fill"] = value }
inline var PATH.stroke: String
get() = attributes["stroke"].orEmpty()
set(value) { attributes["stroke"] = value }
inline var PATH.strokeWidth: String
get() = attributes["stroke-width"].orEmpty()
set(value) { attributes["stroke-width"] = value }
inline var SVG.width: String
get() = attributes["width"].orEmpty()
set(value) { attributes["width"] = value }
inline var SVG.height: String
get() = attributes["height"].orEmpty()
set(value) { attributes["height"] = value }
inline var SVG.viewBox: String
get() = attributes["viewBox"].orEmpty()
set(value) { attributes["viewBox"] = value }
Example usage then looks like this:
fun FlowContent.icon() {
svg {
width = "120"
height = "120"
viewBox = "0 0 120 120"
path {
d = "M10 10 H110 V110 H10 Z"
fill = "none"
stroke = "black"
strokeWidth = "4"
}
}
}Alex Styl
10/19/2025, 1:11 AMfun FlowContent.Locked(classes: String = "size-4") {
svg(
classes = classes,
viewBox = "0 0 24 24",
fill = "none",
stroke = "currentColor",
strokeWidth = "2",
strokeLinecap = "round",
strokeLinejoin = "round"
) {
+"""
<circle cx="12" cy="16" r="1"/><rect x="3" y="10" width="18" height="12" rx="2"/><path d="M7 10V7a5 5 0 0 1 10 0v3"/>
""".trimIndent()
}
}
inline fun FlowOrPhrasingContent.svg(
classes: String? = null,
viewBox: String? = null,
fill: String? = null,
stroke: String? = null,
strokeWidth: String? = null,
strokeLinecap: String? = null,
strokeLinejoin: String? = null,
width: String? = null,
height: String? = null,
crossinline block: SVG.() -> Unit = {},
) = svgInternal(classes) {
if (viewBox != null) attributes["viewBox"] = viewBox
if (fill != null) attributes["fill"] = fill
if (stroke != null) attributes["stroke"] = stroke
if (strokeWidth != null) attributes["stroke-width"] = strokeWidth
if (strokeLinecap != null) attributes["stroke-linecap"] = strokeLinecap
if (strokeLinejoin != null) attributes["stroke-linejoin"] = strokeLinejoin
if (width != null) attributes["width"] = width
if (height != null) attributes["height"] = height
block()
}
I'm ok with copying the path commands as svg. I just copy paste them from svgs anyway, but I hadn't figured out how to bridge that to the dslAlex Styl
10/19/2025, 1:12 AMAlex Styl
10/29/2025, 6:31 AMimport kotlinx.html.svg as svgInternal