From 2ba548810c6ce1ced2df9eb19f2cea03fa96bd9e Mon Sep 17 00:00:00 2001
From: Andrew Barba <barba@hey.com>
Date: Mon, 23 May 2022 16:03:28 -0400
Subject: [PATCH] Support meta tags in StaticHTMLRenderer (#483)

This PR adds the ability to control `<head>` tags using a new `HTMLTitle` view and `HTMLMeta` view. Taking inspiration from Next.js `<NextHead>`, you can use these views anywhere in your view hierarchy and they will be hoisted to the top `<head>` section of the html.

Use as a view:

```swift
var body: some View {
  VStack {
    ...
    HTMLTitle("Hello, Tokamak")
    HTMLMeta(charset: "utf-8")
    ...
  }
}
```

Use as a view modifier:

```swift
var body: some View {
  VStack {
    ...
  }
  .htmlTitle("Hello, Tokamak")
  .htmlMeta(charset: "utf-8")
}
```

And the resulting html (no matter where these are used in your view hierarchy):

```html
<html>
  <head>
    <title>Hello, Tokamak</title>
    <meta charset="utf-8">
  </head>
  <body>
    ...
  </body>
</html>
```
---
 .../MountedViews/MountedCompositeView.swift   |   3 -
 .../MountedViews/MountedElement.swift         |  17 +-
 .../Preferences/PreferenceKey.swift           |  19 +-
 Sources/TokamakCore/StackReconciler.swift     |   6 +
 .../TokamakCore/Views/Containers/Group.swift  |   2 +-
 Sources/TokamakStaticHTML/App.swift           |   2 +-
 .../Preferences/Preferences.swift             |  37 +++
 .../StaticHTMLRenderer.swift                  |  26 +-
 .../TokamakStaticHTML/Views/Head/Meta.swift   |  83 ++++++
 .../TokamakStaticHTML/Views/Head/Title.swift  |  44 +++
 Tests/TokamakStaticHTMLTests/HTMLTests.swift  | 114 ++++++++
 .../HTMLTests/testDoubleTitle.1.html          | 267 ++++++++++++++++++
 .../HTMLTests/testDoubleTitleModifier.1.html  | 260 +++++++++++++++++
 .../HTMLTests/testMetaAll.1.html              | 266 +++++++++++++++++
 .../HTMLTests/testMetaCharset.1.html          | 263 +++++++++++++++++
 .../HTMLTests/testMetaCharsetModifier.1.html  | 260 +++++++++++++++++
 .../testPreferencePropagation.1.html          | 261 +++++++++++++++++
 .../__Snapshots__/HTMLTests/testTitle.1.html  | 263 +++++++++++++++++
 .../HTMLTests/testTitleModifier.1.html        | 260 +++++++++++++++++
 19 files changed, 2425 insertions(+), 28 deletions(-)
 create mode 100644 Sources/TokamakStaticHTML/Preferences/Preferences.swift
 create mode 100644 Sources/TokamakStaticHTML/Views/Head/Meta.swift
 create mode 100644 Sources/TokamakStaticHTML/Views/Head/Title.swift
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitle.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitleModifier.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaAll.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharset.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharsetModifier.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testPreferencePropagation.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitle.1.html
 create mode 100644 Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitleModifier.1.html

diff --git a/Sources/TokamakCore/MountedViews/MountedCompositeView.swift b/Sources/TokamakCore/MountedViews/MountedCompositeView.swift
index 4aad0b8bb..9415fe72a 100644
--- a/Sources/TokamakCore/MountedViews/MountedCompositeView.swift
+++ b/Sources/TokamakCore/MountedViews/MountedCompositeView.swift
@@ -78,9 +78,6 @@ final class MountedCompositeView<R: Renderer>: MountedCompositeElement<R> {
 
       if let preferenceModifier = self.view.view as? _PreferenceWritingViewProtocol {
         self.view = preferenceModifier.modifyPreferenceStore(&self.preferenceStore)
-        if let parent = parent {
-          parent.preferenceStore.merge(with: self.preferenceStore)
-        }
       }
 
       if let preferenceReader = self.view.view as? _PreferenceReadingViewProtocol {
diff --git a/Sources/TokamakCore/MountedViews/MountedElement.swift b/Sources/TokamakCore/MountedViews/MountedElement.swift
index 52959a0d4..70e52c92c 100644
--- a/Sources/TokamakCore/MountedViews/MountedElement.swift
+++ b/Sources/TokamakCore/MountedViews/MountedElement.swift
@@ -94,13 +94,9 @@ public class MountedElement<R: Renderer> {
 
   public internal(set) var environmentValues: EnvironmentValues
 
-  weak var parent: MountedElement<R>?
-  /// `didSet` on this field propagates the preference changes up the view tree.
-  var preferenceStore: _PreferenceStore = .init() {
-    didSet {
-      parent?.preferenceStore.merge(with: preferenceStore)
-    }
-  }
+  private(set) weak var parent: MountedElement<R>?
+
+  var preferenceStore: _PreferenceStore = .init()
 
   public internal(set) var viewTraits: _ViewTraitStore
 
@@ -110,6 +106,7 @@ public class MountedElement<R: Renderer> {
     self.environmentValues = environmentValues
     viewTraits = .init()
     updateEnvironment()
+    connectParentPreferenceStore()
   }
 
   init(_ scene: _AnyScene, _ environmentValues: EnvironmentValues, _ parent: MountedElement<R>?) {
@@ -118,6 +115,7 @@ public class MountedElement<R: Renderer> {
     self.environmentValues = environmentValues
     viewTraits = .init()
     updateEnvironment()
+    connectParentPreferenceStore()
   }
 
   init(
@@ -131,6 +129,7 @@ public class MountedElement<R: Renderer> {
     self.environmentValues = environmentValues
     self.viewTraits = viewTraits
     updateEnvironment()
+    connectParentPreferenceStore()
   }
 
   func updateEnvironment() {
@@ -145,6 +144,10 @@ public class MountedElement<R: Renderer> {
     }
   }
 
+  func connectParentPreferenceStore() {
+    preferenceStore.parent = parent?.preferenceStore
+  }
+
   /// You must call `super.prepareForMount` before all other mounting work.
   func prepareForMount(with transaction: Transaction) {
     // `GroupView`'s don't really mount, so let their children transition if the group can.
diff --git a/Sources/TokamakCore/Preferences/PreferenceKey.swift b/Sources/TokamakCore/Preferences/PreferenceKey.swift
index a9ddc22f9..d8ca9a658 100644
--- a/Sources/TokamakCore/Preferences/PreferenceKey.swift
+++ b/Sources/TokamakCore/Preferences/PreferenceKey.swift
@@ -48,10 +48,12 @@ public extension _PreferenceValue {
   }
 }
 
-public struct _PreferenceStore {
+public final class _PreferenceStore {
   /// The backing values of the `_PreferenceStore`.
   private var values: [String: Any]
 
+  weak var parent: _PreferenceStore?
+
   public init(values: [String: Any] = [:]) {
     self.values = values
   }
@@ -63,23 +65,12 @@ public struct _PreferenceStore {
       ?? _PreferenceValue(valueList: [Key.defaultValue])
   }
 
-  public mutating func insert<Key>(_ value: Key.Value, forKey key: Key.Type = Key.self)
+  public func insert<Key>(_ value: Key.Value, forKey key: Key.Type = Key.self)
     where Key: PreferenceKey
   {
     let previousValues = self.value(forKey: key).valueList
     values[String(reflecting: key)] = _PreferenceValue<Key>(valueList: previousValues + [value])
-  }
-
-  public mutating func merge(with other: Self) {
-    self = merging(with: other)
-  }
-
-  public func merging(with other: Self) -> Self {
-    var result = values
-    for (key, value) in other.values {
-      result[key] = value
-    }
-    return .init(values: result)
+    parent?.insert(value, forKey: key)
   }
 }
 
diff --git a/Sources/TokamakCore/StackReconciler.swift b/Sources/TokamakCore/StackReconciler.swift
index b4c9c4bac..71f84e1f5 100644
--- a/Sources/TokamakCore/StackReconciler.swift
+++ b/Sources/TokamakCore/StackReconciler.swift
@@ -58,6 +58,12 @@ public final class StackReconciler<R: Renderer> {
    */
   public let rootTarget: R.TargetType
 
+  /** A root renderer's main preference store.
+   */
+  public var preferenceStore: _PreferenceStore {
+    rootElement.preferenceStore
+  }
+
   /** A root of the mounted elements tree to which all other mounted elements are attached to.
    */
   private let rootElement: MountedElement<R>
diff --git a/Sources/TokamakCore/Views/Containers/Group.swift b/Sources/TokamakCore/Views/Containers/Group.swift
index 68d84606d..e27e2629c 100644
--- a/Sources/TokamakCore/Views/Containers/Group.swift
+++ b/Sources/TokamakCore/Views/Containers/Group.swift
@@ -19,7 +19,7 @@ public struct Group<Content> {
   }
 }
 
-extension Group: _PrimitiveView & View where Content: View {}
+extension Group: _PrimitiveView, View where Content: View {}
 
 extension Group: ParentView where Content: View {
   @_spi(TokamakCore)
diff --git a/Sources/TokamakStaticHTML/App.swift b/Sources/TokamakStaticHTML/App.swift
index 22b509099..c99e667bc 100644
--- a/Sources/TokamakStaticHTML/App.swift
+++ b/Sources/TokamakStaticHTML/App.swift
@@ -24,7 +24,7 @@ public extension App {
   }
 
   static func _setTitle(_ title: String) {
-    StaticHTMLRenderer.title = title
+    // no-op: use Title view
   }
 
   var _phasePublisher: AnyPublisher<ScenePhase, Never> {
diff --git a/Sources/TokamakStaticHTML/Preferences/Preferences.swift b/Sources/TokamakStaticHTML/Preferences/Preferences.swift
new file mode 100644
index 000000000..94d242785
--- /dev/null
+++ b/Sources/TokamakStaticHTML/Preferences/Preferences.swift
@@ -0,0 +1,37 @@
+// Copyright 2022 Tokamak contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//  Created by Andrew Barba on 5/20/22.
+//
+
+import TokamakCore
+
+public struct HTMLTitlePreferenceKey: PreferenceKey {
+  public static var defaultValue: String = ""
+
+  public static func reduce(value: inout String, nextValue: () -> String) {
+    value = nextValue()
+  }
+}
+
+public struct HTMLMetaPreferenceKey: PreferenceKey {
+  public static var defaultValue: [HTMLMeta.MetaTag] = []
+
+  public static func reduce(
+    value: inout [HTMLMeta.MetaTag],
+    nextValue: () -> [HTMLMeta.MetaTag]
+  ) {
+    value += nextValue()
+  }
+}
diff --git a/Sources/TokamakStaticHTML/StaticHTMLRenderer.swift b/Sources/TokamakStaticHTML/StaticHTMLRenderer.swift
index 6e393c346..d45d3acb3 100644
--- a/Sources/TokamakStaticHTML/StaticHTMLRenderer.swift
+++ b/Sources/TokamakStaticHTML/StaticHTMLRenderer.swift
@@ -58,18 +58,40 @@ struct HTMLBody: AnyHTML {
   ]
 }
 
+extension HTMLMeta.MetaTag {
+  func outerHTML() -> String {
+    switch self {
+    case let .charset(charset):
+      return #"<meta charset="\#(charset)">"#
+    case let .name(name, content):
+      return #"<meta name="\#(name)" content="\#(content)">"#
+    case let .property(property, content):
+      return #"<meta property="\#(property)" content="\#(content)">"#
+    case let .httpEquiv(httpEquiv, content):
+      return #"<meta http-equiv="\#(httpEquiv)" content="\#(content)">"#
+    }
+  }
+}
+
 public final class StaticHTMLRenderer: Renderer {
   private var reconciler: StackReconciler<StaticHTMLRenderer>?
 
   var rootTarget: HTMLTarget
 
-  static var title: String = ""
+  var title: String {
+    reconciler?.preferenceStore.value(forKey: HTMLTitlePreferenceKey.self).value ?? ""
+  }
+
+  var meta: [HTMLMeta.MetaTag] {
+    reconciler?.preferenceStore.value(forKey: HTMLMetaPreferenceKey.self).value ?? []
+  }
 
   public func render(shouldSortAttributes: Bool = false) -> String {
     """
     <html>
     <head>
-      <title>\(Self.title)</title>
+      <title>\(title)</title>
+      \(meta.map { $0.outerHTML() }.joined(separator: "\n  "))
       <style>
         \(tokamakStyles)
       </style>
diff --git a/Sources/TokamakStaticHTML/Views/Head/Meta.swift b/Sources/TokamakStaticHTML/Views/Head/Meta.swift
new file mode 100644
index 000000000..729a3c8f3
--- /dev/null
+++ b/Sources/TokamakStaticHTML/Views/Head/Meta.swift
@@ -0,0 +1,83 @@
+// Copyright 2022 Tokamak contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//  Created by Andrew Barba on 5/20/22.
+//
+
+import TokamakCore
+
+public struct HTMLMeta: View {
+  public enum MetaTag: Equatable, Hashable {
+    case charset(_ charset: String)
+    case name(_ name: String, content: String)
+    case property(_ property: String, content: String)
+    case httpEquiv(_ httpEquiv: String, content: String)
+  }
+
+  var meta: MetaTag
+
+  public init(_ value: MetaTag) {
+    meta = value
+  }
+
+  public init(charset: String) {
+    meta = .charset(charset)
+  }
+
+  public init(name: String, content: String) {
+    meta = .name(name, content: content)
+  }
+
+  public init(property: String, content: String) {
+    meta = .property(property, content: content)
+  }
+
+  public init(httpEquiv: String, content: String) {
+    meta = .httpEquiv(httpEquiv, content: content)
+  }
+
+  public var body: some View {
+    EmptyView()
+      .preference(key: HTMLMetaPreferenceKey.self, value: [meta])
+  }
+}
+
+public extension View {
+  func htmlMeta(_ value: HTMLMeta.MetaTag) -> some View {
+    htmlMeta(.init(value))
+  }
+
+  func htmlMeta(charset: String) -> some View {
+    htmlMeta(.init(charset: charset))
+  }
+
+  func htmlMeta(name: String, content: String) -> some View {
+    htmlMeta(.init(name: name, content: content))
+  }
+
+  func htmlMeta(property: String, content: String) -> some View {
+    htmlMeta(.init(property: property, content: content))
+  }
+
+  func htmlMeta(httpEquiv: String, content: String) -> some View {
+    htmlMeta(.init(httpEquiv: httpEquiv, content: content))
+  }
+
+  func htmlMeta(_ meta: HTMLMeta) -> some View {
+    Group {
+      self
+      meta
+    }
+  }
+}
diff --git a/Sources/TokamakStaticHTML/Views/Head/Title.swift b/Sources/TokamakStaticHTML/Views/Head/Title.swift
new file mode 100644
index 000000000..fb69b39b5
--- /dev/null
+++ b/Sources/TokamakStaticHTML/Views/Head/Title.swift
@@ -0,0 +1,44 @@
+// Copyright 2022 Tokamak contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//  Created by Andrew Barba on 5/20/22.
+//
+
+import TokamakCore
+
+public struct HTMLTitle: View {
+  var title: String
+
+  public init(_ title: String) {
+    self.title = title
+  }
+
+  public var body: some View {
+    EmptyView()
+      .preference(key: HTMLTitlePreferenceKey.self, value: title)
+  }
+}
+
+public extension View {
+  func htmlTitle(_ title: String) -> some View {
+    htmlTitle(.init(title))
+  }
+
+  func htmlTitle(_ title: HTMLTitle) -> some View {
+    Group {
+      self
+      title
+    }
+  }
+}
diff --git a/Tests/TokamakStaticHTMLTests/HTMLTests.swift b/Tests/TokamakStaticHTMLTests/HTMLTests.swift
index 724986ede..e28ac4f30 100644
--- a/Tests/TokamakStaticHTMLTests/HTMLTests.swift
+++ b/Tests/TokamakStaticHTMLTests/HTMLTests.swift
@@ -92,6 +92,120 @@ final class HTMLTests: XCTestCase {
         .render(shouldSortAttributes: true)
     assertSnapshot(matching: insecureHTML, as: .html)
   }
+
+  func testTitle() {
+    let resultingHTML = StaticHTMLRenderer(
+      VStack {
+        HTMLTitle("Tokamak")
+        Text("Hello, world!")
+      }
+    ).render(shouldSortAttributes: true)
+
+    assert(resultingHTML.contains("<title>Tokamak</title>"))
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testDoubleTitle() {
+    let resultingHTML = StaticHTMLRenderer(
+      VStack {
+        HTMLTitle("Tokamak 1")
+        Text("Hello, world!")
+        VStack {
+          HTMLTitle("Tokamak 2")
+        }
+      }
+    ).render(shouldSortAttributes: true)
+
+    assert(resultingHTML.contains("<title>Tokamak 2</title>") == true)
+    assert(resultingHTML.contains("<title>Tokamak 1</title>") == false)
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testTitleModifier() {
+    let resultingHTML = StaticHTMLRenderer(
+      Text("Hello, world!")
+        .htmlTitle("Tokamak")
+    ).render(shouldSortAttributes: true)
+
+    assert(resultingHTML.contains("<title>Tokamak</title>"))
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testDoubleTitleModifier() {
+    let resultingHTML = StaticHTMLRenderer(
+      Text("Hello, world!")
+        .htmlTitle("Tokamak 1")
+        .htmlTitle("Tokamak 2")
+    ).render(shouldSortAttributes: true)
+
+    assert(resultingHTML.contains("<title>Tokamak 2</title>") == true)
+    assert(resultingHTML.contains("<title>Tokamak 1</title>") == false)
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testMetaCharset() {
+    let resultingHTML = StaticHTMLRenderer(
+      VStack {
+        HTMLMeta(charset: "utf-8")
+        Text("Hello, world!")
+      }
+    ).render(shouldSortAttributes: true)
+
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testMetaCharsetModifier() {
+    let resultingHTML = StaticHTMLRenderer(
+      Text("Hello, world!")
+        .htmlMeta(charset: "utf-8")
+    ).render(shouldSortAttributes: true)
+
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testMetaAll() {
+    let resultingHTML = StaticHTMLRenderer(
+      VStack {
+        HTMLMeta(charset: "utf-8")
+        HTMLMeta(name: "description", content: "SwiftUI on the web")
+        HTMLMeta(property: "og:image", content: "https://image.png")
+        HTMLMeta(httpEquiv: "refresh", content: "60")
+        Text("Hello, world!")
+      }
+    ).render(shouldSortAttributes: true)
+
+    assert(resultingHTML.components(separatedBy: "<meta ").count == 5)
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
+
+  func testPreferencePropagation() {
+    var title0 = ""
+    var title1 = ""
+    var title2 = ""
+    var title3 = ""
+
+    let resultingHTML = StaticHTMLRenderer(
+      VStack {
+        HTMLTitle("Tokamak 1")
+          .onPreferenceChange(HTMLTitlePreferenceKey.self) { title1 = $0 }
+        VStack {
+          HTMLTitle("Tokamak 2")
+        }
+        .onPreferenceChange(HTMLTitlePreferenceKey.self) { title2 = $0 }
+        VStack {
+          HTMLTitle("Tokamak 3")
+        }
+        .onPreferenceChange(HTMLTitlePreferenceKey.self) { title3 = $0 }
+      }
+      .onPreferenceChange(HTMLTitlePreferenceKey.self) { title0 = $0 }
+    ).render(shouldSortAttributes: true)
+
+    assert(title0 == "Tokamak 3")
+    assert(title1 == "Tokamak 1")
+    assert(title2 == "Tokamak 2")
+    assert(title3 == "Tokamak 3")
+    assertSnapshot(matching: resultingHTML, as: .html)
+  }
 }
 
 #endif
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitle.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitle.1.html
new file mode 100644
index 000000000..a4d8c4d1a
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitle.1.html
@@ -0,0 +1,267 @@
+<html>
+<head>
+  <title>Tokamak 2</title>
+  
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span>
+<div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"></div></div></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitleModifier.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitleModifier.1.html
new file mode 100644
index 000000000..a826ace62
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testDoubleTitleModifier.1.html
@@ -0,0 +1,260 @@
+<html>
+<head>
+  <title>Tokamak 2</title>
+  
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaAll.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaAll.1.html
new file mode 100644
index 000000000..9d2102a2a
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaAll.1.html
@@ -0,0 +1,266 @@
+<html>
+<head>
+  <title></title>
+  <meta charset="utf-8">
+  <meta name="description" content="SwiftUI on the web">
+  <meta property="og:image" content="https://image.png">
+  <meta http-equiv="refresh" content="60">
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></div></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharset.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharset.1.html
new file mode 100644
index 000000000..59f905217
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharset.1.html
@@ -0,0 +1,263 @@
+<html>
+<head>
+  <title></title>
+  <meta charset="utf-8">
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></div></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharsetModifier.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharsetModifier.1.html
new file mode 100644
index 000000000..8297ce73a
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testMetaCharsetModifier.1.html
@@ -0,0 +1,260 @@
+<html>
+<head>
+  <title></title>
+  <meta charset="utf-8">
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testPreferencePropagation.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testPreferencePropagation.1.html
new file mode 100644
index 000000000..210487e0e
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testPreferencePropagation.1.html
@@ -0,0 +1,261 @@
+<html>
+<head>
+  <title>Tokamak 3</title>
+  
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"></div>
+<div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"></div></div></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitle.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitle.1.html
new file mode 100644
index 000000000..2ac7f16c5
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitle.1.html
@@ -0,0 +1,263 @@
+<html>
+<head>
+  <title>Tokamak</title>
+  
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
+
+
+"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></div></body>
+</html>
\ No newline at end of file
diff --git a/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitleModifier.1.html b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitleModifier.1.html
new file mode 100644
index 000000000..f1c761ce7
--- /dev/null
+++ b/Tests/TokamakStaticHTMLTests/__Snapshots__/HTMLTests/testTitleModifier.1.html
@@ -0,0 +1,260 @@
+<html>
+<head>
+  <title>Tokamak</title>
+  
+  <style>
+    ._tokamak-stack {
+  display: grid;
+}
+._tokamak-hstack {
+  grid-auto-flow: column;
+  column-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-vstack {
+  grid-auto-flow: row;
+  row-gap: var(--tokamak-stack-gap, 8px);
+}
+._tokamak-scrollview-hideindicators {
+  scrollbar-color: transparent;
+  scrollbar-width: 0;
+}
+._tokamak-scrollview-hideindicators::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+._tokamak-list {
+  list-style: none;
+  overflow-y: auto;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+._tokamak-disclosuregroup-label {
+  cursor: pointer;
+}
+._tokamak-disclosuregroup-chevron-container {
+  width: .25em;
+  height: .25em;
+  padding: 10px;
+  display: inline-block;
+}
+._tokamak-disclosuregroup-chevron {
+  width: 100%;
+  height: 100%;
+  transform: rotate(45deg);
+  border-right: solid 2px rgba(0, 0, 0, 0.25);
+  border-top: solid 2px rgba(0, 0, 0, 0.25);
+}
+._tokamak-disclosuregroup-content {
+  display: flex;
+  flex-direction: column;
+  margin-left: 1em;
+}
+._tokamak-buttonstyle-reset {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: transparent;
+  border: none;
+  margin: 0;
+  padding: 0;
+  font-size: inherit;
+}
+@supports (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    -webkit-appearance: default-button;
+  }
+}
+@supports not (-webkit-appearance: default-button) {
+  ._tokamak-button-prominence-increased {
+    background-color: rgb(55, 120, 246);
+    border: 1px solid rgb(88, 156, 248);
+    border-radius: 4px;
+  }
+  ._tokamak-button-prominence-increased:active {
+    background-color: rgb(38, 99, 226);
+  }
+
+  @media (prefers-color-scheme:dark) {
+    ._tokamak-button-prominence-increased {
+      background-color: rgb(56, 116, 225);
+    }
+    ._tokamak-button-prominence-increased:active {
+      background-color: rgb(56, 134, 247);
+    }
+  }
+}
+
+._tokamak-text-redacted {
+  position: relative;
+}
+._tokamak-text-redacted::after {
+  content: " ";
+  background-color: rgb(200, 200, 200);
+  position: absolute;
+  left: 0;
+  width: calc(100% + .1em);
+  height: 1.2em;
+  border-radius: .1em;
+}
+._tokamak-geometryreader {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-navigationview {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+}
+._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
+  padding-top: 50px;
+}
+._tokamak-navigationview-destination {
+  display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  flex-grow: 1;
+  height: 100%;
+}
+
+._tokamak-toolbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  background: rgba(200, 200, 200, 0.2);
+  -webkit-backdrop-filter: saturate(180%) blur(20px);
+  backdrop-filter: saturate(180%) blur(20px);
+}
+
+._tokamak-toolbar-content {
+  flex: 1 1 auto;
+  display: flex;
+  height: 100%;
+}
+._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
+  margin-right: 8px;
+}
+._tokamak-toolbar-trailing > * {
+  margin-left: 8px;
+}
+._tokamak-toolbar-leading {
+  margin-right: auto;
+  align-items: center;
+  justify-content: flex-start;
+  padding-left: 16px;
+}
+._tokamak-toolbar-center {
+  align-items: center;
+  justify-content: center;
+}
+._tokamak-toolbar-trailing {
+  margin-left: auto;
+  align-items: center;
+  justify-content: flex-end;
+  padding-right: 16px;
+}
+
+._tokamak-toolbar-button {
+  padding: 2px 4px;
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  height: 25px;
+  padding: 0 8px;
+  display: flex;
+  align-items: center;
+}
+._tokamak-toolbar-button:hover {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+._tokamak-toolbar-button:active {
+  border-color: transparent;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+._tokamak-toolbar-textfield input {
+  padding: 4px 4px 4px 8px;
+  border-radius: 3px;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-formcontrol, ._tokamak-formcontrol-reset {
+  color-scheme: light dark;
+}
+._tokamak-formcontrol-reset {
+  background: none;
+  border: none;
+}
+
+._tokamak-link {
+  text-decoration: none;
+}
+
+._tokamak-texteditor {
+  width: 100%;
+  height: 100%;
+}
+
+._tokamak-aspect-ratio-fill > img {
+  object-fit: fill;
+}
+
+._tokamak-aspect-ratio-fit > img {
+  object-fit: contain;
+}
+
+@media (prefers-color-scheme:dark) {
+  ._tokamak-text-redacted::after {
+    background-color: rgb(100, 100, 100);
+  }
+
+  ._tokamak-disclosuregroup-chevron {
+    border-right-color: rgba(255, 255, 255, 0.25);
+    border-top-color: rgba(255, 255, 255, 0.25);
+  }
+
+  ._tokamak-toolbar {
+    background: rgba(100, 100, 100, 0.2);
+  }
+  ._tokamak-toolbar-button {
+    border: 1px solid rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-button:hover {
+    background-color: rgba(255, 255, 255, 0.05);
+  }
+  ._tokamak-toolbar-button:active {
+    background-color: rgba(255, 255, 255, 0.1);
+  }
+  ._tokamak-toolbar-textfield input {
+    background-color: rgba(255, 255, 255, 0.05);
+    color: #FFFFFF;
+  }
+}
+  </style>
+</head>
+<body style="margin: 0;display: flex;
+width: 100%;
+height: 100%;
+justify-content: center;
+align-items: center;
+overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
+
+color: rgba(0.0, 0.0, 0.0, 1.0);
+font-style: normal;
+font-weight: 400;
+letter-spacing: normal;
+vertical-align: baseline;
+text-decoration: none;
+text-decoration-color: inherit;
+text-align: left;">Hello, world!</span></body>
+</html>
\ No newline at end of file