phase: 100_page_width_consistency
**Phase 100 — final verification pass: all green.** - Verified the shipped CSS contract directly: `--chat-column: 72rem` in `:root`; 0 literal `max-width: 46rem`; no `@media (min-width: 1500px)` block; exactly 4 token-capped reading columns; tuning/theme/doc-edit shells cap-free, structurally `.sources-shell`; `mock_llm.py` diff is comment-only. - Defect found & fixed (phase-93 suite): `test_theme_semantic_completion.py::test_reset_removes_tag_byte_identical` raced theme.js's post-PUT refetch — it asserted the `#bor-theme` tag was gone right after the result line, but tag removal lands in the reconcile after the re-fetch (failed ~1 in 5 runs after `test_chat_rag`, reproduced). Fixed with an auto-waiting `expect(...).to_have_count(0)` (settled-state wait, the file's existing house pattern). 6/6 clean on the repro loop after. - Tests/lint/coverage: `uv run pytest --cov=app --cov-report=term-missing` → 2052 passed, **99%** on `app/`; `uv run ruff check .` + `uv run pyright` → 0 errors. - E2E in isolation (all passed): `test_wide_desktop_column.py` 3 (the phase suite — chat==tuning==theme==RAG ±4px at 1280 & 1920, ≈1152px; shared ≈1152px; standalone doc ≈1112px; modal unchanged ≈1100px; 360px overflow-free), `test_ui_customization` 4, `test_admin_theme_tab` 5, `test_document_viewer` 7, `test_save_share_ux` 5, `test_sticky_navbar` 3, `test_markdown_tables` 6, `test_responsive_polish` 7, `test_chat_rag` 3, `test_theme_semantic_completion` 8. - Completion criteria: ① measured 72rem everywhere (≥~1200px, ±4px) + full-width below + 360px clean — **PASS** (E2E); ② zero 46rem rules / no 1500px block / four token selectors — **PASS** (grep + unit pins); ③ B4 byte-identical no-op + mobile squeeze — **PASS** (theme + responsive suites green); ④ full suite / coverage / lint — **PASS**; ⑤ atomic commit — left to the harness per executor protocol (all changes in the working tree, uncommitted). - Deviations: none from phase decisions; only change in this pass is the race fix above (test-only, behavior unchanged). - Next pending phase: `98_sync_summary_visibility` (numeric order in `todo/`; also pending: 99, 101–105).
This commit is contained in:
+71
-78
@@ -45,10 +45,13 @@
|
||||
|
||||
--header-h: 64px;
|
||||
|
||||
/* Reading column: 46rem base (PLAN §7 lineage); 2x on wide desktops
|
||||
(owner instruction 2026-08-31, TODO L5 / D2 — chat + shared +
|
||||
document view). */
|
||||
--chat-column: 46rem;
|
||||
/* Reading column: 72rem — EQUAL to the .container's 72rem cap, so
|
||||
the reading columns (chat, shared, the document viewer's .doc-md
|
||||
+ its summary panel) ride the same width as every other page
|
||||
(owner instruction 2026-09-12: "match the width of the RAG page
|
||||
for all other pages" — supersedes the 2026-08-31 instruction, and
|
||||
the wide-desktop doubling with it). */
|
||||
--chat-column: 72rem;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -389,8 +392,11 @@ body::before {
|
||||
/* Chat is a vertical conversation: a centered, capped column is the
|
||||
correct layout here (PLAN §UI/UX). The surrounding frame keeps it
|
||||
from collapsing into a hairline on wide screens. The cap is the
|
||||
--chat-column token: 46rem base, 2x (92rem) at >=1500px wide
|
||||
desktops (owner instruction 2026-08-31, TODO.md L5 / D2). */
|
||||
--chat-column token: 72rem — equal to the .container's cap, so the
|
||||
chat column reads at the RAG page's full container width at every
|
||||
viewport (owner instruction 2026-09-12: "match the width of the
|
||||
RAG page for all other pages" — the wide-desktop doubling was
|
||||
retired with it). */
|
||||
.chat-shell {
|
||||
max-width: var(--chat-column);
|
||||
margin-inline: auto;
|
||||
@@ -409,8 +415,8 @@ body::before {
|
||||
flips this to a vertical stack (flex-direction: column +
|
||||
align-items: stretch — full-width pills, New chat above Share; the
|
||||
existing ≤640px pill rules apply to the stacked pills unchanged).
|
||||
The reading-column contract is untouched (--chat-column: 46rem base,
|
||||
92rem at >=1500px — PLAN §7 lineage).
|
||||
The reading-column contract is untouched (--chat-column: 72rem —
|
||||
the .container width at every viewport).
|
||||
|
||||
Phase 65 (task 03, 2026-09-01, `TODO.md` L3, owner-locked A2): the
|
||||
row is the TOP member of the pinned .chat-bottom cluster (task
|
||||
@@ -504,8 +510,9 @@ body::before {
|
||||
/* GFM pipe tables (phase 44, 2026-08-27, TODO.md L6): the shared
|
||||
renderer wraps every table in .md-table-wrap — the horizontal
|
||||
scroller, so a wide table scrolls inside the bubble instead of
|
||||
breaking the reading column (--chat-column: 46rem base, 92rem at
|
||||
>=1500px) — around a semantic <table class="md-table">
|
||||
breaking the reading column (--chat-column: 72rem — the
|
||||
.container width at every viewport) — around a semantic
|
||||
<table class="md-table">
|
||||
(escape-first cells; alignment colons render left, owner decision).
|
||||
Phase-08 tokens only: --line hairline borders and the thead tinted
|
||||
from the plain surface family — --ink on --surface is 14.5:1 (PLAN
|
||||
@@ -603,8 +610,8 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
--surface ≈10.4:1 (11.6:1 on the page bg), and --ink on --brand-soft
|
||||
in the path `code` ≈11.5:1 — all comfortably AA in the (single dark)
|
||||
theme. Inline rows only: appending lines never shifts the chat
|
||||
column (46rem base; 92rem at >=1500px — no new container), and the
|
||||
rows are not interactive — no focus targets. */
|
||||
column (72rem — the .container width at every viewport; no new
|
||||
container), and the rows are not interactive — no focus targets. */
|
||||
.tool-calls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -960,15 +967,16 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
|
||||
/* ---------- Global tuning page (phase 27) ---------- */
|
||||
/* /tuning.html: create / edit / delete steering notes without a chat
|
||||
conversation. Same width discipline as the chat column — a centered,
|
||||
capped column on the 72rem frame; the form and the note list span its
|
||||
FULL width (no skinny lists). Every interactive target is >=44px;
|
||||
text pairs reuse the Phase-08 AA palette (dark ink on brand 5.2:1,
|
||||
brand-ink/brand-soft 6.9:1, ok 10.6:1, err 9.1:1, ink-soft >=6.9:1).
|
||||
No filter: blur, no CDN, system font stack. */
|
||||
conversation. Full-container width (owner instruction 2026-09-12:
|
||||
"match the width of the RAG page for all other pages" — the
|
||||
phase-27 capped-column discipline is superseded): the shell rides
|
||||
the .container's 72rem frame like the RAG page, and the form and
|
||||
the note list span its FULL width (no skinny lists). Every
|
||||
interactive target is >=44px; text pairs reuse the Phase-08 AA
|
||||
palette (dark ink on brand 5.2:1, brand-ink/brand-soft 6.9:1,
|
||||
ok 10.6:1, err 9.1:1, ink-soft >=6.9:1). No filter: blur, no CDN,
|
||||
system font stack. */
|
||||
.tuning-shell {
|
||||
max-width: 46rem;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
@@ -1241,8 +1249,8 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
/* ---------- Composer ---------- */
|
||||
/* Phase 52 (2026-08-30, TODO.md L3): the composer is PINNED to the
|
||||
viewport bottom. The page scrolls at the document level and
|
||||
`.chat-shell` (the centered reading column — 46rem base, 92rem at
|
||||
>=1500px) is the composer's sticky
|
||||
`.chat-shell` (the centered reading column — 72rem, the
|
||||
.container width at every viewport) is the composer's sticky
|
||||
containing block, so the box sticks to the bottom edge of the
|
||||
viewport at every scroll position and settles back into its normal
|
||||
flow position (above the footer) once the document bottom is
|
||||
@@ -2265,8 +2273,9 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
a fixed full-viewport dim backdrop + a centered panel (z-index
|
||||
1000, above the sticky header (20) + skip-link (100); NO blur —
|
||||
the phase-08 no-blur perf anchor), scaled to a compact dialog:
|
||||
the 46rem chat-column width or the viewport, whichever is
|
||||
narrower. Phase-08 tokens only; system fonts; no CDN.
|
||||
the chat-column width (the --chat-column token — 72rem, the
|
||||
.container width) or the viewport, whichever is narrower.
|
||||
Phase-08 tokens only; system fonts; no CDN.
|
||||
|
||||
AA pairs: title/copy are --ink on --surface (13.8:1); the source
|
||||
value is --ink on --bg (16.7:1); the error line is the err pair
|
||||
@@ -2302,7 +2311,7 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: auto;
|
||||
width: min(46rem, calc(100vw - 2rem));
|
||||
width: min(var(--chat-column), calc(100vw - 2rem));
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
@@ -2405,7 +2414,8 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
#remove-confirm-dialog overlay contract (phase 69): a fixed
|
||||
full-viewport dim backdrop + a centered panel (z-index 1000, above
|
||||
the sticky header (20) + skip-link (100); NO blur — the phase-08
|
||||
no-blur perf anchor), scaled to the 46rem chat-column width or the
|
||||
no-blur perf anchor), scaled to the chat-column width (the
|
||||
--chat-column token — 72rem, the .container width) or the
|
||||
viewport, whichever is narrower. The box: a VISIBLE block label
|
||||
(WCAG — never aria-label-only) over a mono textarea (the box is a
|
||||
data entry, not prose — the var(--mono) stack); the error line is
|
||||
@@ -2442,7 +2452,7 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: auto;
|
||||
width: min(46rem, calc(100vw - 2rem));
|
||||
width: min(var(--chat-column), calc(100vw - 2rem));
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
@@ -3168,8 +3178,9 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
|
||||
/* ---------- Theme view (phase 91, tasks 04 + 05) ----------
|
||||
The shell's seventh view (#view-theme): the admin palette + branding
|
||||
editor (task 05). The centered 46rem column (the .tuning-shell
|
||||
language — this is a form view, the tuning-view width pattern), the
|
||||
editor (task 05). The full 72rem container width (owner
|
||||
instruction 2026-09-12 — the phase-91 form-column cap is
|
||||
superseded), the
|
||||
form card (the #tune-form language: surface fill, --line hairline,
|
||||
radius, shadow), the fieldset groups (Branding / Palette) with the
|
||||
.theme-note sub-copy, the 3-column (desktop) / 1-column (mobile)
|
||||
@@ -3184,8 +3195,6 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
tab (B3). Every pair reuses the Phase-08 AA palette; :focus-visible
|
||||
via the global 3px outline rule. No CDN, system fonts. */
|
||||
.theme-shell {
|
||||
max-width: 46rem;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
@@ -3347,12 +3356,13 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
|
||||
/* ---------- Shared page (phase 51, task 03) ----------
|
||||
/shared/<token>: the anonymous read-only conversation (owner-locked
|
||||
2026-08-29, TODO.md L6). The shell maps to the PLAN §7 centered
|
||||
chat column (--chat-column: 46rem base, 92rem at >=1500px) — the
|
||||
conversation reads exactly like the chat
|
||||
page (the .msg/.bubble/.thinking/.tool-calls/.msg-meta rules apply
|
||||
unchanged) with NO composer, so the column contract holds for a
|
||||
guest. Zero interactive controls (owner-locked): the chips are
|
||||
2026-08-29, TODO.md L6). The shared page reads exactly like the
|
||||
chat page — both at the 72rem container width (--chat-column, now
|
||||
equal to the .container cap — owner instruction 2026-09-12; the
|
||||
wide-desktop doubling was retired) — and the .msg/.bubble/.thinking/
|
||||
.tool-calls/.msg-meta rules apply unchanged, with NO composer, so
|
||||
the column contract holds for a guest. Zero interactive controls
|
||||
(owner-locked): the chips are
|
||||
plain text, so the pill families' pointer treatments are switched
|
||||
off IN THIS SCOPE ONLY — the chat page's interactive chips keep
|
||||
their styles untouched. Every pair reuses the Phase-08 AA palette
|
||||
@@ -3361,8 +3371,8 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
fonts. */
|
||||
.shared-shell {
|
||||
width: 100%;
|
||||
max-width: var(--chat-column); /* the PLAN §7 centered chat column
|
||||
(46rem base; 92rem at >=1500px — phase 58) */
|
||||
max-width: var(--chat-column); /* the centered chat column — 72rem,
|
||||
the .container width at every viewport */
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -3525,9 +3535,10 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
#doc-content { display: flex; flex-direction: column; }
|
||||
.doc-loading { margin: 1.5rem auto; text-align: center; color: var(--ink-soft); }
|
||||
|
||||
/* Markdown: the centered reading column — 46rem base, 2x (92rem) at
|
||||
>=1500px (PLAN §7.1 lineage; owner instruction 2026-08-31, TODO.md
|
||||
L5 / D2). */
|
||||
/* Markdown: the centered reading column — 72rem, the .container width
|
||||
at every viewport (owner instruction 2026-09-12: "match the width
|
||||
of the RAG page for all other pages" — the wide-desktop doubling
|
||||
was retired). */
|
||||
.doc-md {
|
||||
width: 100%;
|
||||
max-width: var(--chat-column);
|
||||
@@ -3576,8 +3587,8 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
/* md/markdown: the panel matches the .doc-md centered reading column
|
||||
(46rem base; 92rem at >=1500px — the same --chat-column token) —
|
||||
it is the column's label. Raw formats stay full width (the
|
||||
(72rem — the same --chat-column token, the .container width at
|
||||
every viewport) — it is the column's label. Raw formats stay full width (the
|
||||
.doc-raw default above), matching the full-width pre; in engines
|
||||
without :has() the panel degrades to that full-width default. */
|
||||
.doc-summary:has(+ .doc-md) {
|
||||
@@ -3743,8 +3754,9 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
/* ---------- Document modal (phase 26) ----------
|
||||
"New documents should open in an almost-fullscreen modal, not in a new
|
||||
page" (TODO.md L4). The overlay reuses the viewer page's .doc-meta
|
||||
badge classes, the .doc-md reading column (46rem base; 92rem at
|
||||
>=1500px), and the .doc-raw
|
||||
badge classes, the .doc-md reading column (72rem — the .container
|
||||
width; inside the modal the 1100px panel stays its effective
|
||||
ceiling), and the .doc-raw
|
||||
pre — this block only adds the chrome (backdrop, panel, header,
|
||||
actions, scroll container). Phase-08 tokens only; NO blur (the
|
||||
phase-08 no-blur perf anchor); no new assets; system fonts.
|
||||
@@ -3886,9 +3898,10 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
}
|
||||
|
||||
/* The scroll container: vertical scroll lives HERE, never the viewport.
|
||||
.doc-md keeps its centered reading column inside (46rem base;
|
||||
92rem at >=1500px, capped there by the 1100px panel); .doc-raw keeps
|
||||
its own overflow-x. tabindex="-1" in the markup is the JS focus target. */
|
||||
.doc-md keeps its centered reading column inside (72rem — wider
|
||||
than the 1100px panel's inner width, which stays its effective
|
||||
ceiling); .doc-raw keeps its own overflow-x. tabindex="-1" in the
|
||||
markup is the JS focus target. */
|
||||
.doc-modal-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@@ -3906,16 +3919,14 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
/doc-edit.html: the admin-gated edit screen for a doc draft (title,
|
||||
in-repo path, markdown body) — a FLOW page, not one of the app's
|
||||
pages, so the header is SLIM (brand + "← Back to chat" only). The
|
||||
46rem base column is HARD-CODED: a form column, not a reading
|
||||
column — it does not ride --chat-column, so phase 58's wide-desktop
|
||||
doubling never stretches the form. The .sources-gate gate is reused
|
||||
verbatim (phases 16/35/50). Every pair reuses the Phase-08 AA
|
||||
palette; touch targets >=44px; :focus-visible via the global 3px
|
||||
outline rule. No CDN, system fonts. */
|
||||
shell rides the full 72rem container like every other page (owner
|
||||
instruction 2026-09-12 — the phase-59 form-column cap is
|
||||
superseded). The .sources-gate gate is reused verbatim
|
||||
(phases 16/35/50). Every pair reuses the Phase-08 AA palette;
|
||||
touch targets >=44px; :focus-visible via the global 3px outline
|
||||
rule. No CDN, system fonts. */
|
||||
.doc-edit-shell {
|
||||
width: 100%;
|
||||
max-width: 46rem; /* the 46rem base column (hard-coded — see above) */
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
@@ -4385,9 +4396,9 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
.theme-reset { width: 100%; }
|
||||
/* Phase 51: the shared page squeezes like the chat column — the
|
||||
title and the note step down (the empty-state-title family); the
|
||||
shell keeps its base 46rem column (the >=1500px 92rem override
|
||||
never applies here — it is already the narrowest box on the
|
||||
page) and .msg-body's 92% override above applies. */
|
||||
shell is full-width here (the 72rem cap never binds on a phone —
|
||||
the container is already 100%) and .msg-body's 92% override
|
||||
above applies. */
|
||||
#shared-title { font-size: 1.35rem; }
|
||||
.shared-note { font-size: 0.88rem; }
|
||||
.footer-inner { flex-direction: column; gap: 0.2rem; text-align: center; }
|
||||
@@ -4418,24 +4429,6 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase 58: wide desktops (viewport >=1500px) read the 2x column —
|
||||
--chat-column doubles to 92rem for the four reading shells (chat,
|
||||
shared, the document viewer's .doc-md and its summary panel). The
|
||||
chat and shared shells ARE their .container (the token max-width
|
||||
overrides the .container's 72rem cap — same specificity, later in
|
||||
the file), but the document page's .container.doc-shell WRAPS the
|
||||
column, so the 72rem cap would bind first and pin .doc-md at
|
||||
~1112px: the wide block lifts the shell's cap to the column plus
|
||||
the container's two 1.25rem gutters, letting .doc-md's own 92rem
|
||||
cap bind (1472px at the 16px root). Everything below 1500px
|
||||
renders exactly as before, and .tuning-shell (a form, not a
|
||||
reading surface) keeps its hard-coded 46rem at every width
|
||||
(owner instruction 2026-08-31, TODO.md L5 / D2). */
|
||||
@media (min-width: 1500px) {
|
||||
:root { --chat-column: 92rem; }
|
||||
.doc-shell { max-width: calc(var(--chat-column) + 2 * 1.25rem); }
|
||||
}
|
||||
|
||||
/* Phase 46: prefers-reduced-motion stills the mobile menu — no
|
||||
180ms slide+fade; open/close snaps (the visibility/opacity flip
|
||||
applies instantly) and stays correct. BOTH states are named: the
|
||||
|
||||
Reference in New Issue
Block a user