docs/specs/2026-09-19-desktop-layout-design.md

v1.40.0
gitbay/docs/specs/2026-09-19-desktop-layout-design.md rendered · source · history · blame · raw

171 lines · 9331 bytes

  1# Desktop layout
  2
  3Ref #226. The phone layout landed in v1.23.0 through v1.29.0; this is
  4the desktop half. Measured on gitbay.org at 1a40b22 (v1.30.0 head) at
  51600 and 1920 wide, and compared against GitHub, GitLab, Codeberg and
  6SourceHut dashboards at the same window.
  7
  8## Findings
  9
 10Three content widths, all pinned to the left edge. Nothing centers and
 11nothing grows past its cap, so a wide screen puts every page in its
 12top-left corner.
 13
 14| Width class | Pages | Content edge at 1920 | Used |
 15|---|---|---|---|
 16| `reading` (72rem, the default) | dashboard, issue and MR lists, explore, notifications, profile, refs, releases, milestones, labels | 1216px | 63% |
 17| `reading`, then 48rem comments + 18rem aside | issue, MR conversation | 1184px | 62% |
 18| `bounded` (48rem) | account settings, admin, new repository, login, 404 | 640px | 33% |
 19| `wide` | tree, blob, blame, log, commit, compare, diff, builds, build, search | 1920px | 100% |
 20
 21- Text columns are at the right measure. A comment at 48rem is about 90
 22  characters a line; wider hurts. Issue and MR pages need centering and
 23  a stronger aside, not wider text.
 24- Lists are laid out as prose. Issue, MR, build and explore rows are two
 25  to four lines inside the 72rem cap: 14 issues a screen at 1080 tall,
 26  9 repositories on explore.
 27- The dashboard is one column. Queues stack, the feed follows at 809px,
 28  below the fold at 1080 tall.
 29- The repository header is 182px on the code tab (identity, description,
 30  website, the toggles hint, tabs) and 110px on task tabs.
 31- Account settings at 48rem wraps SSH fingerprints onto two lines beside
 32  1280px of unused width.
 33- The overview README card is 1216px wide while its prose caps at 78ch,
 34  so paragraphs and code blocks have different right edges.
 35
 36SourceHut uses the same share of the window as gitbay and reads as
 37designed, because it centers and splits into two columns. GitLab's stat
 38tiles are gitbay's three "0" lines done as a strip. Codeberg's one-line
 39feed rows are the row format the lists should adopt.
 40
 41## Rules
 42
 431. **One centered container.** `--container: 100rem`. The repository
 44   header's inner content, `main` and the footer share it, so all three
 45   align at every width. `reading` (72rem) and `bounded` (48rem) stay as
 46   caps inside it, centered. `wide` is the container.
 472. **A left column is a feature.** A page gets a left column only when
 48   the column carries something the page already has as links, query
 49   parameters or sections. No site navigation in it; the top bar is the
 50   navigation. Every column is 15rem, sticky, and reads as one system.
 51   No column persists across pages.
 523. **Text keeps its measure.** Comments, descriptions, README prose and
 53   wiki pages stay at 48rem or 78ch. Width goes to tables and lists.
 544. **Lists are rows.** Above 64rem an issue, MR, build, explore, search
 55   and notification row is one line: title, then labels, then the meta
 56   pushed right, state or check badges at the far edge. The log keeps
 57   two lines; subject over author and date is the convention there.
 585. **Counts are tiles.** Where the dashboard now prints an empty queue
 59   as a heading with a zero, a strip of four tiles carries the counts
 60   and links to the lists. A non-zero count uses `--warn`: what wants
 61   you. Only non-empty queues list rows.
 626. **The repository header is two rows.** Row one: owner/name, the
 63   description with topics and website inline in muted text, the
 64   Pin/Watch/Bookmark/Fork buttons at the right. Row two: the tabs. The
 65   toggles hint moves to `title` text on the three buttons.
 667. **Below 62rem** a facet or section column becomes a closed
 67   disclosure before the content, headed by its word and the filters in
 68   force; the file navigator disappears (the tree page exists).
 69   **Below 80rem** the dashboard's pinned column returns to the chip
 70   row. Nothing the phone layout fixed moves.
 71
 72   *Amended 2026-09-19 (#237).* The rule read "stacks after the
 73   content, the way the issue aside does". An aside carries a note on
 74   one thing; a facet column carries the vocabulary the list is
 75   narrowed by, and a repository with two dozen labels stacked all of
 76   them under the content, taller than it. A column that stops being a
 77   column is a control, so it collapses to one and leads the list.
 78
 79## Pages
 80
 81### With a left column
 82
 83| Page | Column | Source of its contents |
 84|---|---|---|
 85| Dashboard | Pinned repositories with open issue count, open MR count, last build state | `Rail.Pinned` plus per-repository counts (new store read) |
 86| Blob, blame, edit | File navigator: the file's directory, parent link, current file marked with `aria-current` | `gitutil.ListTree` on the directory, the call the tree page makes |
 87| Issues list, MR list | State; labels with counts; milestones with open counts | The `state`, `label`, `milestone`, `assignee` parameters `activeFilters` already handles; counts from the labels and milestones pages' reads |
 88| Builds list | Status; job; branch | The `status`, `job`, `ref` parameters `buildFilter` already handles; branches from the refs read |
 89| Explore | Topics with counts | `?q=<topic>`, the link the topic chips already use |
 90| Site search | Kind: repositories, issues, merge requests | `?kind=repo\|issue\|mr` |
 91| Repository settings | Section nav: Identity, Access, Merge gates, Protected branches, Protected tags, Dependencies, Runners, Lifecycle | Anchors on the existing `h2` headings |
 92| Account settings | Section nav: Profile, SSH keys, Email addresses, OpenPGP keys, Notifications, Appearance, Export | Same |
 93| Admin | Section nav: Webhook deliveries, Mail, Mirrors, Builds, Dependency checks | Same |
 94| Wiki | Page nav | Already there; adopt the 15rem width |
 95
 96Facet links carry the other active filters, the way `activeFilters`
 97builds its clear links. The milestones and labels pages stay for
 98management; the "milestones · labels" links leave the list head.
 99
100### Layout of the pages that gain a column
101
102- **Dashboard**: `15rem | 1fr | 20rem`. Pinned column, then the tile
103  strip and queue rows, then the activity feed as a sticky aside with
104  two-line entries (the aside is too narrow for one).
105- **Blob, blame, edit**: `15rem | 1fr`. Navigator entries in mono, the
106  current file on a `--surface` ground with a 2px `--mark` edge.
107- **Lists**: `15rem | 1fr`. Rows fill the container.
108- **Settings and admin**: `15rem | minmax(0, 56rem)`. Forms and tables
109  at up to 56rem, so the key table stops wrapping.
110
111### Without one
112
113- **Repository overview**: the facts column stays on the right at
114  20rem; the README card caps at 88ch so prose and code share an edge.
115- **Issue, MR**: centered at 72rem; text at 48rem; the aside widens to
116  20rem so the two sit balanced in the container.
117- **Log, commit, compare, refs, releases, build, code search**: the
118  container width, no column.
119- **Notifications**: the container width, no column; rows one line.
120- **Bookmarks, snippets, profile, org**: centered `reading`.
121- **Landing, login, register, new repository, 404, privacy**: centered
122  `bounded`.
123
124## Implementation
125
126- **CSS only where it can be.** The container, centering, header rows,
127  row format, tiles, column grid and breakpoints are `style.css` rules
128  on tokens that exist. New tokens: `--container` only.
129- **Templates** touched: `layout.html` (header rows, container wrapper),
130  `dashboard.html`, `issues.html`, `mrs.html`, `builds.html`,
131  `explore.html`, `globalsearch.html`, `blob.html`, `blame.html`,
132  `edit.html`, `settings.html`, `account.html`, `admin.html`,
133  `notifications.html`, `tree.html` (README cap). Width classes: the
134  list pages and the dashboard move from the default `reading` to
135  `wide`.
136- **Handlers**: `blob`, `blame` and the edit form gain the directory
137  listing; the issue, MR and build list handlers gain facet counts; the
138  dashboard gains per-pinned-repository counts. Every new read is a
139  store query or a `gitutil` call that exists; no new control command,
140  since none of this is a capability the CLI lacks.
141  `TestReadOnlyCommandsWriteNothing` is unaffected.
142- **Facet counts** span only what the viewer can read, the same rule
143  `control.ReadableOrgRepoIDs` applies to org label counts.
144
145## Tests
146
147- `TestEveryTemplateClassHasARule` covers every new class.
148- A template test asserts the width class of each list page and the
149  dashboard is `wide`, and that `layout.html` wraps the header in the
150  container.
151- e2e: the blob page's navigator lists the file's directory and marks
152  the file; a facet link on the issues list keeps `state` and the other
153  active filters; the dashboard's tile count equals the queue length.
154- Recapture the `.claude/screenshots` set at 1280 and 1920 and rerun the
155  axe scan; zero violations is the bar the last release set.
156
157## Out of scope
158
159- A persistent context rail (option 2 in the mockups). Revisit if the
160  dashboard's pinned column proves worth having on every page.
161- A density preference.
162- One-line log rows.
163- Explore facets beyond topics; an `owner` parameter would be new.
164
165## Mockups
166
167`.claude/mock/desktop/` in the main checkout, served by the `mockups`
168entry in `.claude/launch.json`: `option1-dashboard.html`,
169`option1-blob.html`, the rejected `option2-*` pair, and `layouts.html`
170with one wireframe per page type. `desktop.css` there is the override
171layer the rules above were checked against.