Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1b92436
chore(porch): 1053 init pir
amrmelsayed Jun 17, 2026
75ce317
[PIR #1053] Plan draft
amrmelsayed Jun 17, 2026
544e318
chore(porch): 1053 plan-approval gate-requested
amrmelsayed Jun 17, 2026
957655c
chore(porch): 1053 plan-approval gate-approved
amrmelsayed Jun 17, 2026
0e4a3c5
chore(porch): 1053 implement phase-transition
amrmelsayed Jun 17, 2026
3cc0de1
[PIR #1053] Add typography token tier to canvas default-theme.css
amrmelsayed Jun 17, 2026
83593f0
[PIR #1053] Bind typography tokens in VSCode preview + add font-size/…
amrmelsayed Jun 17, 2026
6e1d0b1
[PIR #1053] Thread: implement phase notes
amrmelsayed Jun 17, 2026
a4c8b8e
chore(porch): 1053 dev-approval gate-requested
amrmelsayed Jun 17, 2026
7d35fa0
[PIR #1053] Document markdownPreview font-size/line-height settings i…
amrmelsayed Jun 18, 2026
108d24b
[PIR #1053] Thread: dev-approval Q&A, settings doc, follow-up #1070
amrmelsayed Jun 18, 2026
f2c7d60
[PIR #1053] Add code-foreground token + style code/blockquote/table/h…
amrmelsayed Jun 18, 2026
1bca06f
[PIR #1053] Pair inline code with VSCode textPreformat tokens for dar…
amrmelsayed Jun 18, 2026
28ad8a3
[PIR #1053] Thread: element styling + dark-mode contrast pass
amrmelsayed Jun 18, 2026
001ca84
[PIR #1053] Break long tokens in prose to prevent page-level horizont…
amrmelsayed Jun 18, 2026
91a8916
[PIR #1053] Thread: horizontal-scroll wrapping fix
amrmelsayed Jun 18, 2026
497dcc7
chore(porch): 1053 dev-approval gate-approved
amrmelsayed Jun 18, 2026
a3d745d
chore(porch): 1053 review phase-transition
amrmelsayed Jun 18, 2026
e4d0c0e
[PIR #1053] Review: retrospective + 3 UI/UX lessons
amrmelsayed Jun 18, 2026
2663102
chore(porch): 1053 record PR #1071
amrmelsayed Jun 18, 2026
d2eb2ac
chore(porch): 1053 review build-complete
amrmelsayed Jun 18, 2026
3ce8919
[PIR #1053] Thread: review phase, PR #1071, consult running
amrmelsayed Jun 18, 2026
193a5a2
[PIR #1053] Cover standalone MarkdownView surface with typography (co…
amrmelsayed Jun 18, 2026
596e56f
[PIR #1053] Review: record 3-way consult disposition + MarkdownView fix
amrmelsayed Jun 18, 2026
602c08a
[PIR #1053] Thread: consult disposition + MarkdownView fix
amrmelsayed Jun 18, 2026
0157309
[PIR #1053] Consultation rebuttals (iteration 1)
amrmelsayed Jun 18, 2026
f467e96
chore(porch): 1053 pr gate-requested
amrmelsayed Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions codev/plans/1053-vscode-typography-tokens-for-t.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# PIR Plan: Typography tokens for the Codev Markdown Preview (artifact canvas)

> Issue #1053 · area/vscode · extends the locked 8-token color vocabulary from #945 (spec 945 D4)
> with a typography token tier so prose (specs, plans, reviews) reads cleanly in the VSCode preview.

## Understanding

The Codev Markdown Preview surfaces the `@cluesmith/codev-artifact-canvas` component inside a
VSCode `CustomTextEditor` webview (`packages/vscode/src/markdown-preview/`). The canvas ships
`default-theme.css` with **eight `--codev-canvas-*` tokens that are colors only** (spec 945 D4
deliberately capped the v1 vocabulary at colors). Everything about *type* — size, family,
line-height, paragraph rhythm, heading scale, prose width — falls through to the webview's
inherited defaults, which are tuned for code (the workbench UI font ~13px, line-height ~1.4),
not for sustained reading of long-form documents.

Concretely, in `packages/artifact-canvas/src/styles/default-theme.css` the
`.codev-artifact-canvas` block sets only `color`/`background` and the eight color custom
properties; there is **no rule** that sizes `body`, `p`, `h1`–`h6`, `ul`/`ol`, `blockquote`,
`pre`/`code`, or `table`. In the VSCode host, `preview-template.ts`'s inline `<style>` binds the
eight color tokens to `--vscode-*` variables but adds no typography. The webview body therefore
renders prose at `--vscode-font-size` (UI font, not `--vscode-editor-font-size`) with the
editor's tight inherited line-height.

The canvas now has real consumers (#859 preview, #863 markers + minimap) and the colors-only
limit is a daily readability problem. This issue adds a **typography token tier** with
**github-markdown-css metrics as the chosen baseline** (the familiar mental model for reviewers
who read PRs in that style all day), and binds those tokens to sensible VSCode-theme defaults in
the host.

Prose in the preview is rendered into `.codev-artifact-canvas-body` (see
`ArtifactCanvas.tsx` — the body div holds the sanitized markdown HTML; the standalone
`MarkdownView` uses `.codev-artifact-canvas-rendered`). New prose CSS rules must target
descendants of **both** containers so the tokens work in the composed canvas *and* the standalone
view; scoping to `.codev-artifact-canvas` (the outer container, ancestor of both) covers both.

## Proposed Change

Three tiers, in the issue's order. **Tier 1 + Tier 2 are the committed scope of this PR; Tier 3
is an optional stretch** (see Decision 5).

### Tier 1 — Package typography tokens (`default-theme.css`)

Add a typography token group to `.codev-artifact-canvas` with github-markdown-css-aligned
fallbacks, plus the CSS rules that consume them. **Final token list locked here at
plan-approval** (Decision 1):

| Token | Default | Notes |
|---|---|---|
| `--codev-canvas-font-size` | `16px` | prose body; github-markdown-css base |
| `--codev-canvas-font-family` | `-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"` | github system sans stack |
| `--codev-canvas-line-height` | `1.5` | prose body |
| `--codev-canvas-paragraph-spacing` | `16px` | margin between paragraphs; lists / blockquotes / tables / `pre` derive from this |
| `--codev-canvas-prose-max-width` | `none` | optional readable-measure cap; **off by default** (Decision 4) |
| `--codev-canvas-h1-size` | `2em` | per-level heading sizes (Decision 2) |
| `--codev-canvas-h2-size` | `1.5em` | |
| `--codev-canvas-h3-size` | `1.25em` | |
| `--codev-canvas-h4-size` | `1em` | |
| `--codev-canvas-h5-size` | `0.875em` | |
| `--codev-canvas-h6-size` | `0.85em` | github renders h6 muted + smallest |
| `--codev-canvas-code-font-family` | `ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace` | github mono stack |
| `--codev-canvas-code-font-size` | `0.85em` | github: code ~85% of prose |

That is **8 prose/code tokens + 6 heading tokens = 14 tokens**. The heading set is bounded
(one per element, 1:1 with the rendered tag) and reproduces github's *non-geometric* scale
exactly — see Decision 2 for why per-level beats a single ratio.

CSS rules consuming the tokens (all scoped under `.codev-artifact-canvas`, applied to the prose
container so they don't disturb the comment cards / overlay / minimap chrome):

- Body: `font-size`, `font-family`, `line-height` on the rendered prose container.
- Optional measure cap: `max-width: var(--codev-canvas-prose-max-width)` on the prose container
(a no-op while the default is `none`).
- Paragraphs: `p { margin: 0 0 var(--codev-canvas-paragraph-spacing); }` and zero the last
child's trailing margin.
- Lists / blockquotes / tables / `pre`: bottom margin derived from
`var(--codev-canvas-paragraph-spacing)` for consistent vertical rhythm.
- Headings: `h1..h6 { font-size: var(--codev-canvas-hN-size); }` plus deliberate top/bottom
margins and `line-height: 1.25` (github's heading line-height), so headings render with a
scale instead of user-agent defaults. github's h1/h2 bottom-border is **not** copied (it reads
as chrome in a narrow preview pane; out of scope, noted in the review).
- Code: `pre, code { font-family: var(--codev-canvas-code-font-family); }` and
`font-size: var(--codev-canvas-code-font-size)` for inline `code` and `pre` blocks.

The existing color rules (`a`, `pre`/`code` background, marker/minimap chrome) are untouched.

### Tier 2 — Host mapping (`preview-template.ts`)

Extend the inline `<style>` `.codev-artifact-canvas` block to bind the new typography tokens,
**each binding documented inline** (Decision 3 on which bindings track VSCode vs prose-readability
defaults):

- `--codev-canvas-code-font-family: var(--vscode-editor-font-family)` — code blocks track the
reviewer's **editor** font (the github convention: mono for code).
- `--codev-canvas-code-font-size` — left to the package default (`0.85em`), so code stays
proportional to prose rather than jumping to the editor's absolute size.
- **Prose tokens (`font-size`, `font-family`, `line-height`, `paragraph-spacing`) are left to the
package defaults** — i.e. the preview overrides VSCode's editor/UI font for *prose* and uses the
github system sans stack at 16px / 1.5. Rationale (Decision 3): the editor font is frequently a
small monospace; inheriting it is exactly the cramped baseline this issue fixes. Code still
tracks the editor font via the binding above.

A short inline comment block explains the "prose overrides editor font; code tracks it" split so a
future maintainer doesn't "helpfully" rebind prose to `--vscode-editor-font-*`.

### Tier 3 — User settings (optional, see Decision 5)

If shipped: register `codev.markdownPreview.fontSize` and `codev.markdownPreview.lineHeight` in
`packages/vscode/package.json` `contributes.configuration`, read them in `preview-provider.ts`,
and inject them as inline `--codev-canvas-font-size` / `--codev-canvas-line-height` overrides on
the webview root (so a user value wins over the package default). Kept to two knobs to avoid a
sprawling settings surface; documented in the review.

### Spec-amendment doc trail (#945 D4 contract)

Spec 945 D4 calls the token vocabulary "the locked public contract … do not change shapes without
a spec amendment." This expansion is recorded as a docs-only trail (no protocol gate beyond that,
per the issue):

- `packages/artifact-canvas/src/types.ts` — extend the contract comment to note the typography
tier added by #1053 (the tokens are CSS custom properties, not a TS shape, so no interface
changes; the comment is the contract's prose home).
- `packages/artifact-canvas/README.md` — add the typography tokens to the Theming table with
their defaults and the github-markdown-css pin.
- The plan and review files (this trail) record the amendment, satisfying "recorded in the issue,
plan, and review."

### Reference baseline pin

Document, in `default-theme.css` and the README, the exact github-markdown-css version whose
metrics we mirror (Decision 3 below pins it), so the baseline is reproducible as that project
evolves.

## Files to Change

- `packages/artifact-canvas/src/styles/default-theme.css` — add the 14 typography tokens (with
the version-pin comment) + the prose/heading/code CSS rules.
- `packages/artifact-canvas/src/types.ts` — amend the D4 contract comment to cover the typography
tier (#1053 doc trail).
- `packages/artifact-canvas/README.md:112-135` — extend the Theming token table + note the
github-markdown-css pin.
- `packages/artifact-canvas/src/__tests__/default-theme.test.ts` *(new)* — vitest snapshot/assertion
over the `--codev-canvas-*` token list parsed from `default-theme.css` (AC: "Vitest snapshot
covering the default-theme.css token list passes"). Reads the CSS source, extracts declared
custom-property names, and snapshots the sorted list so any future token add/remove is a
reviewed diff.
- `packages/vscode/src/markdown-preview/preview-template.ts:36-50` — bind the new typography
tokens with inline rationale comments (Tier 2).
- *(Tier 3, optional)* `packages/vscode/package.json` — register
`codev.markdownPreview.fontSize` / `.lineHeight`.
- *(Tier 3, optional)* `packages/vscode/src/markdown-preview/preview-provider.ts` — read the
settings and inject inline token overrides on the webview root.

## Decisions to Lock at plan-approval

**Decision 1 — Final token list.** The 14-token list in the Tier 1 table. Rationale: covers every
prose lever the issue names (size, family, line-height, paragraph rhythm, optional width, heading
scale, code family+size) without inventing knobs nobody asked for. *Confirm or trim.*

**Decision 2 — Heading granularity: per-level tokens (RECOMMENDED) vs scale-base + ratio.**
github-markdown-css's heading sizes are `2 / 1.5 / 1.25 / 1 / 0.875 / 0.85 em` — **not** a clean
geometric progression. A single `--codev-canvas-heading-scale-ratio` + `calc()` *cannot reproduce
the chosen baseline* (e.g. a 1.25 ratio gives 2 / 1.6 / 1.28 / 1.024 / … — wrong at every level).
Since the issue explicitly pins github-markdown-css as the baseline and AC requires "a deliberate
scale," I recommend **per-level `--codev-canvas-h1-size` … `--codev-canvas-h6-size`** for exact
fidelity and a direct per-heading override surface. This *overrides the issue's stated lean*
(scale+ratio for headings) on the grounds that the lean is incompatible with the locked baseline;
flagging it explicitly for your call. *Alternative if you prefer fewer tokens:* `scale-base` +
`ratio` (2 tokens), accepting that headings will diverge from github's exact sizes.

**Decision 3 — VSCode-theme alignment vs prose-readability defaults, + baseline pin.** Prose
tokens override the VSCode editor font (github system sans, 16px, 1.5); `--codev-canvas-code-*`
track the editor font for code blocks. Baseline pinned to **github-markdown-css v5.8.1** (latest
stable; exact version recorded in `default-theme.css` + README). *Confirm the version to pin.*

**Decision 4 — `prose-max-width` default.** Ships as `none` (no cap) so it can't break a host
layout (the issue's stated fallback); the token exists so a host/user can opt into e.g. `72ch`.
*Confirm off-by-default.*

**Decision 5 — Tier 3 scope.** Recommend **landing Tier 1 + Tier 2 firmly** and treating Tier 3
(two settings: `fontSize`, `lineHeight`) as an **optional add within this PR** if the dev-approval
readability review goes smoothly, otherwise spun off to a follow-up issue. Tier 3 is explicitly
"nice-to-have, not gating" in the issue. *Tell me: include Tier 3 now, or defer it.*

## Risks & Alternatives Considered

- **Risk: prose rules leak into the comment cards / minimap / overlay chrome.** Those use
`font-size: 0.9em` (cards) and fixed sizing. Mitigation: scope prose rules to the rendered prose
container and its block descendants, not the chrome classes; verify cards/minimap visually at
dev-approval (AC: "no regression to marker rendering, minimap, comment cards, or color
theming").
- **Risk: `em`-based heading + code sizes compound unexpectedly** if a parent font-size changes.
Mitigation: heading/code sizes are `em` relative to the prose body `font-size` (a single
`px` anchor), matching github-markdown-css's own model; no nested `em` chains.
- **Risk: token bloat on a "locked public contract."** 14 tokens is the upper end. Mitigation:
every token maps to a concrete readability lever the issue named; the heading 6 are bounded and
1:1 with elements. Decision 2's alternative (scale+ratio) trims 4 tokens if you prefer.
- **Risk: the spec-amendment is under-recorded.** Mitigation: the doc trail touches types.ts +
README + plan + review, exactly the surfaces #945 D4 names.
- **Alternative: bind prose to `--vscode-editor-font-*`.** Rejected — the editor font is the
cramped baseline this issue exists to fix; only code should track it (Decision 3).
- **Alternative: a single `--codev-canvas-typography` shorthand.** Rejected — defeats per-lever
host/user overrides and is un-snapshotable.

## Test Plan

- **Unit (vitest, new `default-theme.test.ts`):** parse `default-theme.css`, assert the full
`--codev-canvas-*` token set (colors + new typography) is present and snapshot the sorted list;
assert each new token has a non-empty fallback value. Run from the worktree:
`pnpm --filter @cluesmith/codev-artifact-canvas test`.
- **Unit (existing suites):** `pnpm --filter @cluesmith/codev-artifact-canvas test` stays green
(renderer, overlay, marker tests unaffected).
- **Type/build:** `pnpm --filter @cluesmith/codev-artifact-canvas build` and
`pnpm --filter @cluesmith/codev-vscode build` (or the package's check-types) succeed.
- **Manual at dev-approval (load-bearing — this is why PIR):**
- Open a long real spec/plan/review in the Codev Markdown Preview (run the worktree:
`afx dev pir-1053`, then open the preview on e.g. `codev/specs/945-*.md`).
- Confirm prose reads at 16px / 1.5 line-height with paragraph spacing — visibly looser than the
cramped v3.2.0 baseline.
- Confirm h1–h6 render with the github scale (not user-agent defaults).
- Confirm `pre`/inline `code` stay monospace and track the **editor** font.
- Resize the pane: confirm `prose-max-width` behavior matches Decision 4 (no cap by default).
- Confirm **no regression**: comment cards (#863), right-edge minimap, hover-`+` overlay, and
all color theming render unchanged in both light and dark themes.
- *(If Tier 3 shipped)* change `codev.markdownPreview.fontSize` / `.lineHeight` and confirm the
preview reflows live.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# PIR #1053 — Consultation Rebuttals (iteration 1)

Single advisory pass (`max_iterations: 1`). Disposition of each verdict below.

## Codex — REQUEST_CHANGES (HIGH) → ADDRESSED (real defect, fixed)

**Finding:** Typography was scoped to `.codev-artifact-canvas-body` (and base font tokens to
`.codev-artifact-canvas`), but the exported standalone `MarkdownView` renders
`.codev-artifact-canvas-rendered` with no `.codev-artifact-canvas` ancestor — so the standalone
public surface received no typography tokens or prose rules. This contradicts the approved plan,
which required the rules to cover both containers.

**Assessment:** Valid and HIGH-impact. Verified the facts directly:
- `MarkdownView` is exported from the package's public `index.ts`.
- It renders a bare `.codev-artifact-canvas-rendered` root (`renderer/MarkdownView.tsx:16-21`),
not nested under `.codev-artifact-canvas`.
- The tokens + base font were declared only on `.codev-artifact-canvas`, which is **not** an
ancestor of `-rendered`, so the standalone surface inherited neither.
- My own approved plan explicitly stated the rules must cover both containers.

So it ships a known-broken public surface and diverges from the plan. Treated as a real defect and
fixed (not rebutted).

**Fix (commit `[PIR #1053] Cover standalone MarkdownView surface with typography (consult fix)`):**
- The token vocabulary + base `color`/`background`/`font-family`/`font-size`/`line-height` now
apply to **both** `.codev-artifact-canvas` and `.codev-artifact-canvas-rendered`.
- Every prose element rule now uses an `:is(.codev-artifact-canvas-body,
.codev-artifact-canvas-rendered)` container group (kept DRY with `:is()` rather than duplicating
every selector).
- Overlay-only chrome stays composed-surface-only: the gutter `padding-left`, the
`[data-line]:focus-visible` outline, the comment cards, and the minimap remain
`.codev-artifact-canvas-body` / `.codev-artifact-canvas` scoped (MarkdownView has no overlay).
- `position: relative` (overlay anchor) split out to `.codev-artifact-canvas` only.

**Regression test** (`src/__tests__/default-theme.test.ts` → "covers the standalone MarkdownView
root, not just the composed canvas"): asserts (1) the token/base-font block names the standalone
root, (2) representative prose selectors (`h1`, `code`) name it via the `:is()` group, and (3) the
gutter `padding-left` rule stays bare-`-body` (composed-surface-only). Fails if a future change
drops the standalone root from prose styling or leaks the gutter into the standalone surface.

**No regression to the shipped consumer:** the VSCode preview uses `ArtifactCanvas` →
`.codev-artifact-canvas-body`, which is inside the `:is()` group, so its rendering is unchanged.
Verified the `:is()` selectors land in the bundled `dist/webview/markdown-preview.css`.

**Escalation:** PIR's single advisory pass does not independently re-review this fix, so it is
flagged to the human at the `pr` gate as the remaining reviewer.

## Claude — APPROVE (HIGH) → no action required (but corroborates the Codex fix)

Approved overall. It flagged the *same* `MarkdownView` scoping gap as a non-blocking observation.
Its parenthetical that the standalone view "gets the font baseline" anyway is factually incorrect —
the base font is declared on `.codev-artifact-canvas`, which is not an ancestor of
`.codev-artifact-canvas-rendered` — which is exactly why the gap was worth fixing rather than
accepting. The fix above resolves the observation. All other points (plan adherence, code quality,
test coverage, review-file quality) were positive; nothing to action.

## Gemini — no usable verdict → nothing to action

The Gemini output was a sandbox/environment status message, not a review (no `VERDICT` line, no
findings). Porch defaulted it to REQUEST_CHANGES because no `APPROVE` token was present, but there
is no substantive feedback to address. The consultation harness produced no review content for
this model on this run.

## Net

The one substantive finding (Codex, corroborated by Claude) is a real defect and is fixed with a
regression test. No outstanding REQUEST_CHANGES content remains unaddressed. The human at the `pr`
gate is the remaining reviewer of the fix (PIR single-pass).
29 changes: 29 additions & 0 deletions codev/projects/1053-vscode-typography-tokens-for-t/status.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: '1053'
title: vscode-typography-tokens-for-t
protocol: pir
phase: review
plan_phases: []
current_plan_phase: null
gates:
plan-approval:
status: approved
requested_at: '2026-06-17T21:54:25.731Z'
approved_at: '2026-06-17T22:01:29.466Z'
dev-approval:
status: approved
requested_at: '2026-06-17T22:09:23.451Z'
approved_at: '2026-06-18T04:37:07.103Z'
pr:
status: pending
requested_at: '2026-06-18T04:51:55.413Z'
iteration: 1
build_complete: true
history: []
started_at: '2026-06-17T21:50:52.095Z'
updated_at: '2026-06-18T04:51:55.413Z'
pr_history:
- phase: review
pr_number: 1071
branch: builder/pir-1053
created_at: '2026-06-18T04:39:43.192Z'
pr_ready_for_human: true
Loading
Loading