Files
werkator/docs/prs/2026-09-08-PR#35-ui-kin-design.md
0e5d6a3bb1 ui: the blog's visual language (RFC 0001, option G) — palette, IBM Plex, logo, header, view titles, icons (#35)
The first of RFC 0001's two pull requests: werkator.css rewritten around the
RFC's tokens (white paper, blue-shifted petrol as the only accent, clay for
failures, a neutral dark mode), IBM Plex Sans and Mono bundled as WOFF2 under
static/fonts/, the two-check W as logo and favicon, a site header with the
wordmark, the repository crumb and the instance navigation, view tabs followed
by the view's title and its explanation, statuses as dot and word, stroke
glyphs as CSS masks instead of emoji, and the phone cards in the same
vocabulary. No data change; the repository switcher stays until the overview PR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #35
2026-09-08 17:06:59 +02:00

9.3 KiB

WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.

The Problem

The web UI was a functional port of the legacy generated pages: a system font, blue links, pill badges, emoji as icons, and a page title that named the view and the repository in one line. It looked like every other CI page and had no visual kinship with the author's other sites. RFC 0001 chose a direction — the blog's paper and IBM Plex, a blue-shifted petrol as the only accent, a title hierarchy that names the view first and explains it second, and a new logo — and decided to roll it out in two pull requests: the design first, the cross-repository overview second. This PR is the first of the two.

Non-Goals

  • No Repositories page, no GET /api/repos, no repository line: the <select> switcher stays until the second PR.
  • No data change: every page shows exactly the fields it showed before, from the same API, in the same formats (UiFormats and werkator.js are untouched in what they format).
  • No manual theme toggle; prefers-color-scheme decides, as before.
  • No Current tab: the view is reached from a running row's live link, as decided in PR#13; the tab appears only while the reader is on it.
  • No version bump and no release-notes entry: the author bumps the version with the deployment.

The Scenarios

Feature: the web UI in the blog's visual language

Background

  • The palette and typography are the ones RFC 0001 specifies: the blog's neutrals, the accent #0E6B7D (light) / #63BCCB (dark), clay for failures, ghost grey for cancelled, interrupted and unknown.
  • A repository view is Latest, Branches, History or Current; an instance page is System or the release notes; the artifact page belongs to a repository but is no view.

Scenario#35.01: every page shares the site header, and names its view first

So that a reader sees where they are before what they see, and finds the repository, the instance pages and the live state in the same place on every page.

  • Given any page of the UI
  • When it renders
  • Then it carries the site header: the logo and the wordmark linking to the served repository, the repository as a crumb (the switcher with several served repositories), System as the instance navigation, the live indicator as an outlined chip, and the reload button
    • and below it the view's tabs (repository views only), then the view's title and its one-line explanation (Latest — one build per branch, newest first; Branches — every origin branch and its latest build; History — all stored builds, newest first; Current — running builds with their live log; System — instance metrics since first start)
    • and next to the title the last poll and the watcher state, filled from /api/watcher
    • and the instance pages carry no repository crumb and no tabs
    • and the footer names the version as the link to the release notes, which are not navigation.
Verified by

Scenario#35.02: statuses are dots and words, icons are glyphs, and both are the same from the server and from the script

So that a failed row is serious without shouting, a running row is visibly alive, and a page re-rendered by werkator.js looks exactly like the page the server rendered.

  • Given a build table with running, pending, successful and failed rows
  • When the server renders it, and when werkator.js re-renders it from the API
  • Then each status is a 7 px dot and a Mono word in its status color — the dot outlined for pending, pulsing for running — with status status-<name> as the only markup
    • and the running row sits on the accent tint, the failed row on the clay tint
    • and the artifact, log, permalink, live, copy, restart, delete and cancel controls are stroke glyphs from the stylesheet (icon icon-<name>), titled for assistive technology, and no emoji appears anywhere.
Verified by

Scenario#35.03: the fonts come with the jar

So that the UI looks the same on every host and loads nothing from a third party — the deployments sit behind a reverse proxy and a traffic quota.

  • Given a browser without IBM Plex installed
  • When it opens any page
  • Then the stylesheet declares IBM Plex Sans (400, 500, 600, 700) and IBM Plex Mono (400, 500) by @font-face from /fonts/*.woff2
    • and the server serves those six files (latin subset, about 215 KB in total) from the jar's static/fonts/
    • and a host without them falls back to the stack "Segoe UI", system-ui, sans-serif and ui-monospace, Consolas, monospace without any layout change.
Verified by

Scenario#35.04: the phone keeps the cards, in the new vocabulary

So that the existing small-screen behaviour stays valid.

  • Given a viewport below 680 px
  • When a build table renders
  • Then the rows become cards with their column labels, the running and failed cards with their tint as border and ground
    • and the header keeps the logo, the crumb, System, the live dot and the reload button on one line
    • and the action buttons are 44 px targets.
Verified by
  • Checked in the browser at 390 px (Latest, System); the breakpoint's markup is the one the existing tests render.

The Solution

  • werkator.css is rewritten around the RFC's tokens: the neutrals and the petrol as custom properties on :root, redefined for prefers-color-scheme: dark; IBM Plex by @font-face; the header, tabs, view title, tables, statuses, cards, system page and footer as the drafts draw them. Class names shared with werkator.js and the templates are kept, so the script's rendering did not have to change where the markup did not.
  • fragments.html replaces the h1 with three fragments: siteHeader (the blog's header), viewHead (tabs, title, explanation, the view-meta line and the watcher banner), and footer. The fragments are named siteHeader and viewHead, not header and nav, because Thymeleaf resolves fragments :: nav to the first <nav> element before the fragment of that name.
  • Icons are 24 px stroke paths as CSS masks (.icon-<name> with a --icon data URI), colored by currentColor; the templates write the spans, werkator.js gains icon(name) and iconLink(...), and actionButton takes the glyph name and an optional label instead of a symbol. The live link built by the script is now repository-scoped like the template's (it pointed at /current without the repository prefix before).
  • favicon.svg is the RFC's tile: the two-check W in white on a petrol tile; the header uses the bare mark inline, stroked in --accent so it follows the theme.
  • static/fonts/ holds the six WOFF2 files (latin subset, from the Google Fonts build of IBM Plex, SIL Open Font License).
  • The view-meta line is filled by the existing watcher poll: polled HH:MM:SS · watcher running|stopped.

Open Questions

  • The static resources are served with Cache-Control: no-cache, must-revalidate and a Last-Modified header, as before, so a browser revalidates the six font files on every page load (six conditional requests answered with 304). A long max-age for /fonts/ alone would need a resource-handler configuration; left out until the traffic logs say it matters.

Additional Changes

  • The architecture skill's Web UI section describes the fragments, the tokens, the icons and the fonts.

Prerequisite PRs

  • PR#34 — RFC 0001, round four: the decisions this PR implements.

Follow-up PRs

  • The second PR of RFC 0001: GET /api/repos, the Repositories page, and the repository line replacing the <select>.