Skip to content

aloglu/serein

Repository files navigation

Serein

Serein powers A Poem Per Day, a static site that publishes one poem per day from Markdown source files.

If you would like to share a poem to be published on the website, feel free to get in touch.

Project structure

  • poems/ holds the source poems.
  • templates/ holds the HTML templates.
  • assets/ holds styles, browser scripts, fonts, and branding assets.
  • scripts/ holds the build and maintenance scripts.
  • .github/workflows/ holds CI and deployment automation.

Build system

The site is generated by scripts/build.mjs.

That pipeline is responsible for:

  • loading and validating poem files
  • enforcing filename and directory conventions
  • rendering the homepage, archive, about page, poets index, poet pages, and dated poem pages
  • bundling client-side assets
  • generating social cards and editorial reports
  • generating RSS and sitemap files when SITE_URL is present

The project targets Node.js 24.x LTS and uses npm scripts as the execution layer for build, watch, preview, normalization, and editorial checks. .nvmrc and .node-version are checked in so local Node version managers can select the expected runtime.

In an interactive terminal, serein editorial opens a keyboard-first report viewer. When output is redirected or piped, it falls back to the plain-text report. The editorial report also flags same-poet proximity issues, and the TUI can apply the suggested fix with f on an actionable proximity item.

Content model

Each poem is stored as a Markdown file with frontmatter.

---
title: The Title
poet: The Poet
translator:
publication:
source:
date: 2026-03-10
---

First line of the poem.
Second line of the poem.

Required fields:

  • title
  • poet
  • date
  • poem body

Optional fields:

  • translator
  • publication
  • source

Poem files follow the path pattern poems/YYYY/MM-Month/YYYY-MM-DD-slug.md. Dates are unique across the collection. Missing optional metadata is still surfaced by the editorial checks.

Before running npm run poems:sync (alias: npm run normalize:poems), the date frontmatter may also use symbolic values:

  • next picks the closest unused date on or after the current publication date while respecting the poet cooldown rule described below. Multiple next values in one normalize run are resolved sequentially in alphabetical path order.
  • random-<month> such as random-may picks an unused day in that month of the current publication year.

The normalizer rewrites those symbolic values to concrete YYYY-MM-DD dates, removes empty optional frontmatter fields, rewrites frontmatter in the canonical order (title, poet, translator, publication, source, date), and then renames the poem file/path to match.

Serein also enforces a poet cooldown of 30 days for newly assigned next dates and reports any existing same-poet pairs that are scheduled too close together. If serein poems finds an actionable issue, it prints a suggested command such as serein poems fix-proximity 2026/04-April/2026-04-12-example.md, which moves the selected poem and any later poems by the same poet far enough forward to clear the cooldown.

The poem renderer also supports one small piece of custom markup:

  • ::line builds one visual line from aligned segments. |<...| places text on the left, |^...| centers it, |>...| places it on the right, and |~...| inserts space.

    ::line |<left phrase| |~3ch| |>right phrase|

Publishing model

Each poem is published at /YYYY/MM/DD/. Once a poem is published, its raw Markdown source is also available at /YYYY/MM/DD.md.

The homepage, archive, poets index, and poet pages resolve against the viewer's local date, capped to the site's one-day public share horizon. Dated poem pages at /YYYY/MM/DD/ become shareable as soon as they enter that horizon and only remain blocked for poems beyond it.

The build system also supports date-controlled previews through --as-of and SEREIN_AS_OF. Runtime date overrides are gated behind SEREIN_ENABLE_RUNTIME_AS_OF=1.

Deployment

GitHub Actions validates the site on pushes and pull requests. A separate scheduled workflow triggers the Cloudflare Pages deploy hook at the publication rollover.

Production environments provide SITE_URL so canonical URLs, RSS, and the sitemap can be emitted with the correct domain.

License

Released under the MIT License.

About

Serein powers A Poem Per Day, a static site that publishes one poem per day from Markdown source files

Resources

License

Stars

Watchers

Forks

Contributors