Am I on the IndieWeb yet?

Can’t smash the Like button hard enough for what Miriam Suzanne has to say on the challenging technical hurdles of implementing Webmentions:

The first round required several online services along with HTML & JS changes to my static site, just to verify my indieweb identity. Then more changes to the site and more online services to help fetch any mentions (so far, nothing to see, but that’s probably expected). It seems the only way to test the setup is to launch all those changes publicly, and then ask for other devs to send you mentions.

[…]

I’m an experienced web developer, and I can figure it out. But the steps aren’t simple, and most of my friends are not web developers. So, to me, this all feels like the prototype of an idea – a proof of concept.

A proof of concept for sure. And one that has been around for quite some time. The IndieWeb idea of owning your own data and using your website as a social hub is right up my alley — and likely yours, too, as someone working on the front end.

Yet, I’ve tinkered on and off with it — specifically Webmentions — over the past like three years with little to show for it. The problem isn’t so much the documentation of getting started because it’s all there. It’s more a combination of things…

  • The wiki is confusing. Wikis are super cool in general, but the non-linear nature of it makes it tough to know where to start and where to end.
  • The plugin ecosystem is complex. My personal site is on WordPress and there’s a plugin designed to make it easy to integrate IndieWeb features on it. Except that it’s really one plugin that steers you to install several others, each one introducing a technology that I honestly struggle to understand.
  • There’s a bunch of terms to learn. I mean, “IndieWeb” and “Webmention” are already difficult to grok. Toss in things like “Micropub,” “Microformats,” “IndieAuth,” and “Semantic Linkbacks,” and suddenly it feels like a bunch of puzzle pieces from different puzzles trying to fit together.
  • Some middleware seems necessary? For example, I had to give a service called Bridgy access to my Twitter to get that activity going. It apparently has something to do with Twitter’s shortened t.co URLs and making them play well with microformats.

But, like Miriam, I struggled my way through it and got something working in the end. This is the sort of visual I wish I had when I was first getting started, and maybe it’ll help you too.

Diagram of IndieWeb integration on a WordPress site.

Feels like a lot to get Webmentions going, but maybe that’s only because I have such a light grasp of the tech and how it all fits together. All the pieces are there, though, and even with the initial struggle, I love the IndieWeb concept, er prototype.

To Shared LinkPermalink on CSS-Tricks


Am I on the IndieWeb yet? originally published on CSS-Tricks. You should get the newsletter.

Jumping Into Webmentions With NextJS (or Not)

Webmention is a W3C recommendation last published on January 12, 2017. And what exactly is a Webmention? It’s described as…


[…] a simple way to notify any URL when you mention it on your site. From the receiver’s perspective, it’s a way to request notifications when other sites mention it.

In a nutshell, it’s a way of letting a website know it has been mentioned somewhere, by someone, in some way. The Webmention spec also describes it as a way for a website to let others know it cited them. What that basically bails down to is that your website is an active social media channel, channeling communication from other channels (e.g. Twitter, Instagram, Mastodon, Facebook, etc.).

How does a site implement Webmentions? In some cases, like WordPress, it’s as trivial as installing a couple of plugins. Other cases may not be quite so simple, but it’s still pretty straightforward. In fact, let’s do that now!

Here’s our plan

  1. Declare an endpoint to receive Webmentions
  2. Process social media interactions to Webmentions
  3. Get those mentions into a website/app
  4. Set the outbound Webmentions

Luckily for us, there are services in place that make things extremely simple. Well, except that third point, but hey, it’s not so bad and I’ll walk through how I did it on my own atila.io site.

My site is a server-side blog that’s pre-rendered and written with NextJS. I have opted to make Webmention requests client-side; therefore, it will work easily in any other React app and with very little refactoring in any other JavaScript application.

Step 1: Declare an endpoint to receive Webmentions

In order to have an endpoint we can use to accept Webmentions, we need to either write the script and add to our own server, or use a service such as Webmention.io (which is what I did).

Webmention.io is free and you only need to confirm ownership over the domain you register. Verification can happen a number of ways. I did it by adding a rel="me" attribute to a link in my website to my social media profiles. It only takes one such link, but I went ahead and did it for all of my accounts.

<a
  href="https://twitter.com/atilafassina"
  target="_blank"
  rel="me noopener noreferrer"
>
  @AtilaFassina
</a>

Verifying this way, we also need to make sure there’s a link pointing back to our website in that Twitter profile. Once we’ve done that, we can head back to Webmention.io and add the URL.

This gives us an endpoint for accepting Webmentions! All we need to do now is wire it up as <link> tags in the <head> of our webpages in order to collect those mentions.

<head>
  <link rel="webmention" href="https://webmention.io/{user}/webmention" />
  <link rel="pingback" href="https://webmention.io/{user}/xmlrpc" />
  <!-- ... -->
</head>

Remember to replace {user} with your Webmention.io username.

Step 2: Process social media interactions into Webmentions

We are ready for the Webmentions to start flowing! But wait, we have a slight problem: nobody actually uses them. I mean, I do, you do, Max Böck does, Swyx does, and… that’s about it. So, now we need to start converting all those juicy social media interactions into Webmentions.

And guess what? There’s an awesome free service for it. Fair warning though: you’d better start loving the IndieWeb because we’re about to get all up in it.

Bridgy connects all our syndicated content and converts them into proper Webmentions so we can consume it. With a SSO, we can get each of our profiles lined up, one by one.

Step 3: Get those mentions into a website/app

Now it’s our turn to do some heavy lifting. Sure, third-party services can handle all our data, but it’s still up to us to use it and display it.

We’re going to break this up into a few stages. First, we’ll get the number of Webmentions. From there, we’ll fetch the mentions themselves. Then we’ll hook that data up to NextJS (but you don’t have to), and display it.

Get the number of mentions

type TMentionsCountResponse = {
  count: number
  type: {
    like: number
    mention: number
    reply: number
    repost: number
  }
}

That is an example of an object we get back from the Webmention.io endpoint. I formatted the response a bit to better suit our needs. I’ll walk through how I did that in just a bit, but here’s the object we will get:

type TMentionsCount = {
  mentions: number
  likes: number
  total: number
}

The endpoint is located at:

https://webmention.io/api/count.json?target=${post_url}

The request will not fail without it, but the data won’t come either. Both Max Böck and Swyx combine likes with reposts and mentions with replies. In Twitter, they are analogous.

const getMentionsCount = async (postURL: string): TMentionsCount => {
  const resp = await fetch(
    `https://webmention.io/api/count.json?target=${postURL}/`
  )
  const { type, count } = await resp.json()


  return {
    likes: type.like + type.repost,
    mentions: type.mention + type.reply,
    total: count,
  }
}

Get the actual mentions

Before getting to the response, please note that the response is paginated, where the endpoint accepts three parameters in the query:

  • page: the page being requested
  • per-page: the number of mentions to display on the page
  • target: the URL where Webmentions are being fetched

Once we hit https://webmention.io/api/mentions and pass the these params, the successful response will be an object with a single key links which is an array of mentions matching the type below:

type TMention = {
  source: string
  verified: boolean
  verified_date: string // date string
  id: number
  private: boolean
  data: {
    author: {
      name: string
      url: string
      photo: string // url, hosted in webmention.io
    }
    url: string
    name: string
    content: string // encoded HTML
    published: string // date string
    published_ts: number // ms
  }
  activity: {
    type: 'link' | 'reply' | 'repost' | 'like'
    sentence: string // pure text, shortened
    sentence_html: string // encoded html
  }
  target: string
}

The above data is more than enough to show a comment-like section list on our site. Here’s how the fetch request looks in TypeScript:

const getMentions = async (
  page: string,
  postsPerPage: number,
  postURL: string
): { links: TWebMention[] } => {
  const resp = await fetch(
    `https://webmention.io/api/mentions?page=${page}&per-page=${postsPerPage}&target=${postURL}`
  )
  const list = await resp.json()
  return list.links
}

Hook it all up in NextJS

We’re going to work in NextJS for a moment. It’s all good if you aren’t using NextJS or even have a web app. We already have all the data, so those of you not working in NextJS can simply move ahead to Step 4. The rest of us will meet you there.

As of version 9.3.0, NextJS has three different methods for fetching data:

  1. getStaticProps: fetches data on build time
  2. getStaticPaths: specifies dynamic routes to pre-render based on the fetched data
  3. getServerSideProps: fetches data on each request

Now is the moment to decide at which point we will be making the first request for fetching mentions. We can pre-render the data on the server with the first batch of mentions, or we can make the entire thing client-side. I opted to go client-side.

If you’re going client-side as well, I recommend using SWR. It’s a custom hook built by the Vercel team that provides good caching, error and loading states — it and even supports React.Suspense.

Display the Webmention count

Many blogs show the number of comments on a post in two places: at the top of a blog post (like this one) and at the bottom, right above a list of comments. Let’s follow that same pattern for Webmentions.

First off, let’s create a component for the count:

const MentionsCounter = ({ postUrl }) => {
  const { t } = useTranslation()
  // Setting a default value for `data` because I don't want a loading state
  // otherwise you could set: if(!data) return <div>loading...</div>
  const { data = {}, error } = useSWR(postUrl, getMentionsCount)


  if (error) {
    return <ErrorMessage>{t('common:errorWebmentions')}</ErrorMessage>
  }


  // The default values cover the loading state
  const { likes = '-', mentions = '-' } = data


  return (
    <MentionCounter>
      <li>
        <Heart title="Likes" />
        <CounterData>{Number.isNaN(likes) ? 0 : likes}</CounterData>
      </li>
      <li>
        <Comment title="Mentions" />{' '}
        <CounterData>{Number.isNaN(mentions) ? 0 : mentions}</CounterData>
      </li>
    </MentionCounter>
  )
}

Thanks to SWR, even though we are using two instances of the WebmentionsCounter component, only one request is made and they both profit from the same cache.

Feel free to peek at my source code to see what’s happening:

Display the mentions

Now that we have placed the component, it’s time to get all that social juice flowing!

At of the time of this writing, useSWRpages is not documented. Add to that the fact that the webmention.io endpoint doesn’t offer collection information on a response (i.e. no offset or total number of pages), I couldn’t find a way to use SWR here.

So, my current implementation uses a state to keep the current page stored, another state to handle the mentions array, and useEffect to handle the request. The “Load More” button is disabled once the last request brings back an empty array.

const Webmentions = ({ postUrl }) => {
  const { t } = useTranslation()
  const [page, setPage] = useState(0)
  const [mentions, addMentions] = useState([])


  useEffect(() => {
    const fetchMentions = async () => {
      const olderMentions = await getMentions(page, 50, postUrl)
      addMentions((mentions) => [...mentions, ...olderMentions])
    }
    fetchMentions()
  }, [page])


  return (
    <>
      {mentions.map((mention, index) => (
        <Mention key={mention.data.author.name + index}>
          <AuthorAvatar src={mention.data.author.photo} lazy />
          <MentionContent>
            <MentionText
              data={mention.data}
              activity={mention.activity.type}
            />
          </MentionContent>
        </Mention>
      ))}
      </MentionList>
      {mentions.length > 0 && (
        <MoreButton
          type="button"
          onClick={() => {
          setPage(page + 1)
        }}
        >
        {t('common:more')}
      </MoreButton>
    )}
    </>
  )
}

The code is simplified to allow focus on the subject of this article. Again, feel free to peek at the full implementation:

Step 4: Handling outbound mentions

Thanks to Remy Sharp, handling outbound mentions from one website to others is quite easy and provides an option for each use case or preference possible.

The quickest and easiest way is to head over to Webmention.app, get an API token, and set up a web hook. Now, if you have RSS feed in place, the same thing is just as easy with an IFTT applet, or even a deploy hook.

If you prefer to avoid using yet another third-party service for this feature (which I totally get), Remy has open-sourced a CLI package called wm which can be ran as a postbuild script.

But that’s not enough to handle outbound mentions. In order for our mentions to include more than simply the originating URL, we need to add microformats to our information. Microformats are key because it’s a standardized way for sites to distribute content in a way that Webmention-enabled sites can consume.

At their most basic, microformats are a kind of class-based notations in markup that provide extra semantic meaning to each piece. In the case of a blog post, we will use two kinds of microformats:

  • h-entry: the post entry
  • h-card: the author of the post

Most of the required information for h-entry is usually in the header of the page, so the header component may end up looking something like this:

<header class="h-entry">
  <!-- the post date and time -->
  <time datetime="2020-04-22T00:00:00.000Z" class="dt-published">
    2020-04-22
  </time>
  <!-- the post title -->
  <h1 class="p-name">
    Webmentions with NextJS
  </h1>
</header>

And that’s it. If you’re writing in JSX, remember to replace class with className, that datetime is camelCase (dateTime), and that you can use the new Date('2020-04-22').toISOString() function.

It’s pretty similar for h-card. In most cases (like mine), author information is below the article. Here’s how my page’s footer looks:

<footer class="h-card">
  <!-- the author name -->
  <span class="p-author">Atila Fassina</span>
  <!-- the authot image-->
  <img
    alt="Author’s photograph: Atila Fassina"
    class="u-photo"
    src="/images/internal-avatar.jpg"
    lazy
  />
</footer>

Now, whenever we send an outbound mention from this blog post, it will display the full information to whomever is receiving it.

Wrapping up

I hope this post has helped you getting to know more about Webmentions (or even about IndieWeb as a whole), and perhaps even helped you add this feature to your own website or app. If it did, please consider sharing this post to your network. I will be super grateful! 😉

References

Further reading

The post Jumping Into Webmentions With NextJS (or Not) appeared first on CSS-Tricks.

Weekly Platform News: CSS ::marker pseudo-element, pre-rendering web components, adding Webmention to your site

Šime posts regular content for web developers on webplatform.news.

In this week's roundup: datepickers are giving keyboard users headaches, a new web component compiler that helps fight FOUC, we finally get our hands on styling list item markers, and four steps to getting webmentions on your site.

Using plain text fields for date input

Keyboard users prefer regular text fields over complex date pickers, and voice users are frustrated by the native control (<input type="date">).

Previously, I have relied on plain text inputs as date fields with custom validation for the site, typically using the same logic on the client and the server. For known dates — birthdays, holidays, anniversaries, etc. — it has tested well.

(via Adrian Roselli)

Pre-rendering web components

Stencil is a “web component compiler” that can be used to pre-render web components (including Shadow DOM) or hide them until they are fully styled to avoid the flash of unstyled content (FOUC).

This tool also makes sure that polyfills are only loaded when needed, and its Component API includes useful decorators and hooks that make writing web components easier (e.g., the Prop decorator handles changes to attributes).

import { Component, Prop, h } from "@stencil/core";

@Component({
  tag: "my-component"
})
export class MyComponent {
  @Prop() age: number = 0;

  render() {
    return <div>I am {this.age} years old</div>;
  }
}

(via Max Lynch)

The CSS ::marker pseudo-element

When the CSS display: list-item declaration is applied to an element, the element generates a marker box containing a marker, e.g., a list bullet (the <li> and <summary> elements have markers by default).

Markers can be styled via the ::marker pseudo-element (useful for changing the color or font of the marker), but this CSS feature is currently only supported in Firefox.

(via Rachel Andrew)

Adding Webmention to your website

  1. Sign up on Webmention.io; this is a service that collects webmentions on your behalf.
  2. Add <link rel="webmention"> (with the appropriate href value) to your web pages.

    There are also Webmention plugins available for all major content management systems (CMS) if you prefer building on top of your CMS.

  3. Fetch webmentions from Webmention.io (via Ajax) to display them on your page.
  4. Use webmention.app to automate sending webmentions (when you publish content that includes links to other sites that support Webmention).

(via Daniel Aleksandersen)

The post Weekly Platform News: CSS ::marker pseudo-element, pre-rendering web components, adding Webmention to your site appeared first on CSS-Tricks.

IndieWeb and Webmentions

The IndieWeb is a thing! They've got a conference coming up and everything. The New Yorker is even writing about it:

Proponents of the IndieWeb offer a fairly straightforward analysis of our current social-media crisis. They frame it in terms of a single question: Who owns the servers? The bulk of our online activity takes places on servers owned by a small number of massive companies. Servers cost money to run. If you’re using a company’s servers without paying for the privilege, then that company must be finding other ways to “extract value” from you.

As I understand it, the core concept is running your own website where you are completely in control, as opposed to publishing content you create on a third-party website. It doesn't say don't use third-party websites. It just says syndicate there if you want to, but make your own website the canonical source.

Don't tweet, but instead write a short blog post and auto-post it to Twitter. Don't blog on Medium, but instead write on your own blog and plop it over to Medium. Like that. In that way, you're getting the value of those services without giving anything up.

Continue reading "IndieWeb and Webmentions"