Building a Better Social Media Post with Semantic HTML
I’m trying to push back against an ongoing trend in web development to over-complicate… well… everything.
As a web development nerd, I’m often interested in stuff that would probably never cross most people’s minds. Like, for instance, the front-end code that’s used by social media platforms.
Although I don’t work on anything close to the scale of a social media platform, I’m nevertheless curious about how they do things, and what I can learn from them. With their massive budgets and large teams, not to mention global user bases, how do they write and architect their code? What sort of HTML and CSS do they use? (For what it’s worth, I’m not the only one who asks such questions.)
Now that Threads has a web interface, I took a peek “under the hood” of both Threads and Bluesky, arguably the two biggest Twitter alternatives out there. (Especially since Pebble is no more.) What I saw left me scratching my head.
Consider these sample posts from my profiles on Threads and Bluesky. (Note: I’ve removed the posts’ content in order to just focus on their HTML structure.) Here’s what a standard Threads post looks like:
Bluesky’s markup is similarly convoluted:
Both posts contain aspects of tag soup along with an Inception-esque nesting of <div> and <span> elements filled with inline styles and odd-looking classes like x78zum5, x1b1mbwd, css-175oi2r, and r-1udh08x.
These approaches obviously work. The posts load in a browser, you can read them, and they’re interactive (e.g., you can like and share them). Which is the whole point. As for those weird class names — which are most likely the result of the markup being compiled in a build process — they serve several purposes: preventing code conflicts (which are a definite possibility when you have multiple teams working on the same codebase) and foiling ad blockers.
Setting all that aside, however, and just looking at the posts’ code as code, it does make one’s eyes bleed a bit. Functional though it may be, no one would ever accuse it of being elegant or efficient.
An Elegant Social Media Post
So what might an elegantly coded social media post look like? “Elegant” is subjective, but for my purposes, it means a post written with HTML that’s both semantically correct and semantically rich while using the bare minimum of code necessary to be usable. (I’m a firm proponent of using the right HTML element for the right job while using as little HTML as possible overall.)
After several iterations, here’s what I’ve settled on:
Let’s break it down. First, I’m using <article> for the root element of the entire post, and as such, it has a post class. According to MDN Web Docs, the <article> element “represents a self-contained composition in a document, page, application, or site, which is intended to be independently distributable or reusable” — which sounds a lot like a social media post to me. The <article> is subsequently broken into three main sections or areas.
First is postHeader, which uses a <header> element since it’s, well, the post’s header. It contains information about the post’s author (e.g., their avatar and username) and a relative timestamp for when the post was published.
Second is postContent, which, as the name suggests, contains all of the post’s content, be it text, images, video, or embedded posts (i.e., reposts). It uses a generic <div> element. You may be thinking that I should’ve used the <main> element instead, but as MDN puts it, the <main> element “represents the dominant content of the <body> of a document,” which would probably never be the content of a single post. More likely, <main> would be a feed of posts, or perhaps an article on another site into which the post was embedded.
Finally, there’s postFooter, which uses a <footer> element since it’s, well, the post’s footer. It contains the full publication date as well as a list of available actions for the post (e.g., commenting, liking).
Some Additional Notes
In an earlier version of my post mockup, I wrapped the list of post actions in a <nav> element. Upon further reflection, though, I removed the <nav> element because the post actions aren’t really “navigation” items; they don’t necessarily take you somewhere else. Rather, they trigger functionality, such as firing off a request to the “like” API or toggling a “reposting” menu.
This is also why the actions use <button> rather than <a> elements or <div> elements with role="button" tacked on. Generally speaking, buttons trigger functionality, like submitting a form or launching a modal, while links take you somewhere else. (For more on this, I recommend Ashlee M Boyer’s exhaustive breakdown of when and how to use <button> and <a> elements.) Plus, using <button> elements confers some accessibility bonuses, including keyboard navigation and screenreader notifications.
All dates and timestamps are wrapped inside <time> elements with the datetime attribute specified. This ensures that the dates are machine-readable, which is useful for search engines. (CSS Tricks has a more detailed breakdown of the <time> element’s purpose and benefits.)
You’ll notice several odd-looking classes sprinkled throughout the HTML, including dt-published, e-content, h-entry, and u-photo. These are microformats that add structured data to the post, which makes it more consumable by other services (e.g., search engines) and brings it inline with IndieWeb and POSSE philosophies. (James Gallagher explains why microformats are good thing.) Microformats aren’t necessary, but given how siloed social media has become, I wanted my mockup to embrace as much openness and interoperability as possible from the get-go.
Allow me to now state the obvious: the folks who work on Threads and Bluesky are really smart, and undoubtedly face some unique challenges while building and maintaining platforms that are used by millions of users every single day. (As of right now, Threads has an estimated 141 million users and Bluesky has 2.26 million users.) Nothing I’ve written here is intended to disparage their work. Also, my mockup lacks any of the interactivity that’s a given with such platforms, which necessarily adds a layer or two of complexity.
That said, I’m trying to push back against an ongoing trend in web development to over-complicate… well… everything. Obviously, web applications like social media platforms are, by their very nature, really complex. But the basic building blocks of web development are still what they are, which is easy to forget as new tools, frameworks, and build processes emerge and gain traction. Sometimes it’s just too easy to write a mess of HTML, CSS, and JS — or rather, have a build process generate it for you — when a handful of standard, semantically rich HTML will do just as well, if not better.
Threads and Bluesky’s convoluted code isn’t too surprising when you consider how so much of web development happens these days. But it doesn’t have to be that way. You don’t need a dozen nested layers of <div> elements, each with a glut of abstracted CSS classes, to display text and images — which is essentially what social media posts do. In addition, less code means there’s less to load in a browser (resulting in improved performance) and less to support (resulting in more efficient development and troubleshooting). And as an added bonus, using semantic HTML is an easy way to ensure accessibility.
While tools, frameworks, and build processes can certainly improve some aspects of web development, they present challenges of their own. Specifically, determining how to best rein in their excesses to allow room for simpler approaches and techniques that can work just as well — and with fewer nested <div> elements, to boot.
Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.