Quarto blogs have built-in support for comments via several mechanisms, but not (yet) for Webmentions. Supporting Webmentions allows for additional interactivity from around the web by aggregating comments, replies and likes on a post from other sources. With Webmention support, Quarto blogs can interoperate with the broader indieweb! Fortunately, it’s not too complex to add this support yourself.
Webmentions for Quarto require a few components:
- A webmention endpoint: This is the service that receives requests to post a Webmention to a specific URL. I use webmention.io.
- A designation within given posts that identify their content as possible Webmention targets
- Code that displays Webmentions on a given page
The bits below in my site’s _quarto.yml
assemble some of the pieces:
format:
html:
include-in-header:
- text: <link rel="stylesheet" href="https://alanschussman.com/datablog/webmention-io.css">
- text: <span class="syndications"></a><link rel="webmention" href="https://webmention.io/alanschussman.com/webmention" /></span>
- text: <link rel="me" href="https://github.com/ats">
include-before-body:
- text: <div class="h-entry e-content entry-content">
include-after-body:
- text: </div> <!-- end h-entry and e-content -->
- file: webmention-insert.html
format:
html:
include-in-header:
- text: <link rel="stylesheet" href="https://alanschussman.com/datablog/webmention-io.css">
- text: <span class="syndications"></a><link rel="webmention" href="https://webmention.io/alanschussman.com/webmention" /></span>
- text: <link rel="me" href="https://github.com/ats">
include-before-body:
- text: <div class="h-entry e-content entry-content">
include-after-body:
- text: </div> <!-- end h-entry and e-content -->
- file: webmention-insert.html
In the include-in-header
section, I reference a new stylesheet for the webmentions, and I identify the webmention.io service as my endpoint service.
The include-before-body
and include-after-body
statements serve to wrap each post on the blog in a new HTML div with a specific microformats h-entry. This is necessary for webmention services to be able to identify content that can receive a mention. (Note that this is just about the most minimal possible enablement for webmentions: There’s a whole range of microformats that enhance the way indieweb tools parse and display content, and this is a just barely-viable implementation.)
Finally, I include a file, webmention-insert.html
, after the body of each post. This file contains a bit of Javascript to retrieve and parse mentions for the given URL, and then format that output at the bottom of the post. I’m including that code here. I wish I could remember the lineage of this code: I’ve been tending it along a couple-three blog generations and know that some of it comes from authors around the Webmention or Micro.blog communities.
<div class="mentionsio">
<ul id="facepile">
</ul>
<ul id="reposts">
</ul>
<ul id="mentions">
</ul>
</div>
<script type="text/javascript">
var url_string = window.location.href;
const facepile = document.getElementById('facepile');
const ul = document.getElementById('mentions');
const reposts = document.getElementById('reposts');
const wm = 'https://webmention.io/api/mentions?target='
const fetchargs = '&per-page=200&sort-dir=up';
let fetchurl = `${wm}${url_string}${fetchargs}`;
let facelist = "";
let item = "";
let content = "";
let repostlist = "";
fetch(fetchurl)
.then((response) => response.json())
.then(function(data) {
let links = data.links;
return links.map(function(link) {
// validate some json
let pubdate = "∞";
let content = "";
if (link.data.published) { pubdate += new Date(link.data.published) } else if (link.verified_date) { pubdate += new Date(link.verified_date) }
if (link.data.name) { content = link.data.name } else {content = link.data.content }
// do reply/link/bookmark
if ((link.activity.type == "reply") || (link.activity.type == "link") || (link.activity.type == "bookmark") || (link.activity.type == null)) {
if (item == "") { ul.innerHTML = "<li id=\"rotated\">COMMENTS &<br /> MENTIONS"; }
= `<li class="p-comment h-cite comment"><span class="p-author h-card"><a class="p-name u-url" href="${link.data.author.url}"><img class="img-mention u-photo" style="margin-right: 10px;" src="${link.data.author.photo}" alt="${link.data.author.name} - avatar picture" > ${link.data.author.name}</a></a> <a href="${link.data.url}" class="pubdate">${pubdate}</a><div class="mentionscontent"><p>${content}</p> </div><br clear=left /> `;
item .innerHTML += item;
ul
}// do repost
if (link.activity.type == "repost") {
if (repostlist == "") {
.innerHTML = "<li id=\"rotated\">REPOSTS";
reposts
}= `<li class="p-repost h-cite"><a href="${link.data.url}" class="u-url"><span class="p-author h-card"><img class="img-mention u-photo" src="${link.data.author.photo}" alt="avatar image for ${link.data.author.name}"></span></a>\n`;
repostlist .innerHTML += repostlist;
reposts
}// do likes
if (link.activity.type == "like") {
if (facelist == "") { facepile.innerHTML = "<li id=\"rotated\">LIKES &<br /> FAVES"; }
= `<li class="p-repost h-cite"><a href="${link.data.url}" class="u-url"><span class="p-author h-card"><img class="img-mention u-photo" src="${link.data.author.photo}" alt="avatar image for ${link.data.author.name}"></span></a>\n`;
facelist .innerHTML += facelist;
facepile
}
})document.write(facelist);
;
})</script>
Receiving Webmentions
With the above code, a Quarto blog is wired to display Webmentions, and so now I need to establish a way for my blog to receive them. Put differently, your blog posts need to exist somewhere that Webmentions can be created. There are lots of ways to do this. Here are four. I’m stopping short of a full tutorial here, but am happy to elaborate if someone starts to walk through this and gets stuck:
- Syndicate your blog using Micro.blog: If you use Micro.blog, you can add your Quarto blog’s RSS feed to your account, and then Micro.blog will syndicate your posts automatically. Any reply to your post on the Micro.blog timeline will be treated as a Webmention. Micro.blog can even automatically cross-post to other services like Mastodon; and if you use brid.gy, discussion from those services can be treated like Webmentions![^bridgy]
Use brid.gy to get Webmentions from social media and other platforms: When Brid.gy recognizes a reply to a post that it is tracking as syndicated from your own site, it handles the Webmention activity that the host site doesn’t innately perform, pinging your Webmention endpoint on your behalf. How does it know? You tell Brid.gy about your social media and other platform accounts1, and it periodically polls them for updates; when you post a link from the former to something on your blog, Brid.gy detects it and treats it as a syndicated post, and then sends a mention when it sees like and reply activity. This allows for creating Webmentions from Mastodon, GitHub, Flickr, Bluesky, and more.
Add syndication links to specific posts. This is a little more fiddly, and also relies on brid.gy: Imagine you don’t want to syndicate via a link back to your site, but still want to enable mentions (this is a more common scenario with micro blogging posts). You just make your post to the third-party site, and then place the permalink of that post into the header yaml of your Quarto blog post, like:
format:
html:
include-before-body:
- text: '<a class="u-syndication" style="display: none" href="https://social.lol/@alans/[..rest of my permalink..]">https://social.lol/@alans/[..rest of my permalink..]</a>'
Then you can tell brid.gy about the original post, and when it finds the u-syndication
link in it, it will watch that URL for replies and other activity that it can treat as Webmentions.
- Other indieweb folks can craft posts that cite or respond to your blog. This is truly the mode of hand-crafted, bespoke web content, and the detail is a little beyond the scope of this already-unwieldy blog post, but the gist is: Any web page can be a Webmention! It just needs a link to a second URL and optionally some specific tags that identify it as being a reference type like a reply or like. You can send an http request to the host blog’s webmention endpoint (like an address at webmention.io) via a shortcut, bookmark, CLI app or shell command to ping
target
frommention
like:curl -i -d source=[mention] -d target=[target] https://webmention.io/[webmention_endpoint]
, and if the webmention receiver detects a link to the specified target, it processes and stores the mention.
If you made it this far, you just might be interested in hooking up your own Quarto blog to the indieweb using Webmentions. If you do, consider sending a reply via Webmention! Happy Quarto-blogging, data friends.