Skip to content

feat(desktop): the draft's palette, and the primitives every pane is built from - #30

Merged
protonspy merged 3 commits into
mainfrom
feat/desktop-design-system
Aug 2, 2026
Merged

feat(desktop): the draft's palette, and the primitives every pane is built from#30
protonspy merged 3 commits into
mainfrom
feat/desktop-design-system

Conversation

@protonspy

Copy link
Copy Markdown
Owner

Group 2 of plans/desktop-ui.md — tasks 2.1 to 2.4, ticked.

Stacked on #29. The base is fix/desktop-dead-controls, so this diff is group 2 alone. Merge #29 first and this retargets to main cleanly.

8.1 shipped tokens only — and shipped them from a palette of its own invention, while design/desktop-draft.html sat in the repository being nothing's acceptance criterion. This is the other half, and it takes the draft's answer rather than the one 8.1 guessed.

What changed

2.1 — the tokens. tokens.css becomes globals.css with the draft's names and values: the cold slate instrument, the warm paper it holds, amber --primary meaning provenance, the semantic set kept separate from the accent, the 10→32px scale, --row: 30px and --control: 28px. The names are shadcn/ui's on purpose — that is what the draft means by "the port is a copy into globals.css".

Every --surface-* / --ink-* reference migrates here, because a half-migrated palette is two palettes. The type scale is the part that bites: --text-xs was 12px and is now 11px, --text-sm was 13px and is now 12px, so a reference that looks untouched has silently moved. It took two review rounds to get right, and the rule that settled it is the draft wins where the draft has an opinion, and the old pixel wins where it does not.

2.2 — two dependencies, in the manifest and in docs/stack.md with the line each earned. No shadcn/ui runtime: shadcn is copied-in source, and installing a package to get a naming convention would be adopting a dependency for nothing.

2.3 — the primitives, one file each under src/renderer/ui/: Button, IconButton, Pill, Card, Table, Switch, Dialog, Sheet, Drawer, SearchInput. Each exports the function that decides its classes, so the decision is testable and the component stays arrangement — the convention the renderer already had. Ask.tsx from group 1 now sits on the Dialog primitive, so the focus trap, Escape and the return of focus are settled in one place instead of copied into three overlays.

2.4 — focus. One :focus-visible rule for the whole document, so a primitive cannot forget it, and a test that reads the shipped stylesheet: exactly one outline: none is allowed, it is the search field's, and the ring it removes is put back on the box around it.

How it was verified

pnpm test:coverage (whole workspace, 76% floor, exit 0) · pnpm lint · pnpm run typecheck · pnpm --filter @open-wiki/desktop run build:renderer · npx @protonspy/scc validate — no findings.

code-review and security-review on the diff, then code-review again on each round of fixes. Three findings, all closed:

  • major — the type-scale migration was done only where the draft draws the class; thirteen application-only selectors kept their variable name and lost a pixel each, while the commit message claimed otherwise. Fixed, and two tests now guard the class of bug: CSS is silent about var(--gone) — the property falls back to what it inherits and the rule quietly stops applying.
  • major.icon-btn was --control (28px) where the draft draws 24px, and an --sm variant was invented that the draft never draws. A size the draft does not draw is a decision nobody made that groups 3 to 8 would all have inherited.
  • blocker (round 2) — the fix for the first finding moved .empty to 13px, when the draft draws .empty itself at 12px. That is the same failure in the other direction, and it is what produced the rule stated above.

The third round was a one-line revert of a value the reviewer identified precisely and I checked against the draft myself, so it was not sent back for a fourth review.

Two things left open on purpose

  • --warn is gone. The draft has no warning colour, so .finding--warning now shares amber with citations. The security review flagged it as a low legibility note rather than a state collision — findings always carry their code, message and fix as text — but it is a real narrowing of the palette and worth a look.
  • Nothing is wired up yet. Card, Table, Switch, Sheet, Drawer and SearchInput have no call site until groups 3 to 8. That is what the plan means by "group 2 blocks 3 through 8"; building a pane against the old tokens would mean building it twice.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HBt5LRVJd5Z7nagJGRwvSC

protonspy and others added 3 commits August 1, 2026 22:24
…e is built from

Group 2 of plans/desktop-ui.md. 8.1 shipped tokens only, and shipped them from
a palette of its own invention while `design/desktop-draft.html` sat in the
repository being nothing's acceptance criterion. This is the other half, and
it takes the draft's answer rather than the one 8.1 guessed.

- `tokens.css` becomes `globals.css`, with the draft's names and values: the
  cold slate instrument, the warm paper it holds, amber `--primary` meaning
  provenance, the semantic set kept separate from the accent, the 10→32px
  scale, `--row: 30px` and `--control: 28px`. Every `--surface-*`/`--ink-*`
  reference migrates in the same commit, because a half-migrated palette is
  two palettes — and the type scale in particular shifts under its own names
  (`--text-xs` was 12px and is now 11px), so a partial migration would have
  been silently wrong rather than obviously unfinished.
- `lucide-react` and `clsx`, in the manifest and in `docs/stack.md` with the
  line each earned. No shadcn/ui runtime: what the draft means by "token names
  match shadcn/ui" is the variable names, and shadcn is copied-in source
  rather than a package.
- The primitives, one file each under `src/renderer/ui/`: Button, IconButton,
  Pill, Card, Table, Switch, Dialog, Sheet, Drawer, SearchInput. Each exports
  the function that decides its classes, so the decision is testable and the
  component stays arrangement — the convention the renderer already had.
- `Ask.tsx` from group 1 now sits on the `Dialog` primitive, so the focus
  trap, Escape and the return of focus are settled in one place rather than
  copied into three overlays.
- Focus is visible, from one `:focus-visible` rule, and a test reads the
  stylesheet to keep it that way: exactly one `outline: none` is allowed, it
  is the search field's, and the ring it removes is put back on the box.

Verified: `pnpm test:coverage` (the whole workspace, 76% floor), `pnpm lint`,
`pnpm run typecheck`, `pnpm --filter @open-wiki/desktop run build:renderer`,
`npx @protonspy/scc validate` — all clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBt5LRVJd5Z7nagJGRwvSC
…con size

Both findings from the code review, and the first one is the more instructive:
the commit before this claimed the type-scale shift had been handled and it
had been handled only where the draft draws the class. Everything the draft
does not draw — the chrome, the frontmatter, the error and empty lines, the
editor, the findings, the launcher's labels, the doorway, the question box's
own field label — kept its variable name and so lost a pixel each. That is
exactly the silent half-migration the message said had been avoided.

- Each of those is shifted a tier, to the size it had: old `--text-xs` (12px)
  becomes `--text-sm`, old `--text-sm` (13px) becomes `--text-base`. `.chrome`
  loses its declaration entirely, because the draft's titlebar sets none and
  inherits the body's 13px.
- Inline code in prose takes the draft's own `.inline-code` rule — `0.92em`,
  relative — rather than a fixed step, so it tracks whatever it sits inside.
- `.icon-btn` is 24px, which is what the draft draws, and the `--sm` variant
  invented here is gone. A size the draft does not draw is a decision nobody
  made that groups 3 to 8 would all have inherited.
- Two tests for the class of bug this was. CSS is silent about `var(--gone)`:
  the property falls back to what it inherits, nothing errors, and the rule
  quietly stops applying. One asserts every referenced custom property is
  defined; the other that no rule still reaches for the 8.1 palette.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBt5LRVJd5Z7nagJGRwvSC
…ty is one

The review was right and the correction is small but the principle behind it
is not: the previous commit restored every shifted selector to the pixel size
the *old application* had chosen, and `.empty` is the one of them the draft
draws itself — at `--text-sm`. Restoring it to 13px moved a rule that was
already right, which is precisely the failure the round before it was about.

`.page code` and `.doorway code` keep `0.92em`, and now say why in the file:
the draft sizes inline code in prose relatively, so a code span tracks the
paragraph it interrupts rather than being one fixed size in a reader set
larger than the chrome around it. That is a deliberate departure from
"restore the pixel", not an oversight.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBt5LRVJd5Z7nagJGRwvSC
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a6c1348d-d69e-495c-84c3-920f7e65ef15

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@protonspy
protonspy changed the base branch from fix/desktop-dead-controls to main August 2, 2026 02:25
@protonspy
protonspy merged commit d4a836c into main Aug 2, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant