Skip to content

Add three responsive light and dark blog themes - #160

Open
mraible wants to merge 14 commits into
apache:masterfrom
mraible:feature/modern-blog-themes
Open

mraible wants to merge 14 commits into
apache:masterfrom
mraible:feature/modern-blog-themes

Conversation

@mraible

@mraible mraible commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds three responsive Roller themes, each a port of a popular MIT-licensed blog theme, chosen to look clearly different from one another:

  • PaperMod (Hugo's most popular theme): a clean, minimal reading layout with a light/dark toggle
  • Terminal (Hugo, by panr): a retro console look with monospace type, a dark background, and amber accents
  • Hyde (Jekyll, by Mark Otto): a bold colored sidebar with the blog's title and navigation beside the content

Each theme includes Velocity templates for the weblog, permalink, category, tag, search, and archive pages, follows the reader's light or dark color-scheme preference, and makes no external requests. Each theme.css carries its upstream MIT notice, and LICENSE.txt lists all three. All three sort after Basic, so the create-weblog default is unchanged.

PaperMod

PaperMod theme preview

Terminal

Terminal theme preview

Hyde

Hyde theme preview

Theme discovery coverage in ThemeManagerTest verifies that all three themes and their stylesheets load.

Introduce Horizon, Alpenglow, and Dispatch as modern Roller themes with responsive layouts and automatic color-scheme support. Add theme discovery coverage for all three.
Expand the compact theme templates into readable, consistently indented HTML and Velocity control flow.
Expand the three theme stylesheets for review and use the same neutral test-blog fixtures as Roller’s existing theme previews.
Set the create-weblog form's default theme explicitly so adding an alphabetically earlier shared theme does not change existing setup behavior.

@snoopdave snoopdave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for these themes. Three template bugs found by rendering the templates with Velocity 2.4 (the version Roller uses); details inline. Also, CI on this PR predates the current workflow (no JDK 25 or CodeQL); merging current master would re-run it.

@@ -0,0 +1,28 @@
#foreach($entry in $entries)
<article class="story">
<div class="story-index" aria-hidden="true">$velocityCount</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Velocity 2 removed $velocityCount, and Roller's velocity.properties doesn't enable the old name, so this prints the literal text $velocityCount on every entry. Rendered with Velocity 2.4: #foreach($x in $l)$velocityCount#end gives $velocityCount$velocityCount. Use $foreach.count (as gaurav and frontpage use $foreach.index).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. It uses $foreach.count now.

@@ -0,0 +1,30 @@
#foreach($entry in $entries)
<article class="dispatch">
<div class="dispatch-number">#if($velocityCount<10)0#end$velocityCount</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Velocity 2 removed $velocityCount, and Roller's velocity.properties doesn't enable the old name, so this prints the literal text $velocityCount on every entry. Rendered with Velocity 2.4: #foreach($x in $l)$velocityCount#end gives $velocityCount$velocityCount. Use $foreach.count (as gaurav and frontpage use $foreach.index).

Here: #if($foreach.count<10)0#end$foreach.count.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with #if($foreach.count < 10)0#end$foreach.count, so entries show 01, 02, and so on.

<footer class="site-footer">
<p>
<strong>$model.weblog.name</strong>
— #if($model.weblog.tagline)$model.weblog.tagline#elseField notes from a life in motion.#end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Velocity reads #elseField as one unknown directive, not #else + text. With a tagline this prints My tagline#elseField notes from a life in motion.; without one, the fallback never shows. Use #{else}Field notes from a life in motion. (Horizon's footer works because it has a space after #else).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. The fallback text was hard-coded persona copy, so I removed it instead of switching to #{else}. The footer now shows the tagline only when the weblog has one.

<main id="content" class="article-shell">
<header class="page-heading">
<p class="eyebrow">Topic</p>
<h1>$model.tags</h1>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$model.tags is a List<String>, so this renders with list brackets, e.g. [java] or [java, roller]. Same on line 5 (<title>). Suggest joining the tags with a #foreach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here and in the <title> on line 5. The tags are joined with a #foreach, so a multi-tag page reads java, roller.

<main id="content" class="reading-shell">
<header class="page-title">
<p class="kicker">Stories filed under</p>
<h1>$model.tags</h1>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$model.tags is a List<String>, so this renders with list brackets, e.g. [java] or [java, roller]. Same on line 5 (<title>). Suggest joining the tags with a #foreach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here and in the <title>, the same way as Horizon.

<main id="content" class="reading-shell">
<header class="page-title">
<p class="label">FILTER / TAG</p>
<h1>$model.tags</h1>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$model.tags is a List<String>, so this renders with list brackets, e.g. [java] or [java, roller]. Same on line 5 (<title>). Suggest joining the tags with a #foreach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here and in the <title>, the same way as Horizon.

…w themes

Velocity 2 removed $velocityCount, so Alpenglow and Dispatch printed the literal text on every entry; they now use $foreach.count. Alpenglow's footer used #elseField, which Velocity reads as an unknown directive, so it now shows the tagline only when one is set. The tag pages joined $model.tags as a Java list, which rendered as [java, roller] in the heading and title; they now list the tags separated by commas.
Alpenglow, Dispatch, and Horizon shipped with hard-coded copy written for one blog, so every weblog using them showed the same headline, founding year, and about text. The hero headings now show the weblog name, Dispatch derives its founding year from the weblog's creation date, and the about and tagline sections are omitted when those fields are empty. Long weblog names now wrap in the Alpenglow and Dispatch headlines instead of overflowing on narrow screens.
@mraible

mraible commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I merged current master, so CI now runs on JDK 25 with CodeQL.

I also took two items from the UI audit you attached to #156:

  • UI-07: the three themes now use the weblog's own name, tagline, and about text, and hide those sections when the fields are empty. Dispatch's "EST." year comes from the weblog's creation date.
  • UI-08 Removing empty spaces #1: long names now wrap in the Dispatch and Alpenglow headlines.

I rendered the changed templates with Velocity 2.4.1, with the tagline and about fields both set and empty, and the full test suite passes.

…rMod

Horizon, Alpenglow, and Dispatch are replaced with ports of three established, MIT-licensed blog themes, so Roller users get designs that are already familiar from other blogging communities: Casper (Ghost's default theme), Minima (Jekyll's default theme), and PaperMod (the most popular Hugo theme).

Each port renders weblog, permalink, category, tag, search, and archive pages in its upstream's layout and typography, follows the reader's light or dark color-scheme preference, and makes no external requests. Each theme.css carries its upstream MIT notice, and LICENSE.txt lists all three. Since all three sort after Basic, the create-weblog flow no longer needs to pin Basic as its default.
@mraible

mraible commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

A note for a follow-up pass on this PR: Casper, Minima, and PaperMod are all minimal, text-first designs (system fonts, white backgrounds, little color), so even though their layouts differ, they look like one family side by side. They were picked for popularity and license, not variety.

The plan is to keep PaperMod, since it's the most popular, and replace the other two with MIT-licensed themes that have a clearly different look:

  • Minima → Terminal (Hugo): retro console style, dark background, monospace type, accent colors
  • Casper → Hyde (Jekyll): bold colored sidebar with the title and nav, content beside it

Other candidates if either of those doesn't port well: Chirpy (Jekyll, sidebar layout for technical blogs; needs a check for CDN-loaded fonts and scripts) and Minimal Mistakes (Jekyll, author-profile sidebar with color skins).

The replacements would follow the same rules as the current ports: Velocity templates for every page type, upstream CSS with its MIT notice, a LICENSE.txt entry, light and dark support, and no external requests.

@snoopdave

Copy link
Copy Markdown
Contributor

I will try these themes out locally and submit a review, but Codex found a couple issues:

🤖 Codex: Reviewed head a1819c5. I found three issues worth fixing before merge.

  1. Archives silently omit posts older than the newest 100. All three themes use getRecentWeblogEntries("nil", 100) without pagination or a calendar. On an established weblog, earlier posts disappear from the archive browser. Add navigation to the remaining history. Casper, Minima, PaperMod
  2. Minima and PaperMod drop navigation between days and months. Their controls only use prevLink and nextLink, which paginate within the current collection. They ignore prevCollectionLink and nextCollectionLink, which move between dates. A month containing one page of entries consequently has no archive navigation. Casper handles these links correctly. Minima controls, PaperMod controls
  3. Minima can fail rendering on an oversized numeric page parameter. Its digits-only check accepts values such as 999999999999999999999, then Integer.parseInt() throws. Roller’s request parser already handles that invalid input, but the template parses the raw value again. I reproduced the exception with Velocity 2.4.1. Use a validated page value or bounded conversion. Failing expression

@snoopdave snoopdave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried these out locally and all three look great. Thanks, Matt.

Casper, Minima, and PaperMod are all minimal, text-first designs, so they
looked like one family side by side. PaperMod stays as the most popular;
Minima and Casper are replaced with MIT-licensed themes that look clearly
different:

- Terminal (Hugo, panr): a retro console look with monospace type, a dark
  background, and amber accents, plus a light variant for readers whose
  system prefers light.
- Hyde (Jekyll, Mark Otto): a bold colored sidebar with the blog's title
  and navigation beside the content. The sidebar color is a single CSS
  variable, so custom.css can switch to any of Hyde's Base16 colors.

Both follow the same rules as PaperMod: Velocity templates for every page
type, the upstream CSS with its MIT notice in theme.css, a LICENSE.txt
entry, automatic light and dark modes, and no external requests (the
upstream web fonts are not bundled, so the font stacks fall back to local
faces). Terminal's mobile menu uses <details> instead of its menu.js.
@mraible
mraible force-pushed the feature/modern-blog-themes branch from 08d7516 to 34d083b Compare October 25, 2026 22:47
@mraible

mraible commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

@snoopdave The ones I added all looked similar, so I changed out the last two. Please re-review and merge if OK.

assertPublishingTimeOnOneLine read the three selects' positions with
separate boundingBox calls right after clicking the Advanced toggle, while
Bootstrap was still animating the panel open. A layout shift between reads
made the selects appear on different lines (y=940, 1014, 1030 in CI) even
though they share one flex row. The check now waits for the panel's
"collapse show" state and reads all three positions in a single
page.evaluate call.
…e layout

Replace the wait on Bootstrap's internal collapse classes with the
shown.bs.collapse event, the same way the Insert Media File dialog waits
for shown.bs.modal, and drop the redundant waitFor. Poll until the hour,
minute and second selects share a line instead of measuring once; if they
never do, the assertion still reports where they sit.
@mraible
mraible force-pushed the feature/modern-blog-themes branch from 2347009 to ef5c4ec Compare October 25, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants