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
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.
Related Links
- RFC 0001: Web UI Redesign — the Instrument Panel, revised as Kin, and its drafts.
- PR#34 (the RFC's round four), which decided the direction this PR implements.
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 (
UiFormatsandwerkator.jsare untouched in what they format). - No manual theme toggle;
prefers-color-schemedecides, as before. - No
Currenttab: 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),
Systemas 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.
- and below it the view's tabs (repository views only), then the view's title and its one-line explanation (
Verified by
- UiControllerTest — every page carries the site header with the wordmark and the view's title and explanation
- UiControllerTest — the system page is an instance page: its own title, no repository crumb
- UiControllerTest — with one served repository the pages keep their existing URLs and show no switcher and its multi-repository sibling, unchanged.
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.jsre-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
- UiControllerTest — the icons are stroke glyphs from the stylesheet, not emoji
- UiControllerTest — latest view renders rows with badge, Gitea links, artifact link, actions, and token and the other row-rendering tests, unchanged: the status classes and action attributes they assert are the ones the design keeps.
- The script side has no test harness;
werkator.jswas checked in the browser against the server-rendered page (Latest, History, Current, System, artifact page, light and dark, desktop and 390 px).
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-facefrom/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-serifandui-monospace, Consolas, monospacewithout any layout change.
- and the server serves those six files (latin subset, about 215 KB in total) from the jar's
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.
- and the header keeps the logo, the crumb,
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.cssis rewritten around the RFC's tokens: the neutrals and the petrol as custom properties on:root, redefined forprefers-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 withwerkator.jsand the templates are kept, so the script's rendering did not have to change where the markup did not.fragments.htmlreplaces theh1with three fragments:siteHeader(the blog's header),viewHead(tabs, title, explanation, theview-metaline and the watcher banner), andfooter. The fragments are namedsiteHeaderandviewHead, notheaderandnav, because Thymeleaf resolvesfragments :: navto the first<nav>element before the fragment of that name.- Icons are 24 px stroke paths as CSS masks (
.icon-<name>with a--icondata URI), colored bycurrentColor; the templates write the spans,werkator.jsgainsicon(name)andiconLink(...), andactionButtontakes 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/currentwithout the repository prefix before). favicon.svgis the RFC's tile: the two-check W in white on a petrol tile; the header uses the bare mark inline, stroked in--accentso 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-metaline 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-revalidateand aLast-Modifiedheader, as before, so a browser revalidates the six font files on every page load (six conditional requests answered with 304). A longmax-agefor/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>.