code-browser is a browser for reading code: you move through repos, reviews and agent runs the way you move through the web, and you review what changed — and who changed it — the way you would in an editor. The system is a quiet instrument: warm graphite neutrals, one signal accent, mono where the machine speaks, hairlines instead of shadows. The code and the diff carry the colour; the chrome stays out of the way.
Principles #
- The diff is the interface. Add and delete colours, word emphasis and the review state are the loudest things on screen. Chrome never competes with them.
- Declared generation is visible. Content whose author declared it generated carries the
agenthue or ageneratedtag. Authors are never coloured or tagged by what kind of author they are. Never infer generation, and never imply a person wrote something because it lacks the mark. - One signal.
signalmeans "here" (focus, caret, selection, the ref you are on) or "look at this" (flagged, medium risk, the one primary action). Never decoration. - Everything has an address. Every view has an omnibar URL; every review action has a key.
- Say where code runs. Nothing from a browsed repo runs on the host. Anything that executed — a language server's answer, test output, a build — says which sandbox it ran in, and the boundary is one click away.
- Dense but calm. 13px text on 20px lines, 4px grid, hairline dividers. Density comes from rhythm, not from cramming.
Sandboxes #
- One review is one trust domain is one VM. Name the domain as
repo @ref shawherever a sandbox is shown. - Show where code runs with SandboxBadge in the chrome, and put the details in SandboxCard. There is no "host" state to design for; "no sandbox" is normal and means nothing has executed.
- Boundary words are fixed:
none,read-only,throwaway,limited,open. Onlyopentakes colour (signal). Always pair a word with the concrete detail (no NIC · host proxy · :80 :443). - Workload and sandbox states reuse one glyph set: ○ none or pending · ◌ starting or building (signal) · ◐ indexing · ● ready (add dot) · ◉ running · ✕ failed (del) · ⊘ unavailable.
- Blocked egress is shown, never hidden: a
✕ Ncount on the badge, and every attempt in EgressLog with the proxy's reason. Only an allow-list miss gets a one-click "Allow host". - Output from the sandbox (ExecOutput) marks each line's stream with a gutter glyph and starts with a meta line restating the boundary.
Voice and content #
- Sentence case everywhere. Only
micro(badges, eyebrows) is set in capitals. - Verb first, name the object: "Approve change set", "Revert hunk", "Ask agent". Never "OK", "Submit", "Done".
- Plain and specific, second person when addressing the reviewer: "You haven't viewed 3 files."
- Report facts, not feelings: "5 files · +110 −76 · medium risk", not "Looks good!". No emoji in the UI.
- Code identifiers, paths, refs, run ids and counts are set in mono (
code,code-sm); prose around them stays sans. - Every author's text follows the same voice rules, whoever wrote it.
- The generated tag reads "Generated", optionally followed by the declared model in
code-smmono: "Generated · claude-opus-5-5".
Colour #
Dark is the primary theme; light mirrors it token for token.
- Grounds:
bg-000is the canvas behind code;bg-100the chrome (tabs, omnibar row, sidebars);bg-200raised surfaces (cards, popovers, hunk headers);bg-300hover and current line. - Lines:
linefor hairline dividers (decorative only);line-strongfor control borders, which must meet 3:1. - Text:
inkfor primary text and all code;ink-mutedfor paths, metadata, line numbers;ink-faintfor timestamps and placeholders, only onbg-000–bg-200. - Signal:
signalfor focus (focus), caret, the current ref, selection edges, the flagged state and the single primary button (on-signaltext).signal-softfills selected rows and ranges (selection). - Provenance:
agentonagent-softfor generated content only: generated tags, generated comment grounds, and the 3pxagenttick in the gutter on lines that a declared-generated entry quotes or proposes. - Diff:
add-bg/del-bgfor line grounds,add-emph/del-emphfor changed words,add/delfor the +/− signs, counts and the approved / changes-requested states. Code on any diff ground staysink. - Status mapping: approved →
add, changes requested →del, flagged →signal, generated →agent; info → neutrals; viewed and unreviewed → neutrals. - Never by hue alone.
addanddelare close in lightness, so every diff line draws its + / − sign, every file row its status letter (A M D R), every review state its glyph (○ ◐ ✓ ✕ !), every provenance tag the word "Generated". Keep it that way in anything new. - Every text pair named in a token's usage note holds 4.5:1 in both themes;
line-strong,focusandsignalhold 3:1 onbg-000–bg-200.
Type #
- Instrument Sans (
--font-sans) for interface text; JetBrains Mono (--font-mono) for code and everything the machine produces. Both ship with the system as variable WOFF2 files infonts/(Instrument Sans upright and italic, weights 400–700; JetBrains Mono upright, 100–800), declared bytokens.css. The UI uses 400, 500 and 600. Both are under the SIL Open Font License 1.1; the license texts sit beside the files and must travel with them. - Interface:
title20/28 once per view (the review, run or file);heading15/20 for panels and cards;body13/20 default;label12/16 for buttons, tabs, tree rows;micro11/14 caps for badges. - Code:
code13/20 in diffs and files — its 20px line isrow-code, so code and gutters stay on one rhythm;code-sm12/16 for line numbers, hunk headers, refs, paths, stats and keycaps. display(mono 40/44) is reserved for the product name and empty-state headlines.- File names in trees and tabs are mono; prose titles are sans.
Space, size and layout #
- 4px grid:
space-14 ·space-28 ·space-312 ·space-416 ·space-624 ·space-832 ·space-1248. - Fixed rows keep the instrument steady:
row-code20,row-tree26,control-sm22,control-md28,chrome-h40,gutter-w44 per line-number column. - Layout: the omnibar row on top, on the macOS traffic-light line, then the tab strip below it, only while tabs are open and without the product mark (
bg-100,chrome-heach); file tree left; the diff or file on thebg-000canvas centre; activity and review panel right. Panes are separated byline, never by gaps or shadows. - Pad cards and panels with
space-4; separate sections inside a pane withspace-6.
Shape, borders and elevation #
- Corners:
radius-xs(2) for word marks, stat blocks, keycaps;radius-sm(4) for buttons, inputs, badges, tags, tabs;radius-md(6) for cards, hunks, popovers, the omnibar field.radius-pillis for status dots only. - Docked things are flat and bordered with
line. Only floating layers (omnibar suggestions, menus, command palette, toasts) takeshadow-pop. - No gradients, no coloured card edges, no glows.
States and focus #
- Hover: one step up the ground scale (
bg-100→bg-200→bg-300). - Selected:
signal-softfill; in lists add a 2pxsignaledge. - Focus: 2px solid
focusring with 1px offset on every interactive element, shown on keyboard focus (:focus-visible). - Disabled: 45% opacity, no hover.
- Keys: every primary action has one. A button names its key in its hover tip, never as a keycap in the button; Kbd keycaps are for menus, key lists and chrome.
- Active tab: joins the canvas (
bg-000) with a 2pxsignaltop edge.
Motion #
- Short and functional: 120ms ease-out for hover and selection, 160ms for popovers and toasts opening. Nothing bounces.
- Scrolling to the next hunk is instant or a 160ms ease; never animate code text.
- Any long-running task shows a static ◆ in
ink-mutedunless its progress is known, never a spinner.
Iconography #
- No icon set is part of the system yet. Components use typographic glyphs instead: + − for diff signs, ○ ◐ ✓ ✕ ! for review states, A M D R for file status, ◆ for in progress / working, › ⌄ for disclosure, ← → for back and forward, × for closing a tab (never the ✕ failure state).
- Review threads have their own sets, chosen not to collide with the review states: thread kinds ¶ note · ? question · ▲ issue · ± suggestion · ✦ praise; thread status □ open · ▣ resolved · ⊟ won't fix · ⬚ outdated; review lifecycle ✎ draft · ↑ submitted · ⇢ superseded · ⤺ withdrawn; verdicts ✓ approved · ✕ changes requested · “ comment · – no verdict (the first two reuse the review-state glyphs because they mean the same thing).
- Issue severity is read by word and weight, not hue: only
blockertakes colour (signal).delstays reserved for changes requested and failures. - When icons are added, use a 1.5px-stroke outline set at 16px, drawn in
ink-mutedand takinginkon hover; never colour an icon unless it carries a state colour defined above.
Logo #
The mark is [▮]: a pair of square brackets holding a block caret. The brackets are code — the thing being read; the caret is the reviewer's place in it, in signal. It reads as "you are here, inside the code", which is what code-browser is for.
- Construction (64-unit grid): bracket stems and arms 6 units thick, 48 tall, arms 16 long; the caret 10 × 26, centred. 8 units of clear space are built into the square; keep at least that much again around it.
- Colour: brackets in
ink, caret insignal, using the file made for the ground —-on-darkonbg-000–bg-200in the dark theme,-on-lightin the light theme. Use the mono marks only where one ink is required (embossing, single-colour print, stamps):mark-mono-inkon light grounds,mark-mono-paperon dark. - Lockup: the mark followed by the wordmark, separated by one character cell, the name's cap height centred on the mark. Use it where the product is introduced (sign-in, about, docs header); use the mark alone in app chrome, where the name is already known.
- Wordmark:
code-browseroutlined from JetBrains Mono Medium — lowercase, hyphenated, never split, capitalised or retyped in another face. - Small sizes: the mark holds down to 24px. Below that use
favicon(drawn on a 16px pixel grid, on its ownbg-100tile).app-icon-darkis the default app icon;app-icon-lightis for light OS surfaces. - Don't: recolour the brackets, put the caret in any colour but
signal(or the mono ink), animate the caret blinking in static placements, outline or add shadows, set the mark onsignalor on busy imagery.
Maintaining this system #
This artifact is published output. The source is the design-system/ folder of the code-browser repository, which mirrors this system's project/ folder file for file. Every publish from the repo replaces what's here, so an edit made directly to this artifact (on its page or by publishing files to it) is overwritten by the next one. Make changes in the repo instead: edit, run bun run ds:build, commit, then publish with the repo's push-design-system skill.
The components' source is components/src/ (one typed React module per component, props in types.ts); never edit components/bundle.js or components/index.d.ts, which components/build.mjs builds from it and type-checks. bun run ds:build also recompiles tokens.css from tokens.json and refreshes the Components and Logos tables at the end of this README. These hand-kept parts exist because the design system format's own generation (tokens.css, the README's generated index, manifest.json, api/ cards, and building the bundle from source) doesn't run for systems edited from Claude; if those files ever appear on their own, the hand-kept parts can go.
Consuming this system #
Namespace: CodeBrowser — components/bundle.js assigns window.CodeBrowser, one export per component in the table below. Libraries: React 18 and ReactDOM 18 (not bundled; a Design canvas supplies its own React 18). Stylesheets: components/bundle.css, which expects tokens.css loaded first. Fonts: Instrument Sans and JetBrains Mono, as files under fonts/, declared with @font-face at the top of tokens.css (relative url("fonts/…"), so keep fonts/ next to tokens.css). Nothing loads from the network.
Every path below is a file in this system; read it by path.
- In an app build (the Electron renderer): import from
components/src/index.tsdirectly — it's plain TypeScript React whose only dependencies arereactandreact-dom(18 or 19;Dialogportals withcreatePortal), and it type-checks understrictwithnoUncheckedIndexedAccess— and loadbundle.cssaftertokens.css. The code-browser app does this from the same repository, so it needs no copy. - No
project/api/cards. This system has no generated per-component cards and nomanifest.json. Before using a component, read itsproject/components/<Name>/README.md(guidelines, what the consumer provides) and its entry inproject/components/index.d.ts(props and value shapes); itspreview.htmlis a working example. - Installing on a Design canvas: copy
components/bundle.js,components/bundle.css,components/index.d.ts,tokens.json,tokens.cssand the threefonts/*.woff2files (all served underproject/), keepingfonts/besidetokens.css. Linkds/<folder>/tokens.cssbeforebundle.cssin each artboard. Mount components as<x-import component-from-global-scope="CodeBrowser.<Name>" …>. No provider component is needed. - Logos are uploads in this system, not files: a canvas can't reach them by this system's asset ids. Read the logo you need by id (table below), upload it to the canvas, and pass the canvas's own
/_blob/…URLs (for example toTabs'mark).
Files #
| path | what |
|---|---|
project/tokens.json |
All tokens: colour (themes dark, light), type, spacing, radius, shadow, size. Every token has a usage note. |
project/tokens.css |
The same tokens compiled to CSS: @font-face for the font files, custom properties per theme, and a class per type style. Load it first. |
project/fonts/InstrumentSans-Variable.woff2 · InstrumentSans-Italic-Variable.woff2 · JetBrainsMono-Variable.woff2 |
The font files (variable WOFF2), listed in tokens.json type.fonts. Referenced from tokens.css by relative URL. |
project/fonts/InstrumentSans-OFL.txt · JetBrainsMono-OFL.txt |
SIL Open Font License 1.1 for each family. Ship them with the fonts. |
project/components/src/*.tsx |
The source. One typed React module per component (<Name>.tsx), shared helpers in internal.tsx, prop types in types.ts, everything exported from index.ts. Edit these, never bundle.js. Imports only each other, react and react-dom. |
project/components/build.mjs |
Builds bundle.js and index.d.ts from src/: node build.mjs <path to components> with esbuild, typescript@5, @types/react@19, @types/react-dom@19 and dts-bundle-generator installed. Type-checks first under strict and noUncheckedIndexedAccess (the same strictness the app builds with), so a type error fails the build. |
project/components/bundle.js |
Built from src/ — don't edit by hand. One classic script. Needs window.React and window.ReactDOM 18 loaded first; assigns window.CodeBrowser with every component below. |
project/components/bundle.css |
Component styles. Load after the token CSS. |
project/components/index.d.ts |
Generated from src/: every component's props type (<Name>Props) and signature. |
project/components/<Name>/README.md |
Guidelines per component: what it is, rules, what the consumer provides. |
project/components/<Name>/preview.html |
A live example per component (expects tokens, bundle.css, React and bundle.js preloaded). |
project/assets/Logos/README.md |
Which logo file to use where, and the inks baked into each. |
Tokens as CSS #
Load project/tokens.css. It starts with one @font-face per file in fonts/, then defines every token as --<name>: colours and shadows per theme under :root, [data-theme="dark"] and [data-theme="light"] (aliases such as focus are var(--signal)), spacing, radius and size on :root, the families as --font-sans and --font-mono, and one class per type style (.title, .body, .code, …). Set data-theme="dark" or "light" on <html>. The type classes are deliberately global and unprefixed: they are the format's compiled shape for type styles, and they're the only selectors in the system outside cb-. Nothing in bundle.css depends on them, so a consumer can ignore them, but it shouldn't define its own .title, .heading, .body, .label, .micro, .code, .code-sm or .display with a different meaning. It is compiled from tokens.json; if they ever disagree, tokens.json wins.
Load order: tokens.css → bundle.css → React 18 → ReactDOM 18 → bundle.js. Then const { Button } = window.CodeBrowser.
Components #
| component | group | summary | files |
|---|---|---|---|
Button |
Actions | Buttons commit a review decision or run a command; default is the workhorse and primary (signal fill) appears at most once per view, for the decision the view exists to make. | project/components/Button/README.md · project/components/Button/preview.html |
Kbd |
Actions | A keyboard hint. | project/components/Kbd/README.md · project/components/Kbd/preview.html |
Switch |
Forms | An on/off toggle for boolean settings. | project/components/Switch/README.md · project/components/Switch/preview.html |
Segmented |
Forms | Two to four mutually exclusive options shown at once. | project/components/Segmented/README.md · project/components/Segmented/preview.html |
Select |
Forms | A styled native select for five or more options, or where the options come from data (remotes, branches, themes). | project/components/Select/README.md · project/components/Select/preview.html |
TextField |
Forms | A single-line input. | project/components/TextField/README.md · project/components/TextField/preview.html |
TextArea |
Forms | The multi-line version of TextField, for review summaries, comments and replies. | project/components/TextArea/README.md · project/components/TextArea/preview.html |
Omnibar |
Chrome | The address bar. | project/components/Omnibar/README.md · project/components/Omnibar/preview.html |
Tabs |
Chrome | The browser tab strip, sized chrome-h; each tab says what kind of thing it holds (review, reviews, file, run, web) in a mono prefix. |
project/components/Tabs/README.md · project/components/Tabs/preview.html |
NavButtons |
Chrome | Back and forward through the view history, like a browser's. | project/components/NavButtons/README.md · project/components/NavButtons/preview.html |
FileTree |
Navigation | The changed-files tree. | project/components/FileTree/README.md · project/components/FileTree/preview.html |
DiffHunk |
Review | One unified-diff hunk. | project/components/DiffHunk/README.md · project/components/DiffHunk/preview.html |
DiffStat |
Review | The size of a change. | project/components/DiffStat/README.md · project/components/DiffStat/preview.html |
ReviewBadge |
Review | The review state of a file, hunk, tab or change set — the core vocabulary of code-browser. | project/components/ReviewBadge/README.md · project/components/ReviewBadge/preview.html |
ChangeSetCard |
Review | The summary of one change set waiting for review. | project/components/ChangeSetCard/README.md · project/components/ChangeSetCard/preview.html |
ReviewLifecycleBadge |
Review | A review's own state, as opposed to the state of what it reviews (that's ReviewBadge). | project/components/ReviewLifecycleBadge/README.md · project/components/ReviewLifecycleBadge/preview.html |
VerdictBadge |
Review | A review's verdict, in the review format's own values. | project/components/VerdictBadge/README.md · project/components/VerdictBadge/preview.html |
ReviewListItem |
Review | One review in a reviews list (a repository's reviews, the "waiting on you" inbox, earlier rounds of a re-review). | project/components/ReviewListItem/README.md · project/components/ReviewListItem/preview.html |
ThreadKindTag |
Review threads | What kind of thread this is, and for issues how severe. | project/components/ThreadKindTag/README.md · project/components/ThreadKindTag/preview.html |
ThreadStatus |
Review threads | Where a thread stands. | project/components/ThreadStatus/README.md · project/components/ThreadStatus/preview.html |
Thread |
Review threads | One code thread. | project/components/Thread/README.md · project/components/Thread/preview.html |
ThreadListItem |
Review threads | One row in a review panel's thread list. | project/components/ThreadListItem/README.md · project/components/ThreadListItem/preview.html |
CoverageSummary |
Review threads | How much of the change the reviewer has actually looked at. | project/components/CoverageSummary/README.md · project/components/CoverageSummary/preview.html |
AuthorTag |
Authorship | Who wrote it. | project/components/AuthorTag/README.md · project/components/AuthorTag/preview.html |
GeneratedTag |
Authorship | Marks content whose author declared it generated. | project/components/GeneratedTag/README.md · project/components/GeneratedTag/preview.html |
CodePeek |
Code intelligence | A floating card that answers "what is this?" for one symbol without leaving the diff. | project/components/CodePeek/README.md · project/components/CodePeek/preview.html |
ReferencesCard |
Code intelligence | Every occurrence of a symbol, grouped by file, framed around the reviewer's real question. | project/components/ReferencesCard/README.md · project/components/ReferencesCard/preview.html |
CallHierarchy |
Code intelligence | Callers and callees of one function as a collapsible tree — the change's blast radius. | project/components/CallHierarchy/README.md · project/components/CallHierarchy/preview.html |
TypeHierarchy |
Code intelligence | Supertypes and subtypes of a class or interface. | project/components/TypeHierarchy/README.md · project/components/TypeHierarchy/preview.html |
SandboxBadge |
Sandbox | The chrome indicator for where code from the current review runs. | project/components/SandboxBadge/README.md · project/components/SandboxBadge/preview.html |
SandboxCard |
Sandbox | The sandbox for one review, in one card. | project/components/SandboxCard/README.md · project/components/SandboxCard/preview.html |
EgressLog |
Sandbox | Every connection attempt from the sandbox, allowed or blocked, as the host-side proxy decided it. | project/components/EgressLog/README.md · project/components/EgressLog/preview.html |
ExecOutput |
Sandbox | The streamed output of one command run in the sandbox. | project/components/ExecOutput/README.md · project/components/ExecOutput/preview.html |
Notice |
Feedback | An inline banner for something the reviewer should know about the current view. | project/components/Notice/README.md · project/components/Notice/preview.html |
Dialog |
Feedback | A floating panel for a decision that needs the reviewer's full attention. | project/components/Dialog/README.md · project/components/Dialog/preview.html |
LossReport |
Feedback | The body for export and conversion previews. | project/components/LossReport/README.md · project/components/LossReport/preview.html |
Toast |
Feedback | A brief message about something that just happened. | project/components/Toast/README.md · project/components/Toast/preview.html |
SettingsPage |
Pages | The settings editor. | project/components/SettingsPage/README.md · project/components/SettingsPage/preview.html |
Logos #
Uploads in the Logos group, read by asset id. Guidance in project/assets/Logos/README.md.
| file | asset id |
|---|---|
mark-on-dark.svg |
caa94715df22b5760138a96a37f11296 |
mark-on-light.svg |
a135a80931c6f5580bac1d2d985610c5 |
lockup-on-dark.svg |
c27a0453568446bdf9bf7d8de7898074 |
lockup-on-light.svg |
f0bbafa61a1783e89796371c8cd5f701 |
wordmark-on-dark.svg |
36fd42842e9e559139358dc8997a6d68 |
wordmark-on-light.svg |
61b97255d408e75856bea064c5e48e9d |
mark-mono-ink.svg |
f0f30efe3f5bee39c03862e7ef84fce1 |
mark-mono-paper.svg |
b3ba2d4537f7482cbcad983d66366d2c |
app-icon-dark.svg |
c3d40fc5766fe77a5540a9d3a38de24d |
app-icon-light.svg |
e64e63e76524ba072668464d1ea22dd7 |
favicon.svg |
efdff4ac2329e4b8a3d1e4d70c4f4360 |