This website is basic html + css. I wanted to avoid using any JS framework (even though I love using React), as I wanted to keep things simple.
The styling is via new.css with some small changes made.
Every page is just a standalone html document, but I use vercel for deployment which has some backend magic that removes the from each page in the url.
I wrote a custom template script so that I can reuse html components. The script also performs some convenience functions like swapping all the urls for local vs production builds (so that when I click links during development they don't take me to the deployed page).
Each of my HTML components are placed inside their own file. The format for a component is:
<template-NewTemplate>
<div>
<span>This is some template html.</span>
</div>
</template-NewTemplate>
where NewTemplate can be any name. Note that it doesn't actually require the 'template-' prefix but I think it makes things cleaner. I can use the template by just specifying the template tag later:
<div>
<template-NewTemplate />
<div>
<p>... some other HTML.</p>
</div>
</div>
At the moment the components can't include nested components as everything is just a simple string replace, but it hasn't been a problem so far.
And yes, I know there are plenty of other template engines out there, including WebComponents, but I felt like doing it myself and it's a pretty trivial implementation.
The actual source for the build script is:
const fs = require("fs");
const path = require("path");
const { parse } = require("node-html-parser");
const mode = process.argv.includes("--deploy")
? "production"
: "development";
// (1) Read the files in templates and construct a map of {name, data}
const templatesPath = "src/templates/";
const templates = fs.readdirSync(templatesPath).map((filename) => {
const filepath = path.join(templatesPath, filename);
const file = fs.readFileSync(filepath, { encoding: "utf-8" });
const root = parse(file);
return {
name: `<${root.childNodes[0].rawTagName} />`,
data: root.childNodes[0].childNodes.toString().substring(4).slice(0, -2),
};
});
// Read the file, replace any template tags with the relevant
// template, write back into file
const readSearchReplaceWrite = (filename) => {
const dstFilename = path.join("public", filename.replace("src/", ""));
let file = fs.readFileSync(filename, { encoding: "utf-8" });
for (const { name, data } of templates) file = file.replace(name, data);
if (mode == "production") {
file = file.replaceAll(
"https://cybernaut.dev/",
"https://cybernaut.dev/"
);
file = file.replaceAll("", "");
}
const depth = filename.split("/").length - 2;
file = file.replaceAll(
'<link rel="stylesheet" href="style.css">',
`<link rel="stylesheet" href="${"../".repeat(depth)}style.css">`
);
fs.writeFileSync(dstFilename, file);
};
const recurse = (filepath) => {
const files = fs.readdirSync(filepath);
for (const file of files) {
const filename = path.join(filepath, file);
const fileStat = fs.statSync(filename);
if (fileStat.isDirectory()) {
if (!filename.endsWith("templates")) {
fs.mkdirSync(path.join("public", filename.replaceAll("src/", "")), {
recursive: true,
});
recurse(filename);
}
} else if (file.endsWith("")) {
readSearchReplaceWrite(filename);
}
}
};
fs.rmSync("public", { recursive: true, force: true });
fs.mkdirSync("public");
fs.copyFile("src/style.css", "public/style.css", (err) => {});
// (2) Read through all the files and replace any lines that contain
// template tags with the relevant template
recurse("src");