- Shell 46.3%
- SCSS 39.4%
- HTML 12%
- Ruby 2.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| _includes | ||
| _layouts | ||
| _posts | ||
| _sass | ||
| assets | ||
| .gitignore | ||
| .ruby-version | ||
| 404.html | ||
| _config.yml | ||
| about.md | ||
| apple-touch-icon-precomposed.png | ||
| apple-touch-icon.png | ||
| deploy.sh | ||
| favicon.ico | ||
| Gemfile | ||
| Gemfile.lock | ||
| index.md | ||
| privacy-policy.md | ||
| README.md | ||
| serve.sh | ||
| setup.sh | ||
static-website-template
A Jekyll template for a small, fast, self-contained static site: monospace
layout, no webfont CDN, no analytics, no cookies. Clone it, run setup.sh with
your details, and you have a working site with local preview and one-command
deploy.
The theme is a vendored fork of minima — every layout, include and stylesheet lives in this repo, so there is no gem whose markup you cannot read.
prerequisites
-
Ruby ≥ 3.1 — the pinned version is in
.ruby-version(3.2.5). With rbenv:rbenv install "$(cat .ruby-version)" -
Bundler —
gem install bundler -
rsync and ssh — for deploying (both ship with macOS)
-
a Unix shell —
setup.sh,serve.shanddeploy.share bash. macOS and Linux work as-is; on Windows use WSL.
setup.sh checks all of this before it changes anything.
setup
git clone <this-repo> mysite
cd mysite
./setup.sh --name mysite \
--author "Your Name" \
--mail you@example.com \
--url https://mysite.example.com \
--server my-server \
--server-path /var/www/mysite
Run ./setup.sh with no arguments to be prompted for each value instead, or
./setup.sh --dry-run to see exactly what would change without touching
anything. ./setup.sh --help lists every flag.
| flag | meaning | example |
|---|---|---|
--name |
site title | mysite |
--author |
author name | Your Name |
--mail |
contact address | you@example.com |
--url |
base URL, no trailing slash | https://mysite.example.com |
--server |
ssh host or ~/.ssh/config alias |
my-server |
--server-path |
absolute docroot on the server | /var/www/mysite |
--baseurl |
optional subpath, if not at the domain root | /blog |
--ssh-key |
optional identity file; blank uses the ssh agent | ~/.ssh/id_rsa |
setup.sh then:
- validates every value before writing anything — including the same
docroot guards
deploy.shuses, so a typo in--server-pathfails here rather than during anrsync --deleteagainst a live server - substitutes the
__SITE_*__placeholders in_config.yml - writes
deploy.conf(gitignored) fordeploy.sh - replaces this README with a short one for your site
- deletes
SETUP.mdand itself - removes the
originremote, so you cannot push your site into the template - runs
bundle install
It refuses to run a second time.
local preview
./serve.sh # http://127.0.0.1:4000, opens a browser, livereload
./serve.sh 4001 # a different port
Press Enter or Ctrl-C to stop. _config.yml is not reloaded while the
server runs — restart after editing it.
deploy
./deploy.sh -n # dry run: show what would transfer, change nothing
./deploy.sh # preview the changes, then confirm
./deploy.sh -y # no prompt
./deploy.sh --no-build # upload the existing _site/ as-is
It builds, then mirrors _site/ to the server with rsync --delete. Before
transferring anything it refuses to proceed if the target is a system
directory, if the path has fewer than three segments (the shape an empty
variable collapses to), or if the build output is missing or has no
index.html. After a successful transfer it checks that URL returns HTTP 200.
Settings come from deploy.conf, which setup.sh writes and .gitignore
excludes:
TARGET="my-server:/var/www/mysite"
SSH_KEY="" # blank = ssh agent / ~/.ssh/config
URL="https://mysite.example.com"
writing
-
posts —
_posts/YYYY-MM-DD-title.md, front matterlayout: post. Posts dated in the future stay unpublished (future: false). -
pages — markdown files in the project root with
layout: pageand apermalink. Add them toheader_pagesin_config.ymlto show them in the nav, in that order. -
assets — anything in
assets/. Reference it throughrelative_urlso it survives a move to a subpath:
_posts/2026-07-27-example-post.md demonstrates every styled element —
headings, lists, tables, code, blockquotes, footnotes, images, video embeds.
Keep it while you set things up, then delete it.
structure
_config.yml site identity + build settings; single source
of truth, read via {{ site.title }} etc.
_layouts/
default.html page skeleton
home.html front page: recent posts + OLDER POSTS list
page.html standalone pages
post.html blog posts
_includes/
head.html meta, seo tag, stylesheet, favicons
header.html site title + nav
footer.html contact + url
_sass/
_variables.scss every themeable value, all !default
_mixins.scss media-query, relative-font-size
_fonts.scss @font-face for the self-hosted typeface
minima.scss theme entry point
minima/ base, layout, syntax-highlighting
assets/
main.scss compiles to /assets/main.css
fonts/ JetBrains Mono woff2 + OFL licence
_posts/ YYYY-MM-DD-title.md
index.md about.md privacy-policy.md 404.html
setup.sh serve.sh deploy.sh
customising the look
Every visual value is a !default variable in _sass/_variables.scss.
Override them from assets/main.scss without editing the theme:
@use "minima" with (
$brand-color: #c00,
$content-width: 960px,
);
privacy
The template ships with no cookies, no analytics and no third-party requests.
The typeface is self-hosted from assets/fonts/ (SIL OFL
1.1) rather than loaded from a font CDN, so no visitor
IP reaches anyone else. privacy-policy.md states this, and pulls the
controller's name and address from _config.yml — keep them consistent if you
change how the site is hosted.
licence
The vendored theme derives from minima, MIT-licensed. JetBrains Mono is under
the SIL Open Font License 1.1 (assets/fonts/OFL.txt).