Portfolio Website
2023-04-07 - 2024-04-21 | Blog Article

Git Repos

Rational
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.
Light/dark mode and code block syntax highlighted are supported.
The app is deployed on Netlify via GitHub Actions every time a branch is merged to master. Build artifacts are generated during CI/CD and are not committed to the repository.
Stack
I have the following stack:
- babashka for task automation and build scripts
- figwheel-main for live code reloading
- reagent for react components
- hiccup for DOM representation
- reitit for routing
- malli to validate some configs at the top of markdown files
- markdown-to-hiccup to allow me to write the page content in markdown.
- re-frame a framework for building user interfaces, leveraging reagent
- lasagna-pull to precisely select from deep data structure
- clj-rss to generate RSS feeds for blog posts
Features
The website contains
- a description of the personal/professional projects I worked on as a Software Engineer - my resume
- technical articles related to Clojure for the most part
Content
Organization
Each post has its own markdown files in the folder of the page it belongs to.
.
├── about
│ └── aboutme.md
└── blog
├── blog_django.md
├── flybot_card_games.md
├── flybot_mobile_app.md
├── flybot_website.md
├── magic_nostrand.md
└── portfolio_website.md
Vignette and Post
Some blog posts have a flag to display the post in the home page via what I called vignette. They contain a short description of the post instead of the full content.
When the user clicks on a vignette, he goes to a new route with the full post content.
Markdown Format
Each markdown file uses standard YAML frontmatter for metadata, followed by an optional ## TLDR section for the summary:
---
id: clojure-full-stack-webapp
page: blog
home-page?: true
employer: Flybot Pte Ltd
date:
- 2022-01-01
title: Flybot Website
tags:
- Clojure
- ClojureScript
- Re-Frame
rss-feeds:
- all
- clojure
css-class: flybot-website
image:
src: https://www.flybot.sg/assets/flybot-logo.png
src-dark: https://www.flybot.sg/assets/flybot-logo.png
alt: Flybot Logo
repos:
- ["Flybot", "https://github.com/skydread1/flybot.sg"]
articles:
- ["Deploy Clojure app to AWS", "../blog/deploy-clj-app-to-aws"]
- ["Lasagna-pull Pattern", "../blog/lasagna-pull-applied-to-flybot"]
---
## Introduction
Full article content starts here...
Key points:
- YAML frontmatter (between
---markers) configures post metadata ## TLDRsection (optional) provides a summary for vignettes- Standard markdown for the rest of the content
- Posts with
home-page?: truewill display their vignette on the home page - Posts with
rss-feeds: ["all", "clojure"]will be included in the respective RSS feeds
Compile
At CLJ compile time, the following steps happen:
- Read all markdown files
- Validate the post configs against a
Mallischema - Assoc the post markdown content to the configs
- 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
reititrouter is created - The post markdowns are converted to hiccup via
markdown-to-hiccup.
Build Tasks with Babashka
I use babashka to manage all build tasks through a bb.edn configuration file.
Development
bb dev
Starts a REPL server with the necessary middleware for ClojureScript development and figwheel hot reloading.
Testing
bb test # Local development tests
bb test-ci # CI tests (headless mode)
bb fmt-check # Check code formatting
bb fmt-fix # Fix code formatting
Production Build
bb build # Build optimized ClojureScript bundle
bb rss # Generate all configured RSS feeds (clojure, all, etc.)
bb dist # Create complete distribution in dist/ folder
bb clean # Clean build artifacts
The bb dist command creates a complete static site in the dist/ directory ready for deployment.
RSS Feeds
The portfolio generates multiple RSS feeds using clj-rss to allow readers to subscribe to blog updates.
Available Feeds
Currently, two RSS feeds are available:
- All Blog Posts: /blog/rss/all-feed.xml
- Contains all blog articles
- Updated whenever any blog post is published
- Clojure Feed: /blog/rss/clojure-feed.xml
- Contains only Clojure-related articles
- Useful for readers specifically interested in Clojure content
How It Works
Posts control which feeds they appear in via the rss-feeds field in their YAML frontmatter:
rss-feeds:
- all # Include in "all" feed
- clojure # Include in "clojure" feed
The RSS generation is handled by a generic rss-feed function in src/loicb/build/rss.clj that:
- Loads all blog posts from
md.clj - Filters posts by the specified feed name
- Sorts by date (newest first)
- Generates the RSS XML with full content
- Writes to
resources/public/blog/rss/{feed-name}-feed.xml
A rss-feeds function generates all configured feeds at once during the build process.
Adding New Feeds
To add a new feed (e.g., for Python content):
- Add the feed configuration to
feed-configsinsrc/loicb/build/rss.clj:"python" {:title "Loic Blanchard - Python Blog Feed" :description "Articles related to Python"} - Add the feed identifier to relevant posts:
rss-feeds: - all - python - Run
bb rssto generate the new feed
The generated feeds include full article content (not just summaries), making them suitable for offline reading in RSS readers.
Continuous Integration & Deployment
The project uses GitHub Actions for CI/CD with separate workflows for pull requests and production deployments.
Pull Request Workflow
- Run tests with
bb test-ci - Check code formatting with
bb fmt-check - Build static files with
bb dist - Deploy preview to Netlify with unique URL (
pr-{number})
Production Deployment (main branch)
- Run tests with
bb test-ci - Check code formatting with
bb fmt-check - Build static files with
bb dist - Deploy to production Netlify site
Hosting with Netlify
The website is hosted on Netlify (US servers) and deployed via GitHub Actions using the Netlify CLI. This approach provides:
- Predictable deployments: Build happens in controlled CI environment
- Preview deployments: Each pull request gets its own preview URL
- No committed artifacts: Clean git history without build files
- Environment separation: Different deploy targets for previews vs production
Learn More
Have a look at the repo README for more information.
Contribute
Found any typo, errors or parts that need clarification? Feel free to raise a PR on the GitHub repo and become a contributor.