Very Semantic Html
20 Jul 2024 - Ben
Semantic HTML can be a very charged topic. A quick search on the internet will yield plenty of results about how you should use each tag. Some of them should be pretty much common sense. nav
wraps navigation elements. main
is your main content on the page. header
, footer
are for their respective parts. But div
, article
, and section
have different nuances for different people.
This article is about taking this idea one step further: what if we could make our tags have even more semantic meaning? What if the markup could have semantic meaning for the project we’re in? What if we could model the domain as part of our markup? You want to display an Avatar? Use this Avatar
component. But a single component isn’t always enough. Here are all of the pieces of our Avatar components, use what you need. It has a Root container, a Face, Initials to fallback to, and a Popover that we sometimes include for more information.
Or for form input: this EmailCollection
object holds everything we typically use to request an email. The Root, Label, and Inputs hold all the styling an info to make it easy for you to build one. Need to add something extra? Just add it to the label, or after the label, or wherever you need it. We could write
<EmailCollection.Root>
<EmailCollection.Label>
Please enter your email:
</EmailCollection.Label>
<EmailCollection.Input />
</EmailCollection.Root>
and have that incorporate any styles and business logic related to collecting emails. In raw html, the best I could think of would be something like a BEM-inspired:
<div class="email-collection">
<label class="email-collection--label">
Please enter your email
</label>
<input class="email-collection--input" type="email" name="email" placeholder="ben@example.com">
</div>
which is still pretty good and readable, although it doesn’t have any behavior connected to it. But more importantly, where does that live that it can be reused? How do I know to reach for that markup when I want to request an email input? My EmailCollection
component above is a React component that I can easily import and reuse wherever need be. It’s easy to find in a code base. I know I’m not missing anything about how my company wants email collection to look or behave. The element might literally be the same markup, but semantically I can speed read a page or larger component and easily pick out the email collection components. BEM makes this easier in the html example, but I estimate that it’s more likely to be tailwind utility classes in many companies.
Most templating systems can import/include other templates. But file-based components mean utilities, types, children, and general extras need to live in a different place. I can’t have one object of related tags to build with. It’s tough to build the behavior into these templates. How do I build the same level of flexibility into these templating systems? In Nunjucks, the closest I can get is
{% extends "email-collection.njk" %}
{% block label %}{{super()}} Please enter your email{% endblock %}
{% block input %}{{super()}}{% endblock %}
or maybe something involving macros. But both are whole different files for every new usage. There’s no concept of children or nesting after in includes, very different from html. I’m primarily a JS/TS dev (front- and back-end), are there other templating systems that could allow for something like that?
I’ve been thinking about using Custom Elements for this concept, but those don’t translate down to the raw html that I want. Ultimately I would love to just serve the raw html. Maybe I could use custom elements to define the behavior, but I would want to upgrade some raw markup with the custom element. Otherwise, I’m not sure what I’ve gained over React (other than being a little counter culture and losing a dependency), but I know I’ve still lost all the reactivity that makes React so popular and still a lot of the flexibility of their component system.
Reminder, I’m primarily a JS/TS developer, so I got to wondering if there were a html tagged template literal that could be used as a rendering engine in express, maybe that would afford me the flexibility to encapsulate some behavior, define them flexibly, define children, etc. Or maybe I’m over thinking why file-based templating is really such a problem. Template fragments might help me extract parts of a page, but they don’t help me make the templates I write more semantic; have semantic meaning in the project I’m writing for.
Maybe there’s a JSX-based, backend templating system that doesn’t rely on React or try to mimic React that I could use. It’d be a great gateway system to being able to build my markup the way I want but still be able to transmit raw html over the wire. I did find an article or two about it, but nothing with much steam.
Lots of questions and ideas, no good answers from me today! Why do I want this? I want to build server driven systems using the most powerful transfer markup, HTML! Modern browsers give us so much goodness, and backends and client experiences can be shifted so much easier when you’re only building one place. There’s lots of reasons, basically. But I haven’t found a templating system that makes me feel more productive in yet.
In conclusion, I realize I have a lot of requirements for a templating system! I really like how semantic React allows my markup to become. I want to use a server templating system and just deliver html, but I’m not as happy with the templating options I’ve found. I’ll leave a list of requirements I’ve picked out in a list here at the bottom:
Requirements for building very semantic html
from most to least important:
- easily and flexbily create my own naming conventions, that mostly thin-wrap actual html components
- output raw html
- write in a way that allows for children within the templating spec
- be able to pass attributes (and maybe properties) to each child independently
- be able to define and export multiple components from a single file
nice to have:
- include how a component behaves within the definition (likely include some js, then)
- collect related components into single object
- stretch: include some css that could get translated directly into the head? Maybe this one is too framework-specific. It might be nice, but definitely not a requirement.