Curated by DeiAsPie
A minimalist, fast personal site for sharing recommendations for tools, services, and courses. Built with Hugo + Tailwind, deployed to GitHub Pages.
Dark mode is the default. Use the Theme toggle in the header to switch; your choice is remembered in localStorage.
- 🚀 Performance: AVIF/WebP images, lazy loading, service worker caching, prefetching
- ♿ Accessibility: WCAG 2.2 compliant, ARIA best practices, keyboard navigation, screen reader optimized
- 🔒 Security: Content Security Policy, Subresource Integrity, secure Hugo configuration
- 🎨 Modern Stack: Hugo + Tailwind CSS v4, responsive design, dark mode support
- 📦 PWA Ready: Service worker, offline support, installable
- 🧪 Well-tested: Playwright E2E, Axe accessibility audits, Lighthouse performance checks
Prereqs: Hugo Extended v0.151.0+ and Node.js 20+
# install deps
npm install
# run dev server
hugo server -DPreferred: page bundles with minimal front matter. Create a folder and an index.md:
content/recommendations/my-tool/
index.md
logo.png # optional; first image is auto-used if `image` is omitted
Minimal front matter:
---
title: "My Tool"
link: "https://example.com"
---- Image: omit
imageto auto-pick the first image in the folder. Remote URLs still work if you prefer. - Summary: omit
summaryto auto-use the first sentence of the content for cards. - Tags/Categories: optional.
Key paths used by the site:
content/— Markdown contentcontent/recommendations/— All recommendationscontent/recommendations/courses/— Courses hub. Each course is a page with anareafor groupingcontent/about/— About page
themes/curated/— Theme templates and partialsassets/css/main.css— Tailwind entry and small component stylesstatic/— Images and static assets served as-is
- Preferred: page bundles. Place your image next to
index.md; omitimagein front matter to auto-pick the first local image. This keeps content portable and avoids path mismatches. - Legacy paths under
static/still work, but new content should colocate images with the page.
Responsive optimization (added):
- Partial
responsive-image.htmlauto-generates multiple widths (WebP + original) and emits<picture>withsrcset&sizes. - Pages default widths:
320,480,640,800,1024; cards:200,320,400. - Remote URLs & SVG images bypass processing.
- Fallback letter tile is shown when no image exists.
Manual usage (normally not required):
{{ partial "responsive-image.html" (dict
"Page" .
"Src" .Params.image
"Alt" (printf "%s image" .Title)
"Class" "w-full mb-6"
"Widths" (slice 320 640 1024)
"Sizes" "(max-width: 900px) 100vw, 800px"
) }}
Future ideas: AVIF source, blur placeholder, selective hero preloading.
System stack by default. Optional non-blocking Google Fonts load via params.googleFonts (query portion after css2?). Example:
[params]
googleFonts = "family=Inter:wght@400;600;700&display=swap"The fonts.html partial:
- Adds
preconnecthints - Preloads the stylesheet + media swap (
media="print"→ onload set toall) - Ensures
display=swap - Provides
<noscript>fallback
Remove the param to revert to system fonts only.
Self-host recommendation: download stylesheet & woff2 files, place in static/fonts/, add @font-face rules in assets/css/fonts.css, import from main.css.
Configure likely next pages to prefetch (speeds perceived navigation):
[params]
prefetch = ["/recommendations/", "/recommendations/courses/"]Output: <link rel="prefetch" as="document"> for each path (except the current). A small script prefetches nav destinations on first hover/focus if supported. Keep list short (2–4) to avoid waste. Remove or empty to disable.
Future enhancements: home-only static prefetch, upgrade single target to prerender.
Each course is now a page bundle: content/recommendations/courses/<slug>/index.md plus an image file in the same folder. Courses render grouped by an area front matter field (Computer Science, Programming, Web Development, Systems, Security, Data, Economics, Finance, Other). Example:
---
title: "Introduction to Algorithms"
categories: ["Courses"]
tags: ["course"]
area:
"Computer Science"
# Image optional; prefer placing `introToAlgo.png` in the same folder as `index.md` and omit `image:`
summary: "MIT 6.006 lecture series."Main navigation is defined in hugo.toml under menu.main. Edit there to add or reorder items.
- Run
hugo server -Dduring authoring for live preview. - Use
hugo --gc --minifyfor a production build. - Add new recommendations as page bundles under
content/recommendations/. - For Courses, set
areafor correct grouping on the Courses page.
Push to main. GitHub Actions builds with Hugo and deploys to the gh-pages branch via Pages.
CI notes:
- Uses Hugo Extended v0.148.2 in the Actions workflow for consistent image-processing features.
- Publishes a
.nojekyllfile to prevent GitHub Pages from altering output. - Old URLs are preserved using
aliasesin front matter; keep aliases when renaming/moving content.
# install deps and build Tailwind CSS
npm ci
npm run build:css
# production Hugo build
hugo --minify --gcDo not commit build artifacts. Git ignores:
public/(Hugo output)resources/(Hugo cache/pipeline)
head.html now derives the <meta name="description">, Open Graph, and Twitter description using this fallback chain:
params.description(page front matter)params.summary.Summary(auto-generated excerpt)site.Params.descriptionsite.Title
Provide a concise description in front matter for best control. Leave blank to let Hugo synthesize one.
Added partial: seo-jsonld.html (auto-included for pages) emitting a Schema.org graph with:
Article(every page) –headline,description,author/publisheras site title, publish/modified dates if present.Course(pages under/recommendations/courses/) –name,description,provider.BreadcrumbList– Home → section segments → current page.
Extending: add additional types (e.g. SoftwareApplication) by updating the partial with conditional detection logic.
layouts/404.html supplies a minimalist not-found page with helpful links (Home, Recommendations, Courses) and noindex to keep it out of search results.
OG image selection order:
- Front matter
image(absolute or relative) - First page bundle image resource
- Fallback:
/favicon.svg
- Add
lastReviewedfor frequently updated guides. - Add
SoftwareApplicationschema for tool-specific recommendation pages (fields: name, operatingSystem, applicationCategory, offers). - Generate XML
newsorcoursesspecific feeds if needed. - Add
sameAssocial profile links via site params.
Keyboard and assistive tech users benefit from clear focus indicators. The stylesheet adds a consistent focus-visible ring (ring-2 + offset) across links, buttons, form inputs, nav items, and icon buttons. This avoids overriding the default outline for mouse users while offering strong contrast in both light and dark themes.
Skip link (#main) receives a distinctive fuchsia ring for fast orientation when tabbing from the top of the page.
Main and mobile navigation <nav> elements include aria-label. Active menu links now get aria-current="page" for screen reader announcement of the current location.
- Add
langattribute explicitly via a site param if multilingual variants are added. - Offer reduced motion preference (respect
prefers-reduced-motion) for hover/transition-heavy elements. - Provide high-contrast toggle if expanding beyond current palette.
- Builds Tailwind CSS and the site, runs Python tests (pytest), and enforces a CSS size budget (current size +15%).
- Runs Lighthouse audits and Axe (axe-core CLI) on key pages.
- Blocking thresholds (owner-selected): Performance ≥ 90, Accessibility ≥ 90, Best Practices ≥ 90, SEO ≥ 90. Artifacts are uploaded to
ci/lighthouse/andci/pa11y/.
# serve production-like site in one terminal
hugo server -D &
# Lighthouse (writes to ci/lighthouse)
npx -y @lhci/cli autorun --config=ci/lighthouserc.json
# Axe (serious/critical only in CI)
npx -y @axe-core/cli http://localhost:1313/Goldmark is configured with unsafe = true (raw HTML allowed). We mitigate via a meta CSP in report-only mode first:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline' https:; connect-src 'self' https:; report-uri /csp-report-endpoint">
To enforce later, progressively remove 'unsafe-inline' by moving inline scripts/styles to external files and relaxing directives for required external services (fonts, analytics) as needed.
layouts/partials/responsive-image.htmlgenerates AVIF/WebP with width/height, lazy by default.- Set
Preload=truefor hero images to emit<link rel="preload" as="image">and considerFetchPriority=high.
Add the CI badge after the workflow is on default branch:

This project prefers latest stable tool versions (Node LTS, Hugo extended latest, GitHub Actions latest major). CI pins major ranges only.