Skip to content

skydread1/portfolio

Repository files navigation

Loic logo
Clojure Version CI Netlify Status Contributions welcome

πŸ”· Portfolio SPA in ClojureScript πŸ”·

πŸ”· Rationale

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.

πŸ”· Prerequisites

  • babashka to run the different project tasks.

πŸ”· Features

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.

πŸ”· Content

Organization

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

Markdown Format

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
  • ## TLDR section (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

πŸ”· Getting Started

Clone the repo

Feel free to clone or fork the repository and modify both the code and post content to suit your need.

Dev

bb dev

This 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.

Hot reloading

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.

πŸ”· Tests

Local development tests

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).

Code formatting

Check code formatting:

bb fmt-check

Fix code formatting:

bb fmt-fix

Regression cljs tests on save

The cljs tests are also run on every save and the results are displayed at http://localhost:9500/figwheel-extra-main/auto-testing

CI tests (headless)

In the CI, there is no browser, so we use headless mode:

bb test-ci

πŸ”· Build Tasks

Clean build artifacts

bb clean

Removes target and dist directories.

JS bundle

bb build

Builds the optimized ClojureScript bundle and generates index.html in target/public.

RSS feeds

bb rss

Generates 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 feed

Available RSS feeds:

New feeds can be added by updating the feed-configs in src/loicb/build/rss.clj.

Production release

bb dist

This task runs bb build and bb rss, then gathers all necessary static files into a dist/ folder ready for deployment.

Dependencies

Check for outdated dependencies:

bb outdated

πŸ”· CI/CD

The project uses GitHub Actions for continuous integration and deployment.

For Pull Requests

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

For Main Branch (Production)

  1. Run tests with bb test-ci
  2. Run code formatting checks 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 by default).

To enable deployments, configure these secrets in your GitHub repository settings:

  • NETLIFY_AUTH_TOKEN: Personal access token from Netlify
  • NETLIFY_SITE_ID: Site ID from your Netlify site settings

πŸ”· Implementation Details

Config validation

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.

Compile process

At CLJ compile time, the following steps happen:

  1. Read all markdown files with YAML frontmatter
  2. Parse YAML metadata using markdown-clj
  3. Extract ## TLDR section as post summary
  4. Normalize metadata (convert YAML types to Clojure types)
  5. Validate the post configs against a Malli schema
  6. 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 route table is created
  3. The post markdown content is converted to hiccup via markdown-to-hiccup.

πŸ”· Contributing

Feel free to open new issues if you discover bugs or wish to add new features.

πŸ”· License

Copyright Β© 2023-2025 Loic Blanchard

Distributed under the MIT License - see the LICENSE file for details.

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •