diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/HtmlText.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/HtmlText.kt
index 6ecc6d3072ae7b15398cf197a3be8aee983b4272..04a2dd45f9634ecc52c92b1dec747b724de94456 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/HtmlText.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/HtmlText.kt
@@ -31,7 +31,6 @@ import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.ParagraphStyle
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextStyle
@@ -68,7 +67,6 @@ val listBullets = listOf(
     "\u25aa",
 )
 
-@OptIn(ExperimentalTextApi::class)
 @Composable
 @Suppress("HardCodedStringLiteral")
 fun HtmlText(
@@ -167,108 +165,136 @@ fun HtmlText(
                 }
             }
 
-            fun startParagraph() {
-                pushIndent(0.sp)
-            }
-
-            fun endParagraph() {
-                popIndent()
-            }
-
-            fun startPre() {
-                pushIndent(20.sp)
-                withinPre = true
-                pushStyle(monospace)
-            }
-
-            fun endPre() {
-                popIndent()
-                withinPre = false
-                pop()
-            }
+            data class HtmlNode(
+                val start: (node: Element) -> Unit,
+                val end: () -> Unit = { pop() },
+            )
 
-            fun startCode() {
-                if (!withinPre) pushStyle(monospace)
+            val strong = HtmlNode(start = { pushStyle(bold) })
+            val em = HtmlNode(start = { pushStyle(italic) })
+            fun pushHeader(style: TextStyle) {
+                pushStyle(style.toParagraphStyle())
+                pushStyle(style.toSpanStyle())
             }
 
-            fun endCode() {
-                if (!withinPre) pop()
+            fun popHeader() {
+                pop(); pop()
             }
 
-            fun startLink(node: Element) {
-                val href = node.attr("href")
-
-                pushStringAnnotation("link", href)
-                pushStyle(link)
-            }
-
-            fun endLink() {
-                pop()
-                pop()
-            }
-
-            fun startBlockQuote() {
-                pushIndent(20.sp)
-                pushStringAnnotation("quote", "")
-            }
-
-            fun endBlockQuote() {
-                pop()
-                popIndent()
-            }
-
-            // todo: quotation marks should be properly localized
-            fun startInlineQuote() {
-                appendAndUpdateCursor("\"")
-            }
-
-            fun endInlineQuote() {
-                appendAndUpdateCursor("\"")
-            }
-
-            fun startUnorderedList() {
-                listNesting.push(UNORDERED)
-                listNumbering.push(0)
-                pushIndent(20.sp)
-            }
-
-            fun endUnorderedList() {
-                listNesting.pop()
-                listNumbering.pop()
-                popIndent()
-            }
-
-            fun startOrderedList() {
-                listNesting.push(ORDERED)
-                listNumbering.push(0)
-                pushIndent(20.sp)
-            }
-
-            fun endOrderedList() {
-                listNesting.pop()
-                listNumbering.pop()
-                popIndent()
-            }
-
-            fun startBullet() {
-                check(listNesting.isNotEmpty()) { "<li> outside of list" }
-                val listType = listNesting.top()
-                listNumbering.incrementCurrent()
-                if (listType == UNORDERED) {
-                    val bulletType = listNesting.size - 1
-                    appendAndUpdateCursor(listBullets[bulletType % listBullets.size])
-                    appendAndUpdateCursor(" ")
-                    pushStringAnnotation("bullet", listNesting.size.toString())
-                } else if (listType == ORDERED) {
-                    appendAndUpdateCursor("${listNumbering.top()}. ")
-                    pushStringAnnotation("bullet", listNesting.size.toString())
-                }
-            }
-
-            fun endBullet() {
-                pop()
-                ensureNewline()
-            }
+            val nodes = mapOf(
+                // headers
+                "h1" to HtmlNode(start = { pushHeader(h1) }, end = ::popHeader),
+                "h2" to HtmlNode(start = { pushHeader(h2) }, end = ::popHeader),
+                "h3" to HtmlNode(start = { pushHeader(h3) }, end = ::popHeader),
+                "h4" to HtmlNode(start = { pushHeader(h4) }, end = ::popHeader),
+                "h5" to HtmlNode(start = { pushHeader(h5) }, end = ::popHeader),
+                "h6" to HtmlNode(start = { pushHeader(h6) }, end = ::popHeader),
+
+                // inline formatting
+                "b" to strong, "strong" to strong,
+                "i" to em, "em" to em, "cite" to em,
+                "u" to HtmlNode(start = { pushStyle(underline) }),
+                "strike" to HtmlNode(start = { pushStyle(strikethrough) }),
+                "sub" to HtmlNode(start = { pushStyle(subscript) }),
+                "sup" to HtmlNode(start = { pushStyle(superscript) }),
+                "small" to HtmlNode(start = { pushStyle(small) }),
+                "code" to HtmlNode(
+                    start = { if (!withinPre) pushStyle(monospace) },
+                    end = { if (!withinPre) pop() }
+                ),
+                "q" to HtmlNode(
+                    // todo: quotation marks should be properly localized
+                    start = { appendAndUpdateCursor("\"") },
+                    end = { appendAndUpdateCursor("\"") }
+                ),
+                "a" to HtmlNode(
+                    start = { node ->
+                        val href = node.attr("href")
+
+                        pushStringAnnotation("link", href)
+                        pushStyle(link)
+                    },
+                    end = {
+                        pop()
+                        pop()
+                    }
+                ),
+
+                // lists
+                "ul" to HtmlNode(
+                    start = {
+                        listNesting.push(UNORDERED)
+                        listNumbering.push(0)
+                        pushIndent(20.sp)
+                    },
+                    end = {
+                        listNesting.pop()
+                        listNumbering.pop()
+                        popIndent()
+                    }
+                ),
+                "ol" to HtmlNode(
+                    start = {
+                        listNesting.push(ORDERED)
+                        listNumbering.push(0)
+                        pushIndent(20.sp)
+                    },
+                    end = {
+                        listNesting.pop()
+                        listNumbering.pop()
+                        popIndent()
+                    }
+                ),
+                "li" to HtmlNode(
+                    start = {
+                        check(listNesting.isNotEmpty()) { "<li> outside of list" }
+                        val listType = listNesting.top()
+                        listNumbering.incrementCurrent()
+                        if (listType == UNORDERED) {
+                            val bulletType = listNesting.size - 1
+                            appendAndUpdateCursor(listBullets[bulletType % listBullets.size])
+                            appendAndUpdateCursor(" ")
+                            pushStringAnnotation("bullet", listNesting.size.toString())
+                        } else if (listType == ORDERED) {
+                            appendAndUpdateCursor("${listNumbering.top()}. ")
+                            pushStringAnnotation("bullet", listNesting.size.toString())
+                        }
+                    },
+                    end = {
+                        pop()
+                        ensureNewline()
+                    }
+                ),
+
+                // misc
+                "br" to HtmlNode(start = { appendAndUpdateCursor("\n") }, end = {}),
+                "p" to HtmlNode(
+                    start = { pushIndent(0.sp) },
+                    end = { popIndent() }
+                ),
+                "blockquote" to HtmlNode(
+                    start = {
+                        pushIndent(20.sp)
+                        pushStringAnnotation("quote", "")
+                    },
+                    end = {
+                        pop()
+                        popIndent()
+                    }
+                ),
+                "pre" to HtmlNode(
+                    start = {
+                        pushIndent(20.sp)
+                        withinPre = true
+                        pushStyle(monospace)
+                    },
+                    end = {
+                        popIndent()
+                        withinPre = false
+                        pop()
+                    }
+                )
+            )
 
             val doc = Jsoup.parse(html)
 
@@ -289,39 +315,7 @@ fun HtmlText(
                         }
 
                         is Element -> {
-                            when (node.tagName()) {
-                                // headers
-                                "h1" -> pushStyle(h1)
-                                "h2" -> pushStyle(h2)
-                                "h3" -> pushStyle(h3)
-                                "h4" -> pushStyle(h4)
-                                "h5" -> pushStyle(h5)
-                                "h6" -> pushStyle(h6)
-
-                                // inline formatting
-                                "b", "strong" -> pushStyle(bold)
-                                "i", "em", "cite" -> pushStyle(italic)
-                                "u" -> pushStyle(underline)
-                                "strike" -> pushStyle(strikethrough)
-                                "sub" -> pushStyle(subscript)
-                                "sup" -> pushStyle(superscript)
-                                "small" -> pushStyle(small)
-                                "code" -> startCode()
-                                "q" -> startInlineQuote()
-                                "a" -> startLink(node)
-
-                                // lists
-                                "ul" -> startUnorderedList()
-                                "ol" -> startOrderedList()
-                                "li" -> startBullet()
-
-                                // misc
-                                "br" -> appendAndUpdateCursor("\n")
-                                "blockquote" -> startBlockQuote()
-                                "p" -> startParagraph()
-                                "pre" -> startPre()
-                                // else -> throw Exception("Unsupported tag '${node.tagName()}'")
-                            }
+                            nodes[node.tagName()]?.let { it.start(node) }
                         }
 
                         else -> {
@@ -332,27 +326,7 @@ fun HtmlText(
 
                 override fun tail(node: Node, depth: Int) {
                     if (node is Element) {
-                        when (node.tagName()) {
-                            "h1", "h2", "h3", "h4", "h5", "h6" -> {
-                                pop()
-                                pop()
-                            }
-
-                            "b", "strong", "i", "em", "cite", "u", "strike", "sub", "sup", "small",
-                            -> pop()
-
-                            "code" -> endCode()
-                            "q" -> endInlineQuote()
-                            "a" -> endLink()
-
-                            "ul" -> endUnorderedList()
-                            "ol" -> endOrderedList()
-                            "li" -> endBullet()
-
-                            "p" -> endParagraph()
-                            "blockquote" -> endBlockQuote()
-                            "pre" -> endPre()
-                        }
+                        nodes[node.tagName()]?.let { it.end() }
                     }
                 }
             })
@@ -419,8 +393,3 @@ fun <E> ArrayDeque<E>.top(): E {
 fun ArrayDeque<Int>.incrementCurrent() {
     this[size - 1] = this[size - 1] + 1
 }
-
-private fun AnnotatedString.Builder.pushStyle(style: TextStyle) {
-    pushStyle(style.toParagraphStyle())
-    pushStyle(style.toSpanStyle())
-}