diff --git a/src/Util/HTML.php b/src/Util/HTML.php
new file mode 100644
index 0000000000..c296895ddb
--- /dev/null
+++ b/src/Util/HTML.php
@@ -0,0 +1,91 @@
+.
+// }}}
+
+namespace App\Util;
+
+use Functional as F;
+
+/**
+ * HTML Abstraction
+ *
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+abstract class HTML
+{
+ /**
+ * Creates an HTML tag without attributes
+ *
+ * @param array|string $content
+ */
+ private static function tag(string $tag, $content = '', bool $empty_tag = false): array
+ {
+ return self::attr_tag($tag, '', $content);
+ }
+
+ /**
+ * Create tag, possibly with attributes and indentation
+ *
+ * @param array|string $attrs - element attributes
+ * @param array|string $content - what goes inside the tag
+ */
+ private static function attr_tag(string $tag, $attrs, $content = '', bool $empty_tag = false): array
+ {
+ $html = '<' . $tag . (is_string($attrs) ? ($attrs ? ' ' : '') . $attrs : self::attr($attrs));
+ if (empty($content) || $empty_tag) {
+ $html .= '>';
+ } else {
+ $html .= ">\n" . Common::indent($content) . "\n" . Common::indent("{$tag}>");
+ }
+ return explode("\n", $html);
+ }
+
+ /**
+ * Attribute with given optional value
+ *
+ * @param array $attr
+ */
+ private static function attr(array $attrs): string
+ {
+ return ' ' . implode(' ', F\map($attrs, function ($val, $key, $_) { return "{$key} = '{$val}'"; }));
+ }
+
+ public static function html($html): string
+ {
+ if (is_string($html)) {
+ return $html;
+ } elseif (is_array($html)) {
+ $out = '';
+ foreach ($html as $tag => $contents) {
+ if (isset($contents['empty'])) {
+ $out .= "<{$tag}>";
+ } else {
+ $attrs = isset($contents['attrs']) ? self::attr(array_shift($contents)) : '';
+ $is_tag = preg_match('/[A-Za-z][A-Za-z0-9]*/', $tag);
+ $inner = self::html($contents);
+ $inner = $is_tag ? Common::indent($inner) : $inner;
+ $out .= $is_tag ? "<{$tag}{$attrs}>\n{$inner}\n{$tag}>\n" : $inner;
+ }
+ }
+ return $out;
+ } else {
+ return '';
+ }
+ }
+}