diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 336bb815a..ea50c83e6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,7 +1,7 @@
[versions]
kobweb = "0.23.2-SNAPSHOT"
#------------------------
-commonmark = "0.24.0"
+commonmark = "0.25.1"
compose = "1.8.0"
dokka = "2.0.0"
gradle-publish = "1.3.0"
@@ -24,8 +24,10 @@ vanniktech-publish = "0.34.0"
[libraries]
commonmark-core = { module = "org.commonmark:commonmark", version.ref = "commonmark" }
commonmark-autolink = { module = "org.commonmark:commonmark-ext-autolink", version.ref = "commonmark" }
+commonmark-strikethrough = { module = "org.commonmark:commonmark-ext-gfm-strikethrough", version.ref = "commonmark" }
commonmark-tables = { module = "org.commonmark:commonmark-ext-gfm-tables", version.ref = "commonmark" }
commonmark-tasklist = { module = "org.commonmark:commonmark-ext-task-list-items", version.ref = "commonmark" }
+commonmark-footnotes = { module = "org.commonmark:commonmark-ext-footnotes", version.ref = "commonmark" }
compose-html-core = { module = "org.jetbrains.compose.html:html-core", version.ref = "compose" }
compose-compiler-plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose" }
@@ -62,7 +64,14 @@ truthish = { module = "com.varabyte.truthish:truthish", version.ref = "truthish"
vanniktech-publish-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "vanniktech-publish" }
[bundles]
-commonmark = ["commonmark-core", "commonmark-autolink", "commonmark-tables", "commonmark-tasklist"]
+commonmark = [
+ "commonmark-core",
+ "commonmark-autolink",
+ "commonmark-tables",
+ "commonmark-tasklist",
+ "commonmark-strikethrough",
+ "commonmark-footnotes",
+]
ktor = [
"ktor-serialization-json",
"ktor-server-auth",
diff --git a/playground/site/src/jsMain/resources/markdown/Markdown.md b/playground/site/src/jsMain/resources/markdown/Markdown.md
index 902872fb2..4db7324c7 100644
--- a/playground/site/src/jsMain/resources/markdown/Markdown.md
+++ b/playground/site/src/jsMain/resources/markdown/Markdown.md
@@ -6,7 +6,7 @@ This site is generated from Markdown.
Create rich, dynamic web apps with ease, leveraging [Kotlin](https://kotlinlang.org/) and [Compose HTML](https://github.com/JetBrains/compose-multiplatform#compose-html).
-Markdown of course supports **bold**, _italic_, and _**bold italic**_ text.
+Markdown of course supports **bold**, _italic_, _**bold italic**_, and ~~strikethrough~~ text.
It also supports
@@ -45,6 +45,7 @@ You can link to other Markdown documents with their route overrides resolved cor
[documents/INDEX.md](documents/INDEX.md)
[KotlinLanguage.md](jetbrains/KotlinLanguage.md) (`routeOverride: languages/kotlin`)
[documents/Bananas.md](documents/Bananas.md) (`routeOverride: /fruits/`)
+[tests/unified_test.md](tests/unified_test.md) (`routeOverride: /example/`)
[files/external.md](/files/external.md) (Linking to a Markdown file outside of the processed markdown files)
You can use inlined html tags. You can inspect this page to see that "inlined html" is
diff --git a/playground/site/src/jsMain/resources/markdown/tests/unified_test.md b/playground/site/src/jsMain/resources/markdown/tests/unified_test.md
new file mode 100644
index 000000000..4d1d7ef4f
--- /dev/null
+++ b/playground/site/src/jsMain/resources/markdown/tests/unified_test.md
@@ -0,0 +1,98 @@
+---
+title: Unified Markdown Processing Test
+date: 'July 8, 2025'
+description: Testing unified markdown processing with remark-gfm and rehype-raw plugins
+tags: [ 'kobweb', 'commonmark', 'gfm', 'test', 'md', 'kotlin' ]
+posted: true
+---
+
+# Unified Markdown Processing Test
+
+This content demonstrates markdown features on the **Kobweb Framework**, which uses
+a [CommonMark](https://commonmark.org/) parser
+with [GitHub Flavored Markdown (GFM) extensions](https://github.github.com/gfm/).
+
+## GitHub Flavored Markdown Features
+
+### Strikethrough
+
+~~This text should be struck through~~ or ~one tilde~
+
+### Tables
+| Feature | Status | Notes |
+|------------------|------------------------------------|-------------------------------|
+| Strikethrough | * [x] | Crosses out text |
+| Tables | [x] | Organizes data in a grid |
+| Task Lists |- [x] | Creates checklists |
+| HTML in Markdown | - [x] highlighted text | Allows embedding raw HTML tags |
+| Code Blocks | - [x] | Allows embedding code blocks |
+| Links | - [x] | Allows embedding links |
+| Emphasis | - [x] | Allows embedding emphasis |
+| Column A | Column B | Column C | Column D |
+|----------| :--------- |:--------:|---------:|
+| Left | Left | Center | Right |
+| Data 1 | Data 2 | Data 3 | Data 4 |
+| Row 2 | Row 2 | Row 2 | Row 2 |
+
+### Task Lists
+
+- [x] Completed task
+- [ ] Pending task
+- [x] Another completed task
+- [ ] Another pending task
+- [x] Venus
+- works
+
+### HTML Elements in Markdown
+
+This should work with underlined text and highlighted text.
+
+
+Note: This is HTML content inside markdown that should be preserved with rehype-raw.
+
+
+### Code Blocks
+
+```javascript
+// This is a JavaScript code block
+function hello() {
+ console.log("Hello from unified markdown processing!");
+}
+```
+
+### Links and Autolinks
+
+Regular link: [Kobweb Documentation](https://kobweb.varabyte.com)
+
+Autolink: https://kobweb.varabyte.com and www.example.com.
+
+### Emphasis and Strong
+
+*Italic text* and **bold text** and ***bold italic text***
+
+## Footnote
+
+A note can have a simple footnote.[^1] Here are some more examples:
+
+- A footnote with a link.[^link]
+- A footnote with multiple paragraphs.[^multi]
+- A footnote with a long text.[^long]
+
+[^1]: This is the first simple footnote.
+
+[^link]: This footnote contains a link to [Kobweb](https://kobweb.varabyte.com).
+
+[^multi]: This footnote has multiple paragraphs.
+[foo]: https://example.com/foo
+
+This is the second paragraph of the footnote.
+
+[^long]: Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem
+aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
+ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
+voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit,
+sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
+
+---
+
+✅ If you can see all the above features working correctly, then the CommonMark-java parser with its GFM extensions is functioning properly!
\ No newline at end of file
diff --git a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/KotlinRenderer.kt b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/KotlinRenderer.kt
index 0404792c8..e2b1f2b53 100644
--- a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/KotlinRenderer.kt
+++ b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/KotlinRenderer.kt
@@ -12,13 +12,16 @@ import com.varabyte.kobwebx.gradle.markdown.frontmatter.FrontMatterBlock
import com.varabyte.kobwebx.gradle.markdown.handlers.MarkdownHandlers
import com.varabyte.kobwebx.gradle.markdown.handlers.NodeScope
import com.varabyte.kobwebx.gradle.markdown.util.NodeCache
-import com.varabyte.kobwebx.gradle.markdown.util.escapeQuotes
import com.varabyte.kobwebx.gradle.markdown.util.unescapeQuotes
+import org.commonmark.ext.footnotes.FootnoteDefinition
+import org.commonmark.ext.footnotes.FootnoteReference
+import org.commonmark.ext.gfm.strikethrough.Strikethrough
import org.commonmark.ext.gfm.tables.TableBlock
import org.commonmark.ext.gfm.tables.TableBody
import org.commonmark.ext.gfm.tables.TableCell
import org.commonmark.ext.gfm.tables.TableHead
import org.commonmark.ext.gfm.tables.TableRow
+import org.commonmark.ext.task.list.items.TaskListItemMarker
import org.commonmark.node.AbstractVisitor
import org.commonmark.node.BlockQuote
import org.commonmark.node.BulletList
@@ -339,6 +342,7 @@ class KotlinRenderer internal constructor(
override fun visit(code: Code) {
doVisit(code, handlers.inlineCode)
}
+
override fun visit(emphasis: Emphasis) {
doVisit(emphasis, handlers.em)
@@ -397,7 +401,7 @@ class KotlinRenderer internal constructor(
val route = Route(
frontMatterData?.routeOverride
- ?: nodeCache.metadata.getValue(destinationNode).routeWithSlug!! // Guaranteed set for a page
+ ?: nodeCache.metadata.getValue(destinationNode).routeWithSlug!! // Guaranteed set for a page
)
if (route.isDynamic) {
error("Markdown file link '${link.destination}' links to file with dynamic route override. This is not supported!")
@@ -434,6 +438,10 @@ class KotlinRenderer internal constructor(
}
}
+ private fun visit(strikethrough: Strikethrough) {
+ doVisit(strikethrough, handlers.strikethrough)
+ }
+
override fun visit(strongEmphasis: StrongEmphasis) {
doVisit(strongEmphasis, handlers.strong)
}
@@ -441,6 +449,39 @@ class KotlinRenderer internal constructor(
override fun visit(text: Text) {
doVisit(text, handlers.text)
}
+ override fun visit(customBlock: CustomBlock) {
+ when (customBlock) {
+ is KobwebCallBlock -> {
+ val visitor = KobwebCallBlockVisitor()
+ customBlock.accept(visitor)
+ visitor.call?.let { call ->
+ visit(call)
+ visitor.childrenNodes?.let { children ->
+ ++indentCount
+ children.forEach { node -> node.accept(this) }
+ --indentCount
+ output.appendLine("$indent}")
+ }
+ }
+ }
+
+ is FrontMatterBlock -> {
+ // No-op. We don't need to do anything here because we already handled parsing front matter earlier.
+ }
+
+ is TableBlock -> visit(customBlock)
+ is FootnoteDefinition -> {
+ doVisit(customBlock, handlers.footnoteDefinition)
+ }
+
+ else -> {
+ val simple = customBlock::class.simpleName
+ val unhandledBlockName = simple!!
+ reporter.warn("Unhandled Markdown custom block: $unhandledBlockName. Consider reporting this at: https://github.com/varabyte/kobweb/issues/new?labels=bug&template=bug_report.md&title=Unhandled%20Markdown%20block%20%22$unhandledBlockName%22")
+
+ }
+ }
+ }
override fun visit(customNode: CustomNode) {
when (customNode) {
@@ -448,10 +489,13 @@ class KotlinRenderer internal constructor(
output.appendLine("$indent${customNode.toFqn(projectGroup)}")
}
+ is Strikethrough -> visit(customNode)
is TableHead -> visit(customNode)
is TableBody -> visit(customNode)
is TableRow -> visit(customNode)
is TableCell -> visit(customNode)
+ is FootnoteReference -> visit(customNode)
+ is TaskListItemMarker -> visit(customNode)
else -> {
val unhandledNodeName = customNode::class.simpleName!!
@@ -460,6 +504,10 @@ class KotlinRenderer internal constructor(
}
}
+ private fun visit(footnoteReference: FootnoteReference) {
+ doVisit(footnoteReference, handlers.footnoteReference)
+ }
+
private fun visit(table: TableBlock) {
doVisit(table, handlers.table)
}
@@ -483,34 +531,8 @@ class KotlinRenderer internal constructor(
doVisit(tableCell, handlers.td)
}
}
-
- override fun visit(customBlock: CustomBlock) {
- when (customBlock) {
- is KobwebCallBlock -> {
- val visitor = KobwebCallBlockVisitor()
- customBlock.accept(visitor)
- visitor.call?.let { call ->
- visit(call)
- visitor.childrenNodes?.let { children ->
- ++indentCount
- children.forEach { node -> node.accept(this) }
- --indentCount
- output.appendLine("$indent}")
- }
- }
- }
-
- is FrontMatterBlock -> {
- // No-op. We don't need to do anything here because we already handled parsing front matter earlier.
- }
-
- is TableBlock -> visit(customBlock)
-
- else -> {
- val unhandledBlockName = customBlock::class.simpleName!!
- reporter.warn("Unhandled Markdown custom block: $unhandledBlockName. Consider reporting this at: https://github.com/varabyte/kobweb/issues/new?labels=bug&template=bug_report.md&title=Unhandled%20Markdown%20block%20%22$unhandledBlockName%22")
- }
- }
+ private fun visit(taskListItemMarker: TaskListItemMarker) {
+ doVisit(taskListItemMarker, handlers.taskListItemMarker)
}
}
}
diff --git a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/MarkdownFeatures.kt b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/MarkdownFeatures.kt
index ea4f8d470..550d59964 100644
--- a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/MarkdownFeatures.kt
+++ b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/MarkdownFeatures.kt
@@ -6,6 +6,8 @@ import com.varabyte.kobwebx.gradle.markdown.ext.kobwebcall.KobwebCallExtension
import com.varabyte.kobwebx.gradle.markdown.frontmatter.FrontMatterExtension
import org.commonmark.Extension
import org.commonmark.ext.autolink.AutolinkExtension
+import org.commonmark.ext.footnotes.FootnotesExtension
+import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
import org.commonmark.ext.gfm.tables.TablesExtension
import org.commonmark.ext.task.list.items.TaskListItemsExtension
import org.commonmark.parser.IncludeSourceSpans
@@ -37,6 +39,16 @@ abstract class MarkdownFeatures {
@get:Input
abstract val autolink: Property
+
+ /**
+ * If true, support footnotes like "Main text[^1]" with definitions "[^1]: Footnote text".
+ *
+ * Defaults to `true`.
+ *
+ * Note: Inline footnotes via `^[inline]` are not supported at this time.
+ */
+ @get:Input
+ abstract val footnotes: Property
/**
* If true, support front matter (a header YAML block at the top of your markdown file with key/value pairs).
*
@@ -73,6 +85,15 @@ abstract class MarkdownFeatures {
*/
@get:Input
abstract val kobwebCallDelimiters: Property>
+ /**
+ * If true, support GFM strikethrough syntax using double tildes, e.g. `~~text~~`.
+ *
+ * Defaults to `true`.
+ *
+ * @see Strikethrough
+ */
+ @get:Input
+ abstract val strikethrough: Property
/**
* If true, support creating tables via pipe syntax.
@@ -100,11 +121,14 @@ abstract class MarkdownFeatures {
@get:Input
abstract val taskList: Property
+
init {
autolink.convention(true)
+ footnotes.convention(true)
frontMatter.convention(true)
kobwebCall.convention(true)
kobwebCallDelimiters.convention('{' to '}')
+ strikethrough.convention(true)
tables.convention(true)
taskList.convention(true)
}
@@ -117,12 +141,18 @@ abstract class MarkdownFeatures {
if (autolink.get()) {
extensions.add(AutolinkExtension.create())
}
+ if (footnotes.get()) {
+ extensions.add(FootnotesExtension.create())
+ }
if (frontMatter.get()) {
extensions.add(FrontMatterExtension.create())
}
if (kobwebCall.get()) {
extensions.add(KobwebCallExtension.create(kobwebCallDelimiters.get()) { createParser() })
}
+ if (strikethrough.get()) {
+ extensions.add(StrikethroughExtension.create())
+ }
if (tables.get()) {
extensions.add(TablesExtension.create())
}
@@ -130,6 +160,7 @@ abstract class MarkdownFeatures {
extensions.add(TaskListItemsExtension.create())
}
+
return Parser.builder()
.extensions(extensions)
.includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES)
diff --git a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/handlers/MarkdownHandlers.kt b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/handlers/MarkdownHandlers.kt
index 8bb8b95ff..3e6ab4161 100644
--- a/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/handlers/MarkdownHandlers.kt
+++ b/tools/gradle-plugins/extensions/markdown/src/main/kotlin/com/varabyte/kobwebx/gradle/markdown/handlers/MarkdownHandlers.kt
@@ -1,5 +1,14 @@
@file:Suppress("LeakingThis") // Following official Gradle guidance
+/**
+ * Enhanced Markdown handlers for Kobweb projects.
+ *
+ * Recent enhancements:
+ * - Task list support: Automatically detects and renders task list items (- [x] / - [ ]) in all text contexts,
+ * including table cells where CommonMark doesn't normally recognize them as TaskListItemMarker nodes.
+ * Uses FontAwesome icons when Silk is available, falls back to HTML input checkboxes otherwise.
+ */
+
package com.varabyte.kobwebx.gradle.markdown.handlers
import com.varabyte.kobweb.common.collect.Key
@@ -12,11 +21,15 @@ import com.varabyte.kobwebx.gradle.markdown.util.escapeDollars
import com.varabyte.kobwebx.gradle.markdown.util.escapeQuotes
import com.varabyte.kobwebx.gradle.markdown.util.escapeTripleQuotes
import com.varabyte.kobwebx.gradle.markdown.util.nestedLiteral
+import org.commonmark.ext.footnotes.FootnoteDefinition
+import org.commonmark.ext.footnotes.FootnoteReference
+import org.commonmark.ext.gfm.strikethrough.Strikethrough
import org.commonmark.ext.gfm.tables.TableBlock
import org.commonmark.ext.gfm.tables.TableBody
import org.commonmark.ext.gfm.tables.TableCell
import org.commonmark.ext.gfm.tables.TableHead
import org.commonmark.ext.gfm.tables.TableRow
+import org.commonmark.ext.task.list.items.TaskListItemMarker
import org.commonmark.node.BlockQuote
import org.commonmark.node.BulletList
import org.commonmark.node.Code
@@ -79,6 +92,28 @@ class NodeScope(val reporter: Reporter, val data: TypedMap, private val indentCo
* }
* }
* }
+ *
+ * ## Task List Support
+ *
+ * This handler automatically detects and renders task list items (checkboxes) in markdown text,
+ * including those inside table cells where CommonMark doesn't automatically recognize them as TaskListItemMarker nodes.
+ *
+ * Task list patterns like `- [x]` (checked) and `- [ ]` (unchecked) are converted to:
+ * - **With Silk**: FontAwesome icons (`FaSquareCheck` for checked, `FaSquare` for unchecked)
+ * - **Without Silk**: HTML input checkboxes (disabled, with proper checked state)
+ *
+ * This works in all contexts including:
+ * - Regular paragraphs
+ * - Table cells
+ * - List items
+ * - Any other text nodes
+ *
+ * Example:
+ * ```markdown
+ * | Feature | Status |
+ * |---------|--------|
+ * | Task A | - [x] |
+ * | Task B | - [ ] |
* ```
*/
abstract class MarkdownHandlers @Inject constructor(project: Project) {
@@ -140,57 +175,69 @@ abstract class MarkdownHandlers @Inject constructor(project: Project) {
abstract val idGenerator: Property<(String) -> String>
@get:Nested
- abstract val text: Property String>
- @get:Nested
- abstract val img: Property String>
- @get:Nested
- abstract val heading: Property String>
+ abstract val a: Property String>
@get:Nested
- abstract val p: Property String>
+ abstract val blockquote: Property String>
@get:Nested
abstract val br: Property String>
@get:Nested
- abstract val a: Property String>
+ abstract val code: Property String>
@get:Nested
abstract val em: Property String>
@get:Nested
- abstract val strong: Property String>
+ abstract val footnoteDefinition: Property String>
+ @get:Nested
+ abstract val footnoteReference: Property String>
+
+ @get:Nested
+ abstract val heading: Property String>
+
@get:Nested
abstract val hr: Property String>
@get:Nested
- abstract val ul: Property String>
+ abstract val html: Property String>
@get:Nested
- abstract val ol: Property String>
+ abstract val img: Property String>
+ @get:Nested
+ abstract val inlineCode: Property String>
+
+ @get:Nested
+ abstract val inlineTag: Property String>
+
@get:Nested
abstract val li: Property String>
@get:Nested
- abstract val code: Property String>
+ abstract val ol: Property String>
@get:Nested
- abstract val inlineCode: Property String>
+ abstract val p: Property String>
@get:Nested
- abstract val blockquote: Property String>
+ abstract val rawTag: Property String>
+ @get:Nested
+ abstract val strikethrough: Property String>
+ @get:Nested
+ abstract val strong: Property String>
+
@get:Nested
abstract val table: Property String>
@get:Nested
- abstract val thead: Property String>
+ abstract val taskListItemMarker: Property String>
@get:Nested
abstract val tbody: Property String>
@get:Nested
- abstract val tr: Property String>
- @get:Nested
abstract val td: Property String>
@get:Nested
- abstract val th: Property String>
+ abstract val text: Property String>
- /** Handler which is fed the raw text (name and attributes) within an opening tag, e.g. `span id="demo"` */
@get:Nested
- abstract val rawTag: Property String>
+ abstract val th: Property String>
@get:Nested
- abstract val inlineTag: Property String>
-
+ abstract val thead: Property String>
@get:Nested
- abstract val html: Property String>
+ abstract val tr: Property String>
+ @get:Nested
+ abstract val ul: Property String>
+ /** Handler which is fed the raw text (name and attributes) within an opening tag, e.g. `span id="demo"` */
fun String.escapeSingleQuotedText() = escapeQuotes().escapeDollars()
fun String.escapeTripleQuotedText() = escapeDollars().escapeTripleQuotes()
@@ -258,7 +305,11 @@ abstract class MarkdownHandlers @Inject constructor(project: Project) {
// region Markdown Node handlers
- text.convention { text -> "$JB_DOM.Text(\"${text.literal.escapeSingleQuotedText()}\")" }
+ text.convention { text ->
+ val literal = text.literal
+ // Standard text rendering
+ "$JB_DOM.Text(\"${literal.escapeSingleQuotedText()}\")"
+ }
img.convention { image ->
processImage(image) { data ->
buildString {
@@ -299,6 +350,8 @@ abstract class MarkdownHandlers @Inject constructor(project: Project) {
}
em.convention { "$JB_DOM.Em" }
strong.convention { "$JB_DOM.B" }
+ // Compose HTML does not expose a Del composable; use a generic tag to match HTML semantics.
+ strikethrough.convention { "$KOBWEB_DOM.GenericTag(\"del\")" }
hr.convention {
if (useSilk.get()) {
"$SILK.layout.HorizontalDivider"
@@ -436,6 +489,30 @@ abstract class MarkdownHandlers @Inject constructor(project: Project) {
sb.toString()
}
+ taskListItemMarker.convention { marker ->
+ val isChecked = marker.isChecked
+ if (useSilk.get()) {
+ val iconCode = if (isChecked) {
+ "com.varabyte.kobweb.silk.components.icons.fa.FaSquareCheck"
+ } else {
+ "com.varabyte.kobweb.silk.components.icons.fa.FaSquare"
+ }
+ "$KOBWEB_DOM.GenericTag(\"span\", \"style=\\\"margin-right: 0.5em;\\\"\") { $iconCode() }"
+ } else {
+ val checkedAttr = if (isChecked) "checked" else ""
+ "$KOBWEB_DOM.GenericTag(\"input\", \"type=\\\"checkbox\\\" disabled $checkedAttr style=\\\"margin-right: 0.5em;\\\"\")"
+ }
+ }
+
+ footnoteDefinition.convention { definition ->
+ val label = definition.label
+ "$KOBWEB_DOM.GenericTag(\"div\", \"class=\\\"footnote-item\\\" id=\\\"fn-$label\\\"\")"
+ }
+
+ footnoteReference.convention { reference ->
+ val label = reference.label
+ "$KOBWEB_DOM.GenericTag(\"sup\", \"id=\\\"fnref-$label\\\"\") { $KOBWEB_DOM.GenericTag(\"a\", \"href=\\\"#fn-$label\\\"\") { $JB_DOM.Text(\"$label\") } }"
+ }
// endregion
}
}