Building this website
Hi!
First long-format for this blog, documenting my journey building it. I'm quite happy to be finally doing this, and what better time to start than a Saturday morning at 6am after researching the topic all night.
Why this websiteโ
Keeping myself up-to-date with static websites generation tooling, oh and, because I want to write stuff down and share on the Internet, sometimes, as some sort of an archive for my life adventures.
The struggle leading to Docusaurusโ
In the past 20 years, I've iterated many times over building personal websites (or for the plethora of projects where I told myself "This needs a website!"). It all started by learning HTML in the early 2000s with the help of my mother (who was working as an IT administrator, IBM AS/400 Operator and later administrating the intranet of an international shipping corporation, quite a cheatcode to learn CS as a kid!) to PHP when I wanted to do more advanced websites for my private WoW & Lineage 2 servers, and of course the plenty of CMS and forum softwares that exists (ever heard of Joomla?).
Of course, Wordpress would be the easy choice, it's mature, battle-tested, has a massive community and ecosystem allowing you to do pretty much everything you want with, and I even have an entire set of Kubernetes charts made for a previous project, meaning I could have a production ready instance deployed in 30min. But we're not here to make things easy for ourselves, today we do my favorite sin: we over-engineer (kinda).
I wanted to have more control while reducing the maintenance burden; CMS are great with their low barrier of entry for content management and theming, but the need for a database makes the infrastructure so much more painful to maintain on your own. Cloud-based solutions like Odoo, Squarespace or Wordpress also exists but it's not the solution I'm looking for, mainly because of the lack of control cloud-based solutions offers.
Instead, I choose to go with static website generation frameworks, which is now extremely popular when it comes to building landing pages, documentation and also blogs! As the name suggests, the output is a static website which can be served efficiently on many Content Delivery Networks, usually for free. You will end up with a snappy website that will barely require any maintenance once the initial setup is done.
I've played with Hugo a few years ago to build a very ugly landing page for Renegade from a template found on GitHub, but I felt more comfortable leaning towards the JavaScript/TypeScript ecosystem and its large amount of available frameworks. With Renegade, we used Vue 2.0 at the time (yeah...), which despite the lack of TypeScript support, was not too difficult to learn and has a decent ecosystem one can rely on (Vuetify my beloved...). I've looked at VuePress, but as I had the opportunity to write some React at work, I figured I'd be better looking at frameworks like Next.js or Docusaurus to reinforce my understanding of the framework.
I've setup a new project with Next.js and started to play with it, trying to build a homepage, and after a 3 hours of editing various templates and frankeinsteining them together, the best I could do was this:
Not so great yeah... As I got quite frustrated while trying to iterate, I stopped working on this idea for some time.
Why Docusaurus?โ
After a few months of putting this website project aside, I felt it was time to revive it and to actually do it as I have a few other projects I want to document down and write articles about, so this website not being online is now a blocker for these projects, great!
I've heard quite a lot about Docusaurus in recent years and seen many many open-source project and companies starting to use it, but what really made me pull the trigger is their blog module, which is everything I need to do content management, leaving all the fun for the frontend part ๐
Moving ahead with Docusaurusโ
After setting up the scaffold project very quickly with npx create-docusaurus@latest my-website classic --typescript
, doing some very basic config change and cleanup, let me show where I am right now:
Ok, this article finally caught up with my progress, an immediate next step for me is to figure out how to open images in a larger format when clicking on them. I'd hope there's a plugin or setting, so let's see with a quick Google search: docusaurus plugin image zoom
- how convenient a plugin is named just like that?! Let's run npm install docusaurus-plugin-image-zoom
, add the required config bits and it just works! That's a great first experience while trying to solve a simple problem.
True Colorsโ
This thing run and I can write stuff into it on the blog page which is great, but, it still uses the default theme and doesn't look like a personal website at all:
Dark mode looks a little better tho:
Right, I can start by disabling the docs module, don't think i'll ever need it. It's only a case of setting docs
to false
inside docusaurus.config.ts
, easy enough.
First issue each time I need to design a website is deciding what I'd like to do:
- Dark mode only, with some deep purple as primary color and either pink or magenta as accent.
- Clean and simple, especially the homepage, the principal CTAs should be my socials and blog articles
- Likely yet another website using a space-looking particles animation
As I'm a stackoverflow aficionados, my first reflex is to take a look at other things people built; https://docusaurus.io/showcase is a cool collection of what can be built with Docusaurus, cool.
But immediate thing I can try is feed the current src/css/custom.css
into ChatGPT and ask it for shades of Magenta to replace the current green, which could help a lot already with styling.
This is the default css:
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
With the most boring prompt ever
propose me a theme with shades of magenta instead
Here's the output:
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #d33682; /* Magenta (Primary) */
--ifm-color-primary-dark: #b02d6e; /* Dark Magenta */
--ifm-color-primary-darker: #96255f; /* Darker Magenta */
--ifm-color-primary-darkest: #7a1e50; /* Darkest Magenta */
--ifm-color-primary-light: #e04a92; /* Light Magenta */
--ifm-color-primary-lighter: #e85da1; /* Lighter Magenta */
--ifm-color-primary-lightest: #f075b3; /* Lightest Magenta */
--ifm-code-font-size: 95%; /* Adjusted Code Font Size */
--docusaurus-highlighted-code-line-bg: rgba(211, 54, 130, 0.1); /* Highlighted Code Line Background */
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #ff77c7; /* Light Magenta (Primary) */
--ifm-color-primary-dark: #e066b2; /* Dark Light Magenta */
--ifm-color-primary-darker: #cc5da2; /* Darker Light Magenta */
--ifm-color-primary-darkest: #b3538f; /* Darkest Light Magenta */
--ifm-color-primary-light: #ff85cd; /* Lighter Light Magenta */
--ifm-color-primary-lighter: #ff99d4; /* Even Lighter Magenta */
--ifm-color-primary-lightest: #ffb3e1; /* Lightest Magenta */
--docusaurus-highlighted-code-line-bg: rgba(255, 119, 199, 0.2); /* Highlighted Code Line Background */
}
With these first theming changes, I am suprisingly pleased by how the blog look in Dark mode
Let's force dark mode by adding a few optionals theme config in docusaurus.config.ts
;
Docusaurus docs was great to get a better understanding of the theme configuration and options https://docusaurus.io/docs/api/themes/configuration#color-mode---dark-mode
export default {
themeConfig: {
colorMode: {
defaultMode: 'light',
disableSwitch: false,
respectPrefersColorScheme: false,
},
},
};
And here's how the main page looks like now after some more clean-up in other places:
Because why not, let's add a logo as old as Bitcoin:
Adding recent blog posts on the homepageโ
I wanted to rework a little bit the homepage: a Google search, one Codepen and a few prompts later (yeah I know...) the homepage looks a bit more friendly already.
Oh, by the way, did you know the autoplay
argument inside a <video>
HTML tag needs to be autoPlay
? I didn't. Nice 30min lost because I didn't check console. Invalid DOM property `autoplay`. Did you mean `autoPlay`?
Ok, now to actually add our recent blog posts to the main page. After some googling and unsucessful prompts, it seems Docusaurus added reading data between plugins quite recently: https://github.com/facebook/docusaurus/pull/9931. This beautiful article https://www.cheehow.dev/blog/2024/04/01/show-recent-posts-in-docusaurus/ references how he implemented this functionality for his website, and it's not too different from what I'd like to do.
A bit of fiddling, few prompts and of copy iterations later, and this is what we have now:
Funny thing, the truncating on homepage works better than on the blog content plugin from Docusaurus ๐
Polishing the homepageโ
Believe it or not, the most difficult part was the homepage copy iteration. ๐
Getting the homepage in a 'good enough' state involved a few steps:
- Replace the slogan/tagline by a randomly picked word from an array
- Add some padding to the recent blog posts
- Add the read time on the recent writings at the left of the article date
- Squeeze the footer a little bit
To squeze the footer, I'm going to experiment with Docusaurus' "Swizzling" feature to customize the "Footer" component from @docusaurus/theme-classic
. Docusaurus has a CLI command to help you automatically generate the necessary file to swizzle a component. In our use-case, we want to use Typescript and want to wrap around the existing component instead of ejecting and reimplementing the entire component.
nimoa@mbp14 > npx docusaurus swizzle @docusaurus/theme-classic Footer
? Which language do you want to use? โบ - Use arrow-keys. Return to submit.
JavaScript
โฏ TypeScript
[Exit]
? Which swizzle action do you want to do? โบ - Use arrow-keys. Return to submit.
โฏ Wrap (Safe)
Eject (Safe)
[Exit]
[SUCCESS]
Created wrapper of Footer from @docusaurus/theme-classic in
- "[...]/src/theme/Footer/index.tsx"
Once we have our Wrapper component created by the CLI tool, we simply tell it to get a class footerWrapper
from our css file
import styles from './FooterWrapper.module.css';
export default function FooterWrapper(props: Props): JSX.Element {
return (
<div className={styles.footerWrapper}>
<Footer {...props} />
</div>
);
}
.footerWrapper :global(.footer) {
padding: 1rem 0;
}
I'm pretty sure I gonna be yelled at because I used :global(.footer)
which is very likely rendering the swizzling useless, but this is my own technical debt, not yours! ๐คช Anyway, we are quite close to the final product UI-wise now:
I'm quite satisfied with the result, for once. Think it's a sign that I shouldn't mess more with this homepage, and now start my favourite part...
Continuous Integration and Continous Deploymentโ
It's time to deploy this amazing (no) website to the interwebs, but first, let's push everything to a git repo on Gitlab. Let's cleanup some files, add a .gitignore
, switch to Yarn aaand we can push.
I have a bunch of solutions I can explore to deploy the website. I was originally planning to build a Docker image and to deploy it in my homelab but I said "low maintenance" blog so I need to stick to this philosophy for deployment as well. There are many services allowing you to deploy static webpages and serve them to the web for free: Netlify, GitHub Pages, Vercel... and Cloudflare Pages! As I use Cloudflare to manage all my domains, the choice is pretty straight-forward.
I use Gitlab for anything needing CI/CD, and handily enough, Cloudflare Pages has a direct Gitlab integration AND an article to document the process. So convenient!
Heading over to Pages and Workers settings, I create a new project, link my Gitlab account to CF, pick my repo and looks like they have a bunch of predefined presets, that's super neat and easy so far.
Time to press deploy :o ...And the build failed, a few times. Fortunately, after removing yarn and generating a new package-lock.json, the build was successful!
02:15:05.182 [INFO] [en] Creating an optimized production build...
02:15:06.101 [info] [webpackbar] Compiling Client
02:15:06.123 [info] [webpackbar] Compiling Server
02:15:15.660 [success] [webpackbar] Server: Compiled successfully in 9.54s
02:15:23.357 [success] [webpackbar] Client: Compiled successfully in 17.26s
02:15:26.837 [SUCCESS] Generated static files in "build".
02:15:26.838 [INFO] Use `npm run serve` command to test your build locally.
02:15:26.918 Finished
02:15:26.918 Note: No functions dir at /functions found. Skipping.
02:15:26.918 Validating asset output directory
02:15:28.608 Deploying your site to Cloudflare's global network...
02:15:31.290 Uploading... (0/68)
02:15:32.303 Uploading... (23/68)
02:15:32.384 Uploading... (46/68)
02:15:32.417 Uploading... (68/68)
02:15:32.418 โจ Success! Uploaded 68 files (2.17 sec)
02:15:32.418
02:15:33.009 โจ Upload complete!
02:15:34.835 Success: Assets published!
02:15:36.252 Success: Your site was deployed!
And the website is available on https://nimoa-docusaurus.pages.dev/, cool! Every time I will commit to my Gitlab repository, it will automatically deploy it in production. :party: Time to attach it to my domain now! Only need to set a CNAME with the pages.dev URL and boom, it's now on https://nimoa.fr, so cool!
Conclusionโ
As a hobbyist developer, this was a rather pleasing experience. I was expecting to hit more bumps along the way with Docusaurus but its simplicity yet incredible customizability contributes a lot to its ease of use, I can surely admire that fact. Deploying it with Cloudflare Pages and their Gitlab integration is also quite powerful, it saves hours and hours in setup work and future maintenance. Hopefully, time will prove me right!s
Well well well...โ
Something came up as I tried to make a tweet for this post, and it's actually quite important nowadays: social media cards. I'll make a future article to explore how to automate social card generation with Vercel's OG, but for now I made a template in Photoshop and manually built the social cards for the first two articles.
Thanks for reading along!