This portfolio was implemented with ClojureScript. It is a Single Page Application that leverages reagent, a minimalistic interface between ClojureScript and React.
For frontend management, re-frame is used. To compile the cljs code and perform hot reloading, I used figwheel-main.
The routing is done with reitit.
The markdown content is converted into hiccup (a clojure-friendly markup) and the post/vignette configurations use standard YAML frontmatter which is validated at compile time with a malli schema.
At compile time, RSS feeds are generated for blog posts using clj-rss. Multiple feeds can be configured (e.g., "clojure" for Clojure-specific posts, "all" for all blog posts).
The app is deployed on Netlify via GitHub Actions every time a branch is merged to master. Build artifacts (JS bundles, RSS feeds) are generated during CI/CD and are not committed to the repository.
- babashka to run the different project tasks.
All the posts/vignettes are written in standard markdown files in content folder.
The UI supports light/dark mode and is responsive.
At the top of each markdown file, you can provide YAML frontmatter to configure post metadata such as title, date, tags, image, github repos, article links, and RSS feed inclusion. A ## TLDR section can be added to provide a summary that appears in post vignettes.
When you create a pull request (PR) to master, Netlify provides a preview of what the new app version would look like.
For development, starting a clj/cljs REPL with figwheel allows you to have the local changes reflected on port 9500 on file save.
Each post has its own markdown file in the folder of the page it belongs to.
For instance:
.
βββ about
β βββ aboutme.md
βββ blog
β βββ some-article.md
Each markdown file uses standard YAML frontmatter for metadata, followed by an optional ## TLDR section for the summary, and then the full article content:
---
title: My New Project
date: 2024-02-01
page: blog
id: my-new-project
tags:
- Clojure
- Web
rss-feeds:
- clojure
css-class: my-project
image:
src: /assets/some-illustrative-img.png
src-dark: /assets/some-illustrative-img-dark.png
alt: A logo
repos:
- ["Project Name", "https://github.com/user/repo"]
---
## TLDR
A brief summary of the project. This will appear in the vignette/preview.
## Introduction
The full article content starts here...Key points:
- YAML frontmatter (between
---markers) configures post metadata ## TLDRsection (optional) provides a summary shown in vignettes- Standard markdown for the rest of the content
- The TLDR section is automatically extracted and removed from the full article view to avoid duplication
Feel free to clone or fork the repository and modify both the code and post content to suit your need.
bb devThis starts a REPL server with the necessary middleware for ClojureScript development.
Then connect to the REPL server with project type deps.edn + Figwheel Main and tick the :dev alias.
Once the REPL started, saving a cljs file will automatically update the js in your localhost.
However, if you change the content of a markdown file, you will need to save the file md.clj which is where the macro that reads all the markdown files resides. Refresh your browser and the new markdown content will appear.
bb test This task runs both clj and cljs tests:
- clj tests ensure that all your markdown files respect the malli schema.
- cljs tests ensure that the state (in our re-frame DB) is as expected after user actions (navigation, theme, post interaction etc).
Check code formatting:
bb fmt-checkFix code formatting:
bb fmt-fixThe cljs tests are also run on every save and the results are displayed at http://localhost:9500/figwheel-extra-main/auto-testing
In the CI, there is no browser, so we use headless mode:
bb test-cibb cleanRemoves target and dist directories.
bb buildBuilds the optimized ClojureScript bundle and generates index.html in target/public.
bb rssGenerates all configured RSS feeds in resources/public/blog/rss/.
Currently available feeds:
- clojure: Posts tagged with Clojure content
- all: All blog posts
To include a post in specific RSS feeds, add to your YAML frontmatter:
rss-feeds:
- clojure # Include in Clojure feed
- all # Include in all posts feedAvailable RSS feeds:
- blog/rss/clojure-feed.xml - Clojure-related articles
- blog/rss/all-feed.xml - All blog articles
New feeds can be added by updating the feed-configs in src/loicb/build/rss.clj.
bb distThis task runs bb build and bb rss, then gathers all necessary static files into a dist/ folder ready for deployment.
Check for outdated dependencies:
bb outdatedThe project uses GitHub Actions for continuous integration and deployment.
- Run tests with
bb test-ci - Run code formatting checks with
bb fmt-check - Build static files with
bb dist - Deploy preview to Netlify with unique URL (
pr-{number})
- Run tests with
bb test-ci - Run code formatting checks with
bb fmt-check - Build static files with
bb dist - Deploy to production Netlify site
The website is hosted on Netlify (US servers by default).
To enable deployments, configure these secrets in your GitHub repository settings:
NETLIFY_AUTH_TOKEN: Personal access token from NetlifyNETLIFY_SITE_ID: Site ID from your Netlify site settings
To know what params can be provided to the config map, you can have a look at the malli schema in validation. You can take inspiration from my own posts in content.
At CLJ compile time, the following steps happen:
- Read all markdown files with YAML frontmatter
- Parse YAML metadata using
markdown-clj - Extract
## TLDRsection as post summary - Normalize metadata (convert YAML types to Clojure types)
- Validate the post configs against a
Mallischema - A macro stores a vector of the posts to be loaded in the re-frame DB
At CLJS compile time, the following steps happen:
- A re-frame event initializes the re-frame DB, loading all the posts from the clojure macro and the theme from local storage.
- The
reititroute table is created - The post markdown content is converted to hiccup via
markdown-to-hiccup.
Feel free to open new issues if you discover bugs or wish to add new features.
Copyright Β© 2023-2025 Loic Blanchard
Distributed under the MIT License - see the LICENSE file for details.
