Loïc Blanchard

Loïc Blanchard

Portfolio Website

2023-04-07 - 2024-04-21 | Blog Article
Logo referencing Aperture Science
ClojureScript
Figwheel
Re-Frame
Reagent
Lasagna-pull
Reitit
Babashka

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:

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
  • ## TLDR section (optional) provides a summary for vignettes
  • Standard markdown for the rest of the content
  • Posts with home-page?: true will 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:

  1. Read all markdown files
  2. Validate the post configs against a Malli schema
  3. Assoc the post markdown content to the configs
  4. A macro stores a vector of the posts to be loaded in the re-frame DB

At CLJS compile time, the following steps happen:

  1. A re-frame event initializes the re-frame DB, loading all the posts from the clojure macro and the theme from local storage.
  2. The reitit router is created
  3. 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:

  1. All Blog Posts: /blog/rss/all-feed.xml
    • Contains all blog articles
    • Updated whenever any blog post is published
  2. 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:

  1. Loads all blog posts from md.clj
  2. Filters posts by the specified feed name
  3. Sorts by date (newest first)
  4. Generates the RSS XML with full content
  5. 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):

  1. Add the feed configuration to feed-configs in src/loicb/build/rss.clj:
       "python" {:title "Loic Blanchard - Python Blog Feed"
                 :description "Articles related to Python"}
       
  2. Add the feed identifier to relevant posts:
       rss-feeds:
         - all
         - python
       
  3. Run bb rss to 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

  1. Run tests with bb test-ci
  2. Check code formatting with bb fmt-check
  3. Build static files with bb dist
  4. Deploy preview to Netlify with unique URL (pr-{number})

Production Deployment (main branch)

  1. Run tests with bb test-ci
  2. Check code formatting with bb fmt-check
  3. Build static files with bb dist
  4. 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.