A bare-bones static blog generator that I intend to post on my Free Apps page
in a week or two (as an in-house script). Maybe someone will find value in it.
The main reason for its existence was that I wanted to offer RSS on neocities.
Some of the other features were created to improve a specific workflow.
Before I upload, I'd like to polish it a bit(1), write a help file(2), maybe
add a template or two(3).
(1) 24apr: added a configuration section at the top of the script
(2) 24apr: the section below may become the script's help section but...
I have to see how well it communicates.
(3) 29apr: created a new template (nicer, I think, than the original)
-------------------------------------------------------------------------------
This is what the static blog generator looks like locally: /local/directory
┌── blog.py (Creates the index*.html and rss.xml files. The only )
│ (files you upload. )
│
├── template.html (I have two basic templates. Easy to create your own )
│
├── ./posts/ (The script is hard-coded to look here. You manually )
│ │ (create the entire tree to become familiar with how )
│ │ (the script works. All posts are organized here. )
│ │
│ ├── ./2025/
│ │ ├── 12.14--the.council.of.linux.greybeards.html
│ │ ├── 12.30--happy.new.year.bash.html
│ │ └── MM.DD--title.of.post.html
│ │
│ └── ./2026/
│ ├── 03.30--summer.glau.a.sci-fi.fantasy.icon.html
│ ├── 04.18--words.to.live.by.html
│ └── MM.DD--title.of.post.html
│
│ (File naming is critical because the script uses the )
│ (MM.DD (Month/Day, 2-digits for each) and filename to)
│ (organize the posts within the index.html static file)
│ (created by ./blog.py )
│
│ (Example: 04.18--words.to.live.by.html )
│
│ (The script will look for MM.DD, "--" and filename )
│ (The anchor link will look like this: )
│ (<div id="post-20260418-04.18--words.to.live.by"... )
│
│ (The script is designed to be safe - never writing to)
│ (this directory - performing only read operations. )
│
└── ./resources/ (local images/files you may link to in your posts. )
(This directory is also manually created, if needed. )
(You can name it anything because it's you who links )
(to it from within each hand-written post. You can )
(even create multiple dirs: ./images ./assets ... )
-------------------------------------------------------------------------------
This is the public facing website directory after you upload the files:
┌── index.html (An HTML 3.2 static webpage I use. No JS, little CSS)
├── index-2.html (page 2: older posts)
│
├── rss.xml (the last 10 full posts, default/configurable)
│
└── ./resources/ (images/files that index.html links to locally)
-------------------------------------------------------------------------------
The blog.py and template.html files work together to create the index* pages
blog.py: The 'render_posts()' function adds two things to the final
index.html file.
+-------------------------+----------------------+-------------------+
| Element | What it does | Appears Where? |
+-------------------------+----------------------+-------------------+
| <div id="post-###" | Wraps each post and | Around each |
| class="post-content"> | adds anchor links | individual post |
| | that RSS uses | |
+-------------------------+----------------------+-------------------+
| <div class="nav"> | Navigation links: | Bottom of page |
| | "Newer-posts" | |
| | "Older-posts" | |
+-------------------------+----------------------+-------------------+
The template.html file provides everything else:
* <html>, <head>, <body> structure
* CSS styles
* Header with blog title and RSS link
* The placeholder string where all this content gets inserted:
<!-- POSTS GO HERE -->
* Every new template must contain this placeholder string exactly as it's
written. The blog.py file searches for the exact string.
-------------------------------------------------------------------------------
Outline: Each time blog.py is run, it reads all the files in ./posts/ and
recreates index.html, index-2.html, etc... as well as rss.xml
I only have to upload the index*.html and rss.xml files. Maybe new
local images to ./resources/ that I linked to in a post. That's it.
----------------------------------------------------------------------
Process: Just write a post inside the current year subdirectory.
Currently it would be: ./posts/2026/
The date and filename should match the date and title of each post
for organizational purposes. It is not a requirement but it's useful.
example: ./posts/2026/04.18--words.to.live.by.html
Once you have the post written and saved in ./posts/2026/
Run: ./blog.py
This creates: index.html (index-2.html if necessary) and rss.xml.
That's it! Just upload those files and you'll have RSS and a great
looking blog (if you opt to create a nicer looking template.html;-)
____________________________________________________________________________
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
notes: This is a custom built static blog generator that is very specific to
how I work. There will be a few manual tweaks required but I'm
currently adding a configuration section to make things easier.
There are warts, for sure, because I just needed something simple to
provide RSS. The biggest complaint someone might make would be about
the lack of permalinks.
* The RSS file links each story in its XML file to an anchor link that
matches the relevant post inside index.html. I decided to do it this
way in order to simplify things. I didn't want to upload:
(1) index*.html, rss.xml, ./resources/anImage.png (nice, simple) but
(2) also upload and track the entire ./posts/ tree and all the
individual posts, in addition to
(3) scripting for relative linking when index.html is at /blog/ and
the post is located in /blog/posts/2026/.
Other small issues:
* The individual posts are written as HTML. No markdown, no other
human-friendly syntax or even plain text that is converted.
* The current template is here: https://640kb.neocities.org/blog
I think most users will likely find the template a bit "meh" but,
it's really easy to create your own.
-----------------------------------------------------------------------------
(Last updated: 30 Apr 2026)
-----------------------------------------------------------------------------