<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Maverick Times</title>
    <subtitle>Maverick Times is a dedicated platform studying and tracking startup growth playbooks across Hyderabad, Warangal, and Vijayawada.</subtitle>
    <link href="https://mavericktimes.in/feed.xml" rel="self" type="application/atom+xml" />
    <link href="https://mavericktimes.in" rel="alternate" type="text/html"/>
    <author>
        <name>Max Böck</name>
    </author>
    
    <updated>2024-12-31T00:00:00Z</updated>
    
    <id>https://mavericktimes.in/</id>
        <entry>
            <title>A year in review: 2024</title>
            <link href="https://mavericktimes.in/blog/year-in-review-2024/"/>
            <updated>2024-12-31T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/year-in-review-2024/</id>
            <content type="html"><![CDATA[
                <p class="lead">2024 was in many ways a very challenging year for me, but it was also one of the most significant.</p><p>
</p><p>This year’s annual review post is a bit different.</p>
<p>In previous years, I reflected on the work that I did, the web projects I built, the posts that I wrote and so on. There was lots of that in 2024 too of course (well maybe except the blogging part, that seems to <a href="https://front-end.social/@matuzo/113729399287780395">become a pattern</a>).</p>
<p>But the truth is, most of my energy this year went towards building a life for our new family.</p>
<p>My son was born in November, and he’s happily sleeping on my chest as I am typing this. I’ve never felt more grateful or proud about anything in my life, and I still can’t believe he’s with us now.</p>
<figure class="extend">
    <img src="https://res.cloudinary.com/mxb/image/upload/v1735639452/baby_qat7qw.jpg" width="800" height="454" loading="lazy" alt="me with a sleeping baby on my chest, exhausted in the hospital" />
    <figcaption>First night in the hospital with my newborn son</figcaption>
</figure>
<p>The months leading up to his arrival were quite stressful at times, supporting my wife’s pregnancy and preparing everything as best I could for the steps ahead. We’re planning a move next year, and there’s still lots of work to do before we can settle into our new home.</p>
<p>But all of it is very rewarding, and I can’t wait to see where 2025 takes us. Despite everything that’s going wrong in the world right now, I feel hopeful for the future.</p>

            ]]></content>
        </entry>
        <entry>
            <title>A year in review: 2023</title>
            <link href="https://mavericktimes.in/blog/year-in-review-2023/"/>
            <updated>2023-12-31T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/year-in-review-2023/</id>
            <content type="html"><![CDATA[
                <p class="lead">Haven't done one of these since 2020, but this feels like a good opportunity to get some writing in just before the new year. Let's see if I can still remember how to do this blogging thing.</p><p>
</p><h2 id="work" tabindex="-1">Work</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-work"><span class="sr-only">Permalink to “Work”</span> <span aria-hidden="true">#</span></a><p>We built a lot of interesting projects in 2023 with <a href="https://www.codista.com/">Codista</a>, and we’ve had a very good year working with established clients and partners. Some of it has been quite challenging, but we managed to pull off a stellar track record of successful projects, and I’m really proud of our small company!</p>
<p>We also hired our first front-end developer (other than myself). I’ve posted the <a href="https://front-end.social/@mxbck/110383832577664007">job listing</a> on our own website and on Mastodon, and with the help from the web dev community, we found somebody who fits our team really well and I’m very happy with them. A big thanks to everyone who boosted the post or mentioned it at meetups!</p>
<h2 id="health" tabindex="-1">Health</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-health"><span class="sr-only">Permalink to “Health”</span> <span aria-hidden="true">#</span></a><p>I’ve struggled a bit with personal health issues this year. I have a moderately severe form of atopic dermatitis, an auto-immune disease of the skin. I’ve had it all my life. Some days it feels OK, while on others it feels like my skin is on fire and it’s hard to concentrate on anything else besides the impulse to scratch. If you’ve ever seen me give a talk and my face was bright red, it’s not (only) because I’ve been drinking the night before.</p>
<p>When I was younger I’ve tried different forms of therapy, but none of them really worked - so I’ve become pretty much used to living with it. But then it got a lot worse in 2023. I couldn’t sleep properly anymore and it started to affect other areas of my life, so I decided to take another shot at fighting it.</p>
<p>I got a new doctor and started treatment with a new drug that recently got EMA approval. After half a year, I switched to a <a href="https://en.wikipedia.org/wiki/Dupilumab">different drug</a>, which I’m currently still evaluating. First results have been promising though, so fingers crossed that this is the one. Would certainly make my 2024 more pleasant.</p>
<h2 id="traveling" tabindex="-1">Traveling</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-traveling"><span class="sr-only">Permalink to “Traveling”</span> <span aria-hidden="true">#</span></a><p>I was fortunate enough to see some beautiful places this year. Event though my pre-pandemic days of traveling the more remote parts of the world are likely over, it’s still nice to get out and explore again.</p>
<p>I went to Zurich for a client workshop in the spring, and immediately followed that up with a trip to Amsterdam for <a href="https://cssday.nl/2023">CSS Day 2023</a>. That conference was one of the best I’ve ever been to. Even though I caught a pretty bad cold and had to skip some of the fun - the talks, people and just overall atmosphere of that event were amazing.</p>
<p>I’m grateful I got to see so many familiar faces and talk to some of my web friends in person. I also left the conference feeling very inspired and (as always) a little guilty that I can’t find enough time for community work and writing. Hopyfully next year my schedule will allow for a bit more of that.</p>
<figure class="extend">
    <img src="https://res.cloudinary.com/mxb/image/upload/w_800/cssday_dudf29.png" width="800" height="599" loading="lazy" alt="a group selfie of people in a hotel lobby, smiling faces all around" />
    <figcaption>With Nils and Una at the CSS Café Meetup, the day after the conference</figcaption>
</figure>
<p>In the summer, we went for a vacation in the south of Croatia and spent two weeks on the Dalmatian coast.</p>
<figure class="extend">
    <img src="https://res.cloudinary.com/mxb/image/upload/w_800/croatia_rgwops.png" width="800" height="600" loading="lazy" alt="clear, turquoise water and green pine trees in a small bay. a sailing boat in the background" />
    <figcaption>A nice quiet bay at the shores of Hvar island</figcaption>
</figure>
<p>We also did some more work on our small garden house in Lower Austria and had a few lovely days in autumn hiking the surrounding hills and vineyards.</p>
<figure class="extend">
    <img src="https://res.cloudinary.com/mxb/image/upload/w_800/senftenberg_ujjfhf.jpg" width="800" height="450" loading="lazy" alt="a small red house in a garden in spring, pink blossoms on a tree in the background" />
    <figcaption>The matching birdhouse really brings it together</figcaption>
</figure>
<h2 id="music%2C-movies%2C-books-and-stuff" tabindex="-1">Music, Movies, Books and Stuff</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-music2c-movies2c-books-and-stuff"><span class="sr-only">Permalink to “Music, Movies, Books and Stuff”</span> <span aria-hidden="true">#</span></a><p>I enjoyed lots of stuff this year, so I’ll just give you the top 3 of each:</p>
<h3 id="music" tabindex="-1">Music</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-music"><span class="sr-only">Permalink to “Music”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://open.spotify.com/artist/19f2JXwlRU26376TCKmp6L">GoGo Penguin</a> (Jazz/Fusion)</li>
<li><a href="https://open.spotify.com/artist/4RnBFZRiMLRyZy0AzzTg2C">Run The Jewels</a> (Hip Hop)</li>
<li><a href="https://open.spotify.com/artist/6dgwEwnK0YtDfS9XhRwBTG">Broken Bells</a> (Indie Rock)</li>
</ul>
<h3 id="movies" tabindex="-1">Movies</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-movies"><span class="sr-only">Permalink to “Movies”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://www.imdb.com/title/tt11358390/">Renfield</a> (Horror/Action)</li>
<li><a href="https://www.imdb.com/title/tt6710474/">Everything, Everywhere, All at once</a> (Action/Sci-Fi)</li>
<li><a href="https://www.imdb.com/title/tt1517268/">Barbie Movie</a> (Comedy)</li>
</ul>
<h3 id="books" tabindex="-1">Books</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-books"><span class="sr-only">Permalink to “Books”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://www.goodreads.com/series/56399-the-expanse">“The Expanse”</a> by James S.A. Corey (9-part Series, Sci-Fi)</li>
<li><a href="https://www.goodreads.com/book/show/60865502-der-h-ftling-aus-moabit">“Felix Blom”</a> by Alex Beer (German, Thriller)</li>
<li><a href="https://www.goodreads.com/book/show/43706493-novacene">“Novacene”</a> by James Lovelock (Non-Fiction)</li>
</ul>
<h3 id="stuff" tabindex="-1">Stuff</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-stuff"><span class="sr-only">Permalink to “Stuff”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://us.seenebula.com/products/capsule-max-d2423">Anker Nebula Capsule Max</a> - a great portable projector the size of a beer can, supports streaming and external drives</li>
<li><a href="https://www.fender.com/en-US/acoustic-guitars/dreadnought/pd-220e-dreadnought/0970310303.html">Fender Paramount PD-220E</a> - acoustic guitar I got after my old one was stolen. really bright, warm sound</li>
<li><a href="https://www.superdry.com/mens/jackets/puffer-jackets/details/240885/sports-puffer-hooded-jacket-dark-moss">Superdry Sports Puffer</a> - just a nice comfy winter jacket</li>
</ul>
<h2 id="tech" tabindex="-1">Tech</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2023/h-tech"><span class="sr-only">Permalink to “Tech”</span> <span aria-hidden="true">#</span></a><p>I think I’ve grown a bit weary of the tech industry this year. I still love the web and I love what I do for work, but I lost some of my interest in new developments, new frameworks, the <em>hot tech of the day</em>. I just don’t care as much anymore.</p>
<p>It may also be the rising topic of AI that is popping up everywhere you look, or the crypto-esque attitude that seems to go along with it. Instead of being excited about new possibilites, I feel a bit disheartened by the trends I see. AI looks like a great tool, but everything I read about how it’s actually applied comes from the same hyper-captitalist mindset that brought us gems like bitcoin mining and NFTs. I don’t know, maybe I’m just tired.</p>
<p>There are other trends as well that I feel more optimistic about. Ones that call for a <a href="https://www.rollingstone.com/culture/culture-commentary/internet-future-about-to-get-weird-1234938403/">weirder web</a> with more original, human content. It could just be wishful thinking, but I’ve caught glimpses of that version of the future <a href="https://mavericktimes.in/blog/the-return-of-the-90s-web/">for a while now</a>. Here’s hoping.</p>
<p>Well, that’s all folks. See you in 2024!</p>

            ]]></content>
        </entry>
        <entry>
            <title>Webmention Analytics</title>
            <link href="https://mavericktimes.in/blog/webmention-analytics/"/>
            <updated>2021-02-07T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/webmention-analytics/</id>
            <content type="html"><![CDATA[
                <p class="lead">I'm a fan of webmentions. I've written about <a href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/">how to use them</a> before, and I'm quite happy with having them on my site.</p>
<p>However, it can get difficult to see what’s going on with them - especially if there’s a lot of “background noise”. Many sites just scrape content from well-known blogs and republish it for SEO juice. If that content includes a link to your site, it can lead to webmention spam.</p>
<p>Unlike on social media, you also don’t get notifications or reports about incoming webmentions. You’re just handed a bunch of raw data to use however you like. That’s part of the beauty of the Indieweb though: you can tailor it to whatever suits you best.</p>
<p>I recently started playing around with the data I get from webmention.io to see if it could be displayed in a more meaningful way. The result is a new side project I call:</p>
<p>✨✨✨ <strong>Webmention Analytics</strong> ✨✨✨<br />
You can see it in action in <a href="https://webmentions.mxb.dev/">this demo</a> on my site.</p>
<figure class="extend">
    <a href="https://webmentions.mxb.dev/2020-06/">
        <img src="https://mavericktimes.in/blog/webmention-analytics/cover.jpg" width="1200" height="750" alt="barchart showing days of the month at the x-axis and different amounts of webmentions on the y-axis." />
    </a>
    <figcaption>Breakdown of webmentions per day</figcaption>
</figure>
<p>I built this with <a href="https://11ty.dev/">Eleventy</a> and <a href="https://netlify.com/">Netlify</a>, mainly because that’s my favorite tech stack to tinker with. But for analytics that don’t have to be real-time, static site generators are actually a really good fit.</p>
<p>Expensive computations like parsing and analyzing 8000+ data points like this can be run once a day through a periodic build hook. The reports it generates are then instantly available to the user, while still being up-to-date enough.</p>
<h2 id="features" tabindex="-1">Features</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webmention-analytics/h-features"><span class="sr-only">Permalink to “Features”</span> <span aria-hidden="true">#</span></a><ul>
<li>group webmention data by month</li>
<li>overview of webmentions by type (like, reply, repost, mention, bookmark…) and day</li>
<li>show top source URLs sending webmentions to your site</li>
<li>show top target URLs on your site receiving webmentions</li>
<li>show top tweets generating webmentions through brid.gy</li>
<li>check incoming webmentions against a blocklist of known “content scrapers” and spam domains</li>
<li>automatic daily updates with cron job</li>
</ul>
<figure class="extend">
    <a href="https://webmentions.mxb.dev/2020-06/">
        <img src="https://mavericktimes.in/blog/webmention-analytics/targets-and-sources.jpg" width="1200" height="788" alt="table showing most common sources and targets for webmentions" />
    </a>
    <figcaption>Top sources and targets</figcaption>
</figure>
<figure>
    <a href="https://webmentions.mxb.dev/2020-06/">
        <img src="https://mavericktimes.in/blog/webmention-analytics/flagged.jpg" width="1000" height="788" alt="" />
    </a>
    <figcaption>Webmentions flagged as spam</figcaption>
</figure>
<h2 id="get-your-own-instance!" tabindex="-1">Get your own instance!</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webmention-analytics/h-get-your-own-instance"><span class="sr-only">Permalink to “Get your own instance!”</span> <span aria-hidden="true">#</span></a><p>If you also use webmention.io to show webmentions on your site, you can <a href="https://github.com/maxboeck/webmention-analytics">fork the code on Github</a> and make your own instance of <code>webmention-analytics</code>. Just follow the <a href="https://github.com/maxboeck/webmention-analytics#get-your-own-instance">instructions</a> in the README to get started.</p>
<div class="callout callout--info"><span class="callout__icon"><svg class="icon icon--info" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-info"></use></svg></span><div class="callout__content"><p>Keep in mind that this is still a very early version of a weekend side project, so there's probably a few things to iron out. Cheers!</p></div></div>

            ]]></content>
        </entry>
        <entry>
            <title>A year in review: 2020</title>
            <link href="https://mavericktimes.in/blog/year-in-review-2020/"/>
            <updated>2020-12-19T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/year-in-review-2020/</id>
            <content type="html"><![CDATA[
                <p class="lead">I don't think I have to tell anyone why this year sucked, what with the pandemic and all. 2020 is going down in history as a massive crapstorm.</p>
<p>Still, I want to <a href="https://mavericktimes.in/blog/year-in-review-2019">continue the tradition</a> of “end-of-the-year” blogposts and since there’s already enough doom out there these days, I’m trying to focus on the good things that happened instead.</p>
<h2 id="work" tabindex="-1">Work</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-work"><span class="sr-only">Permalink to “Work”</span> <span aria-hidden="true">#</span></a><p>The web industry is among the fortunate ones that are very well suited for remote and distributed work, which is why I was able to keep working from home throughout most of the year.</p>
<p>We rented a great new office in the spring that I’ve hardly been to since, but our team at <a href="https://www.codista.com/">Codista</a> is quite used to working remote and we already had all the necessary infrastructure in place.</p>
<p>We had more than enough projects on our hands and we did some really interesting, challenging stuff that I can’t talk about (yet) 😉 - so all in all, work was good.</p>
<h2 id="writing" tabindex="-1">Writing</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-writing"><span class="sr-only">Permalink to “Writing”</span> <span aria-hidden="true">#</span></a><p>I wrote nine posts in 2020 - which is fewer than last year, but all things considered, that’s ok. The most popular ones were:</p>
<ul>
<li><a href="https://mavericktimes.in/blog/the-return-of-the-90s-web/">The Return of the 90s Web</a>: a look at some early trends on the web and how they might be resurrected</li>
<li><a href="https://mavericktimes.in/blog/emergency-website-kit/">The Emergency Website Kit</a>: an attempt to build a template for critical information websites</li>
<li><a href="https://mavericktimes.in/blog/color-theme-switcher/">Color Theme Switcher</a>: a case study of this blog’s weird color scheme feature (see header)</li>
</ul>
<h2 id="side-projects" tabindex="-1">Side Projects</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-side-projects"><span class="sr-only">Permalink to “Side Projects”</span> <span aria-hidden="true">#</span></a><p>When the first lockdown hit, I kept occupied by building things - mostly in and around Eleventy, which helped me get ideas off the ground quickly. Here are some of these:</p>
<ul>
<li>
<p><a href="https://github.com/maxboeck/eleventastic">Eleventastic</a>: my personal starter kit for Eleventy projects. I wanted to get rid of “external” build tools like Gulp and manage all pipelines inside Eleventy itself.</p>
</li>
<li>
<p><a href="https://github.com/maxboeck/resume">Eleventy Resumé</a>: a simple microsite that functions as a CV/Resumé in web and print.</p>
</li>
<li>
<p><a href="https://whimsical.club/">Whimsical Website Club</a>: a collection of websites that spark joy by doing things a little bit less serious.</p>
</li>
</ul>
<h2 id="speaking" tabindex="-1">Speaking</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-speaking"><span class="sr-only">Permalink to “Speaking”</span> <span aria-hidden="true">#</span></a><p>I had some talks planned for 2020 which of course didn’t happen. I did a few online talks though and I participated in <a href="https://inclusivedesign24.org/2020/">Inclusive Design 24</a>, a free 24-hour livestream event where I talked about another side project, the “Emergency Website Kit”:</p>
<div class="embed embed--16-9">
    <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8RdrRCq8VzU?start=53" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
<h2 id="events" tabindex="-1">Events</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-events"><span class="sr-only">Permalink to “Events”</span> <span aria-hidden="true">#</span></a><p>The <a href="http://webclerks.at/">Webclerks</a> team and I had the pleasure of hosting our own little virtual meetup event <a href="https://webclerks.at/vienna-calling/">“Vienna Calling”</a> on Twitch, and we had a phenomenal lineup. A big thank you again to all the speakers who joined us, as well as the rest of the team who made this happen behind the scenes.</p>
<p>BTW: You can find the full event as a playlist on Youtube:</p>
<div class="embed embed--16-9">
    <iframe width="560" height="315" src="https://www.youtube.com/embed/videoseries?list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
<h2 id="traveling" tabindex="-1">Traveling</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-traveling"><span class="sr-only">Permalink to “Traveling”</span> <span aria-hidden="true">#</span></a><p>In the summer, the situation improved enough for me and my girlfriend to take some much needed vacation time. With international travel still closed, we decided to go on a road trip through Austria instead and it was awesome. This country has some really beautiful places in store.</p>
<!-- <blockquote class="twitter-tweet"><p lang="en" dir="ltr">I&#39;m staying in this cabin in the mountains for a few days without internet. So if there&#39;s any dev twitter drama kindly do it without me 😉 <a href="https://t.co/O62x9NoxG0">pic.twitter.com/O62x9NoxG0</a></p>&mdash; Max Böck (@mxbck) <a href="https://twitter.com/mxbck/status/1289958987647995904?ref_src=twsrc%5Etfw">August 2, 2020</a></blockquote> -->
<figure class="extend">
    <img src="https://mavericktimes.in/blog/year-in-review-2020/mountains.jpg" alt="A small cabin in the Austrian alps, under a clear blue sky" loading="lazy" />
    <figcaption>The Kanisalpe mountain range in Vorarlberg, Austria</figcaption>
</figure>
<h2 id="we-need-the-web" tabindex="-1">We need the Web</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-we-need-the-web"><span class="sr-only">Permalink to “We need the Web”</span> <span aria-hidden="true">#</span></a><p>This year, more than ever, I realized the enormous impact the web has on all of us, and how important it is to keep it free and open. I know we’re all sick of doing things online all the time, but imagine for a moment what this year would have looked like had the web never been invented.</p>
<p>Millions of people would be completely isolated, even more would be out of their jobs. Schools could not operate. Civil rights movements would be almost impossible to organize. Global research projects like the development of a vaccine would take years longer. And you probably wouldn’t have seen the faces of your loved ones in months.</p>
<p>The web has become such an integral part of our lives that we sometimes take it for granted. It’s not. In fact this shitshow of a year should probably remind us that we need to take really good care of the things that are still connecting us.</p>
<h2 id="goals-for-2021" tabindex="-1"><s>Goals for 2021</s></h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-goals-for-2021"><span class="sr-only">Permalink to “Goals for 2021”</span> <span aria-hidden="true">#</span></a><p>I’m not going to compare my goals from last year with what I’ve accomplished in 2020. I don’t think it matters. Give yourself a break this year - it’s OK if things didn’t turn out the way you wanted.</p>
<p>I’ll see you all in 2021. And hopefully we’ll all have a vaccine in our system and a better year ahead of us.</p>
<h2 id="other-year-in-review-posts" tabindex="-1">Other Year-in-Review Posts</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2020/h-other-year-in-review-posts"><span class="sr-only">Permalink to “Other Year-in-Review Posts”</span> <span aria-hidden="true">#</span></a><p>Have you written one of these yourself? Let me know and get added here.</p>
<ul>
<li><a href="https://hiddedevries.nl/en/blog/2020-12-17-2020-in-review/">Hidde de Vries</a></li>
<li><a href="https://marcus.io/blog/my-2020">Marcus Herrmann</a></li>
</ul>

            ]]></content>
        </entry>
        <entry>
            <title>A year in review: 2019</title>
            <link href="https://mavericktimes.in/blog/year-in-review-2019/"/>
            <updated>2019-12-31T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/year-in-review-2019/</id>
            <content type="html"><![CDATA[
                <p class="lead">As the final hours of 2019 are winding down, I want to take a moment and look back at everything that happened this year - because it was a busy one.</p>
<h2 id="work" tabindex="-1">Work</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-work"><span class="sr-only">Permalink to “Work”</span> <span aria-hidden="true">#</span></a><p>At the beginning of 2019, I became a partner at <a href="https://www.codista.com/">Codista</a>, the software studio where I’ve been working for some time now. Thomas, Luis and I now run the company as a trio, and building together has been great.</p>
<p>Besides working as a frontend developer on a number of challenging projects this year, I was also responsible for the rebranding of our own identity. There are some interesting new ideas in the works for our studio in 2020, and I’m excited to see them come to life.</p>
<h2 id="writing" tabindex="-1">Writing</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-writing"><span class="sr-only">Permalink to “Writing”</span> <span aria-hidden="true">#</span></a><p>Counting this one, I’ve written ten new posts in 2019. That’s more than last year, but I’d still like to increase that number.</p>
<p>The most popular posts were:</p>
<ul>
<li><a href="https://mavericktimes.in/blog/the-css-mindset/">The CSS Mindset</a>: a collection of helpful mental models for writing CSS,</li>
<li><a href="https://mavericktimes.in/blog/on-simplicity/">On Simplicity</a>: how sometimes “less is more” in tech, and</li>
<li><a href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/">Static Indieweb pt2: Using Webmentions</a>: a rundown of how to implement webmentions with Eleventy.</li>
</ul>
<p>The feedback from the web community on these posts was amazing, and it never gets old to hear that anything you’ve made actually helped people in some way.</p>
<p>Like in 2018, all my writing was published on my own website. I’m increasingly fond of the IndieWeb and the principles behind it, and I want to continue striving for more independence. Eleventy and Netlify both have been very valuable improvements for my personal site this year, and I really like working with them.</p>
<h2 id="speaking" tabindex="-1">Speaking</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-speaking"><span class="sr-only">Permalink to “Speaking”</span> <span aria-hidden="true">#</span></a><p>I wrote two new talks in 2019 and had the chance to speak at a few community events.</p>
<p>First I was invited as a guest speaker to the <a href="https://mozilla-tito-devr.netlify.com/">Mozilla Developer Roadshow</a>, where I had a really interesting “Fireside Chat” about CSS with HJ Chen. It was awesome meeting her and I’m very grateful for the opportunity to speak there.</p>
<p>Following my blog post about the topic, I gave a talk on <a href="https://www.youtube.com/watch?v=1TsFOfBB4GA">the CSS Mindset</a> at CSS-Minsk-JS in September and had a wonderful time. A big thank you to the organizers and attendees, who made me feel very much at home in Minsk.</p>
<figure>
    <img src="https://mavericktimes.in/blog/year-in-review-2019/minsk.jpg" alt="me and Ian in a bar in minsk" loading="lazy" />
  <figcaption>I met <a href="https://twitter.com/IanPouncey">Ian Pouncey</a> at CSS-Minsk-JS</figcaption>
</figure>
<figure>
    <img src="https://mavericktimes.in/blog/year-in-review-2019/slide.jpg" alt="diagram of five personal websites, connected thorugh the anarchy symbol" loading="lazy" />
  <figcaption>One of the slides in my <a href="https://noti.st/mxb/lhMFMv/rage-against-the-content-machine">IndieWeb talk</a></figcaption>
</figure>
<p>A big part of this year was devoted to making our own <a href="https://webclerks.at/">Webclerks Conference</a> happen in Vienna. It was the first time for me to be involved in the organization of such an event and I really had no idea how it would turn out, so I’m overjoyed that it went so well.</p>
<p>I also shared my experiences with the IndieWeb in a talk called <a href="https://youtu.be/ucLEMETfrTA?t=7282">“Rage against the Content Machine”</a> there. I’d love to do that one again in the new year.</p>
<h2 id="traveling" tabindex="-1">Traveling</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-traveling"><span class="sr-only">Permalink to “Traveling”</span> <span aria-hidden="true">#</span></a><p>I’ve been fortunate enough to see many beautiful places on this planet now. I made a <a href="https://mavericktimes.in/traveling">little map</a> to keep track of them here.</p>
<p>This year, I visited Portugal, Georgia, Belarus, the Czech Republic and the Azores. My personal favourites were the ancient city of Tbilisi and the raw nature of São Miguel island, both fascinating places in their own regard.</p>
<figure>
    <img src="https://mavericktimes.in/blog/year-in-review-2019/tbilisi.jpg" alt="nighttime view of tbilisi city from a nearby hilltop" loading="lazy" />
  <figcaption>Tbilisi, Georgia</figcaption>
</figure>
<figure>
    <img src="https://mavericktimes.in/blog/year-in-review-2019/saomiguel.jpg" alt="twin lakes of sete cidades, sao miguel island in the azores" loading="lazy" />
  <figcaption>Sete Cidades, São Miguel, Azores</figcaption>
</figure>
<h2 id="personal" tabindex="-1">Personal</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-personal"><span class="sr-only">Permalink to “Personal”</span> <span aria-hidden="true">#</span></a><p>2019 was also a source of frustration in many ways. From the ongoing global shift to right-wing populism to increasingly dystopian technology trends to the looming threat of climate change. I’ve had some issues with worrying and stress in my personal life as well.</p>
<p>But I’d like to think that the new decade can be an opportunity to learn from past mistakes and change things for the better. I’m going to try and focus on that going forward.</p>
<h2 id="goals-for-2020" tabindex="-1">Goals for 2020</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/year-in-review-2019/h-goals-for-2020"><span class="sr-only">Permalink to “Goals for 2020”</span> <span aria-hidden="true">#</span></a><ul>
<li>help to further grow Codista into a sustainable, ethical business</li>
<li>write more regularly</li>
<li>continue involvement in the IndieWeb community</li>
<li>attend and speak at more conferences</li>
<li>make more music</li>
<li>read more non-tech books</li>
<li>keep up with friends and enjoy life a little more</li>
</ul>
<p>That’s all folks. See you tomorrow!</p>

            ]]></content>
        </entry>
        <entry>
            <title>Webclerks Conference</title>
            <link href="https://mavericktimes.in/blog/webclerks-conf-2019/"/>
            <updated>2019-11-28T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/webclerks-conf-2019/</id>
            <content type="html"><![CDATA[
                <p class="lead">It has been three days now since I stood on stage at Urania Vienna, hearing Manuel close our <a href="https://webclerks.at/">very first conference</a>. I'm still not really sure how we pulled that off.</p>
<figure class="extend">
    <img src="https://mavericktimes.in/blog/webclerks-conf-2019/webclerks-team.jpg" alt="The webclerks team on stage in front of a screen" />
    <figcaption>Photo by Sergey Poliakov <a href="https://twitter.com/sergeypoliakov/status/1199253984579670018">(Twitter)</a></figcaption>
</figure>
<p>I have been to quite a few web development conferences as an attendee, and lately even as a speaker. But the organization of such events always eluded me. I of course noticed when an event was particularly well put together, but I always assumed that the people running the show had a large team, lots of experience and were just very talented organizers.</p>
<p>That’s why it felt so weird to suddenly find myself in that group. Clearly we’re just a bunch of people who thought it would be cool to invite some of our heroes and heroines to Vienna, with little prior experience in creating an event at that scale.</p>
<p>But then it worked! We’d somehow gotten ourselves an absolutely incredible line-up of speakers and a sold out venue.</p>
<h2 id="welcome-to-webclerks-conference" tabindex="-1">Welcome to Webclerks Conference</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webclerks-conf-2019/h-welcome-to-webclerks-conference"><span class="sr-only">Permalink to “Welcome to Webclerks Conference”</span> <span aria-hidden="true">#</span></a><figure class="extend">
    <img src="https://mavericktimes.in/blog/webclerks-conf-2019/webclerks-jeremy.jpg" alt="the full auditorium listening to jeremy's keynote" />
    <figcaption>Jeremy Keith giving his opening keynote</figcaption>
</figure>
<p>Fast forward to last Monday, when months of work finally resulted in that one special day. Sleep had been rare in the days before, and the tension was high.</p>
<p>None of us really knew how this would turn out, so we just gave it our best shot. And the <a href="https://adactio.com/journal/16175">feedback</a> so far from speakers and attendees alike has been great:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It&#39;s hard to believe that the <a href="https://twitter.com/wearewebclerks?ref_src=twsrc%5Etfw">@wearewebclerks</a> conference was the first one ever by the organisers. It was slick, welcoming, friendly, and interesting. Thanks for asking me to speak! ♥️ <a href="https://twitter.com/hashtag/webclerks?src=hash&amp;ref_src=twsrc%5Etfw">#webclerks</a></p>&mdash; Charlie Don&#39;t Surf (@sonniesedge) <a href="https://twitter.com/sonniesedge/status/1199240143699423232?ref_src=twsrc%5Etfw">November 26, 2019</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">The <a href="https://twitter.com/wearewebclerks?ref_src=twsrc%5Etfw">@wearewebclerks</a> conference today was seriously awesome! Great talks, great organization and great people! And all of that in Vienna ♥️ Looking forward to the next year <a href="https://twitter.com/hashtag/webclerks?src=hash&amp;ref_src=twsrc%5Etfw">#webclerks</a> <a href="https://t.co/GHOqfgzsNB">pic.twitter.com/GHOqfgzsNB</a></p>&mdash; Lisa Gringl (@kringal) <a href="https://twitter.com/kringal/status/1199049566244540417?ref_src=twsrc%5Etfw">November 25, 2019</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">✅ brilliant speakers<br />✅ surprise acts<br />✅ live captions<br />✅ tasty food from &quot;Speisen ohne Grenzen&quot;<br />✅ water bottles to reduce waste<br /><br />Congratulations to the organizers of a great <a href="https://twitter.com/hashtag/webclerks?src=hash&amp;ref_src=twsrc%5Etfw">#webclerks</a> community conference <a href="https://t.co/sOflfHkA1F">pic.twitter.com/sOflfHkA1F</a></p>&mdash; Harald Atteneder (@urbantrout) <a href="https://twitter.com/urbantrout/status/1199043552858329089?ref_src=twsrc%5Etfw">November 25, 2019</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">This conference was a great example of getting it right from the beginning. Amazing speakers, inclusive, accessible and with all details taken care of. Congratulations to the team! I&#39;m really looking forward to next year&#39;s event <a href="https://twitter.com/hashtag/webclerks?src=hash&amp;ref_src=twsrc%5Etfw">#webclerks</a> <a href="https://t.co/YlRfzyt5kD">pic.twitter.com/YlRfzyt5kD</a></p>&mdash; Magdalena Adrover (@madrovergaya) <a href="https://twitter.com/madrovergaya/status/1199036748279496705?ref_src=twsrc%5Etfw">November 25, 2019</a></blockquote>
<h2 id="lessons-learned" tabindex="-1">Lessons Learned</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webclerks-conf-2019/h-lessons-learned"><span class="sr-only">Permalink to “Lessons Learned”</span> <span aria-hidden="true">#</span></a><p>Here are some of my personal takeaways from the experience:</p>
<ul>
<li>
<p>It really pays off to have a <strong>good tech team</strong>. We could not have asked for a better crew than <a href="https://www.eventsolutions.cc/">ALC</a>, they handled the event flawlessly by themselves. A huge thing to get off your shoulders as an organizer.</p>
</li>
<li>
<p>If you want to livestream an event on youtube, that apparently requires a “brand account”. These take 24 hours to approve, so it’s best to not discover this <strong>the night before the conference</strong>. (Thankfully <a href="https://twitter.com/jkphl">Joschi</a> from a11yclub helped us out there!)</p>
</li>
<li>
<p>The <strong>glass water bottles</strong> we put into everyone’s goodie bag were a big hit. People liked that they could refill anytime. Less waste, more hydration!</p>
</li>
<li>
<p>You can <strong>make your own rules!</strong> Not all tech confs have to follow the same recipe. In fact some of the little differences are what makes you stand out.</p>
</li>
<li>
<p>We’re not a faceless recruiting event, but a <strong>community of humans</strong>, and we wanted to be as inclusive as possible. That thought went into a lot of aspects of our conference from ticket pricing to info emails to food choices. Definitely something to keep.</p>
</li>
</ul>
<figure class="extend">
    <img src="https://mavericktimes.in/blog/webclerks-conf-2019/webclerks-remy.jpg" alt="Manuel and Remy chatting between talks" />
</figure>
<h2 id="roll-credits" tabindex="-1">Roll Credits</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webclerks-conf-2019/h-roll-credits"><span class="sr-only">Permalink to “Roll Credits”</span> <span aria-hidden="true">#</span></a><p>I really have to highlight the insane amount of work that <a href="https://twitter.com/mmatuzo">Manuel</a> and <a href="https://twitter.com/claudia_laber">Claudi</a> have put into this event. There’s an internal Trello board with about a gazillion tasks on it, and most of those had their names on them. It is beyond me how they did it.</p>
<p><a href="https://twitter.com/lctdnl">Daniel</a> has been an absolute beast as well - he did all the designs, printed badges, flags, shirts, stickers - you name it. If you thought webclerks looked like a professional brand, it’s because of him.</p>
<p>We also had lots of help from awesome volunteers who dedicated their time and energy to our event. <a href="https://twitter.com/jeannineprueger">Jeannine</a>, <a href="https://twitter.com/KerstinWuk">Kerstin</a>, Michel, Anne and <a href="https://twitter.com/greg_808">Gregor</a> have been incredibly helpful and I’m so glad they joined our team.</p>
<h2 id="community-matters" tabindex="-1">Community Matters</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webclerks-conf-2019/h-community-matters"><span class="sr-only">Permalink to “Community Matters”</span> <span aria-hidden="true">#</span></a><p>I honestly can’t think of many industries where it would have been possible to put an event like this together from scratch. I believe the reason this worked at all is the incredible openness displayed in the web community.</p>
<p>Where international speakers come to a small local conference to share their knowledge. Where companies like mozilla support a relatively unknown event and fund inclusiveness. Where attendees pay it forward to enable underrepresented others to join.</p>
<p>I’m happy and proud to be a part of that community.</p>
<p>Thank you! 🎉</p>

            ]]></content>
        </entry>
        <entry>
            <title>A Webring Kit</title>
            <link href="https://mavericktimes.in/blog/webring-kit/"/>
            <updated>2019-04-14T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/webring-kit/</id>
            <content type="html"><![CDATA[
                <p class="lead">After Tatiana Mac proposed to <a href="https://twitter.com/TatianaTMac/status/1114388079630929926">bring webrings back</a>, I hacked something new together over the weekend: A starter kit for hosting your own webring!</p>
<h2 id="what%E2%80%99s-a-webring%3F" tabindex="-1">What’s a Webring?</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-whate28099s-a-webring3f"><span class="sr-only">Permalink to “What’s a Webring?”</span> <span aria-hidden="true">#</span></a><p>It’s a blast from the past: In the 90s, sites about a common topic could join together in a central index. To be a member, you had to embed a little widget on your page that contained a “forward”, a “backward”, and a “random” button. These buttons would then link to the next or previous site in the ring.</p>
<div class="callout callout--info"><span class="callout__icon"><svg class="icon icon--info" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-info"></use></svg></span><div class="callout__content"><p>Since the term &quot;webring&quot; is trademarked in the US, this needs another cool name. Know any? Please <a href="https://github.com/maxboeck/webring/issues/1">add it to this thread</a>!</p></div></div>
<h2 id="a-curated-community" tabindex="-1">A curated community</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-a-curated-community"><span class="sr-only">Permalink to “A curated community”</span> <span aria-hidden="true">#</span></a><p>To keep the ring from getting spammed or flooded with trolls, it has to be curated. The project does that by hosting the member index on Github, in a simple <a href="https://github.com/maxboeck/webring/blob/master/src/data/members.json">JSON file</a>. Admins can accept or decline pull requests from people who want to join the ring, after reviewing their sites. There’s also a Code of Conduct that every member has to follow in order to be part of the ring.</p>
<p>For people who are not technical enough to submit a pull request, there’s also a simple signup form (using Netlify forms) to send the admin your site’s info via email and let them add you.</p>
<figure>
  <img src="https://mavericktimes.in/blog/webring-kit/webring-card.png" alt="a card showing the webring description and memberlist" />
  <figcaption>You can build webrings for anything</figcaption>
</figure>
<h2 id="free-and-open" tabindex="-1">Free and Open</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-free-and-open"><span class="sr-only">Permalink to “Free and Open”</span> <span aria-hidden="true">#</span></a><p>I wanted to make this as easy as possible, so people can start linking their personal sites together straight away. So I made the boilerplate using <a href="https://www.11ty.io/">Eleventy</a>. After forking <a href="https://github.com/maxboeck/webring">the codebase</a>, the proud webring admin only needs to set a title and a bit of meta data.</p>
<p>Eleventy then generates a <a href="https://webringdemo.netlify.com/">site like this</a> that lists all the members, shows the Code of Conduct and the instructions on how to join.</p>
<p>You can deploy it to <a href="https://www.netlify.com/">Netlify</a>, a free static site host, with just a few clicks. Netlify also lets you either use one of their subdomains, or a custom one you own.</p>
<h2 id="a-central-widget" tabindex="-1">A central widget</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-a-central-widget"><span class="sr-only">Permalink to “A central widget”</span> <span aria-hidden="true">#</span></a><p>Members of the ring can copy a code snippet to embed a banner on their site. I borrowed a bit from Twitters embed widget here: The basic markup is just a link to the index, and the prev/random/next links. But if you also include the script tag, it will replace that with a custom web component, designed by the ring admin.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>webring-banner</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Member of the <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webringdemo.netlify.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>An Example Webring<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> webring<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webringdemo.netlify.com/prev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Previous<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webringdemo.netlify.com/random<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Random<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webringdemo.netlify.com/next<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Next<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>webring-banner</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">async</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webringdemo.netlify.com/embed.js<span class="token punctuation">"</span></span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>This will automatically show the title, member count, maybe a logo. And it can be edited from a central location. It might look something like this:</p>
<webring-banner>
    <p>Member of the <a href="https://webringdemo.netlify.com/">An Example Webring</a> webring</p>
    <a href="https://webringdemo.netlify.com/prev">Previous</a>
    <a href="https://webringdemo.netlify.com/random">Random</a>
    <a href="https://webringdemo.netlify.com/next">Next</a>
</webring-banner>
<script async="" src="https://webringdemo.netlify.com/embed.js" charset="utf-8"></script>
<h2 id="rss-feeds" tabindex="-1">RSS Feeds</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-rss-feeds"><span class="sr-only">Permalink to “RSS Feeds”</span> <span aria-hidden="true">#</span></a><p>If a member publishes an RSS feed on their site, they can add that to the ring as well: the index page will generate an <a href="https://de.wikipedia.org/wiki/Outline_Processor_Markup_Language">OPML file</a>, so people can subscribe to all members at once.</p>
<h2 id="host-your-own-ring!" tabindex="-1">Host your own Ring!</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-host-your-own-ring"><span class="sr-only">Permalink to “Host your own Ring!”</span> <span aria-hidden="true">#</span></a><p>If you want to start your own webring, go ahead! Fork the <a href="https://github.com/maxboeck/webring">repository on Github</a> and follow the instructions there - It’s free and doesn’t take long!</p>
<aside class="signup js-signup-widget" data-nosnippet=""><div class="signup__front"><h2 class="signup__title">By the way...</h2><div class="signup__desc">I'm running an email list for people interested in personal websites and the IndieWeb! If you enjoy that kind of stuff, you can join here and I'll notify you whenever I publish a new post. No strings attached, unsubscribe anytime.</div><div class="signup__form"><form action="https://dev.us18.list-manage.com/subscribe/post" method="POST" class="form form--signup"><div class="form__body"><input type="hidden" name="u" value="64781452976687d0f4f2ea370" /> <input type="hidden" name="id" value="772b9208b5" /> <input type="hidden" name="SOURCE" value="/blog/webring-kit/" /><div class="form__fields"><p class="form__field"><label for="mce-FNAME" class="form__label">First Name (optional)</label> <input type="text" class="form__input" value="" name="FNAME" id="mce-FNAME" placeholder="Sam" /></p><p class="form__field"><label for="mce-EMAIL" class="form__label">Email Address</label> <input type="email" class="form__input" value="" name="EMAIL" id="mce-EMAIL" placeholder="sam@website.com" required="" /></p></div><div class="sr-only" aria-hidden="true"><input type="text" name="b_64781452976687d0f4f2ea370_772b9208b5" tabindex="-1" value="" /></div></div><div class="form__actions"><button type="submit" class="btn btn--primary" name="subscribe">Subscribe</button><div class="form__feedback js-signup-widget-feedback" hidden=""></div><div class="spinner"><div class="spinner__layercontainer"><div class="spinner__layer spinner__layer--1"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--2"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--3"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--4"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div></div></div></div></form></div></div><div class="signup__back js-signup-backside"></div><div class="signup__icon"><svg class="icon icon--check" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-check"></use></svg></div></aside>
<h2 id="read-more" tabindex="-1">Read More</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/webring-kit/h-read-more"><span class="sr-only">Permalink to “Read More”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://www.sonniesedge.net/posts/webrings">Webrings</a> by Charlie Owen, further elaborating on Tatiana’s tweet.</li>
<li><a href="https://bryanlrobinson.com/blog/2019/02/07/bring-fansites-back-to-the-web/">Let’s bring Fan Sites and webrings back!</a> by Bryan Robinson, who wrote a first implementation of webring lambda functions.</li>
</ul>

            ]]></content>
        </entry>
        <entry>
            <title>Using Webmentions in Eleventy</title>
            <link href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/"/>
            <updated>2019-01-10T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/using-webmentions-on-static-sites/</id>
            <content type="html"><![CDATA[
                <p class="lead">In last week's post, I talked about syndicating content from a static site to Twitter. But getting content out is only half the challenge.</p>
<p>The real value of social media (apart from the massive ad revenue and dystopian data mining) is in the reactions we get from other people. The likes, reposts and replies - they’re what makes it “social”. To gain control over our own content, we need to capture these interactions as well and pull them back to our sites. In indieweb terms, that’s known as <a href="https://indieweb.org/backfeed">“backfeed”</a>.</p>
<h2 id="hello-webmentions" tabindex="-1">Hello Webmentions</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-hello-webmentions"><span class="sr-only">Permalink to “Hello Webmentions”</span> <span aria-hidden="true">#</span></a><div class="callout callout--info"><span class="callout__icon"><svg class="icon icon--info" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-info"></use></svg></span><div class="callout__content"><p>A <a href="https://indieweb.org/Webmention">Webmention</a> is an open standard for a reaction to something on the web. It's currently in W3C recommendation status. When you link to a website, you can send it a Webmention to notify it.</p><p>It's comparable to pingbacks, except that webmentions contain a lot more information than a simple &quot;ping&quot;. They can be used to express likes, reposts, comments or other things.</p></div></div>
<p>To make a site support webmentions, it needs to declare an endpoint to accept them. That endpoint can be a script hosted on your own server, or in the case of static sites, a third-party service like <a href="https://webmention.io/">webmention.io</a>.</p>
<p>Webmention.io is a free service made by indieweb pioneer Aaron Parecki that does most of the groundwork of receiving, storing and organizing incoming webmentions for you. It’s awesome!</p>
<p>To use it, sign up for a free account there using the <a href="https://indieauth.com/">IndieAuth</a> process, then include a link tag in the <code>head</code> of your site:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pingback<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/mxb.dev/xmlrpc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>webmention<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/mxb.dev/webmention<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<h3 id="turning-social-media-interactions-into-webmentions" tabindex="-1">Turning social media interactions into webmentions</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-turning-social-media-interactions-into-webmentions"><span class="sr-only">Permalink to “Turning social media interactions into webmentions”</span> <span aria-hidden="true">#</span></a><p>Cool. So that’s all very nice, but the real party is still over at <em>[currently hip social network]</em>, you say. Nobody ever sends me any webmentions.</p>
<p>Well, while your platform of choice is still around, you can use a tool to automatically turn social media interactions into beautiful open webmentions. <a href="https://brid.gy/">Bridgy</a> is another free service that can monitor your Twitter, Facebook or Instagram activity and send a webmention for every like, reply or repost you receive.</p>
<p>So if you were to publish a tweet that contains a link back to your site, and somebody writes a comment on it, Bridgy will pick that up and send it as a webmention to your endpoint!</p>
<p>The resulting entry on webmention.io then looks something like this:</p>
<pre class="language-json"><code class="language-json">    <span class="token punctuation">{</span>
      <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"entry"</span><span class="token punctuation">,</span>
      <span class="token property">"author"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"card"</span><span class="token punctuation">,</span>
        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Sara Soueidan"</span><span class="token punctuation">,</span>
        <span class="token property">"photo"</span><span class="token operator">:</span> <span class="token string">"https://webmention.io/avatar/pbs.twimg.com/579a474c9b858845a9e64693067e12858642fa71059d542dce6285aed5e10767.jpg"</span><span class="token punctuation">,</span>
        <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://sarasoueidan.com"</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://twitter.com/SaraSoueidan/status/1022009419926839296"</span><span class="token punctuation">,</span>
      <span class="token property">"published"</span><span class="token operator">:</span> <span class="token string">"2018-07-25T06:43:28+00:00"</span><span class="token punctuation">,</span>
      <span class="token property">"wm-received"</span><span class="token operator">:</span> <span class="token string">"2018-07-25T07:01:17Z"</span><span class="token punctuation">,</span>
      <span class="token property">"wm-id"</span><span class="token operator">:</span> <span class="token number">537028</span><span class="token punctuation">,</span>
      <span class="token property">"wm-source"</span><span class="token operator">:</span> <span class="token string">"https://brid-gy.appspot.com/comment/twitter/mxbck/1022001729389383680/1022009419926839296"</span><span class="token punctuation">,</span>
      <span class="token property">"wm-target"</span><span class="token operator">:</span> <span class="token string">"https://mxb.dev/blog/layouts-of-tomorrow/"</span><span class="token punctuation">,</span>
      <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"content-type"</span><span class="token operator">:</span> <span class="token string">"text/plain"</span><span class="token punctuation">,</span>
        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"This looks great!"</span><span class="token punctuation">,</span>
        <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"This looks great!"</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token property">"in-reply-to"</span><span class="token operator">:</span> <span class="token string">"https://mxb.dev/blog/layouts-of-tomorrow/"</span><span class="token punctuation">,</span>
      <span class="token property">"wm-property"</span><span class="token operator">:</span> <span class="token string">"in-reply-to"</span><span class="token punctuation">,</span>
      <span class="token property">"wm-private"</span><span class="token operator">:</span> <span class="token boolean">false</span>
    <span class="token punctuation">}</span></code></pre>
<h3 id="but-wait%2C-there%E2%80%99s-more!" tabindex="-1">But wait, there’s more!</h3>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-but-wait2c-theree28099s-more"><span class="sr-only">Permalink to “But wait, there’s more!”</span> <span aria-hidden="true">#</span></a><p>The beauty of webmentions is that unlike with regular social media, reactions to your content are not limited to users of one site. You can combine comments from Facebook and Twitter with replies people posted on their own blogs. You can mix retweets and shares with mentions of your content in newsletters or forum threads.</p>
<p>You also have complete control over who and what is allowed in your mentions. Content silos often only allow muting or blocking on your own timeline, everyone else can still see unwanted or abusive @-replies. With webmentions, you’re free to moderate reactions however you see fit. Fuck off, Nazis!</p>
<h2 id="including-webmentions-in-static-sites" tabindex="-1">Including webmentions in static sites</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-including-webmentions-in-static-sites"><span class="sr-only">Permalink to “Including webmentions in static sites”</span> <span aria-hidden="true">#</span></a><p>Once the webmention endpoint is in place, we still need to pull the aggregated data down to our site and display it in a meaningful way.</p>
<p>The way to do this depends on your setup. Webmention.io offers an <a href="https://github.com/aaronpk/webmention.io#api">API</a> that provides data as a JSON feed, for example. You can query mentions for a specific URL, or get everything associated with a particular domain (allthough the latter is only available to site owners.)</p>
<p>My site uses <a href="https://11ty.io/">Eleventy</a>, which has a conventient way to pull in external data at build time. By providing a <a href="https://www.11ty.io/docs/data-js/#using-js-data-files">custom function</a> that queries the API, Eleventy will fetch my webmentions and expose them to the templates when generating the site.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// data/webmentions.js</span>
<span class="token keyword">const</span> <span class="token constant">API_ORIGIN</span> <span class="token operator">=</span> <span class="token string">'https://webmention.io/api/mentions.jf2'</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> domain <span class="token operator">=</span> <span class="token string">'mxb.dev'</span>
    <span class="token keyword">const</span> token <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">WEBMENTION_IO_TOKEN</span>
    <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token constant">API_ORIGIN</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">?domain=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>domain<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;token=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>token<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>

    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> feed <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> feed
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
        <span class="token keyword">return</span> <span class="token keyword">null</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p><em>The feed can now be accessed in the {{ webmentions }} variable.</em></p>
<p>Here’s <a href="https://github.com/maxboeck/mxb/blob/master/src/data/webmentions.js">the complete function</a> if you’re interested. Other static site generators offer similiar methods to fetch external data.</p>
<h2 id="parsing-and-filtering" tabindex="-1">Parsing and Filtering</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-parsing-and-filtering"><span class="sr-only">Permalink to “Parsing and Filtering”</span> <span aria-hidden="true">#</span></a><p>Now that the raw data is available, we can mold it into any shape we’d like. For my site, the processing steps look like this:</p>
<ul>
<li>Filter the raw data for each post, only include mentions targeting that URL.</li>
<li>Only allow “mentions” and “replies” in the comment section. Likes and Reposts go somewhere else.</li>
<li>Remove entries that dont have any content to display.</li>
<li>Sanitize the output - strip HTML tags, truncate long content, etc.</li>
</ul>
<pre class="language-js"><code class="language-js"><span class="token comment">// filters.js</span>
<span class="token keyword">const</span> sanitizeHTML <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'sanitize-html'</span><span class="token punctuation">)</span>

<span class="token keyword">function</span> <span class="token function">getWebmentionsForUrl</span><span class="token punctuation">(</span><span class="token parameter">webmentions<span class="token punctuation">,</span> url</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> allowedTypes <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'mention-of'</span><span class="token punctuation">,</span> <span class="token string">'in-reply-to'</span><span class="token punctuation">]</span>

    <span class="token keyword">const</span> <span class="token function-variable function">hasRequiredFields</span> <span class="token operator">=</span> <span class="token parameter">entry</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> <span class="token punctuation">{</span> author<span class="token punctuation">,</span> published<span class="token punctuation">,</span> content <span class="token punctuation">}</span> <span class="token operator">=</span> entry
        <span class="token keyword">return</span> author<span class="token punctuation">.</span>name <span class="token operator">&amp;&amp;</span> published <span class="token operator">&amp;&amp;</span> content
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> <span class="token function-variable function">sanitize</span> <span class="token operator">=</span> <span class="token parameter">entry</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> <span class="token punctuation">{</span> content <span class="token punctuation">}</span> <span class="token operator">=</span> entry
        <span class="token keyword">if</span> <span class="token punctuation">(</span>content<span class="token punctuation">[</span><span class="token string">'content-type'</span><span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">'text/html'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            content<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token function">sanitizeHTML</span><span class="token punctuation">(</span>content<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> entry
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> webmentions
        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">entry</span> <span class="token operator">=></span> entry<span class="token punctuation">[</span><span class="token string">'wm-target'</span><span class="token punctuation">]</span> <span class="token operator">===</span> url<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">entry</span> <span class="token operator">=></span> allowedTypes<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>entry<span class="token punctuation">[</span><span class="token string">'wm-property'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>hasRequiredFields<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>sanitize<span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>In Eleventy’s case, I can set that function as a custom filter to use in my post templates.<br />
Each post will then loop over its webmentions and output them underneath.</p>
<pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- webmentions.njk --></span>
{% set mentions = webmentions | getWebmentionsForUrl(absoluteUrl) %}
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>webmentions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
{% for webmention in mentions %}
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>webmentions__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
        {% include 'webmention.njk' %}
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span>
{% endfor %}
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ol</span><span class="token punctuation">></span></span></code></pre>
<p>You can see the result by scrolling down to the <a href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/#webmentions">end of this post</a> (if there are any replies 😉).</p>
<aside class="signup js-signup-widget" data-nosnippet=""><div class="signup__front"><h2 class="signup__title">By the way...</h2><div class="signup__desc">I'm running an email list for people interested in personal websites and the IndieWeb. If you enjoy that kind of stuff, you can join here and I'll notify you whenever I publish a new post. No strings attached, unsubscribe anytime.</div><div class="signup__form"><form action="https://dev.us18.list-manage.com/subscribe/post" method="POST" class="form form--signup"><div class="form__body"><input type="hidden" name="u" value="64781452976687d0f4f2ea370" /> <input type="hidden" name="id" value="772b9208b5" /> <input type="hidden" name="SOURCE" value="/blog/using-webmentions-on-static-sites/" /><div class="form__fields"><p class="form__field"><label for="mce-FNAME" class="form__label">First Name (optional)</label> <input type="text" class="form__input" value="" name="FNAME" id="mce-FNAME" placeholder="Sam" /></p><p class="form__field"><label for="mce-EMAIL" class="form__label">Email Address</label> <input type="email" class="form__input" value="" name="EMAIL" id="mce-EMAIL" placeholder="sam@website.com" required="" /></p></div><div class="sr-only" aria-hidden="true"><input type="text" name="b_64781452976687d0f4f2ea370_772b9208b5" tabindex="-1" value="" /></div></div><div class="form__actions"><button type="submit" class="btn btn--primary" name="subscribe">Subscribe</button><div class="form__feedback js-signup-widget-feedback" hidden=""></div><div class="spinner"><div class="spinner__layercontainer"><div class="spinner__layer spinner__layer--1"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--2"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--3"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div><div class="spinner__layer spinner__layer--4"><div class="spinner__circle-clipper spinner__left"><div class="spinner__circle"></div></div><div class="spinner__gap-patch"><div class="spinner__circle"></div></div><div class="spinner__circle-clipper spinner__right"><div class="spinner__circle"></div></div></div></div></div></div></form></div></div><div class="signup__back js-signup-backside"></div><div class="signup__icon"><svg class="icon icon--check" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-check"></use></svg></div></aside>
<h2 id="client-side-rendering" tabindex="-1">Client-Side Rendering</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-client-side-rendering"><span class="sr-only">Permalink to “Client-Side Rendering”</span> <span aria-hidden="true">#</span></a><p>Because static sites are, well, static - it’s possible that new mentions have happened since the last build. To keep the webmention section up-to-date, there’s an extra step we can take: client side rendering.</p>
<p>Remember I said the webmention.io API can be used to only fetch mentions for a specific URL? That comes in handy now. After the page has loaded, we can fetch the latest mentions for the current URL and re-render the static webmention section with them.</p>
<p>On my site, I used <a href="https://preactjs.com/">Preact</a> to do just that. It has a very small (~3kB) footprint and lets me use React’s mental model and JSX syntax. It would probably also have been possible to re-use the existing <code>nunjucks</code> templates, but this solution was the easiest and most lightweight for me.</p>
<p>I essentially used the same logic here as I did in the static build, to ensure matching results. The rendering only starts after the API call returned valid data though - if anything goes wrong or the API is unavailable, there will still be the static content as a fallback.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// webmentions/index.js</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> h<span class="token punctuation">,</span> render <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'preact'</span>
<span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">'./App'</span>

<span class="token operator">...</span>
<span class="token keyword">const</span> rootElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'webmentions'</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>rootElement<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">fetchMentions</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">data</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token function">render</span><span class="token punctuation">(</span><span class="token operator">&lt;</span>App webmentions<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">,</span> rootElement<span class="token punctuation">)</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
            console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<div class="callout callout--info"><span class="callout__icon"><svg class="icon icon--info" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-info"></use></svg></span><div class="callout__content"><p>I also made an <a href="https://github.com/maxboeck/eleventy-webmentions">Eleventy Starter Template</a> with basic webmention support, using some of the techniques in this post. Check it out!</p></div></div>
<p>There are of course still some missing pieces, most notably the ability to send outgoing webmentions to URLs linked to in your own blog posts. <s>I might have to look into that.</s></p>
<h2 id="update%3A-outgoing-webmentions!" tabindex="-1">Update: Outgoing Webmentions!</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-update3a-outgoing-webmentions"><span class="sr-only">Permalink to “Update: Outgoing Webmentions!”</span> <span aria-hidden="true">#</span></a><p><a href="https://remysharp.com/">Remy Sharp</a> has recently published a very useful new tool that takes care of handling <strong>outgoing</strong> webmentions for you. <a href="https://webmention.app/">Webmention.app</a> is a platform agnostic service that will check a given URL for links to other sites, discover if they support webmentions, then send a webmention to the target.</p>
<p>You can use that service in a number of ways, including your own command line. If you host your site on Netlify though, it’s also very straightforward to integrate it <a href="https://webmention.app/docs#how-to-integrate-with-netlify">using deployment webhooks</a>!</p>
<h2 id="jekyll-plugin" tabindex="-1">Jekyll Plugin</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-jekyll-plugin"><span class="sr-only">Permalink to “Jekyll Plugin”</span> <span aria-hidden="true">#</span></a><p>My implementation was heavily inspired by Aaron Gustafson’s excellent Jekyll Plugin (link below), which goes even further with customization and caching options. If you’re running a Jekyll site, use that for almost instant webmention support 👍.</p>
<h2 id="further-resources" tabindex="-1">Further Resources</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/using-webmentions-on-static-sites/h-further-resources"><span class="sr-only">Permalink to “Further Resources”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://www.w3.org/TR/webmention/">W3C Recommendation</a> for the Webmention Standard</li>
<li><a href="https://github.com/aarongustafson/jekyll-webmention_io">Webmention.io Jekyll Plugin</a> by Aaron Gustafson</li>
<li><a href="https://indieweb.org/Webmention-developer#IndieWeb_Examples">Indieweb Examples</a> of people using Webmentions on their sites</li>
</ul>

            ]]></content>
        </entry>
        <entry>
            <title>You&#39;re Offline</title>
            <link href="https://mavericktimes.in/blog/youre-offline/"/>
            <updated>2017-07-12T00:00:00Z</updated>
            <id>https://mavericktimes.in/blog/youre-offline/</id>
            <content type="html"><![CDATA[
                <figure>
  <img src="https://mavericktimes.in/blog/youre-offline/notification-sample.jpg" alt="" />
</figure>
<p class="lead">A truly responsive website should adapt to all kinds of situations. Besides different viewport sizes, there are other factors to consider. A change in connectivity is one of them.</p>
<p>Earlier this week, I was sitting in a train on my way to speak at a local meetup. InterCity trains in Austria all have WIFI now, so I was doing some last-minute work on my slides online. Train WIFI being what it is though, the network wasn’t exactly reliable. The connection kept dropping everytime we went through a tunnel or too many passengers were logged on.</p>
<p>This is quite a common scenario. People are on the move, network coverage can be poor, internet connections fail. Luckily, we can prepare our websites for this and make them more resilient by <a href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">building them offline-first</a>.</p>
<p>Offline support is awesome, however your users might not be aware of these capabilites - and they shouldn’t have to be. In some cases they might not even know that they’ve gone offline. That’s why it’s important to communicate what’s going on.</p>
<p>Chances are not <strong>every</strong> part of your site will work offline. Certain things may not be cached, others may require server interaction. This is fine of course, but the interface should reflect that. Just like a responsive layout adapts to changes in viewport size, your offline-optimized site should adapt to changes in connectivity.</p>
<h2 id="checking-for-offline" tabindex="-1">Checking for Offline</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/youre-offline/h-checking-for-offline"><span class="sr-only">Permalink to “Checking for Offline”</span> <span aria-hidden="true">#</span></a><p>The key ingredients here are the <code>offline</code> event and the <code>navigator.onLine</code> property. By combining them, we can check for network changes and react accordingly.</p>
<p>Here’s an example of a simple connectivity check:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> isOffline <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> checkConnectivity<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// when the page has finished loading,</span>
<span class="token comment">// listen for future changes in connection</span>
<span class="token keyword">function</span> <span class="token function">checkConnectivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'online'</span><span class="token punctuation">,</span> updateStatus<span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'offline'</span><span class="token punctuation">,</span> updateStatus<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// check if we're online, set a class on &lt;html> if not</span>
<span class="token keyword">function</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> navigator<span class="token punctuation">.</span>onLine <span class="token operator">!==</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    isOffline <span class="token operator">=</span> <span class="token operator">!</span>navigator<span class="token punctuation">.</span>onLine<span class="token punctuation">;</span>
    document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">'is-offline'</span><span class="token punctuation">,</span> isOffline<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>⚠️ Note: With the <code>online</code> event, there’s a slight possibility of false positives: A user might be connected to a network (which is interpreted as being online), but something higher up might block actual internet access. The <code>offline</code> event is a bit more reliable, in the sense that an “offline” user can be expected <strong>NOT</strong> to have access.</p>
<h2 id="get-notified" tabindex="-1">Get Notified</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/youre-offline/h-get-notified"><span class="sr-only">Permalink to “Get Notified”</span> <span aria-hidden="true">#</span></a><p>Now we want to display some kind of notification to offline users, so they know what’s going on. This can be done in a number of ways; however I would recommend using <code>aria-live</code> regions to make it accessible and have screen readers announce the connection change as well.</p>
<p>Using such a notification bar is pretty straightforward. First, define an element to display messages on your page:</p>
<pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- notification container --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> 
  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>notification<span class="token punctuation">"</span></span> 
  <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>notification<span class="token punctuation">"</span></span> 
  <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>assertive<span class="token punctuation">"</span></span> 
  <span class="token attr-name">aria-relevant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> 
  <span class="token attr-name">hidden</span>
<span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The <code>aria-live</code> attribute tells screen readers to announce changes to this element. “assertive” means it will interrupt whatever it is currently announcing at the time and prioritize the new message. The <code>aria-relevant</code> tells it to listen for changes in the text content of the element.</p>
<p>You can extend the handler function from before to populate the notification area whenever you detect that a user has gone offline:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token operator">...</span>
  <span class="token keyword">const</span> notification <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#notification'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>isOffline<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    notification<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'You appear to be offline right now.'</span><span class="token punctuation">;</span>
    notification<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    notification<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>
    notification<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is a very simple implementation - you can of course always get a bit fancier with an animated notification bar (or “toast message”). There are also some nice <a href="https://getmdl.io/components/index.html#snackbar-section">pre-made components</a> for this.</p>
<p>If you’re reading this on <a href="https://mxb.at/">my site</a>, you can see a version of these notifications in action if you simply switch off your WIFI for a second.<br />
Go ahead, I’ll wait.</p>
<p>If you’re somewhere else or your browser doesn’t support service worker / offline events, here’s how this could look:</p>
<div class="extend" style="margin-top:2rem;">
  <video poster="https://mavericktimes.in/blog/youre-offline/offline-notification.png" width="944" height="528" alt="Offline Notification" controls="">
    <source src="https://mavericktimes.in/blog/youre-offline/offline-notification.webm" type="video/webm" />
    <source src="https://mavericktimes.in/blog/youre-offline/offline-notification.mp4" type="video/mp4" />
  </video>
</div>
<h2 id="telling-the-user-what%E2%80%99s-available" tabindex="-1">Telling the User what’s available</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/youre-offline/h-telling-the-user-whate28099s-available"><span class="sr-only">Permalink to “Telling the User what’s available”</span> <span aria-hidden="true">#</span></a><p>Notifications are a good start, but it would be even nicer if we could give the user some visual indication of which parts they can actually use offline, and which not.</p>
<p>To do this, we can loop over all the links on page load and check their <code>href</code> against the cache. If they point to a cached resource (e.g. will work offline), they get a special class.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> links <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'a[href]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>links<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">link</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>link<span class="token punctuation">.</span>href<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">ignoreSearch</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      link<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'is-cached'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Once the <code>offline</code> event fires, we toggle a class on the body and visually disable all links that aren’t cached. This should only apply to URLs, so we can ignore <code>tel:</code>, <code>mailto:</code> and anchor links.</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">.is-offline </span><span class="token punctuation">{</span>
  <span class="token comment">/* disable all links to uncached pages */</span>
  <span class="token property">a</span><span class="token punctuation">:</span><span class="token function">not</span><span class="token punctuation">(</span>.is-cached<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token property">cursor</span><span class="token punctuation">:</span>not-allowed<span class="token punctuation">;</span>
    <span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
    <span class="token property">opacity</span><span class="token punctuation">:</span>.5<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">/* ignore anchors, email and phone links */</span>
  <span class="token selector">a[href^="#"],
  a[href^="mailto"],
  a[href^="tel"] </span><span class="token punctuation">{</span>
    <span class="token property">cursor</span><span class="token punctuation">:</span>auto<span class="token punctuation">;</span>
    <span class="token property">pointer-events</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
    <span class="token property">opacity</span><span class="token punctuation">:</span>1<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="offline-forms" tabindex="-1">Offline Forms</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/youre-offline/h-offline-forms"><span class="sr-only">Permalink to “Offline Forms”</span> <span aria-hidden="true">#</span></a><p>Another way we might use this is to prevent users from filling out forms. Most forms pass data to the server and require a connection to work, so they won’t be very useful when offline.</p>
<p>What’s worse is that users might not know there is a problem until it’s too late: imagine filling out a lengthy form and finally hitting the submit button, only to find a network connection error page and all your inputs gone. That’s frustrating.</p>
<pre class="language-scss"><code class="language-scss"><span class="token comment">/* Disable Forms when offline */</span>
<span class="token selector">.is-offline form </span><span class="token punctuation">{</span>
  <span class="token property">position</span><span class="token punctuation">:</span>relative<span class="token punctuation">;</span>
  <span class="token property">opacity</span><span class="token punctuation">:</span>.65<span class="token punctuation">;</span>
  <span class="token property">cursor</span><span class="token punctuation">:</span>not-allowed<span class="token punctuation">;</span>
  <span class="token property">pointer-events</span><span class="token punctuation">:</span>none<span class="token punctuation">;</span>
  
  <span class="token selector"><span class="token parent important">&amp;</span>::after </span><span class="token punctuation">{</span>
    <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">'Sorry, you\'re offline.'</span><span class="token punctuation">;</span>
    <span class="token property">display</span><span class="token punctuation">:</span>block<span class="token punctuation">;</span>
    <span class="token property">position</span><span class="token punctuation">:</span>absolute<span class="token punctuation">;</span>
    <span class="token property">top</span><span class="token punctuation">:</span>50%<span class="token punctuation">;</span>
    <span class="token property">left</span><span class="token punctuation">:</span>50%<span class="token punctuation">;</span>
    <span class="token property">transform</span><span class="token punctuation">:</span><span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span>#FFFFFF<span class="token punctuation">;</span>
    <span class="token property">background-color</span><span class="token punctuation">:</span>#2D2D2D<span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span>1rem 2rem<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<figure>
  <img src="https://mavericktimes.in/blog/youre-offline/form-offline.jpg" alt="a disabled form with the words 'sorry, youre offline' in a box on top" />
  <figcaption>No contact forms in offline country.</figcaption>
</figure>
<p>That effectively disables every form on the page, indicating that this functionality is currently not available. Depending on what your form does, you might also consider applying these styles just to the submit button - that way a user could pre-fill the form (possibly even have it validated in JS), and then submit it once they come back online.</p>
<p>If you’re doing this, remember to suppress “submit on enter” as well, and make sure the user knows why submitting won’t work at the moment.</p>
<p><strong>UPDATE:</strong> I found a better way to handle this - by storing form submissions in <code>localStorage</code> and then checking for them once the connection comes back online. Read about it in <a href="https://mxb.at/blog/offline-forms/">“Offline-Friendly Forms”</a>.</p>
<h2 id="further-reading" tabindex="-1">Further Reading</h2>
<a class="heading-anchor" href="https://mavericktimes.in/blog/youre-offline/h-further-reading"><span class="sr-only">Permalink to “Further Reading”</span> <span aria-hidden="true">#</span></a><ul>
<li>Intro: <a href="http://offlinefirst.org/">OfflineFirst.org</a></li>
<li>Google Developers: <a href="https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/offline-for-pwa">Offline Storage</a></li>
<li>Jake Archibald at I/O 2016: <a href="https://www.youtube.com/watch?v=cmGr0RszHc8">Building offline-first PWAs</a> (Video)</li>
</ul>

            ]]></content>
        </entry></feed>
