<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Good Blog, Karl</title>
        <link>https://blog.karlswedberg.com/</link>
        <description>Random quasi-technical stuff I want to remember</description>
        <lastBuildDate>Thu, 01 Jan 2026 21:19:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Feed for node.js</generator>
        <language>en-US</language>
        <image>
            <title>Good Blog, Karl</title>
            <url>https://blog.karlswedberg.com/_assets/icon-512.DOTNU4s0.png</url>
            <link>https://blog.karlswedberg.com/</link>
        </image>
        <copyright>© Karl Swedberg. License: Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) (https://creativecommons.org/licenses/by-sa/3.0/)</copyright>
        <atom:link href="https://blog.karlswedberg.com/rss2.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[My web development feed]]></title>
            <link>https://blog.karlswedberg.com/my-web-development-feed/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/my-web-development-feed/</guid>
            <pubDate>Mon, 13 Mar 2023 14:17:23 GMT</pubDate>
            <description><![CDATA[A collection of links (and an OPML file) to web development blogs]]></description>
            <content:encoded><![CDATA[<p>After reading a <a href="https://ericwbailey.website/published/i-doubled-down-on-rss/">couple</a> <a href="https://timkadlec.com/remembers/2023-02-23-investing-in-rss/">posts</a> suggesting the resurgence of RSS, I decided to clean up my own list of web development feeds. Some of the super-old-school blogs I followed (like <a href="https://456bereastreet.com">456 Berea Street</a>) haven't seen new posts in 7+ years, so I pruned those. Others trickle in once or twice a year, which is totally fine by me, while a few are so prodigious in their output it makes me wonder how the people behind them find time for their paid work. I've been using a feed reader as my main source of web-development news and information for more than fifteen years. I can't remember what I started with, but <a href="https://netnewswire.com/">NetNewsWire</a> was my go-to app for a number of years until its developer neglected it and let it stagnate. I switched to <a href="https://reederapp.com/">Reeder</a>, a Mac/iOS app, which I've stuck with ever since, even though I've heard great things about NetNewsWire's resurrected versions.</p>
<p>Anyway, I thought maybe someone could benefit from seeing what I keep in rotation. If you have any suggestions, or if there are any glaring omissions, I hope you'll let me know (you can find ways to reply below). You can check out these links individually, or you can import the full list's <a href="https://blog.karlswedberg.com/webdev.opml">OPML file</a>.</p>
<ul>
<li><a href="https://24ways.org/">24 ways</a> (<a href="http://feeds.feedburner.com/24ways">feed</a>)</li>
<li><a href="https://2ality.com/">2ality - technology, life</a> (<a href="https://2ality.com/feeds/posts.atom">feed</a>)</li>
<li><a href="https://www.a11yproject.com/">The A11Y Project</a> (<a href="https://www.a11yproject.com/feed/feed.xml">feed</a>)</li>
<li><a href="https://adactio.com/journal/">Adactio</a> (<a href="https://adactio.com/journal/rss">feed</a>)</li>
<li><a href="https://adactio.com/links/">Adactio: Links</a> (<a href="https://adactio.com/links/rss">feed</a>)</li>
<li><a href="https://adrianroselli.com">Adrian Roselli</a> (<a href="https://adrianroselli.com/feed">feed</a>)</li>
<li><a href="https://axesslab.com">Axess Lab</a> (<a href="https://axesslab.com/feed/">feed</a>)</li>
<li><a href="https://benfrain.com">Ben Frain</a> (<a href="https://benfrain.com/feed/">feed</a>)</li>
<li><a href="https://bocoup.com">Bocoup Web Log</a> (<a href="https://bocoup.com/feed">feed</a>)</li>
<li><a href="https://bradfrost.com/blog/category/link/">Brad Frost - Links</a> (<a href="https://bradfrost.com/blog/category/link/feed/">feed</a>)</li>
<li><a href="https://bradfrost.com/blog/">Brad Frost - Posts</a> (<a href="https://bradfrost.com/blog/category/post/feed/">feed</a>)</li>
<li><a href="https://www.bram.us">Bram.us</a> (<a href="https://www.bram.us/feed/">feed</a>)</li>
<li><a href="https://brettterpstra.com">BrettTerpstra.com</a> (<a href="http://brett.trpstra.net/brettterpstra">feed</a>)</li>
<li><a href="https://brucelawson.co.uk">Bruce Lawson's personal site</a> (<a href="https://brucelawson.co.uk/feed/">feed</a>)</li>
<li><a href="https://bryanlrobinson.com">bryanlrobinson.com</a> (<a href="https://bryanlrobinson.com/feed.xml">feed</a>)</li>
<li><a href="https://chriscoyier.net/">Chris Coyier</a> (<a href="https://chriscoyier.net/feed/">feed</a>)</li>
<li><a href="https://clagnut.com/">Clagnut</a> (<a href="https://clagnut.com/feeds/fullposts.xml">feed</a>)</li>
<li><a href="https://codersblock.com">Coder's Block</a> (<a href="https://codersblock.com/rss.xml">feed</a>)</li>
<li><a href="https://blog.codinghorror.com/">Coding Horror</a> (<a href="https://blog.codinghorror.com/rss/">feed</a>)</li>
<li><a href="https://tympanus.net/codrops/">Codrops</a> (<a href="https://tympanus.net/codrops/feed/">feed</a>)</li>
<li><a href="https://css-irl.info/">CSS In Real Life</a> (<a href="https://css-irl.info/rss.xml">feed</a>)</li>
<li><a href="https://css-tricks.com">CSS-Tricks</a> (<a href="http://feeds.feedburner.com/CssTricks">feed</a>)</li>
<li><a href="https://daverupert.com">daverupert.com</a> (<a href="https://daverupert.com/atom.xml">feed</a>)</li>
<li><a href="https://dbushell.com">dbushell.com</a> (<a href="https://dbushell.com/rss.xml">feed</a>)</li>
<li><a href="https://digwp.com">Digging Into WordPress</a> (<a href="https://digwp.com/feed/">feed</a>)</li>
<li><a href="https://davidflanagan.com/">djf.log()</a> (<a href="https://davidflanagan.com/feed.xml">feed</a>)</li>
<li><a href="https://eligrey.com/blog/">Eli Grey</a> (<a href="http://feeds.feedburner.com/eligrey">feed</a>)</li>
<li><a href="https://every-layout.dev">The Every Layout Blog</a> (<a href="https://every-layout.dev/feed.xml">feed</a>)</li>
<li><a href="https://geoffgraham.me">Geoff Graham</a> (<a href="https://geoffgraham.me/feed/">feed</a>)</li>
<li><a href="https://gomakethings.com">Go Make Things</a> (<a href="https://gomakethings.com/feed/index.xml">feed</a>)</li>
<li><a href="https://blog.karlswedberg.com/">Good Blog, Karl</a> (<a href="https://blog.karlswedberg.com/rss.xml">feed</a>)</li>
<li><a href="https://heydonworks.com">HeydonWorks</a> (<a href="https://heydonworks.com/feed.xml">feed</a>)</li>
<li><a href="https://hicks.design/journal">hicksdesign - journal</a> (<a href="https://hicks.design/feeds/journal/">feed</a>)</li>
<li><a href="https://hidde.blog">Hidde's blog</a> (<a href="https://hidde.blog/feed">feed</a>)</li>
<li><a href="https://html5accessibility.com/stuff/">HTML Accessibility</a> (<a href="https://html5accessibility.com/stuff/feed/">feed</a>)</li>
<li><a href="http://blog.methvin.com/">Inconsistently Insightful</a> (<a href="http://blog.methvin.com/feeds/posts/default">feed</a>)</li>
<li><a href="https://informationisbeautiful.net">Information is Beautiful</a> (<a href="http://feeds.feedburner.com/InformationIsBeautiful">feed</a>)</li>
<li><a href="https://blog.jim-nielsen.com">Jim Nielsen’s Blog</a> (<a href="https://blog.jim-nielsen.com/feed.xml">feed</a>)</li>
<li><a href="https://notes.jim-nielsen.com">Jim Nielsen’s Notes</a> (<a href="https://notes.jim-nielsen.com/feed.xml">feed</a>)</li>
<li><a href="https://johnresig.com">John Resig’s Blog</a> (<a href="https://johnresig.com/category/blog/feed/">feed</a>)</li>
<li><a href="https://keithjgrant.com">Keith J. Grant</a> (<a href="https://keithjgrant.com/posts/index.xml">feed</a>)</li>
<li><a href="https://kittygiraudel.com">Kitty Giraudel</a> (<a href="https://kittygiraudel.com/rss/index.xml">feed</a>)</li>
<li><a href="https://lea.verou.me">Lea Verou</a> (<a href="http://leaverou.me/feed/">feed</a>)</li>
<li><a href="https://letsencrypt.org/">Let's Encrypt</a> (<a href="https://letsencrypt.org/feed.xml">feed</a>)</li>
<li><a href="https://lisilinhart.info/">Lisi Linhart's Blog</a> (<a href="https://lisilinhart.info/feed.xml">feed</a>)</li>
<li><a href="https://www.matuzo.at">Manuel Matuzović - Today I Learned</a> (<a href="https://www.matuzo.at/feed_til.xml?rev=1672334848998">feed</a>)</li>
<li><a href="https://www.matuzo.at">Manuel Matuzović - Web development blog</a> (<a href="https://www.matuzo.at/feed.xml?rev=1672334848998">feed</a>)</li>
<li><a href="https://www.marcozehe.de/">Marco's accessibility blog</a> (<a href="https://www.marcozehe.de/rss.xml">feed</a>)</li>
<li><a href="https://marcus.io/blog">marcus.io</a> (<a href="https://marcus.io/feed">feed</a>)</li>
<li><a href="https://mathiasbynens.be/notes">Mathias Bynens</a> (<a href="https://mathiasbynens.be/notes.atom">feed</a>)</li>
<li><a href="https://michaelnthiessen.com">Michael Thiessen's Blog</a> (<a href="https://michaelnthiessen.com/rss.xml">feed</a>)</li>
<li><a href="https://mikeindustries.com/blog/">Mike Davidson</a> (<a href="http://mikeindustries.com/blog/feed">feed</a>)</li>
<li><a href="https://www.mikestreety.co.uk">Mike Street's Blog</a> (<a href="https://www.mikestreety.co.uk/rss.xml">feed</a>)</li>
<li><a href="https://miketaylr.com/posts/">Mike Taylr Dot Com Web Log</a> (<a href="https://miketaylr.com/posts/rss.xml">feed</a>)</li>
<li><a href="https://paul.kinlan.me/">Modern Web Development with Chrome</a> (<a href="https://paul.kinlan.me/index.xml">feed</a>)</li>
<li><a href="https://humanwhocodes.com/blog/">Human Who Codes</a> (<a href="https://humanwhocodes.com/feeds/blog.xml">feed</a>)</li>
<li><a href="https://andrewdupont.net">Painfully Obvious » JavaScript</a> (<a href="https://andrewdupont.net/categories/web/development/javascript/feed/">feed</a>)</li>
<li><a href="https://www.paulirish.com/">Paul Irish</a> (<a href="http://feeds2.feedburner.com/paul-irish">feed</a>)</li>
<li><a href="https://perishablepress.com">Perishable Press</a> (<a href="https://perishablepress.com/feed/">feed</a>)</li>
<li><a href="https://ma.tt">Photo Matt</a> (<a href="https://ma.tt/feed/">feed</a>)</li>
<li><a href="https://www.phpied.com">phpied.com</a> (<a href="https://www.phpied.com/feed/">feed</a>)</li>
<li><a href="https://quirksmode.org/blog/">QuirksBlog</a> (<a href="https://quirksmode.org/blog/index.xml">feed</a>)</li>
<li><a href="https://nolanlawson.com">Read the Tea Leaves</a> (<a href="https://nolanlawson.com/feed/">feed</a>)</li>
<li><a href="https://remysharp.com">remy sharp's b:log</a> (<a href="https://remysharp.com/feed.xml">feed</a>)</li>
<li><a href="https://paulbakaus.com/">Renaissance Geek</a> (<a href="https://paulbakaus.com/rss/">feed</a>)</li>
<li><a href="https://blog.reybango.com">Rey Bango</a> (<a href="https://blog.reybango.com/feed/">feed</a>)</li>
<li><a href="https://shapeof.com/">The Shape of Everything</a> (<a href="http://shapeof.com/rss.xml">feed</a>)</li>
<li><a href="https://simonwillison.net/">Simon Willison's Weblog</a> (<a href="https://simonwillison.net/atom/everything/">feed</a>)</li>
<li><a href="https://snook.ca/">snook.ca - a collection of tips, tricks and bookmarks in web development</a> (<a href="https://snook.ca/posts/index.rss">feed</a>)</li>
<li><a href="https://www.stefanjudis.com/">Stefan Judis Web Development</a> (<a href="https://www.stefanjudis.com/rss.xml">feed</a>)</li>
<li><a href="https://stopdesign.com/">Stopdesign</a> (<a href="https://stopdesign.com/feed">feed</a>)</li>
<li><a href="http://www.stubbornella.org/content/">Stubbornella</a> (<a href="http://www.stubbornella.org/content/feed/">feed</a>)</li>
<li><a href="https://www.xanthir.com/blog/">Tab Atkins</a> (<a href="https://www.xanthir.com/blog/atom/">feed</a>)</li>
<li><a href="https://www.taniarascia.com">Tania Rascia | RSS Feed</a> (<a href="https://www.taniarascia.com/rss.xml">feed</a>)</li>
<li><a href="http://thecodebarbarian.com">TheCodeBarbarian.com</a> (<a href="http://thecodebarbarian.com/feed.xml">feed</a>)</li>
<li><a href="https://meyerweb.com/eric/thoughts/">Thoughts From Eric</a> (<a href="http://meyerweb.com/eric/thoughts/feed/?scope=summary">feed</a>)</li>
<li><a href="https://timotijhof.net">Timo Tijhof</a> (<a href="https://timotijhof.net/feed/">feed</a>)</li>
<li><a href="https://tink.uk/">Tink - Léonie Watson - On technology, food &amp; life in the digital age</a> (<a href="https://tink.uk/feed.xml">feed</a>)</li>
<li><a href="https://www.trysmudford.com/blog/">Trys Mudford's Blog</a> (<a href="https://www.trysmudford.com/blog/index.xml">feed</a>)</li>
<li><a href="https://medium.com/vue-mastery">Vue Mastery</a> (<a href="https://medium.com/feed/vue-mastery">feed</a>)</li>
<li><a href="https://www.zachleat.com/web/">zachleat.com</a> (<a href="https://www.zachleat.com/web/feed/">feed</a>)</li>
<li><a href="https://wordpress.org/news/">WordPress Development Blog</a> (<a href="https://wordpress.org/news/feed/">feed</a>)</li>
<li><a href="https://helloanselm.com/writings">XML-RSS Feed for Anselm Hannemann’s Writings</a> (<a href="https://helloanselm.com/feed/rss">feed</a>)</li>
<li><a href="https://www.zeldman.com/">Zeldman Daily Report</a> (<a href="https://www.zeldman.com/feed/">feed</a>)</li>
<li><a href="https://zellwk.com/">Zell Liew</a> (<a href="https://zellwk.com/feed.xml">feed</a>)</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making links big]]></title>
            <link>https://blog.karlswedberg.com/making-links-big/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/making-links-big/</guid>
            <pubDate>Sun, 26 Feb 2023 19:40:04 GMT</pubDate>
            <description><![CDATA[Back in the days before HTML5, wrapping a link around block-level elements wasn't semantically appropriate. Even now, some situations—like linking a table row—call for a more creative solution]]></description>
            <content:encoded><![CDATA[<p>Back in the days before HTML5, our pretty little doctype — <code>&lt;!DOCTYPE html&gt;</code> — looked more like <code>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;</code>. If you were especially masochistic, you used <code>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;</code> with the <code>application/xhtml+xml</code> mime type.</p>
<div class="grid grid-cols-2 gap-2">
<Image class="inline-block" alt="Valid HTML 4.0.1 badge" src="https://upload.wikimedia.org/wikipedia/commons/e/e1/Valid_HTML_4.0.1.svg" />
<Image class="inline-block" alt="Valid XHTML 1.0 badge" src="https://upload.wikimedia.org/wikipedia/commons/1/1f/Valid_XHTML_1.0.svg" />
</div>
Standards compliance was all the rage, and webmasters loved to show that their sites passed the <a href="https://validator.w3.org/">W3C markup validator</a>. But such compliance meant that you couldn't have some nice things, like block-level elements inside a link (aka "anchor" element). For example, this was not valid:
<pre><code class="language-html">&lt;a href=&quot;https://example.com&quot;&gt;
  &lt;h3&gt;heading&lt;/h3&gt;
  &lt;div&gt;excerpt&lt;/div&gt;
&lt;/a&gt;
</code></pre>
<p>With HTML 4.01, you could probably get away with it, though you might be shamed into removing your badge. With XHTML and the &quot;proper&quot; application/xhtml+xml mime type, however, a browser might stop rendering the page altogether.</p>
<p>At one point (many years ago) I resorted to writing a Big Link jQuery plugin to handle this scenario. If you assigned a class of <code>big-link</code> to a containing element, the plugin would find the first href attribute among its inner elements. Clicking anywhere in the container would trigger <code>location.href = [innerElement's href]</code>. All that was left was a splash of CSS to fake the link</p>
<pre><code class="language-css">.big-link {
  cursor: pointer;
}
.big-link:hover a {
  text-decoration: underline;
}
</code></pre>
<p>Something about that felt dirty, though. I mean, sure, it was &quot;progressive enhancement&quot; in a way, but the idea of using JavaScript to do what an anchor element gives us for free was like paying extra for a knock-off brand. In my experience, JavaScript solutions get more complicated the more I think about them, and this one is no exception. To be consistent with browsers, I'd also have to account for <kbd>⇧</kbd>+click, <kbd>^</kbd>+click, and <kbd>⌘</kbd>+click behaviors—which I'm sure I didn't even consider back then.</p>
<p>Fortunately, this issue is moot with HTML5, because it does allow block-level elements inside anchors. As long as the <code>&lt;a&gt;</code> is given a <code>display</code> value of <code>block</code> (or <code>flex</code> or <code>grid</code> etc.), its contents should behave and render as expected.</p>
<h2>Linking a table row</h2>
<p>And yet, even the more tolerant HTML5 doesn't account for every edge case. A table row, for example, can only have a <code>thead</code> or a <code>tbody</code> as its parent, and it can only have a <code>th</code> or <code>td</code> as its direct child. So, how can we link the entire row when we can neither wrap the <code>&lt;tr&gt;</code> nor wrap its children in a link?</p>
<p>One possibility that I've seen suggested is to wrap each table cell in a link with the same <code>href</code>, while giving all but the first link <code>tabindex=&quot;-1&quot;</code>:</p>
<pre><code class="language-html">&lt;tr&gt;
  &lt;td&gt;&lt;a href=&quot;https://example.com&quot;&gt;one&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;&lt;a tabindex=&quot;-1&quot; href=&quot;https://example.com&quot;&gt;two&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;&lt;a tabindex=&quot;-1&quot; href=&quot;https://example.com&quot;&gt;three&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
</code></pre>
<table class="ex-table">
<caption>Links have padding & display:block; tr:hover changes td background-color</caption>
<tbody class="not-prose">
<tr class="link-cells">
  <td><a href="https://example.com">one</a></td>
  <td><a tabindex="-1" href="https://example.com">two</a></td>
  <td><a tabindex="-1" href="https://example.com">three</a></td>
</tr>
</tbody>
</table>
<p>Better than nothing, I suppose, but still not ideal.</p>
<p>The best solution I know of here is to only include one link and use the <code>::after</code> pseudo-element to stretch that link across the row. If we had a table in which the first cell of each row included a link we wanted to stretch across, the CSS might look something like this [<em>Edit: I had to add a <code>transform</code> rule to the row to get it to work in Safari</em>]:</p>
<pre><code class="language-css">tr.link-row {
  position: relative;
  transform: translateX(0);
}
tr.link-row td:first-child &gt; a::after {
  content: &quot;&quot;;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
</code></pre>
<p>And here is the result, with only one link per row:</p>
<table class="ex-table">
<caption>One link per row, in the first column</caption>
<tbody class="not-prose">
<tr class="link-row">
  <td><a href="https://example.com/foo">one</a></td>
  <td>two</td>
  <td>three</td>
</tr>
  <tr class="link-row">
    <td><a href="https://example.com/bar">ayyyy</a></td>
    <td>beeeee</td>
    <td>seeeee</td>
  </tr>
</tbody>
</table>
<p>My only concern is that the non-link text (in columns 2 and 3 here) is no longer selectable by the user. Is this a major accessibility blunder? I suppose there's a way to accommodate user selection by listening for <code>mousedown</code> and <code>mousemove</code> events. But then we'd also have to account for double-clicking to select as well. Again, JavaScript solutions get complicated. I can't think of an elegant way around this.</p>
<p>I ran into this situation a few days ago, except the last cell in each row had a button that needed to be clickable. Fortunately, the button had a fixed width, so I just needed to adjust the link's width like so:</p>
<pre><code class="language-css">td:first-child &gt; a::after {
  /* other stuff */
  width: calc(100% - 3.75rem);
}
</code></pre>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rebuilding the site with Astro]]></title>
            <link>https://blog.karlswedberg.com/rebuilding-the-site-with-astro/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/rebuilding-the-site-with-astro/</guid>
            <pubDate>Sun, 19 Feb 2023 20:17:24 GMT</pubDate>
            <description><![CDATA[After 12 years, I decide to rebuild the site using Astro, a JavaScript-based static site generator]]></description>
            <content:encoded><![CDATA[<p>When I started this blog 12 years ago, I was playing around a bit with Ruby — mostly dabbling in Rails and fiddling with Rake files — so I figured <a href="https://jekyllrb.com/">Jekyll</a> would be a good vehicle for continuing my exploration. I loved the idea of writing blog posts in Markdown and having a build task convert everything to HTML, especially since I knew I'd be the only person blogging here and wouldn't need a content management system to support the less technically inclined.</p>
<p>The site worked pretty well, too. It loaded quickly and looked about as okay as you'd expect of something built by a design-challenged developer like myself. But after a couple years of posting sporadically for an audience of maybe a handful of people, I started to lose interest and let the site lay dormant.</p>
<h2>All in with JS</h2>
<p>Then in 2015 I came across <a href="https://metalsmith.io/">Metalsmith</a>, another static site generator, but this one written in JavaScript, my programming language BFF. Here was my chance to rebuild the site with the language I loved and, in the process, reignite my blogging flame. Well, I rebuilt it, but I must have lost my matches because I didn't even have a spark of interest in writing anything new.</p>
<p>Now here I am once more, slapping on a fresh new coat of paint. There wasn't anything particularly wrong with Metalsmith, but I hadn't been keeping up with its updates and I was getting tired of seeing all the security warnings that Snyk and dependabot were sending my way, even though I knew they were largely irrelevant. The vulnerabilities exist in an online context, and I only used the tool to build the site on my local machine. That's one of the advantages of a static site, after all.</p>
<p>This latest iteration of the blog is using <a href="https://astro.build">Astro</a>, another JavaScript-based static site generator. I've really enjoyed working with Astro. Most of the process has been pain free with only a few snags along the way. One question that has been dogging me, though, is, &quot;Why didn't I use <a href="https://www.11ty.dev/">Eleventy</a> instead?&quot; I've heard wonderful things about it, and I think its creator, <a href="https://www.zachleat.com/">Zach Leatherman</a>, is a super great guy. I honestly can't explain what drove me to use Astro. I don't regret it, but I still have that nagging feeling. Anyway, the <a href="https://github.com/kswedberg/blog.karlswedberg.com">new code</a> is up on Github.</p>
<h2>What I like about Astro</h2>
<p>There is a lot to like about Astro. First, the way it continues the tradition of creating URLs from a directory of Markdown files is a relief and a joy. I like its concept of &quot;collections&quot; and the flexibility with which it handles front matter. I could pretty much drop my existing files into a new directory, adjust some configuration, and have a baseline blog running with the new system in a matter of minutes. Second, the hot-module reloading is super fast. I could work on templates, layouts, and styles and receive nearly instantaneous updates in the browser.</p>
<p>The integrations are another terrific aspect of Astro. For this blog I included Tailwind CSS, sitemap, image, and compression integrations. I also initially had the RSS integration installed, but I swapped it out for a <a href="https://github.com/jpmonette/feed">node-based feed generator</a> that also supports Atom and JSON formats. The site had been using Atom, so I thought it would be rude to my 3 or 4 followers to discontinue that without warning. there is an unofficial Service Worker integration that I've been considering, but I'm not sure it's worth using Service Workers here. It's just a blog, not an app, and it loads incredibly fast. I can't imagine anyone wanting to continue navigating around the site in offline mode or adding it to their home screen, but if people start clamoring for it, I'll reconsider (haha). Astro also has an official Vue integration that I might install and play around with if I decide to build in some interactive elements to the site or write about some of the work I've done with Vue.</p>
<p>Overall I found the Astro documentation helpful and intuitive. I learn best through example, though, so I probably spent more time looking at starter templates and other projects built with Astro, especially <a href="https://github.com/cadecuddy/milkroll">cadecuddy/milkroll</a> and <a href="https://github.com/onwidget/astrowind/">onwidget/astrowind</a>.</p>
<h2>Struggles</h2>
<p>A few aspects of Astro have been harder than others. It seems like it's a Typescript-first framework. I realize Typescript has all sorts of advantages, but I've never quite latched onto the syntax. When I've used it, I've often found myself spending more time getting Typescript to work without complaining and less time writing the thing I'm actually trying to build. But I don't want to get off on a tangent here about Typescript, and I don't want to raise the ire of the legions of Typescript fans, so I'll just say that <em>for me</em> the Typescript focus was a bit of a challenge.</p>
<p>One feature of the site that took far too long to get working was the Markdown &quot;reading time&quot; extension. I saw it done well in the projects that were serving as my inspiration, but for some reason I couldn't get the <code>readingTime</code> property to show up in the frontmatter object when I tried to display it in certain places (maybe because I was using a collection?). It probably had to do with the order of processing various components, but I couldn't figure it out. Finally, I noticed that astrowind had its own utility function to create an array of posts, so I borrowed from that and imported that function for all pages (or feeds) that show one or more posts.</p>
<p>I was kind of excited about the Partytown integration, hoping to see how it loaded some third-party JavaScript in a Web Worker for me. Unfortunately, I wasn't able to get it to work.</p>
<h2>Changes</h2>
<p>While almost all of the content and most of the site's structure has remained the same, there have been a few changes from the last iteration:</p>
<ul>
<li><strong>Design</strong>: The design is the most noticeable difference. The font size is a bit bigger all around, and the look is a bit fresher, for lack of a better word. Also, it supports dark mode if the user has designated it at the system preference level. I thought about adding a &quot;theme switcher,&quot; but decided against it in an attempt to avoid unnecessary complexity. It's hard for me to imagine someone opting into dark mode at the OS level but wanting light mode on this site, or, conversely, wanting dark mode for this site but light mode everywhere else.</li>
<li><strong>Metadata</strong>: I've added a number of <code>&lt;meta&gt;</code> tags, mostly for Facebook and Twitter, even though I barely use Facebook and I killed my Twitter account last month.</li>
<li><strong>Feeds</strong>: As mentioned above, I kept the <a href="https://blog.karlswedberg.com/atom.xml">Atom</a> feed out of solidarity with my loyal users, but I also added <a href="https://blog.karlswedberg.com/rss.xml">RSS 2.0</a> and <a href="https://blog.karlswedberg.com/feed.json">JSON</a> feeds.</li>
<li><strong>Comments</strong>: I kept the old clunky Disqus commenting system on the site, but now it is &quot;opt in&quot; only. At the bottom of each blog post, the user now has to click the &quot;Load comments&quot; button to either read or write comments. I did this because I want the initial page load to be fast, most people probably don't care about comments, and Disqus seems to load a crap ton of JavaScript, some of which is probably there to track you. Some day I'll look into a less obnoxious way to have comments on a static site.</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HTML5's Web * Features]]></title>
            <link>https://blog.karlswedberg.com/html5-web-asterisk-features/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/html5-web-asterisk-features/</guid>
            <pubDate>Sun, 03 Nov 2013 14:47:24 GMT</pubDate>
            <description><![CDATA[A list of Web * APIs because sometimes I can't remember which is which]]></description>
            <content:encoded><![CDATA[<p>One thing I've noticed about HTML5 features is that a lot of them start with the word &quot;Web.&quot;  Also, some of them have slightly overlapping uses. This makes it difficult for me to remember which is which. I have the same problem when I meet a bunch of people who have the same basic body type and face shape. It's hard for me to tell one from the others. What follows is a cheat sheet for myself, an attempt to keep these APIs straight so I don't fumble around in my head the next time I need to pick one up.</p>
<h2>Web Sockets</h2>
<p>Two-way open connections for sending/receiving data.</p>
<p>They are more full-featured and robust than Server-Sent Events (SSE) in that SSE go one way: from server to browser. While Web Sockets are supported by more browsers than SSE are, they can't be polyfilled and need special server setup. SSE, on the other hand, can be polyfilled and are sent over simple http. If all you need is to send messages (typically as strings) across domains or from frame to frame, you might be better off using <code>onmessage</code>/<code>postMessage</code>.</p>
<ul>
<li><a href="http://www.w3.org/TR/websockets/">W3C Spec</a></li>
<li><a href="http://www.html5rocks.com/tutorials/eventsource/basics/">html5rocks article</a></li>
<li><a href="http://robertnyman.com/2010/10/22/introducing-html5-web-sockets-taking-bidirectional-communication-on-the-web-to-the-next-level-2/">Introduction by Robert Nyman</a></li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=websockets">Modern browsers and IE10+; not Android Browser</a></li>
</ul>
<h2>Web Workers</h2>
<p>Allow developers to run computationally intensive processes in background threads so they don't lock up the browser/UI.</p>
<p>This gets around the single-threaded nature of JavaScript.</p>
<ul>
<li><a href="http://www.w3.org/TR/workers/">W3C Spec</a></li>
<li><a href="https://developer.mozilla.org/En/Using_web_workers">MDN article</a></li>
<li><a href="http://www.html5rocks.com/en/tutorials/workers/basics/">html5rocks article</a> (provides good use cases)</li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=webworkers">Modern browsers and IE10+; not Android Browser</a></li>
</ul>
<h2>Web Components</h2>
<p>The W3C's <a href="http://www.w3.org/TR/2013/WD-components-intro-20130606/">Introduction to Web Components</a> lays it all out:</p>
<blockquote>
<p>The component model for the Web (&quot;Web Components&quot;) consists of five pieces:</p>
<ol>
<li><strong>Templates</strong>, which define chunks of markup that are inert but can be activated for use later.</li>
<li><strong>Decorators</strong>, which apply templates based on CSS selectors to affect rich visual and behavioral changes to documents.</li>
<li><strong>Custom Elements</strong>, which let authors define their own elements, with new tag names and new script interfaces.</li>
<li><strong>Shadow DOM</strong>, which encapsulates a DOM subtree for more reliable composition of user interface elements.</li>
<li><strong>Imports</strong>, which defines how templates, decorators, and custom elements are packaged and loaded as a resource.</li>
</ol>
</blockquote>
<ul>
<li>The <a href="http://www.polymer-project.org/">Polymer Project</a>, currently in Pre-Alpha, implements Web Components in its &quot;Elements (UI)&quot; modules. Support is limited to the latest version of self-updating browsers.</li>
<li>Mozilla's' <a href="http://mozilla.github.io/brick/">Brick</a>, &quot;UI Components for Modern Web Apps,&quot; currently aims to provide better browser support than Polymer does. Most components are designed to work in IE9+ and Grade A mobile browsers.</li>
<li>The Ember framework has a components module, as well. According to the <a href="http://emberjs.com/guides/components/">Ember Components Guide</a>, &quot;Ember's implementation of components hews as closely to the Web Components specification as possible. Once Custom Elements are widely available in browsers, you should be able to easily migrate your Ember components to the W3C standard and have them be usable by other frameworks.&quot;</li>
</ul>
<h2>Web Storage</h2>
<p>Allows data to be set and retrieved on the local storage (<code>window.localStorage</code>) and session storage (<code>window.sessionStorage</code>) objects.</p>
<p>It can be used when capacity requirements are too great for cookies to handle. Unlike cookies, Web (or DOM) Storage allows the storing of objects, not just strings. Also unlike cookies, Web Storage does not have a built-in expiration mechanism (other than <code>sessionStorage</code>), so if you need that you'll have to roll your own by adding an expiration timestamp property when setting and checking it against the current time when getting.</p>
<ul>
<li><a href="http://diveintohtml5.info/storage.html">diveintohtml5</a></li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=namevalue-storage">Modern browsers and IE8+</a></li>
</ul>
<h2>Web Notifications</h2>
<p>Notices that a web page can send outside of the browser at the system level so the user will see them even if the browser isn't the currently active app.</p>
<p>Not to be confused with <a href="http://www.w3.org/TR/webmessaging/">Web Messaging</a> (<code>onmessage</code>, <code>postMessage</code>, et al).</p>
<ul>
<li><a href="http://www.w3.org/TR/notifications/">W3C Spec</a></li>
<li><a href="http://www.html5rocks.com/tutorials/notifications/quick/">html5rocks tutorial</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/WebAPI/Using_Web_Notifications">MDN article</a></li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=notifications">Current Chrome, Firefox, Safari; no IE as of 11</a></li>
</ul>
<h2>WebRTC</h2>
<p><strong>R</strong>eal-<strong>T</strong>ime <strong>C</strong>ommunication for the web, without the need for plugins.</p>
<p>It looks really cool, but it's still early days (as of November 3, 2013).</p>
<ul>
<li><a href="http://www.w3.org/TR/webrtc/#peer-to-peer-connections">W3C Spec</a></li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=notifications">Prefixed Chrome and Firefox; no IE as of 11; virtually no mobile</a></li>
</ul>
<h2>WebGL</h2>
<p>Hardware-accelerated canvas graphic API.</p>
<p>Notably, WebGL is the only feature listed here that is not under the auspices of the W3C. It comes instead from the <a href="http://www.khronos.org/webgl/">Khronos Group</a>. So, it's not technically an HTML5 feature. It's also beyond my understanding.</p>
<ul>
<li><a href="http://www.khronos.org/webgl/">Khronos Spec</a></li>
<li><strong>Support</strong>: <a href="http://caniuse.com/#feat=webgl">Modern browsers and IE 11+</a>, as long as the user has up-to-date video drivers.</li>
</ul>
<h2>Web SQL</h2>
<p>A deprecated spec for indexed web storage, so don't use it.</p>
<p>Instead, use Web Storage (described above) or IndexedDB. If Safari support is required, you'll
need to use a <a href="https://github.com/axemclion/IndexedDBShim">polyfill</a>.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cleaning up dead git branches]]></title>
            <link>https://blog.karlswedberg.com/cleaning-up-dead-git-branches/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/cleaning-up-dead-git-branches/</guid>
            <pubDate>Sat, 13 Oct 2012 20:23:20 GMT</pubDate>
            <description><![CDATA[A super neat git trick for finding out which branches we can delete when we want to do a little housekeeping]]></description>
            <content:encoded><![CDATA[<p>At <a href="http://fusionary.com">Fusionary</a>, we create a lot of feature branches in git to isolate what we're
working on, and then we merge those branches into either the dev or master branch to prepare to deploy the changes. The system works very well, except that some projects seem to spawn a ton of old feature branches that get left out to rot. It's hard to know sometimes what to do with them, since we're not always entirely organized about such things.</p>
<p>The other day my coworker Ed showed me a super neat git trick for finding out which branches we can delete when we want to do a little housekeeping:</p>
<pre><code class="language-bash"># on the command line:
git branch --merged
</code></pre>
<p>If I had read the manual, I would have known this already; at the very end of the help for <code>git branch</code> these two lines appear:</p>
<blockquote>
<ul>
<li><code>--merged</code> is used to find all branches which can be safely deleted, since those branches are fully contained by <code>HEAD</code>.</li>
<li><code>--no-merged</code> is used to find branches which are candidates for merging into <code>HEAD</code>, since those branches are not fully contained by <code>HEAD</code>.</li>
</ul>
</blockquote>
<p>Nice.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[yeoman, ulimit, and unmet dependency]]></title>
            <link>https://blog.karlswedberg.com/yeoman-ulimit-and-unmet-dependency/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/yeoman-ulimit-and-unmet-dependency/</guid>
            <pubDate>Thu, 25 Jul 2013 13:25:25 GMT</pubDate>
            <description><![CDATA[Quick fix for a warning when running the Yeoman CLI]]></description>
            <content:encoded><![CDATA[<p>After installing Yeoman and some generators, I kept seeing a bunch of lines beginning with <code>npm WARN unmet dependency</code>. Something was amiss. A quick Google search brought me to a <a href="https://github.com/yeoman/yeoman/issues/1096">GitHub issue</a> from <a href="https://github.com/linhmtran168">a user</a> who was having the same problem. The fix, which the OP discovered himself (herself?), is to run this line in the Terminal:</p>
<pre><code class="language-bash">ulimit -n 10000
</code></pre>
<p>Once I did that, I just had to install Yeoman (<code>npm install -g yo</code>) and my preferred generators again, and my troubles were gone.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Recent reads]]></title>
            <link>https://blog.karlswedberg.com/recent-reads/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/recent-reads/</guid>
            <pubDate>Thu, 26 Apr 2012 23:05:39 GMT</pubDate>
            <description><![CDATA[A list of things I've read recently related to web development]]></description>
            <content:encoded><![CDATA[<p>Nothing sticks in my head long enough to put it down in writing these days, so I thought that instead of trying to write a sustained, coherent article, I'd quickly jot down a few things I've read recently that have caught my interest.</p>
<ul>
<li><a href="http://generatedcontent.org/post/21279324555/viewportunits">Responsive viewport units</a> by David Storey talks about the fairly new vh, vw, and vmin units. While currently support is meager—only found in Chrome Canary, IE 9, and some Motorola devices(?)—it's definitely something I'm going to keep an eye on.</li>
<li>The <a href="https://google.github.io/styleguide/jsguide.html">Google JavaScript Style Guide</a> gives some reasonable advice.</li>
<li>I really like where Rick Waldron is going with his <a href="https://github.com/rwldrn/idiomatic.js">Idiomatic.js</a> repo on GitHub. My own style differs from his in some ways (such as using single quotes rather than double quotes), but I'd still like to use his as a foundation for a JavaScript style guide at <a href="http://www.fusionary.com/">Fusionary</a> where I work. I am way too inconsistent in my own style, especially when it comes to putting space at the open and close of parentheses and brackets.</li>
<li>Lea Verou has an interesting piece on <code>background-attachment: local</code> and some cool effects you can achieve with it: <a href="http://lea.verou.me/2012/04/background-attachment-local/">Pure CSS scrolling shadows with background-attachment: local</a>. Unfortunately, Firefox doesn't support the <code>local</code> value.</li>
<li><a href="http://www.edankwan.com/lab/3dit">3D it!</a> is a very cool demo.</li>
<li><a href="http://ericbidelman.tumblr.com/post/21649963613/idb-filesystem-js-bringing-the-html5-filesystem-api">idb.filesystem.js - Bringing the HTML5 Filesystem API to More Browsers</a>: Whoa. Is it possible to make offline work well without the headache of AppCache? I need to investigate this more.</li>
<li>Ben Alman is rocking the house with <a href="https://github.com/cowboy/grunt">Grunt</a>, a &quot;task-based command line build tool for JavaScript projects.&quot; I want to start using this in my own projects.</li>
</ul>
<p>There are so many cool things happening on the frontiers of web development thse days that it gets a little overwhelming. I despair of ever being able to keep up.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ReWriteRule always considered first]]></title>
            <link>https://blog.karlswedberg.com/rewriterule-always-considered-first/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/rewriterule-always-considered-first/</guid>
            <pubDate>Sun, 23 Oct 2011 14:58:55 GMT</pubDate>
            <description><![CDATA[A reminder about priority order for Apache's mode_rewrite]]></description>
            <content:encoded><![CDATA[<p>So much of Apache's <i>mod_rewrite</i> is a mystery to me that I wonder if I'll ever be able to write anything but the most basic ruleset without multiple trials and errors. Today, though, I stumbled upon a nice bit of information in <i>Apache Cookbook</i> by Ken Coar and Rich Bowen that I'm sure will come in handy next time I start hacking away in the <i>.htaccess</i> file.</p>
<p>The authors present this snippet for giving users their own web space on a server:</p>
<pre><code class="language-apache">ReWriteEngine On
RewriteCond &quot;/home/$1/public_html&quot;
RewriteRule &quot;/([^/]+)/(.*)&quot; &quot;home/$1/public_html/$2&quot;
</code></pre>
<p>While I doubt I'll ever need to use that exact ruleset, this paragraph in the &quot;Discussion&quot; section — especially the part that I mark in bold — has helped, more than anything else I've read, to reduce my bewilderment over <i>mod_rewrite</i>:</p>
<blockquote>
<p>This rewrite ruleset takes advantage of a little-known fact about <i>mod_rewrite</i> — in particular, that <strong>a <i>RewriteRule</i> is always considered first, and, if it matches, the <i>RewriteCond</i> is evaluated after that</strong>. Consequently, we can use <code>$1</code> in the <i>RewriteCond</i> , even though the value of that variable is set in the <i>RewriteRule</i> appearing on the following line.</p>
</blockquote>
<h2>Note to self</h2>
<p>Commit this to memory: The <i>RewriteRule</i> is always considered first!</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My (mis)adventures with web video]]></title>
            <link>https://blog.karlswedberg.com/my-misadventures-with-web-video/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/my-misadventures-with-web-video/</guid>
            <pubDate>Fri, 13 Jan 2012 01:55:14 GMT</pubDate>
            <description><![CDATA[Problems loading video on web sites and ways to fix them]]></description>
            <content:encoded><![CDATA[<p>Over the past few weeks I've run into a number of problems while adding video to web sites as part of my job at <a href="http://www.fusionary.com/">Fusionary</a>. I'm recording them here so I can repeat as few of my mistakes and avoid as many of my previous issues as possible. I also strongly believe that <em>nobody should have to go through all the pain that I've gone through to get video to work consistently on the web</em>. I'll be adding to this blog post as I run across more problems.</p>
<h2>Embedded YouTube Videos in a Lightbox</h2>
<p>While most of the issues involve video served from the same site as the document, at least one problem appeared with YouTube videos shown in a lightbox. At some point I had to add <code>?wmode=transparent</code> to the iframe's <code>src</code> because the close button was appearing behind the video. However, in IE9 (maybe 64-bit mode only), <code>wmode=transparent</code> rendered the video as a solid black background. The controls appeared, the audio played, but no video. The workaround I came up with involved checking for an &quot;ie9&quot; class added to the <code>html</code> element with a conditional comment. Then, I'd add the <code>wmode=transparent</code> query param to a link's <code>href</code> (which was later used to build the YouTube iframe) if that class wasn't there. Here is what it sort of looked like:</p>
<pre><code class="language-js">  var wmode = /ie9/.test(document.documentElement.className) ? '' : 'wmode=transparent';

  if ( wmode ) {
    $('a.yt-link').prop('href', function(i, val) {
      val += this.search ? '&amp;' : '?';
      return val += wmode;
    });
  }
</code></pre>
<h2>HTML5 Video with a Flash &quot;fall-through&quot;</h2>
<p>The embedded YouTube issue was certainly interesting, but it was only a momentary diversion from the real (mis)adventures with web video. For most recent projects, I've been using the now-classic <a href="http://camendesign.com/code/video_for_everybody">Video for Everybody</a> markup popularized by Croc Kamen:</p>
<pre><code class="language-html">&lt;video id=&quot;video-1&quot; src=&quot;/videos/test.mp4&quot;
    poster=&quot;/img/video-still.jpg&quot;
    width=&quot;480&quot; height=&quot;320&quot;&gt;
  &lt;source type=&quot;video/mp4&quot; src=&quot;/videos/test.mp4&quot;&gt;
  &lt;source type=&quot;video/webm&quot; src=&quot;/videos/test.webm&quot;&gt;
  &lt;object type=&quot;application/x-shockwave-flash&quot;
      data=&quot;/assets/swf/flashmediaelement.swf&quot;&gt;
    &lt;param name=&quot;movie&quot; value=&quot;/assets/swf/flashmediaelement.swf&quot;&gt;
    &lt;param name=&quot;flashvars&quot; value=&quot;controls=true&amp;amp;file=/videos/test.mp4&quot;&gt;
    &lt;img src=&quot;/img/no-vid.png&quot; title=&quot;No video playback capabilities&quot;&gt;
  &lt;/object&gt;
&lt;/video&gt;
</code></pre>
<p>The one exception is that I'm not including an .ogv file, because Firefox 3.6 is the only browser I somewhat care about that could use it—and couldn't use .webm—and I don't mind giving it the Flash fall-through.</p>
<p>Along with this markup, I've been including the <a href="http://mediaelementjs.com/">mediaelement.js</a> script, which provides a consistent, cross-browser UI with HTML5-first video and a Flash &quot;fall-through.&quot; When a browser can't play native HTML5 video, the mediaelement.js script attempts to play the .mp4 video using Flash. This kind of thing is essential for reducing the amount of code forking I need to do while still letting <abbr title="Internet Explorer 8">IE8</abbr> and below users see videos, too.</p>
<h2>Video Files and Server Configuration (.htaccess)</h2>
<p>I know enough not to run into this problem when I start a project, but I sometimes forget to make sure everything is set up correctly when I jump into a project midstream to add video.</p>
<p><strong>Problem:</strong> HTML5 video won't play at all unless they are served correctly.</p>
<p><strong>Solution 1:</strong> If you're running an Apache server, set the proper MIME type for video files in the site's .htaccess file:</p>
<pre><code class="language-apache">  # video
  AddType video/ogg                      ogv
  AddType video/mp4                      mp4 m4v
  AddType video/webm                     webm
</code></pre>
<p><strong>Solution 2:</strong> Make sure that the server is not compressing (with gzip/deflate, etc.) those video files.</p>
<h2>HTML5 Video with Varnish</h2>
<p><strong>Problem:</strong> We've been using <a href="https://www.varnish-cache.org/">Varnish Cache</a> on a few sites and have been very impressed with the performance boost it has provided. Unfortunately, it doesn't play well with large video files. As soon as we turned Varnish on, the videos stopped working. Firefox showed NaN/NaN for the timeline, and other browsers just showed 00:00. It was as if the videos' metadata couldn't be read.</p>
<p><strong>Solution:</strong> Once we discovered what was going on, our super-duper server guy set up another port to serve the files without Varnish, and I set up a redirect for .webm and .mp4 files to point to the other port.</p>
<h2>IE8, Flash Fall-through, and F12 Developer Tools</h2>
<p><strong>Problem:</strong> Testing video playback in IE8 was a nightmare for a number of reasons. The worst of it had to do with the seemingly random breakage of video—another case of the black background for video while audio carries on its merry way.</p>
<p><strong>Solution:</strong> Make sure the F12 Developer Tools are turned off.</p>
<p><strong>Unsolved Mystery:</strong> I'm still not sure how to test video using Windows 7 and IE9 in IE8-compatibility mode, since that requires having the Developer Tools open. Anyone know of a workaround?</p>
<h2>Codec Issues with mp4 files</h2>
<p><strong>Problem:</strong> The videos weren't working in all webkit browsers.</p>
<p><strong>Solution:</strong> Make sure .mp4 files are encoded as H.264, not FFMpeg/MPEG-4.</p>
<h2>mediaelement.js and the Flash plugin path</h2>
<p>This one was my fault, but not immediately obvious. The mediaelement.js script is awesome, but in this one particular case, it's too awesome for me. When used as a jQuery plugin, the mediaelement player can be invoked like so:</p>
<pre><code class="language-js">  $('video').mediaelementplayer();
</code></pre>
<p><strong>Problem:</strong> By default, mediaelement.js assumes that the plugin is in the same path as the mediaelement.js file. If scripts are placed in a different folder on the production site (due to concatenating/minifying/caching, etc.), this awesome feature that works in the dev environment will no longer work when the site is live.</p>
<p><strong>Solution:</strong> Explicitly set the <code>pluginPath</code> option. For example (set it to the actual path on your own site):</p>
<pre><code class="language-js">$('video').mediaelementplayer({
  pluginPath: '/assets/scripts/lib/mediaelement/'
});
</code></pre>
<h2>Progressively downloading mp4 files through Flash</h2>
<p><strong>Problem:</strong> I was having a vexing problem in that the entire video had to load before it would start playing. It turns out that the video encoders I had been using, at least with the settings I had on them, were putting the &quot;moov atom&quot; at the end of the .mp4 file. This moov atom is the metadata contains the index for the entire file, and Flash needs to read it before it can start progressively loading (buffering) the video. Unfortunately, when the metadata is at the end of the file, Flash needs to download the whole thing before it can get to that information. So, I had to somehow get the moov atom to the beginning of the file.</p>
<p><strong>Solution 1, QTIndexSwapper:</strong> This is one of those things that I never would have known if I hadn't hit upon the magic combination of google words (<a href="http://www.google.com/search?ie=UTF-8&amp;q=can+flash+buffer+mp4+files">can flash buffer mp4 files</a>), which brought me to <a href="http://www.blogstitution.com/2010/08/universal-flash-ipod-video-codec-using-imovie-html5/">a blog post that laid it all out</a>. Most of the advice in that post wasn't relevant to my needs, but this part saved my bacon:</p>
<blockquote>
<p>Fortunately, Renaun Erickson over at Adobe has created a simply little AIR utility to fix this atom feed placement…: a program called &quot;<a href="http://renaun.com/blog/code/qtindexswapper/">QTindexswapper</a>&quot;.</p>
</blockquote>
<p><strong>Solution 2, moovrelocator</strong> Once I knew I could search for <a href="http://www.google.com/search?ie=UTF-8&amp;q=mp4+moov+atom">mp4 moov atom</a> I found a PHP class that also claims to &quot;move the moov.&quot; (I say &quot;claims&quot; only because I haven't tested it; I have no reason to doubt that it does what it says it does.) I may explore this option more later.</p>
<p><strong>Solution 3, Handbrake:</strong> One of those video encoders that put the moov atom in the wrong place for me was <a href="http://handbrake.fr/">Handbrake</a>. Now I know that it <em>can</em> put it at the start of the file if you check the <em>Web optimized</em> checkbox. However, the meaning of &quot;Web optimized&quot; wasn't clear to me, and <em>none of the Handbrake presets have it checked by default.</em> Seems obvious looking at it now, but when grasping at straws and having to consider Flash, Internet Explorer, JavaScript, HTML, VMWare, hardware acceleration, and other codec conundrums (FFMpeg, H.264, Frame rate, bitrate, etc.), it's easy to dismiss a checkbox like this as &quot;something that will probably degrade video quality.&quot;</p>
<p><img src="../../assets/img/handbrake.png" alt="Handbrake settings"></p>
<p>I saved a custom preset in Handbrake that I now use for converting to mp4, starting with the iPhone 4 preset and unchecking &quot;Large File Size&quot; and checking &quot;Web optimized.&quot;</p>
<h2>IE9, VMWare, and Hardware Acceleration</h2>
<p><strong>Problem:</strong> No HTML5 video in IE9 when loaded in a virtual machine.</p>
<p><strong>Solution:</strong> In VMWare Fusion, go to the Settings panel of the virtual machine running Windows 7. Choose <em>Display</em> and uncheck &quot;Accelerate 3D Graphics&quot;</p>
<p><img src="../../assets/img/no-accelerated-graphics.png" alt="VMWare Display Settings - 3D Disabled"></p>
<h2>Conclusion</h2>
<p>There are many opportunities to break video on the web. Have you come across any other problems? If you have any horror stories, cautionary tales, or success stories you'd like to share, please add a comment.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Forcing MS Office files to download]]></title>
            <link>https://blog.karlswedberg.com/forcing-ms-office-files-to-download/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/forcing-ms-office-files-to-download/</guid>
            <pubDate>Fri, 08 Jul 2011 02:51:46 GMT</pubDate>
            <description><![CDATA[Every time clients start putting up links to Microsoft Office files on their sites, I'm sent scrambling around my hard drive with a find ~/Sites -name ".htaccess" -exec grep "ms-word" '{}' \; -print command or searching the googles for htaccess...]]></description>
            <content:encoded><![CDATA[<p>Every time clients start putting up links to Microsoft Office files on their sites, I'm sent scrambling around my hard drive with a <code>find ~/Sites -name &quot;.htaccess&quot; -exec grep &quot;ms-word&quot; '{}' \; -print</code> command or searching the googles for <code>htaccess force download ms-word</code>. Fortunately, it doesn't take too long to find what I'm looking for:</p>
<pre><code>```apache
AddType application/vnd.ms-word.document.macroEnabled.12 .docm
AddType application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
AddType application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
AddType application/vnd.ms-powerpoint.template.macroEnabled.12 potm
AddType application/vnd.openxmlformats-officedocument.presentationml.template potx
AddType application/vnd.ms-powerpoint.addin.macroEnabled.12 ppam
AddType application/vnd.ms-powerpoint.slideshow.macroEnabled.12 ppsm
AddType application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
AddType application/vnd.ms-powerpoint.presentation.macroEnabled.12 pptm
AddType application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
AddType application/vnd.ms-excel.addin.macroEnabled.12 xlam
AddType application/vnd.ms-excel.sheet.binary.macroEnabled.12 xlsb
AddType application/vnd.ms-excel.sheet.macroEnabled.12 xlsm
AddType application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
AddType application/vnd.ms-excel.template.macroEnabled.12 xltm
AddType application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx
```
</code></pre>
<p>I suppose I could set them all to <code>application/octet-stream</code> or some such if I truly wanted to force the download, but I'm pretty sure that doing it the long way will allow the user to choose whether to open the file in the appropriate Office Application or save it. In any case, the clients seem to love it. Microsoft Word to your mother!</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mobile Safari debug console]]></title>
            <link>https://blog.karlswedberg.com/mobile-safari-debug-console/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/mobile-safari-debug-console/</guid>
            <pubDate>Tue, 02 Aug 2011 13:30:09 GMT</pubDate>
            <description><![CDATA[Late to the party, once again. Just discovered Debug Console in iPhone while looking through the Safari Developer Library and stumbling upon Safari on iPhone Tools: Safari on iPhone OS provides a debug console which reports messages, coding errors, warnings,...]]></description>
            <content:encoded><![CDATA[<p>Late to the party, once again. Just discovered Debug Console in iPhone while looking through the Safari Developer Library and stumbling upon <a href="https://developer.apple.com/library/safari/#codinghowtos/Mobile/Tools/_index.html#//apple_ref/doc/uid/DTS40008250">Safari on iPhone Tools</a>:</p>
<blockquote>
<p>Safari on iPhone OS provides a debug console which reports messages, coding errors, warnings, and tips. In the iPhone Simulator or on iPhone or iPod touch, launch the Settings application. Navigate to &quot;Safari&quot;, then to &quot;Developer&quot; and toggle the &quot;Debug Console&quot; switch to &quot;ON&quot;.</p>
</blockquote>
<p>This is going to help. A lot.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JSMake]]></title>
            <link>https://blog.karlswedberg.com/jsmake/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/jsmake/</guid>
            <pubDate>Tue, 21 Jun 2011 11:29:41 GMT</pubDate>
            <description><![CDATA[Yet another build script tool]]></description>
            <content:encoded><![CDATA[<p>I've been hacking around with Make, Rake, and Ant build files, learning just enough to remain dangerous. So far, I feel the most comfortable with Rake, the Ruby version of Make. I like how it provides access to Bash commands but also has its own API. Recently another tool caught my interest:</p>
<blockquote>
<p><a href="http://gimmi.github.com/jsmake/">JSMake</a> is a simple Javascript build program with capabilities similar to make</p>
</blockquote>
<p>The allure of a tool that would let me write my build scripts in JavaScript is strong. But it does make me wonder: What advantage, other than my familiarity with JavaScript, does JSMake have over other such tools? And is it worth my time to learn another <abbr title="Domain Specific Language">DSL</abbr> when others work just fine?</p>
<p>On a broader level, these questions touch on the issue that I, and I'm sure many other web developers, have regarding the balance between chasing after the newest, shiniest things and deepening my understanding of more established technologies.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rakefile]]></title>
            <link>https://blog.karlswedberg.com/rakefile/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/rakefile/</guid>
            <pubDate>Sun, 06 Mar 2011 22:13:38 GMT</pubDate>
            <description><![CDATA[My goal for this site is to make it as easy for me to use as possible. That's why I'm using Jekyll (which I'll blog about another time). I also want to automate as much of the blogging, version control,...]]></description>
            <content:encoded><![CDATA[<p>My goal for this site is to make it as easy for me to use as possible. That's why I'm using <a href="https://github.com/mojombo/jekyll">Jekyll</a> (which I'll blog about another time). I also want to automate as much of the blogging, version control, and deployment as possible. Up until today I was using a simple <code>rsync</code> alias to get the new files on the &quot;live&quot; hosted server. But I thought it would be more fun and more convenient to have a little more automation, so I scoured GitHub for some good examples and found <a href="https://github.com/appden/appden.github.com/blob/master/Rakefile">one written by Scott Kyle</a>, which I took and modified.</p>
<p>My favorite part is the <code>rake deploy</code> command. If I have uncommitted changes in the site repo, it'll abort the Rake operation and open GitX so I can commit my recent work. If everything is committed, it'll build the files, run <code>git push</code>, and then do the <code>rsync</code> update.</p>
<pre><code class="language-ruby"># Adapted from Scott Kyle's Rakefile
# http://github.com/appden/appden.github.com/blob/master/Rakefile

task :default =&gt; :server

desc 'Build site with Jekyll'
task :build do
  jekyll '--no-server --no-auto'
end

desc 'Build and start server with --auto'
task :server do
  jekyll '--server --auto'
end

desc 'git commit or push'
task :gitx do
  xed = false
  IO.popen('git status') do |io|
    io.each_line do |line|
      if ( line =~ /^#\s*modified:/ &amp;&amp; !xed ) then
        sh 'gitx'
        xed = true
        raise &quot;\n!!! Do a git commit first !!!\n\n&quot;
      end
    end
  end
  if ( !xed ) then
    sh 'git push'
    puts &quot;Committed files were pushed&quot;
  end
end

desc 'Build and deploy'
task :deploy =&gt; [:build, :gitx] do
  sh 'rsync -auz --progress _site/ uname@server:/path/to/file/'
end

def jekyll(opts = '')
  sh 'rm -rf _site'
  sh 'jekyll ' + opts
end
</code></pre>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating a new remote git repo]]></title>
            <link>https://blog.karlswedberg.com/creating-a-new-remote-git-repo/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/creating-a-new-remote-git-repo/</guid>
            <pubDate>Tue, 15 Mar 2011 01:24:52 GMT</pubDate>
            <description><![CDATA[It took me a while to track down how to set up a new git repo on my remote server hosted by DreamHost. I imagine that most, if not all, of this stuff probably applies to any shared hosting service....]]></description>
            <content:encoded><![CDATA[<p>It took me a while to track down how to set up a new git repo on my remote server hosted by DreamHost. I imagine that most, if not all, of this stuff probably applies to any shared hosting service. Here's what I have to do — in case I forget:</p>
<h2>One time only</h2>
<p>Before I do anything else, I need to make sure that git is installed on the dreamhost server. If it isn't, I need to install it. I can easily find instructions elsewhere, so no need to reproduce them here.</p>
<p>Also, I should have a common location where I can store all my git repos on the server. I'm putting them in <code>/home/username/git/</code>, where user name is my actual user name.</p>
<h2>Each new git project</h2>
<p>For the following instructions, assume that you've already set up a local git repo. Also, replace <code>username</code>, <code>mydomain</code>, and <code>PROJECTNAME</code> with the appropriate values.</p>
<h4>1. ON LOCAL: SSH into mydomain.com</h4>
<pre><code class="language-bash">ssh username@mydomain.com
</code></pre>
<h4>2. ON REMOTE: create new bare remote git directory</h4>
<pre><code class="language-bash">cd /home/username/git/
mkdir PROJECTNAME.git
cd PROJECTNAME.git
git  init --bare
</code></pre>
<h4>3. BACK ON LOCAL: cd into local git repo. do an initial commit</h4>
<pre><code class="language-bash">git push --all ssh://username@mydomain.com/home/username/git/PROJECTNAME.git

git remote add origin ssh://username@mydomain.com/home/username/git/PROJECTNAME.git
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
git fetch
git merge master
</code></pre>
<p>Combined with the <a href="https://blog.karlswedberg.com/Rakefile/"><code>rake deploy</code> command</a>, this little git setup makes updating and versioning my blog pretty painless.</p>
<h3>Hold the phone!</h3>
<p>After writing this post, I came across a <a href="http://toroid.org/ams/git-website-howto">really nice explanation</a> that goes into more detail and even shows how to set up a post-receive hook on the remote server. In fact, using a post-receive hook appears more convenient than what I'm doing. I may have to revisit my deployment situation.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Another media query detection script]]></title>
            <link>https://blog.karlswedberg.com/another-media-query-detection-script/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/another-media-query-detection-script/</guid>
            <pubDate>Sat, 19 Mar 2011 14:53:03 GMT</pubDate>
            <description><![CDATA[After I linked to a Media Query detection script written by Nicholas Zakas, Paul Irish pinged me to let me know about a gist he adapted from Scott Jehl. This one looks even cooler. It takes advantage of the new...]]></description>
            <content:encoded><![CDATA[<p>After I <a href="https://blog.karlswedberg.com/media-query-detection/">linked</a> to a Media Query detection script written by Nicholas Zakas, Paul Irish pinged me to let me know about <a href="https://gist.github.com/786768">a gist he adapted from Scott Jehl</a>. This one looks even cooler. It takes advantage of the new <a href="http://dev.w3.org/csswg/cssom-view/#dom-window-matchmedia">matchMedia</a> function when it's available, and defines its own when the native one isn't there:</p>
<pre><code class="language-js">if ( !(window.matchMedia) ){

  window.matchMedia = (function(doc,undefined){

  // ... lots of stuff ...

  })(document);

}
</code></pre>
<p>Here's the cool thing: That matchMedia function is immediately invoked. Now, why should that happen? Isn't the point of this thing to invoke it when I, as its consumer, want to call it from somewhere? Yep. But, it's not ready for me just yet. inside that function <em>it's returning another function</em>, which can then be called whenever I do <code>matchMedia(mymediaquery)</code>:</p>
<pre><code class="language-js">if ( !(window.matchMedia) ){

  window.matchMedia = (function(doc,undefined){
    // ... prep work ...

    return function(q){
      // ... other stuff ...
    };

  })(document);

}
</code></pre>
<p>There is also some result caching going on in there, so if you repeat the function call with the same argument, it won't do the work of figuring out support each time; instead, after the first time it'll just use the result it stored in the cache.</p>
<p>Very fun and cool stuff!</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[pushing an existing local repo to new github repo]]></title>
            <link>https://blog.karlswedberg.com/pushing-an-existing-local-repo-to-new-github-repo/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/pushing-an-existing-local-repo-to-new-github-repo/</guid>
            <pubDate>Thu, 14 Apr 2011 22:00:36 GMT</pubDate>
            <description><![CDATA[I usually start my projects with a local git repo and then at some later date decide to put it up on github.com. That's usually a no-brainer if I'm the one who creates the remote repo; GitHub provides clear instructions...]]></description>
            <content:encoded><![CDATA[<p>I usually start my projects with a local git repo and then at some later date decide to put it up on <a href="http://github.com/">github.com</a>. That's usually a no-brainer if I'm the one who creates the remote repo; GitHub provides clear instructions on the very next page. But if the project is for work and someone else has already created an empty remote repo, I don't get to see those instructions. So, here's what I need to do:</p>
<pre><code class="language-bash"> cd ~/Sites/local-repo-dir

 git remote add origin git@github.com:fusionary/remote-repo-name.git

 git push -u origin master
</code></pre>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[rsync shortcut]]></title>
            <link>https://blog.karlswedberg.com/rsync-shortcut/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/rsync-shortcut/</guid>
            <pubDate>Sun, 27 Feb 2011 04:53:40 GMT</pubDate>
            <description><![CDATA[The OS X Finder is the undisputed loser in the file management category. What's particularly crazy is that OS X has rsync built into the OS. I can never remember the flags to use for the various rsync options, so...]]></description>
            <content:encoded><![CDATA[<p>The OS X Finder is the undisputed loser in the file management category. What's particularly crazy is that OS X has rsync built into the OS. I can never remember the flags to use for the various rsync options, so I wrote a little bash function:</p>
<pre><code class="language-bash">function cps() {

  dirname=$1

  trailingslash=`expr $dirname : '\(.*\)/$'`
  if [[ ${trailingslash} == '' ]]; then
    dirname=&quot;${dirname}/&quot;
  fi

  OPTIONS=&quot;All New Update&quot;

  select OPTION in $OPTIONS
  do
    if [[ $OPTION = &quot;All&quot; ]]
    then
      COPYTYPE=&quot;-av&quot;
      break
    fi

    if [[ $OPTION = &quot;New&quot; ]]
    then
      COPYTYPE=&quot;-av --ignore-existing&quot;
      break
    fi

    if [[ $OPTION = &quot;Update&quot; ]]
    then
      COPYTYPE=&quot;-auv&quot;
      break
    fi

  done


  # echo &quot;You chose ${COPYTYPE}&quot;
  echo &quot;copying ${dirname} to ${2} ...&quot;
  rsync ${COPYTYPE} ${dirname} ${2}
  echo &quot;done!&quot;
}
</code></pre>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Media query detection]]></title>
            <link>https://blog.karlswedberg.com/media-query-detection/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/media-query-detection/</guid>
            <pubDate>Wed, 16 Mar 2011 20:55:52 GMT</pubDate>
            <description><![CDATA[This gist from Nicholas C. Zakas looks really handy: This function determines if the browser is currently in a particular media media mode. Use the same media query as you would in CSS or on a &lt;link&gt; element. I can...]]></description>
            <content:encoded><![CDATA[<p><a href="https://gist.github.com/08602e7d2ee448be834c">This gist</a> from Nicholas C. Zakas looks really handy:</p>
<blockquote>
<p>This function determines if the browser is currently in a particular media media mode. Use the same media query as you would in CSS or on a <code>&lt;link&gt;</code> element.</p>
</blockquote>
<p>I can see myself using it quite a bit. His usage examples look like this:</p>
<pre><code class="language-js">if (isMedia(&quot;screen and (max-width:800px)&quot;){
    //do something for the screen
}

if (isMedia(&quot;all and (orientation:portrait)&quot;)){
    //react to portrait mode
}
</code></pre>
<p>I'm thinking it might be even more useful for me to detect <code>!isMedia('something)</code> and fallback to js stuff in those cases. Not sure yet. Still need to ponder.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[quick object merge]]></title>
            <link>https://blog.karlswedberg.com/quick-object-merge/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/quick-object-merge/</guid>
            <pubDate>Fri, 25 Feb 2011 05:38:37 GMT</pubDate>
            <description><![CDATA[Sometimes I load a small init.js file in the &lt;head&gt; and load all the other scripts before the closing &lt;body&gt; tag, and it's nice to have a few utility functions. This is one of those functions I use every now...]]></description>
            <content:encoded><![CDATA[<p>Sometimes I load a small init.js file in the <code>&lt;head&gt;</code> and load all the other scripts before the closing <code>&lt;body&gt;</code> tag, and it's nice to have a few utility functions. This is one of those functions I use every now and then:</p>
<pre><code class="language-js">var FM = FM || {};

(function() {

  // utility function to merge objects.
  FM.extend = function() {
    var args = Array.prototype.slice.call( arguments ),
        al = args.length,
        firstArg = al === 1 ? FM : args.shift();

    while (--al &gt; -1) {
      var arg = args[al];
      if (typeof arg  == 'object') {
        for (var prop in arg) {
          firstArg[ prop ] = arg[ prop ];
        }
      }
    }

    return firstArg;
  };

})();
</code></pre>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[bash-it]]></title>
            <link>https://blog.karlswedberg.com/bash-it/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/bash-it/</guid>
            <pubDate>Sun, 27 Feb 2011 22:59:21 GMT</pubDate>
            <description><![CDATA[Lately I've been dabbling with bash scripting, mostly setting up aliases and very simple functions to make my life on the command line a little easier and more efficient. A couple weeks ago I stumbled upon a bash-scripting gold mine...]]></description>
            <content:encoded><![CDATA[<p>Lately I've been dabbling with bash scripting, mostly setting up aliases and very simple functions to make my life on the command line a little easier and more efficient. A couple weeks ago I stumbled upon a bash-scripting gold mine called &quot;bash-it.&quot; It's a <a href="https://github.com/revans/bash-it">GitHub Repo</a> with lots of custom aliases, completions for Git, Rake, SSH, Homebrew, and others, command prompt themes, custom functions, and a bunch of other goodies. I already <a href="https://github.com/kswedberg/bash-it">forked it</a> and sent a pull request for my own command prompt theme.</p>
<p>If my 20-year-old self could see my 43-year-old self, he would be amazed by how incredibly geeky I've become.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JavaScript Garden]]></title>
            <link>https://blog.karlswedberg.com/javascript-garden/</link>
            <guid isPermaLink="false">https://blog.karlswedberg.com/javascript-garden/</guid>
            <pubDate>Sat, 12 Mar 2011 14:24:52 GMT</pubDate>
            <description><![CDATA[
JavaScript Garden is a growing collection of documentation about the most quirky parts of the JavaScript programming language.

]]></description>
            <content:encoded><![CDATA[<blockquote>
<p><a href="http://bonsaiden.github.com/JavaScript-Garden/">JavaScript Garden</a> is a growing collection of documentation about the most quirky parts of the JavaScript programming language.</p>
</blockquote>
]]></content:encoded>
        </item>
    </channel>
</rss>