vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Refresh the web surface with Everforest, readable type, and clearer navigation
796ac8c0ce77461174cf520d5fad4dea37744662 · Keenan Nemetz · 12d ago · parent ac00929f
docs/ui-plan.md +43−14
| @@ -9,9 +9,20 @@ deliberately rather than by drift. | |||
| 9 | 9 | Avoid GitHub's whitespace-heavy chrome; avoid Sourcehut's bareness. A row of | |
| 10 | 10 | data should be readable at a glance, and a page should fit on one screen when | |
| 11 | 11 | it can. | |
| 12 | - **Dark by default.** Every colour comes from a custom property on `:root`. | ||
| 13 | A light theme is a second block of variables, not a rewrite; it currently | ||
| 14 | follows the operating system's `prefers-color-scheme`. | ||
| 12 | - **Everforest is the theme.** Every colour comes from a custom property on | ||
| 13 | `:root`; `static/everforest.css` supplies six variants over the same layout: | ||
| 14 | dark and light, each in hard, medium, and soft contrast. The picker lives on | ||
| 15 | the Account page, and `static/theme.js` applies the saved variant before the | ||
| 16 | first paint. Its `System` default follows the operating system's dark or | ||
| 17 | light preference and uses the medium contrast; the base sheet's tokens remain | ||
| 18 | the fallback. | ||
| 19 | - **Readability first.** Lexend is the UI face and JetBrains Mono is the code | ||
| 20 | face; both are self-hosted WOFF2 under `static/fonts/` under the SIL Open Font | ||
| 21 | License 1.1, with the system stacks as fallback. The medium (100%) base is | ||
| 22 | 15.4px with a 1.55 line height and subpixel-friendly smoothing. Every explicit | ||
| 23 | font size is multiplied by `--font-scale`; the Account page's Text size presets | ||
| 24 | are 90%, 100%, and 110%, stored in this browser and applied before first paint. | ||
| 25 | Medium is the default and matches the former Large size. | ||
| 15 | 26 | - **Server rendered.** Go `html/template` produces the page. HTMX is the default | |
| 16 | 27 | dynamic layer, vendored into the binary; JavaScript is required — passkeys, | |
| 17 | 28 | clipboard copy, and rich widgets such as the label/assignee token input all | |
| @@ -21,16 +32,17 @@ deliberately rather than by drift. | |||
| 21 | 32 | answer a fragment when `HX-Request: true` and a redirect otherwise. | |
| 22 | 33 | - **Identity later.** Keep the `vole` wordmark as a placeholder and keep colour, | |
| 23 | 34 | type, radius, and spacing in tokens so a real identity is a token swap. | |
| 24 | - **Single tenant.** No organisation/workspace switcher. The global bar has one | ||
| 25 | public destination, plus the account menu; the repository owns its own | ||
| 26 | horizontal tabs. | ||
| 35 | - **Single tenant.** No organisation/workspace switcher. The global bar is | ||
| 36 | limited to Home, Activity, search, and the account menu; the repository owns | ||
| 37 | its own horizontal tabs. | ||
| 27 | 38 | ||
| 28 | 39 | ## Navigation model | |
| 29 | 40 | ||
| 30 | Global bar (slim): `vole` · `Activity` · search · account menu. The wordmark is | ||
| 31 | home, so there is no separate Repositories link. The account menu carries | ||
| 32 | Account, Status for instance admins, and Sign out; the current destination is | ||
| 33 | marked. Search is a field on wide screens and takes its own row on narrow ones. | ||
| 41 | Global bar (slim): `vole` · `Home` · `Activity` · search · account menu. The | ||
| 42 | wordmark links home too, but the text destination keeps the mark from being the | ||
| 43 | only label. The account menu carries Account, Status for instance admins, and | ||
| 44 | Sign out; the current destination is marked. Search is a field on wide screens | ||
| 45 | and takes its own row on narrow ones. | ||
| 34 | 46 | ||
| 35 | 47 | Repository header: the repository name and its short description sit above the | |
| 36 | 48 | tabs at the home-page header scale, with a lock for a private repository or a | |
| @@ -40,8 +52,8 @@ the tabs, not the page, are the navigation. The tabs are: | |||
| 40 | 52 | `Overview` · `Activity` · `Code` · `Issues` · `Change requests` · `Releases` · `Runs` · `Locks` · `Settings` | |
| 41 | 53 | ||
| 42 | 54 | No sidebar. A sidebar only pays for its width when there are many persistent | |
| 43 | destinations; here the repository has several and the global bar has one, and | ||
| 44 | they fit on one row at desktop widths. On narrow screens the tab row scrolls | ||
| 55 | destinations; here the repository has several and the global bar is separate, | ||
| 56 | and they fit on one row at desktop widths. On narrow screens the tab row scrolls | ||
| 45 | 57 | horizontally rather than collapsing into a menu. | |
| 46 | 58 | ||
| 47 | 59 | Every tab opens with the same page header: a title and one-line summary on the | |
| @@ -72,7 +84,7 @@ Status: `done` exists today · `wip` in progress · `todo` missing. | |||
| 72 | 84 | ||
| 73 | 85 | - done — repository list (`/`) | |
| 74 | 86 | - done — sign-in, registration, enrollment link | |
| 75 | - done — account (name, keys, passkeys, sessions) | ||
| 87 | - done — account (name, theme, text size, keys, passkeys, sessions) | ||
| 76 | 88 | - done — styled 404 page everywhere, including missing detail resources and | |
| 77 | 89 | download paths | |
| 78 | 90 | - done — styled 500 page everywhere; a handler panic before the first byte is | |
| @@ -332,6 +344,21 @@ stored as path data in `server/web/icons.go` and rendered inline with | |||
| 332 | 344 | `currentColor` and size with the surrounding text. A test checks that every icon | |
| 333 | 345 | named in a template exists. | |
| 334 | 346 | ||
| 347 | The header emblem is `static/vole-head.png` used as a CSS mask and tinted with the | ||
| 348 | theme's ink, so it follows the selected Everforest variant. The favicon is a | ||
| 349 | separate transparent mark: `favicon.svg` and the light/dark PNG fallbacks switch | ||
| 350 | between black and white from `prefers-color-scheme` (the browser window's mode), | ||
| 351 | not from the in-app theme choice. That keeps the tab icon legible against the | ||
| 352 | browser's own tab colour. | ||
| 353 | |||
| 354 | ## Fonts | ||
| 355 | |||
| 356 | Lexend (UI) and JetBrains Mono (code) are self-hosted variable WOFF2 subsets under | ||
| 357 | `server/web/static/fonts/`, split into Latin and Latin-ext. Their OFL 1.1 license | ||
| 358 | texts sit beside them, and the system stacks in `--font`/`--mono` cover scripts | ||
| 359 | outside those ranges and the pre-load state. No CDN and no runtime font request | ||
| 360 | outside `/static/`. | ||
| 361 | |||
| 335 | 362 | ## Rules | |
| 336 | 363 | ||
| 337 | 364 | - Every state-changing form carries the CSRF hidden input. | |
| @@ -349,6 +376,8 @@ named in a template exists. | |||
| 349 | 376 | it; anything two surfaces share goes in `chrome.html`, `components.html`, | |
| 350 | 377 | `diff.html` or its own component file. | |
| 351 | 378 | - Stylesheet sections are listed in the table of contents at the top of | |
| 352 | `vole.css`; add a line there when a section is added or moved. | ||
| 379 | `vole.css`; add a line there when a section is added or moved. The base sheet | ||
| 380 | stays the authority; every Everforest rule in `everforest.css` must stay | ||
| 381 | scoped to `[data-theme^="everforest-"]`. | ||
| 353 | 382 | - Tables are for tabular data; navigation and page chrome are not tables. | |
| 354 | 383 | - Test the page at 1440, 1024, and 390 px wide before calling it done. | |