Features & Changelog
What's built into skui.io — theme, admin, search, shortcodes and infrastructure.
Contents
Site & Theme#
- Custom editorial theme — hand-written CSS, no framework. Light/dark toggle stored in
localStorageso preference persists across pages and the admin area. - Responsive two-column layout — content + sticky right rail. Collapses to single column below 768 px.
- Full-text search — SQLite FTS5 index built from Hugo’s
/index.jsonat server startup. Live dropdown in the header (180 ms debounce). Dedicated/searchresults page. - RSS feed — auto-generated at
/index.xml. - Taxonomy — categories and tags, each with its own listing page and sidebar.
Admin Area#
Login-gated at /admin/. Python stdlib server — no third-party dependencies.
| Section | What you can edit |
|---|---|
| Theme / Text | Site title, subtitle, hero eyebrow, hero heading, hero lead |
| About Card | Name, bio, avatar, social links (with icon picker) |
| Notes | Pinned quote, hybrid, random or auto-rotate modes. Enable/disable per note. |
| Projects | The cards on /projects/ — name, tagline, description, status, tags, URL. DB-backed, live (no rebuild). |
| Working On | Checklist items shown in the right rail |
| Status Widget | Manual status lines, uptime monitors (ping/HTTPS), Docker containers, host uptime, external API sources. Drag-and-drop ordering. |
| Footer | Tagline, copyright text, footer links |
Admin follows the same light/dark preference as the public site.
Right Rail Widgets#
Rendered server-side at build time from data/homepage.yaml; live-hydrated from /api/sidebar on every page load without a full reload.
- About — avatar, name, bio, links
- Homelab Status — aggregates monitors, containers, hosts and API sources into a single status card with coloured indicators
- Working On — live checklist
- Note / Quote — front page only; shows when at least one note is enabled
Shortcodes#
Custom (this site)#
| Shortcode | Usage |
|---|---|
{{< audio src="file.mp3" title="Title" cover="cover.jpg" >}} | Audio player with optional cover art |
{{< gallery match="*.jpg" >}} | Photo gallery with click-to-zoom |
From Blowfish theme#
alert · badge · button · accordion / accordionItem · tabs / tab · chart · mermaid · timeline / timelineItem · carousel · video · youtubeLite · lead · keyword / keywordList · icon · katex · mdimporter · codeimporter · screenshot · swatches · gitea · github · codeberg · forgejo · gist
Page Templates#
| Template | URL pattern | Notes |
|---|---|---|
| Homepage | / | Kanban board (Featured · Latest · Docs · Lab Notes), topic chips |
| Post single | /posts/slug/ | Hero image, TOC, tags, prev/next |
| Post list | /posts/ | Postlist with sidebar |
| Taxonomy term | /categories/x/ /tags/x/ | Postlist with sidebar |
| Projects | /projects/ | Custom grid; DB-backed cards hydrated from /api/projects |
| CV | /cv/ | Dedicated CV template |
| Search | /search/ | Live results from /api/search |
| 404 | — | Custom error page |
Project Status Badges#
Each project card on /projects/ shows a status pill. The status value is free text; the
badge gets the CSS class status-<status lowercased>. Two statuses have a dedicated colour —
anything else uses the neutral default.
| Status | Badge colour |
|---|---|
| Live | Green — running in production |
| Building | Amber — work in progress |
| Maintained | Neutral grey — stable, kept up to date (default style — any other value, e.g. Planned or Archived, looks the same) |
To give another status its own colour, add a .project-status.status-<name> rule in
assets/css/editorial.css (build-time CSS — needs a rebuild).
Infrastructure#
- Hugo 0.161.1 static site generator (Blowfish theme + custom editorial override)
- Python 3.13 stdlib HTTP server — serves
public/,/api/*and/admin/* - SQLite (WAL mode) for admin settings, FTS5 search index, homelab state
- Docker multi-stage build — Hugo builder → Python Alpine runtime
- Docker Compose —
compose.yml(prod) andcompose.dev.yml(local dev) - Gitea Actions CI/CD — builds and pushes the image on push to
main - Tecnativa docker-socket-proxy — read-only socket proxy so the web container never touches the Docker socket directly