⫶☰ Table of contents
Hacklab Interface
About
The Hacklab Interface is the program that generates the static website for hacklab.nl. The images and text are compressed and combined, and synced to the website. The layout is styled with tailwindcss. The content is stored in markdown files, and has special metadata (frontmatter).
Requirements
- Maudit is a static site builder library
- Markdown
- Rust
- Tailwind
Setup
Setup maudit rust project
cargo new mywebsite
cd mywebsite
cargo add maudit
cargo add maud
cargo add serde
cargo add chrono --features serde
Setup tailwindcss
Create a package.json
npm add @tailwindcss/cli
npm add tailwindcss
Setup project structure
mkdir assets
mkdir -p content/articles
mkdir static
setup main.rs
use maud::{DOCTYPE, Markup, PreEscaped, html};
use maudit::content::markdown_entry;
use maudit::route::prelude::*;
use maudit::{
AssetsOptions, BuildOptions, BuildOutput, PrefetchOptions, PrefetchStrategy,
content::glob_markdown, content_sources, coronate, routes,
};
// layout
pub fn hero(ctx: &mut PageContext) -> Markup {
let back_image = ctx
.assets
.add_image_with_options(
"assets/back.jpg",
ImageOptions {
width: None,
height: None,
format: Some(maudit::assets::ImageFormat::WebP),
},
)
.unwrap();
let url = back_image.url();
let back_style = format!("background-color: black; background-image: url('{}');",
url);
html! {
section.bg-white.dark:bg-gray-900 style=(back_style) {
h1.mb-4.text4l.font-extrabold.text-gray.text-center.h-20
md:text-5xl.lg:text-6xl.dark:text-white {
"Hello"
}
}
}
}
pub fn layout(content: Markup, hero: Option<Markup>, ctx: &mut PageContext) -> Markup {
ctx.assets
.include_style_with_options("assets/hli.css",
StyleOptions { tailwind: true }
)
.unwrap();
html! {
(DOCTYPE)
html lang="en" {
head {
meta charset="utf-8";
meta name="viewport" content="width=device-width, initial-scale=1";
title { "hacklab" }
meta name="description" content="Programming blog of David";
}
body.bg-blue-950.text-blue-100 {
(hero.unwrap_or_default())
div {
(content)
}
}
}
}
}
// content types
#[markdown_entry]
pub struct ArticleContent {
pub title: String,
pub description: String,
pub category: String,
}
//content structs
#[route("/")]
pub struct Index;
impl Route for Index {
fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
let articles = ctx.content::<ArticleContent>("articles");
let hero = hero(ctx);
let articles = html! {
ul {
@for entry in articles.entries() {
@let article = entry.data(ctx);
@let url = &Article.url(ArticleParams { article: entry.id.clone() });
li {
a href=(url) {
(article.title)
}
p { (article.description) }
}
}
}
};
layout(articles, Some(hero), ctx)
}
}
#[route("/articles/[article]")]
pub struct Article;
#[derive(Params, Clone)]
pub struct ArticleParams {
pub article: String,
}
impl Route<ArticleParams> for Article {
fn pages(&self, ctx: &mut DynamicRouteContext) -> Pages<ArticleParams> {
let articles = ctx.content::<ArticleContent>("articles");
articles.into_pages(|entry| {
Page::from_params(ArticleParams {
article: entry.id.clone(),
})
})
}
fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
let params = ctx.params::<ArticleParams>();
let articles = ctx.content::<ArticleContent>("articles");
let article = articles.get_entry(¶ms.article);
let content = html! {
div.max-w-7xl.mx-auto.px-6.py-6 {
h3.text-3xl.md:text-4xl.font-black.uppercase.mb-8 {
(article.data(ctx).title)
}
section.prose.prose-lg.max-w-none {
(PreEscaped(article.render(ctx)))
}
}
};
layout(content, None, ctx)
}
}
fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> {
coronate(
// add the index route, routes for artciles, and the about page
routes![Index, Article],
// add markdown content articles from the content/articles directory, content has the ".md" extension
content_sources![
"articles" => glob_markdown::<ArticleContent>("content/articles/*.md")
],
BuildOptions {
// build options
// set the path of the tailwindcss binary, that is installed with @tailwindcss/cli
assets: AssetsOptions {
tailwind_binary_path: "./node_modules/.bin/tailwindcss".into(),
..Default::default()
},
// disable the prefetch js scripts
prefetch: PrefetchOptions {
strategy: PrefetchStrategy::None,
..Default::default()
},
..Default::default()
},
)
}
In assets/hli.css the tailwind css
@import "tailwindcss";
.prose ul {
list-style-type: disc;
margin-top: 1em;
margin-bottom: 1em;
padding-left: 1.5em;
}
.prose li {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.prose a {
text-decoration-line: underline;
}
.prose p {
margin-top: 1em;
margin-bottom: 1em;
line-height: 1.75;
}
Content
In content/articles/hello.md
---
title: Hacklab Interface
description: Hacklab Interface is a static website written in Rust and Markdown build with maudit.
category: web
---
## Hi there
Background image
wget https://placecats.com/1024/200 -O assets/back.jpg
Start the initial build
This is quite slow, cargo needs to download and compile all the packages.
maudit dev
Deploy
rm -rf dist
maudit build
rsync -vr dist/ you@server:/var/www/mysite