- CSS 34.8%
- JavaScript 34.3%
- Astro 17.8%
- TypeScript 9.1%
- Dockerfile 2%
- Other 2%
Describe local Geist assets, system-aware persistence, and the expanded production checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|---|---|---|
| public | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| astro.config.mjs | ||
| Dockerfile | ||
| justfile | ||
| nginx.conf | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
blog
Source for the static blog published at blog.keo.sh. Built with Astro, content collections, and a custom editorial interpretation of the terminal-modern theme. The site has locally bundled fonts, one small framework-free theme script, and no analytics or remote runtime dependencies.
Prerequisites
- Node.js
>=22.12.0and npm>=9.6.5(seeenginesinpackage.json) just(optional; every recipe has an equivalentnpm runscript)- Docker with the
buildxplugin (only needed for container image builds)
Commands
just |
npm |
Action |
|---|---|---|
just install |
npm ci |
Install dependencies from the lockfile |
just dev |
npm run dev |
Start the local dev server |
just check |
npm run check |
Type-check with astro check |
just build |
npm run build |
Build dist/ and run the safety/theme checks |
just preview |
npm run preview |
Preview the production build locally |
just clean |
npm run clean |
Remove generated dist/ and .astro/ |
just validate |
npm run validate |
Install, check, and build in one step |
just image-build |
npm run image:build |
Build the container image locally |
just image-push |
npm run image:push |
Build and push the container image |
Run just with no arguments (or just --list) to see all recipes.
Adding a post
Create a new Markdown file under src/content/posts/, e.g.
src/content/posts/my-new-post.md, with frontmatter matching the schema
in src/content.config.ts:
---
title: 'My New Post'
description: 'One or two sentences describing the post.'
pubDate: 2024-06-01
# updatedDate: 2024-06-15
# draft: true
# tags:
# - writing
# - beginnings
---
Post body in Markdown.
The post appears automatically on the home page, in /rss.xml, and in
the sitemap once draft is false (or omitted). The route is derived
from the filename, so this example would be published at
/posts/my-new-post/.
Tags are optional. When present, each trimmed tag string generates a
matching archive at /tags/<slug>/ (for example, writing becomes
/tags/writing/).
Theme
All colors, typography, spacing, radii, motion, and layout widths are
defined as CSS custom properties in src/styles/theme.css.
src/styles/global.css consumes those semantic tokens for both the warm
light and graphite dark themes.
Geist Sans and Geist Mono are bundled as local Latin variable WOFF2
assets through the Fontsource packages. Their SIL OFL-1.1 license files
are retained under public/licenses/; the browser never contacts a font
CDN.
src/scripts/theme.js is the site's only client-side script. Astro emits
it as a content-hashed same-origin asset and BaseLayout.astro loads it
before the stylesheet so a saved preference is applied before first
paint. With no saved blog-theme value, the site follows
prefers-color-scheme; selecting Light or Dark stores that explicit
choice. If JavaScript is disabled, CSS still follows the operating-system
preference and hides the otherwise inert control.
scripts/check-theme-output.mjs verifies the color contrast contract,
font output, bootstrap order, theme controls, and canonical favicon after
every production build.
Public-content safety
This repository, and everything it builds, is expected to be safe for a
public audience. Rendered pages, feeds, and source files must not
contain real names, infrastructure details, credentials, internal URLs,
host paths, or any information about the systems used to build or serve
this site. npm run build runs scripts/check-public-output.mjs
against dist/ after every build and fails if it finds source maps,
credential-shaped strings, private IP addresses, or internal host/service
terms. The separate theme-output check then validates the built visual
contract. Keep post content and metadata equally generic.
Container image
The Dockerfile builds the site with Node and serves the static output
with nginx-unprivileged. Images are tagged with the full Git commit
SHA only; there is no latest tag.
# one-time, interactive: log in to the registry
docker login git.keo.sh
# build and push git.keo.sh/keo/blog:<full-git-sha>
just image-push
scripts/image.mjs derives the tag from git rev-parse HEAD, refuses to
run against a dirty worktree, and never accepts or prints credentials -
authentication is handled entirely by the docker login step above.
CI
There is no CI workflow in this repository yet. Publishing is a manual,
local just image-push today. Once a hardened, self-hosted runner is
available for the Forgejo instance at git.keo.sh, this project should
migrate to a Forgejo Actions workflow that runs just validate and
just image-push on tagged commits.