Over the past weekend I finally did a thing I had been wanting to do for quite a while. I created an automated page of interesting (to me) links with a little commentary around each one. It’s called Linkity Link. I often joke that if I could make a living from surfing the web, I would do it in a heartbeat. This isn’t that though.
I’ve been loving the human web (cozy web? small web?) since a friend from the KU computer lab showed up with a copy of Mosaic. I have literally never stopped. The web has always been filled with some of the most amazing, generous, curious and lovely people I’ve ever (and never) met. Yes yes, it’s also fill with awful people, but so is the world and I really try not to give the awful people space in my head or in my writing. The web is also filled with garbage and, increasingly, AI slop that is changing the web as we know it. And it is not doing so for the better, at least not yet.
What has always made the web great are the humans. To this day, there are few things that excite me more than stumbling upon someone I have never heard of, some blog I have never read, where someone is sharing something of themselves. It could be words, art, music, a video, a zine, whatever! This happens every single day. Nearly every evening when my kids are in bed and I knock out some work, I go surfing. And I always find things. And when I find anything remotely interesting to me, I bookmark it. Today those bookmarks live in Raindrop. Before that they were in Pinboard. And before that they were in del.icio.us.
After all these years, I’ve curated around 66,000 bookmarks, mostly for myself. I share a lot of them with people directly, many of them show up on this blog in some form, but now I have a way to publish the most recent 18 links and even use a single one as a headline. The links reflect my interests and attention. I generally don’t post news. There are plenty of other places to get news in plain text.
Now for the nerdy details. I made Linkity Link with Claude Code, which is my coding assistant of choice. I run it in Terminal and Cursor. You can check out the repo on GitHub. It started with a long, rambling prompt that essentially described what I wanted to create, some of the values and some of the features. After that, it was a lot of back-and-forth, yes to that, absolutely not to that and after around four hours of that, it was pretty close to what you see today.
There is no build process, no framework, just good old HTML, CSS, and vanilla JavaScript. Here’s the stack:
- HTML: Semantic markup, nothing fancy
- CSS: Custom styles with a retro pixel font (PressStart2P)
- JavaScript: Vanilla JS for fetching and displaying bookmarks
- Raindrop.io API: Where the bookmarks live
- Hosting: GitHub Pages + Netlify with a custom domain
The entire app is client-side. When someone visits the site, the browser loads the static HTML/CSS/JS files, JavaScript fires off API calls to Raindrop.io, bookmarks are fetched and rendered into the DOM and everything happens in the visitor’s browser. The end.
You might ask - why not use React? Or Next.js? For a simple bookmark site, vanilla JavaScript is perfect. There’s no build process, which means nothing to break, fast-loading means happy visitors and it’s easy (for me) to understand, which means I can maintain it on my own (with some help from Claude).
Speaking of maintaining, instead of having to maintain a database or JSON file of bookmarks, Linkity Link pulls directly from Raindrop.io collections. Raindrop is already the place where I’m creating bookmarks as I browse, so I figured if I could integrate with Raindrop’s API, that would probably be the way to go. There are two collections – one called “Linkity Link” (for all of the links) and another called “Linkity Link Headline” (for the, uh, headline).
The app makes parallel API requests on load:
const [headlineBookmarks, regularBookmarks] = await Promise.all([
fetchHeadlineBookmarks().catch(() => []),
fetchBookmarks().catch(() => [])
]);
Each request hits the Raindrop API with specific parameters:
sort=-created
- Most recent firstperpage=18
- Limit results (orperpage=1
for headline)
The site uses a personal access token stored right in config.js
, which under normal circumstances I probably wouldn’t have done, but in this case, since the token only has read access to bookmarks I’m already making public, who cares?
const RAINDROP_CONFIG = {
TEST_TOKEN: 'your-token-here',
BASE_URL: 'https://api.raindrop.io/rest/v1',
// ...
}
No login flow needed, no OAuth, just straight API calls.
How about the cursor?! Instead of the default cursor, visitors get an animated ASCII character that cycles through different symbols:
const cursorChars = ['+', 'x', '*', 'o', '•', '○'];
Click anywhere and you’ll see ASCII particles float up and fade away. Pure CSS animations, no libraries needed.
The headline link cycles through rainbow colors using a CSS animation:
@keyframes rainbow {
0% { color: #ff0000; }
14% { color: #ff8c00; }
28% { color: #ffd700; }
/* ... and so on */
}
The bookmarks distribute across three columns using a simple algorithm:
const itemsPerColumn = Math.ceil(bookmarks.length / 3);
const columnIndex = Math.floor(index / itemsPerColumn);
On mobile, it collapses to a single column. No grid framework needed, just flexbox.
I feel great about what I was able to accomplish in a matter of a few hours over a weekend and I’m not sure how much more I will add to this in terms of features. Since pushing it live, I’ve made some speed improvements, cleaned up the code and added support for RSS. If there’s anything else you’d like to see, drop me a note and let me know.