Tag Builder
What It Is
A C# class for creating HTML tags and structures in codeStatus
I haven’t used it much, honestly. The goal was to provide a way to return small snippets of HTML (for use with something like HTMX) without having to have a separate template. I think I’ll use it in the future.
(I realized there’s a similar class built into Nemmet. I need to consolidate these at some point. There’s a lot of overlap between these two libraries.)
Details
A simple class that allows you to create HTML tags from C# much like the XElement
class. You can nest them in constructors, and assign names, IDs, and classes using an Emmet-style shorthand.
Tag
automatically converts to string
. Passing it as a string will do the same thing as calling ToString()
on it.
Basic usage:
var tag = new Tag("p");
var html = tag.ToString();
// html will equal "<p></p>"
var tag = new Tag("p", "Deane was here");
// <p>Deane was here</p>
var tag = new Tag("p#lede", "Deane was here");
// <p id="lede">Deane was here</a>
var tag = new Tag("p#lede.small-caps", "Deane was here");
// <p id="lede" class="small-caps">Deane was here</a>
You can nest tags in the constructor (theoretically infinitely).
var tag = new Tag("p", "Deane was here", new Tag("br"));
// <p>Deane was here<br/></a>
var tag = new Tag("p",
new Tag("ul",
children.Select(c => new Tag("li", c))
)
)
// <p><ul><li>Alec</li><li>Gabby</li><li>Bells</li></ul></p>
It’s fluent-ish…
var tag = new Tag("p")
.SetContent("Deane was here")
.AddStyle("display", "none")
.AddClass("foo")
.AddClass("bar")
// <p class="foo bar" style="display:none;">Deane was here</p>
var tag = new Tag("img")
.SetAttribute("src", "/image.jpg")
// <img src="/image.jpg" />
I tend to subclass this for common tags. Since the tag name is now known, the “tag spec” becomes less important and can be optional.
public class Link : Tag
{
public Link(string text, string url, string spec = null) : base(spec, text)
{
Name = "a";
SetAttribute("href", url);
}
}
var link = new Link("Home", "/");
var link = new Link("Home", "/", ".home-link");
I made some shortcuts for setting attributes and content.
After the tag specification, you can pass in a series of attribute/values.
input name=foo value=bar type=text
You don’t need a quote around the values, unless the value has a space. Values which contain a space can be enclosed in quotes:
input name=foo type=text placeholder="Enter something here…"
Content for a tag is placed in quotes:
p.foo "This is the content"
Or you can precede it with a colon (:
) as the last parameter:
p.foo :This is the content
Finally, there’s a static method – Tag.ParseAsTags
– that will parse a series of lines, each as a different tag spec, and correctly organize them in parent/child relationships, based on indents.
header
h1 :My Website
article
p.first :Welcome to my website!
img src=me.jpg
p "I'm glad you're here."
footer
small "Copyright 2025"
This will return an IEnumerable<Tag>
of the three top-level tags: header
, article
, and footer
. Each of them will have the indented tags as correctly nested descendants.
Be consistent with indents. You can indent in whatever style you want, but always use a single tab, or always use two spaces, or whatever. If you’re inconsistent, weird things will happen.
(Specifically, when you have to “back up” an indent level – like when the article
line realizes it has less of an indent than the line before it – will will look for the last line at it’s same indent level to figure out where to put itself. If your indents aren’t consistent, it will put itself in the wrong place.)
There is no automation of this syntax, but I have used Fluid to automate it. For example.
header
h1 :My Website
article
p.first :Welcome to my website!
ul
{% for i in item %}
li
a href={{ i.url }} :{{ i.title }}
{% endfor %}
footer
small "Copyright 2025"
I run that through Fluid, then pass it to Tag.ParseAsTags
.
The Fluid looping constructs execute and leave blank lines in their place, which are simply ignored when processing for tags.