Astro template for a fastfetch / neofetch-style terminal portfolio. Two YAML files drive the site, project metadata is synced from GitHub, and the generated config.json is gitignored — clean repo, single source of truth, reproducible builds.
Built initially as a personal portfolio, designed to be reused as a template: every piece of content lives in
src/data.ymlorsrc/systems.yml.
26 distro color schemes, switchable from the header. Four shown below:
All 26: arch, archcraft, debian, ubuntu, linuxmint, kali, fedora, opensuse, alpine, gentoo, manjaro, centos, void, nixos, slackware, pop, elementary, cachyos, windows, macos, android, raspbian, proxmox, truenas_scale, parrot, gnu. Each one defined in src/systems.yml with its own color palette, kernel, shell, DE, WM, theme, terminal, font, and ASCII logo (fetched from fastfetch-cli).
- Astro — static site generator
- Tailwind CSS 4 — via
@tailwindcss/vite - Bun — runtime, package manager, YAML parser
- Biome — lint + format
- GitHub Actions — CI
.
├── .github/workflows/sync.yml # CI: build on push to data files
├── .env.example # optional GH_TOKEN
├── astro.config.mjs # Tailwind plugin + @/* alias
├── tsconfig.json # extends astro/tsconfigs/strict + @/* alias
├── package.json
├── public/
│ ├── robots.txt # crawler config
│ ├── apple-touch-icon.png # iOS home screen (180×180)
│ ├── favicon.svg
│ └── favicon.ico
├── scripts/
│ ├── lib/project.ts # shared GitHub fetch helpers
│ └── sync.ts # YAML + GitHub → config.json
└── src/
├── components/
│ ├── seo.astro # <title>, OG, Twitter Card, JSON-LD…
│ ├── distro-toggle.astro # theme picker
│ ├── section-title.astro
│ ├── logos/ # fastfetch-style ASCII logos
│ └── tui/ # TUI-style sections
│ ├── btop/ # CPU / memory / hardware / peripherals
│ ├── lazygit/ # bio + status + paths
│ ├── mutt/ # contact + inbox
│ └── ranger/ # projects tree + detail
├── data.yml # site + content config
├── systems.yml # the 26 distro entries
├── layouts/default.astro # base layout (uses Seo)
├── lib/ # cn util, themes, fonts, fastfetch cache
├── pages/index.astro
└── styles/global.css
Two YAML files are the only manual config. Everything else is generated.
Top-level site config + manual content that doesn't come from GitHub:
site: # SEO defaults (title, description, url, image, theme_color…)
current: # active system slug
booted_at: # ISO timestamp for the live-computed uptime
host: # hardware info (manual, doesn't change often)
about: # bio, paths, lazygit-style status block
uses: # btop: cpu/memory/hardware/peripherals
contact: # email, inbox, hints
projects_ui: # projects section command + root
projects: # list of GitHub repo URLs to fetchThe 26 distro entries shown in every per-distro row (kernel, shell, DE, WM, theme, terminal, font, colors…). Lives in its own file because it's ~570 lines and would dwarf data.yml.
Generated by scripts/sync.ts from the two YAML files + GitHub. Do not edit by hand — it gets overwritten. The page reads this at build time.
scripts/sync.ts is the build pipeline:
- Reads
data.yml+systems.yml - Skips entirely if neither YAML changed since the last
config.json(mtime check) - For each URL in
data.yml.projects:- Cached? Use it (
SYNC_TTLcontrols cache age, default 1h;SYNC_TTL=0disables cache) - Otherwise fetch from GitHub (uses
GH_TOKEN/GITHUB_TOKENif set, raises rate limit 60/h → 5000/h) - Refresh existing project entries in place, append new ones
- Drop entries no longer in the queue
- Cached? Use it (
- Writes
src/config.json+.cache/sync.json
It runs automatically as a predev and prebuild hook in package.json, so bun run dev and bun run build always have fresh data.
GH_TOKEN/GITHUB_TOKEN— optional, raises API rate limitSYNC_TTL— cache TTL in ms (default3600000= 1h,0disables cache)SYNC_FORCE— if set, ignore the mtime skip check
Copy .env.example to .env to set them locally.
| Command | Action |
|---|---|
bun install |
Install dependencies |
bun run dev |
Start dev server (auto-runs sync via predev) |
bun run build |
Build static site to dist/ (auto-runs sync via prebuild) |
bun run preview |
Preview the build locally |
bun run sync |
Run the sync script standalone |
bun run check |
Biome lint + format |
.github/workflows/sync.yml runs on:
pushtosrc/data.ymlorsrc/systems.ymlonmain- bi-weekly cron (
0 0 */14 * *) - manual
workflow_dispatch
It installs deps and runs bun run build, which auto-runs sync via the prebuild hook. Since config.json is gitignored, no commit step is needed — Astro just builds from the freshly-generated file.
The workflow passes secrets.GITHUB_TOKEN as GH_TOKEN so the sync script uses authenticated requests.
src/components/seo.astro renders a complete meta block from data.yml.site:
- Primary —
<title>,description,theme-color,canonical,apple-touch-icon - Open Graph —
og:type,og:url,og:title,og:description,og:image+og:image:width/height,og:locale,og:site_name - Twitter Card —
twitter:card,twitter:url,twitter:title,twitter:description,twitter:image,twitter:creator(only if set) - Structured data — JSON-LD
WebSiteschema with a nestedPersonauthor block (auto-generated fromsite.social.github/twitter/linkedin) - robots.txt —
public/robots.txtallows everything by default
The page sets <title> via the title prop on <Layout>. If the title already contains site.title (case-insensitive), no suffix is added; otherwise · {site.title} is appended.
Two caches, both in .cache/ (gitignored):
| Path | Purpose | TTL |
|---|---|---|
.cache/sync.json |
GitHub repo metadata (stars, descriptions, dates) | 1h (env-overridable) |
.cache/logos/ |
ASCII logos fetched from fastfetch-cli | no TTL (delete to refresh) |
TypeScript + Vite resolve @/* to src/*. Use it in imports:
import config from "@/config.json";
import { cn } from "@/lib/cn";
import Prompt from "@/components/tui/commons/prompt.astro";cn is a small util in src/lib/cn.ts that wraps tailwind-merge to merge Tailwind classes safely.
- Fork or clone the repo
- Edit
src/data.yml— replacesiteblock (title, description, url, author, social) - Edit
src/systems.yml— keep, remove, or add distros - Replace
src/data.yml#projectswith your own GitHub repo URLs - Edit
src/data.yml#about.bio,paths,contact.inboxfor your content - Add your own
public/og.png(1200×630) and replacepublic/apple-touch-icon.png - (Optional) set
GH_TOKENin.envfor higher API rate limits bun install && bun run dev



