/* ========================================================================== mb.bes.is/git — the repository browser's own stylesheet. app.css dresses a page you read: an 80ch measure, type that grows with the viewport, and a spacing scale built on 1.5 so everything breathes. A repository browser is the opposite — a wall of fixed-width rows you scan, where a screenful of commits beats a well-set paragraph. GitLayout links this after app.css, on /git pages only, so it can override the base outright rather than fight it. What it keeps is the monospace, the light-dark() colours and the bracketed terminal idiom; what it replaces is every measurement. 1. Tokens the dense scale, and the colours only a diff needs 2. Base what app.css set for prose, undone 3. Frame page, status bar, footer 4. Blocks the bordered box every section is 5. Tables the shape most of these pages are 6. Code trees, blobs, diffs 7. Small parts badges, pagers, key/value lists ========================================================================== */ /* 1. Tokens ================================================================ */ /* app.css grows the root font with the viewport (`1rem + 0.4vw`), which lands near 21px on a desktop. That is a reading size. Back to the reader's own base, which the scale below then works down from. */ html { font-size: 100%; } .git { /* One step under the base: small enough to fit a commit table on a laptop, still sized in rem so a reader who runs their browser at 20px gets a browser at 20px. */ font-size: 0.875rem; line-height: 1.45; /* A flat four-step scale rather than a ratio. Density is a decision about absolute room — 4px between a label and its value — not a proportion. */ --g0: 0.125rem; --g1: 0.25rem; --g2: 0.5rem; --g3: 1rem; /* Hairlines and fills. The page's own --color is too heavy for a rule between every row of a forty-row table. */ --g-line: light-dark(#d5d5d5, #2b2b2b); --g-fill: light-dark(#f1f1f1, #131313); --g-dim: light-dark(#6b6b6b, #909090); /* A row the URL points at: #L42, or the file a diff was opened for. */ --g-mark: light-dark(#fff6cc, #2c2611); /* Diff colours carry meaning, so they are stated rather than tinted: a wash behind the line, and a stronger shade for the sign in the gutter. */ --g-add: light-dark(#e4f5e4, #0e2410); --g-add-mark: light-dark(#186c18, #77d177); --g-del: light-dark(#fbe9e9, #2a1212); --g-del-mark: light-dark(#9e1414, #ff8a8a); --g-hunk: light-dark(#e8eef6, #101720); } /* 2. Base ================================================================== */ /* Every one of these exists only to undo a rule app.css set for prose. */ .git :is(h1, h2, h3, h4, h5, h6) { margin-block: 0; font-size: 1em; line-height: 1.45; text-wrap: nowrap; } .git p { margin-block: 0; text-wrap: pretty; } .git :is(ul, ol) { margin-block: 0; padding-inline-start: 0; list-style: none; } /* app.css sets `pre-line` so indented examples can stay indented in the .razor source. Here the whitespace is the file's, and it is load-bearing. */ .git pre { margin-block: 0; white-space: pre; overflow-wrap: normal; } /* Underlining every link in a table of hashes and paths turns the page into noise, so links here are plain until you point at one. */ .git a { color: inherit; text-decoration: none; } .git a:hover, .git a:focus-visible { background-color: transparent; color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.15em; } /* 3. Frame ================================================================= */ .git { flex: 1; display: flex; flex-direction: column; gap: var(--g2); /* Wide, but not edge to edge: past this a commit table is a row of five words stranded at either end of a monitor. */ max-inline-size: 100rem; inline-size: 100%; margin-inline: auto; padding: var(--g2); } /* The status line, the way a terminal does it: inverted, full width, one row. It is also the whole of the site header here — a logo and a tagline are exactly the vertical room this page is trying not to spend. */ .git-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--g1) var(--g3); padding: var(--g1) var(--g2); background-color: var(--color); color: var(--background); } .git-bar a:focus-visible { outline-color: var(--background); } .git-crumbs { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--g1); min-inline-size: 0; /* A deep path breaks between segments rather than mid-name. */ overflow-wrap: anywhere; } .git-crumbs .sep { opacity: 60%; } .git-crumbs strong { font-weight: 700; } /* Tabs read as keys you can press. The current one is punched out of the bar. */ .git-tabs { display: flex; gap: var(--g1); flex: none; } .git-tabs a { padding-inline: var(--g2); } .git-tabs a::before { content: "["; opacity: 50%; } .git-tabs a::after { content: "]"; opacity: 50%; } .git-tabs a.on { background-color: var(--background); color: var(--color); text-decoration: none; } .git-tabs a.on::before, .git-tabs a.on::after { opacity: 100%; } .git-main { flex: 1; display: flex; flex-direction: column; gap: var(--g2); /* main's rules in app.css frame a reading column with dotted rules. */ border: none; padding-block: 0; } .git-foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--g1) var(--g3); padding-block-start: var(--g2); border-block-start: 1px solid var(--g-line); color: var(--g-dim); } /* 4. Blocks ================================================================ */ /* One bordered box per thing on the page. Square corners and a single hairline everywhere, so two boxes stacked read as one grid rather than as two cards. */ .block { border: 1px solid var(--g-line); min-inline-size: 0; } .block-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--g1) var(--g2); padding: var(--g1) var(--g2); background-color: var(--g-fill); border-block-end: 1px solid var(--g-line); font-weight: 700; } /* The right-hand half of a heading: counts, stats, a branch name. */ .block-note { font-weight: 400; color: var(--g-dim); text-wrap: nowrap; } /* Padding for a block holding prose or a list rather than a table, which supplies its own cell padding and wants to run to the border. */ .block-body { padding: var(--g2); } .block-body > * + * { margin-block-start: var(--g2); } /* Nothing to show is worth a row of its own rather than an empty box. */ .empty { padding: var(--g2); color: var(--g-dim); } /* 5. Tables ================================================================ */ /* A table wider than the screen scrolls inside its block. On a phone that is most of them, and it beats every alternative: wrapping a hash, hiding a column, or pushing the whole page sideways. */ .scroll { overflow-x: auto; } .git table { inline-size: 100%; border-collapse: collapse; /* Numbers, hashes and sizes are compared down the column. */ font-variant-numeric: tabular-nums; } .git :is(th, td) { padding: var(--g1) var(--g2); border-block-end: 1px solid var(--g-line); text-align: start; vertical-align: baseline; text-wrap: nowrap; } .git th { font-weight: 400; color: var(--g-dim); } .git tbody tr:last-child :is(th, td) { border-block-end: none; } .git tbody tr:hover { background-color: var(--g-fill); } /* The one column per table that carries the sentence: a commit subject, a description, a tag message. It takes the leftover width and gives up its own before any of the fixed columns do. */ .git .wide { inline-size: 100%; text-wrap: wrap; overflow-wrap: anywhere; } /* Hashes, sizes and counts: right where they are compared, left where they are read. */ .git .num { text-align: end; inline-size: 1%; } .git .dim { color: var(--g-dim); } /* A column that earns its place on a laptop and not on a phone. Dropping it is what lets the remaining columns fit, so the subject wraps in place instead of the whole row scrolling sideways to reach a name you were not reading anyway. */ @media (width < 40rem) { .git .when-wide { display: none; } } .git .sha { font-variant-numeric: tabular-nums; } /* A row whose link fills the cell should feel clickable across the cell. */ .git td > a { display: inline-block; max-inline-size: 100%; } /* 6. Code ================================================================== */ /* -- Trees --------------------------------------------------------------- */ /* The mode column is decoration until you need it, and then it is the whole answer, so it stays but stays quiet. */ .tree .mode { color: var(--g-dim); } /* The link sits in the name cell, not straight under the row. */ .tree tr.dir .wide a::after { content: "/"; color: var(--g-dim); } /* -- Blobs and diffs ------------------------------------------------------ */ /* Line numbers sit in their own cells and are marked unselectable, so copying a file or a hunk out of the page yields the code and nothing else. */ .code-table { inline-size: max-content; min-inline-size: 100%; } .code-table td { padding-block: 0; border-block-end: none; vertical-align: top; } .code-table .ln { inline-size: 1%; padding-inline: var(--g2); text-align: end; color: var(--g-dim); background-color: var(--g-fill); user-select: none; -webkit-user-select: none; } .code-table .ln a { color: inherit; } .code-table .code { inline-size: 100%; padding-inline: var(--g2) var(--g3); white-space: pre; } /* Hovering a row of code should not repaint the whole line. */ .code-table tbody tr:hover { background-color: transparent; } .code-table tr:target, .code-table tr:target .ln { background-color: var(--g-mark); } /* -- Diffs ---------------------------------------------------------------- */ .diff .hunk td { padding-inline: var(--g2); background-color: var(--g-hunk); color: var(--g-dim); border-block: 1px solid var(--g-line); } /* The first hunk sits straight under the file's heading, which already has a rule of its own. */ .diff tr:first-child.hunk td { border-block-start: none; } .diff .add .code { background-color: var(--g-add); } .diff .del .code { background-color: var(--g-del); } /* The sign is repeated from the gutter into the line so the diff survives being copied, pasted or read without colour — but never selected with it. */ .diff .sign { user-select: none; -webkit-user-select: none; } .diff .add .sign { color: var(--g-add-mark); } .diff .del .sign { color: var(--g-del-mark); } .diff .note td { color: var(--g-dim); } /* One file's heading inside the commit's diff: path on the left, its own stat on the right. */ .diff-file + .diff-file { margin-block-start: var(--g2); } .diff-file .block-head { overflow-wrap: anywhere; text-wrap: wrap; } /* 7. Small parts =========================================================== */ /* A branch or tag name sitting next to a commit. Bracketed rather than pill-shaped: this page has no rounded corners anywhere else. */ .ref { margin-inline-start: var(--g2); color: var(--g-dim); /* Breakable: a branch called release/2026-09-the-long-one is one unbreakable word, and on a phone it would otherwise set the width of the whole commit table. */ overflow-wrap: anywhere; } .ref::before { content: "["; } .ref::after { content: "]"; } .ref.head { color: inherit; font-weight: 700; } /* +42 -3, on a commit row or a file heading. */ .stat { text-wrap: nowrap; } .stat .plus { color: var(--g-add-mark); } .stat .minus { color: var(--g-del-mark); } /* Key and value down the page: clone URL, author, committer, parents. */ .pairs { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--g1) var(--g3); } .pairs dt { color: var(--g-dim); } .pairs dd { margin: 0; overflow-wrap: anywhere; } /* The clone line is there to be selected in one go. */ .pairs .copyable { user-select: all; -webkit-user-select: all; } /* Older / newer, at the foot of the log. */ .pager { display: flex; gap: var(--g2); justify-content: space-between; padding: var(--g1) var(--g2); border-block-start: 1px solid var(--g-line); } .pager a::before, .pager span::before { content: "< "; } .pager :last-child::before { content: ""; } .pager :last-child::after { content: " >"; } .pager span { color: var(--g-dim); } /* A commit's message body, under its subject. Pre-wrapped: a commit message is written to a width and often has a list in it. */ .message { white-space: pre-wrap; overflow-wrap: anywhere; color: var(--g-dim); } .subject { font-weight: 700; text-wrap: wrap; overflow-wrap: anywhere; } /* A branch picker and a path search are one short row of controls, not a form with labels stacked above fields. */ .controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g2); } .controls label { display: flex; align-items: center; gap: var(--g1); color: var(--g-dim); } .git :is(button, input, select) { font: inherit; border-radius: 0; border-color: var(--g-line); padding: var(--g0) var(--g1); } .git button { min-inline-size: 0; padding-inline: var(--g2); }