I recently wrote about adding bookmarks to my website. While planning and doing that, a slightly larger idea came to my mind: building a directory of websites that provide a curated collection of bookmarks and/or links to other websites.
As far as I know, there’s no such directory.
A quick question I posted on Mastodon didn’t surface one either:

Original link to the post from Feb. 7th: @ttntm/111891597461871340
It took me another week to find a suitable (and cheap enough) TLD for the project and I eventually bought BUKMARK.CLUB on Feb. 17th.
I used a little spare time on the following weekend and built an MVP using Eleventy while my wife spent a little of her time on the logo:

Building the Website
There are 2 sections of code that might be worth highlighting:
1, Generating the Directory Index
I’ve recycled the logic that builds this website’s blog archive, a bit of code originally taken from a post by Darek Kay called Group posts by year in Eleventy, and made some changes:
config.addCollection('directory', (collection) => {
return _.chain(collection.getFilteredByGlob('./src/directory/*.md'))
.sort((a, b) => a.data.title.localeCompare(b.data.title))
.groupBy((item) => String(item.data.title).toUpperCase()[0])
.toPairs()
.value()
})This collection sorts directory entries alphabetically and groups them based on the first letter of their respective title.
The function passed to sort() is using String.prototype.localeCompare() (MDN), groupBy() takes the first letter of each site’s name and uppercases it.
Rendering the directory uses two consecutive loops that process the collection:
<h2>Index</h2>
<ul class="flex flex-wrap gap1 mb2">
{% for letter, letterPages in collections.directory %}
<li>
<a href="#section-{{ letter }}">[{{ letter }}]</a>
</li>
{% endfor %}
</ul>
{% for letter, letterPages in collections.directory %}
<article class="{% if not loop.last %}mb2{% endif %}">
<div class="...">
<h2 id="section-{{ letter }}" class="m0" style="border: 0;">
[{{ letter }}]
</h2>
<a class="block" href="#top" title="Back to top">[^]</a>
</div>
<ul class="grid grid3 gap2 my1">
{% for page in letterPages %}
{% include "card.njk" %}
{% endfor %}
</ul>
</article>
{% endfor %}
2, Updating the Directory Stats
I’m using a Node.js script for this task. It was inspired by a post called Find the Newest File in Directory Using NodeJS.
const fs = require('fs')
const path = require('path')
function getMostRecentFile(dir) {
const files = orderRecentFiles(dir)
return files.length
? {
total: files.length-1,
result: files[0]
}
: undefined
}
function orderRecentFiles(dir) {
return fs.readdirSync(dir)
.filter((file) => fs.lstatSync(path.join(dir, file)).isFile())
.map((file) => ({
file,
btime: fs.lstatSync(path.join(dir, file)).birthtime
}))
.sort((a, b) => b.btime.getTime() - a.btime.getTime())
}
function main() {
const {
total,
result
} = getMostRecentFile('./src/directory/')
fs.writeFileSync('./src/_data/info.json', JSON.stringify({
directorySize: total,
mostRecentUpdate: result.btime
}))
}
main()The script checks a folder (./src/directory/) and orders the files based on the time they were created (birthtime). It then proceeds to write an info.json data file into Eleventy’s _data folder. Both directorySize and mostRecentUpdate can be used in templates without any further configuration. The only thing I had to do, was to define a custom formatDate filter.
In case you noticed: the lack of error handling is not just lazy - it should alert contributors of the fact that something’s wrong before committing changes that would otherwise lead to missing data.
–
Right now, the BUKMARK.CLUB is a 0 JS, sub 100 kB website that uses less than 500 lines of CSS.
Directory content is stored in Markdown files that Eleventy currently processes in less than 1 second. Thanks to Eleventy, it “just works”, which is great for a small side project like that.