Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions content/guides/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,20 @@ authority on frontmatter.
A section-nav link for a pane that lives in Review switches modes and then
jumps to that pane, so no link pretends a hidden pane is on screen.

Author mode carries that promise through the chrome as well. Project recedes
to a file drawer under the writing page instead of a second equal card, the
section nav leads with Project and Source while the Review destinations recede
into a captioned, faint cluster — still live links, so activating one still
switches modes and lands — and Source is drawn as the page itself: the card
edge and drop shadow give way to one focus ring, worn by the page rather than
doubled inside it, and on a wide window the page is capped at a comfortable
reading measure and centered, so you get a column to write in rather than a
wider dashboard tile. The top band quiets as well: the
title and the connection status share one row under a smaller product mark, the
connection readout is a compact state chip whose full sentence is one
activation away, and the theme control shows just the current theme. Review
keeps the wide working surface beside its rail.

## Compiler-backed commands and problems

The Problems pane runs a fixed allowlist of Boris invocations against saved
Expand Down
62 changes: 62 additions & 0 deletions docs/changelog.d/994-editor-flat-chrome.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
### Changed

- Author mode stops reading as an equal-card dashboard: Project is demoted to a
file drawer under the writing page (no shared height, elevation, or heavy
edge) instead of a peer card, the section nav leads with Project and Source
while the Review destinations recede into a captioned, faint cluster that
still switches modes and lands on activation, and Source takes page material
rather than a wider dashboard tile — no card elevation, a focus-within edge,
and a readable-measure cap centered in its column on wide windows, so the
pane edge is the page edge and heading, editor, gutter, and status line share
one column. The top band quiets too: the header drops its decorative eyebrow
so the product mark and the live connection status share one row, the
connection readout collapses to a compact state chip whose honest sentence is
one activation away (the live region announces the short label rather than
re-reading a sentence), the theme control states only its state, and Author
takes a smaller product mark and tighter header/nav bands than Review. Links:
[the editor
guide](/content/guides/editor.md#density-modes-and-the-writing-surface),
[#993](https://github.com/drawmeanelephant/boris/issues/993).

### Fixed

- Review corrections to the Author surface above. The Author app title step is
its own link in the type chain (`--text-2xl-compact`) rather than the
pane-title size, which had made `h1` exactly as large as the pane titles it
outranks. The theme control drops `aria-pressed`: a pressed state presumes a
name that does not change with it, and here the changing label *is* the
state. The Author Source textarea keeps no focus ring of its own now that the
shell owns one, so the page shows a single edge instead of a frame within a
frame. The connection readout's live region is text-only, matching every other
status region in the editor, with the disclosure chip as a sibling rather than
a button nested inside an atomic region.

### Added

- Every ordered scale the editor declares — the type chain, the spacing rhythm,
the corner steps, and the stacking ladder — now writes its order down exactly
once, in a `--scale-*` list in `editor/ui/src/lib/tokens.css`, and nothing
else keeps a copy. `--scale-space-base` declares the spacing rhythm's base
instead of describing it in prose, and the six bare `z-index` values in
`styles.css` became `--layer-*` names, so the ladder is the only authority on
stacking order.
- `editor/ui/scripts/check-scales.mjs`, run by `npm run check`, holds the static
half: every declared token of each family is classified, every listed name
exists, each list ascends (numerically where a value is resolvable without a
viewport, and reported as deferred where it is a `clamp`), spacing steps are
whole multiples of the declared base, the layer ladder holds bare numbers,
every scale token is declared once and only in `tokens.css`, every `var()`
reference lands on a declared step, no `z-index` is a bare number, and each
documented section names its scale's list without naming tokens that do not
exist. It runs its own self-tests first, so the checker is checked.
- `editor/ui/tests/scales.spec.ts` holds the half that needs a viewport. It
resolves the same lists out of the *applied* stylesheet at four widths, which
is the only way to check a `clamp` step, a value overridden further down the
cascade, or the rhythm's base — and it asserts that the sticky nav and the
skip link carry the declared layer rather than a loose number. The rendered
heading levels stay in `editor/ui/tests/reading-hierarchy.spec.ts`, now
measured in both density modes.
- That last point is why any of this exists: the prior hierarchy suite pinned
Review density only, so an Author-only override could flatten the app title
onto the pane-title size with every suite green. Which is exactly what
happened, and what these scales are now walked against.
95 changes: 87 additions & 8 deletions editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -710,17 +710,34 @@ The editor's chrome is one type scale, defined in
size, not only by weight and color, or every label competes with the prose it
labels.

- **Scale.** Two meta steps sit below the 16px body (`--text-2xs`,
`--text-xs`), then one step per heading level above it: `--text-lg` for
sub-pane titles (h3), `--text-xl` for pane titles (h2), `--text-2xl` for the
app title (h1). The ratio is ~1.2 pairwise, which is what makes a pane title
read as a heading beside body copy. `--text-code` is the monospace working
size for the source surface.
- **Scale.** The order is written down once, in `--scale-type` in
[`ui/src/lib/tokens.css`](ui/src/lib/tokens.css), and nothing else keeps a
copy of it. Two meta steps sit below body copy (`--text-2xs` for kbd chips and
micro labels, `--text-xs` for eyebrows and status meta), then `--text-sm` for
ledes and field labels and `--text-md` for the 16px body itself, and one step
per heading level above it: `--text-lg` for sub-pane titles (h3), `--text-xl`
for pane titles (h2), `--text-2xl-compact` for the app title (h1) in Author's
calmer band, and `--text-2xl` for that title in Review. The ratio is ~1.2
pairwise, which is what makes a pane title read as a heading beside body copy.
`--text-code` is the monospace working size for the source surface, and it is
listed as off-chain because it is an orthographic axis: it may interleave with
the steps but never coincide with one. Every step stays ordered at every
viewport, so a mode may choose a different step but two levels never collapse
onto one size.
- **No off-scale sizes.** Component rules must consume a token rather than a
raw `rem`. An off-scale size is exactly how the surface drifted into
everything-is-16px, and it is how a sub-pane heading ended up rendering
*larger* than the pane title above it (unstyled `h3` inherits the user
agent's `1.17em`). Extend the chain in `tokens.css` instead.
- **Enforced, not checked by eye.** A new `--text-*` token must be classified
in the type scale, and this section must name `--scale-type` —
[`ui/scripts/check-scales.mjs`](ui/scripts/check-scales.mjs) fails
`npm run check` otherwise, with no browser needed.
`ui/tests/scales.spec.ts` then resolves the chain out of the *applied*
stylesheet at four widths, asserting strict ascent with no collisions, and
`ui/tests/reading-hierarchy.spec.ts` measures the rendered heading levels in
*both* density modes. The prose, the stylesheet, and the browser are all held
to the one list.
- **Reading rhythm.** `:root` sets `--leading-normal` (1.55) so prose gets a
real line-height by default; headings take `--leading-tight` and controls
opt in explicitly. `--measure-prose` caps a lede's line length.
Expand All @@ -744,6 +761,43 @@ labels.

Presentation only: no endpoint, no Boris surface, and no pipeline change.

## Spacing, radius, and stacking

Three more ordered scales sit beside the type chain, declared the same way and
in the same block of [`ui/src/lib/tokens.css`](ui/src/lib/tokens.css): the order
lives in one `--scale-*` list, and nothing else keeps a copy.

- **Spacing (`--scale-space`).** Eight steps on a 0.25rem base, `--space-1`
through `--space-8` (0.25rem to 3rem). Padding and gaps come from these, and
every step is a whole multiple of the base — that is what keeps a surface on
one rhythm instead of drifting into one-off values. The base is declared too,
as `--scale-space-base`, so the checks read it rather than trusting this
sentence.
- **Radius (`--scale-radius`).** `--radius-sm`, `--radius-md`, `--radius-lg`,
and `--radius-pill`: three corner steps plus the pill, ascending.
- **Stacking (`--scale-layer`).** One ladder for the whole editor,
`--layer-focus-mirror` < `--layer-focus-editor` < `--layer-focus-zen` <
`--layer-nav` < `--layer-skip-link` < `--layer-overlay`. The first three order
elements inside the focus editor's own stacking context — the measuring
mirror, the text above it, the dimming veil over both; the last three are
global chrome: the sticky section nav, the skip link, and the full-viewport
focus overlay. `z-index` in `styles.css` must name a layer, because a bare
number is how two layers silently swap places.

Elevation tiers (`--shadow-1`, `--shadow-2`, `--shadow-3`) are deliberately not
a declared scale: a shadow list has no scalar order for a list to protect.

[`ui/scripts/check-scales.mjs`](ui/scripts/check-scales.mjs) enforces all of this
from `npm run check` — every declared token of each family is classified, every
listed name exists, each list ascends (numerically where a value is resolvable
without a viewport, and reported as deferred where it is a `clamp`), spacing
steps are whole multiples of the base, the layer ladder holds bare numbers, and
this section names each scale's list. Then
[`ui/tests/scales.spec.ts`](ui/tests/scales.spec.ts) re-reads the same lists out
of the *applied* stylesheet: that is the half which catches what no file parse
can, namely a value overridden in a later block, a media query, or the dark
theme.

## Density modes and the writing surface

The shell has two density modes (#990), persisted per browser under
Expand All @@ -759,11 +813,36 @@ preferences. The mode is disposable UI state, never project truth:
that clamps at max scroll keeps the target's `aria-current` while the
target's box still covers the reading line; scrolling off it releases the
marker.
- The residual-flat polish (#993) carries the same intent through the
chrome: Project recedes to a file drawer under the writing page (it no
longer stretches to the page's height, and drops the card elevation and
heavy edge for a recessed neutral surface with tighter padding), the nav
leads with Project and Source while the Review destinations recede into a
captioned, faint cluster (emphasis only — those links stay live, so
activating one still switches modes and lands), and Source takes page
material rather than a wider dashboard tile. The top band quiets too: the
header drops its decorative eyebrow so the product mark and the live
connection status share one baseline row, the connection readout collapses
to a compact state chip whose honest sentence is one activation away (the
region is text-only and announces the short label, not a sentence re-read
on every host blip), the theme control states only its state
("Dark"/"Light", matching its accessible name) and carries no
`aria-pressed` — a pressed state presumes a name that does not change with
it — and Author takes a smaller product mark and tighter header/nav bands
than Review.
- **Review**: the full diagnostics chrome, unchanged.

Source is the hero in both modes (#989): the writing column outweighs the
file and rail columns, and the editing surface is a bordered, elevated shell
whose chrome is presentation only:
file and rail columns, and the editing surface is a bordered shell whose
chrome is presentation only. It keeps its elevation in Review; in Author the
card edge and drop shadow give way to a quiet hairline and a `:focus-within`
ring the shell owns alone (the focused textarea keeps no second ring inside
it), and the pane itself is capped at a readable measure (~80 monospace
columns) and centered in its grid column — the pane edge is the page edge, so
heading, editor, gutter, and status line share one column instead of a narrow
measure stretched across a wide tile. Below the cap the pane just fills its
column, and the cap is Author-only: Review keeps the wide working surface
beside its rail. The chrome is presentation only:

- a measured **line gutter** — numbers for the visible window are placed at
each line's measured position and the current line is highlighted, so
Expand Down
2 changes: 1 addition & 1 deletion editor/scripts/test-editor-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fi

run_stage "editor format" zig fmt --check editor/build.zig editor/build.zig.zon editor/src
run_stage "editor host unit tests" zig build --build-file editor/build.zig test
run_stage "editor UI static checks (svelte-check + key hints)" npm --prefix editor/ui run check
run_stage "editor UI static checks (svelte-check + key hints + scales)" npm --prefix editor/ui run check
run_stage "editor UI build" npm --prefix editor/ui run build
run_stage "editor host build (boris-editor + contract probe)" build_host_binaries
run_stage "editor UI end-to-end suite (Playwright, mocked host)" npm --prefix editor/ui run test:e2e
Expand Down
2 changes: 1 addition & 1 deletion editor/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"check": "svelte-check --tsconfig ./tsconfig.json && node scripts/check-key-hints.mjs",
"check": "svelte-check --tsconfig ./tsconfig.json && node scripts/check-key-hints.mjs && node scripts/check-scales.mjs",
"test:e2e": "playwright test"
},
"devDependencies": {
Expand Down
Loading