style: flat menu on the tiled ground in both views; tiled page cards

- The tiled ground moves from .main-content to .app-body, so the menu sits
  on it too and the grid keeps one origin whichever view is showing. A
  second tile on .main-content would have started 210px in and seamed.
- Desktop: the workspace sidebar is flat (transparent, no border), the same
  as the assistant rail, which drops the white box it briefly had. The phone
  sheets stay white because they slide over the page.
- Workspace page cards get the assistant chat panel's tiled-white fill.

Measured in Chromium: name card 848-892 at x=8, brand, switch and 210px
width identical in both views, expanded and collapsed; no page scroll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4rptBNvn6RYieQw54GXNS
This commit is contained in:
Daniel 2026-09-10 12:23:31 +02:00
parent f3a2309536
commit 8756c9a140
3 changed files with 42 additions and 16 deletions

View file

@ -563,17 +563,17 @@ body.assistant-mode-workspace .assistant-rail-workspace { display:flex; }
body.assistant-sources-open .assistant-sources-fab { background:var(--blue-light); border-color:var(--blue-light); }
}
/* Desktop: the rail is the same white sidebar the workspace uses, flush to the
window edge. It used to sit inside the content area's 14px padding, on the
tiled ground so the menu changed background between the two views, the
name card sat 6px lower and further in, and the page was 28px taller than
the window. The 14px now belongs to the panels beside the rail. */
/* Desktop: the rail is laid out as the workspace sidebar is, flush to the
window edge and flat on the page's tiled ground. It used to sit inside the
content area's 14px padding, so its name card sat 6px lower and further in
and the page was 28px taller than the window. The 14px now belongs to the
panels beside the rail. */
@media (min-width:961px) {
body.assistant-workspace .main-content { padding:0; }
body.assistant-workspace .assistant-layout { padding-right:14px; }
body.assistant-workspace .assistant-main,
body.assistant-workspace .assistant-side { margin-block:14px; height:auto; }
body.assistant-workspace .assistant-history { padding:0; gap:0; background:white; border-right:1.5px solid var(--g200); }
body.assistant-workspace .assistant-history { padding:0; gap:0; }
body.assistant-workspace .assistant-rail-actions { padding:0 8px; }
body.assistant-workspace .assistant-history .card { padding:0 8px; }
/* Workspace links: the app's .tab-btn, full-bleed with the same inset. */

View file

@ -66,11 +66,20 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
}
/* APP BODY LAYOUT */
.app-body{display:flex;min-height:100vh;}
/* The tiled ground covers the whole page menu and content alike from one
origin, so switching between the assistant and a workspace page never moves
the grid or changes what the menu sits on. */
.app-body{display:flex;min-height:100vh;
background-image:linear-gradient(var(--g100) 1px, transparent 1px), linear-gradient(90deg, var(--g100) 1px, transparent 1px);
background-size:28px 28px;background-position:-1px -1px;background-color:var(--g50);}
/* SIDEBAR */
.sidebar{width:210px;flex-shrink:0;background:white;border-right:1.5px solid var(--g200);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow:hidden;z-index:100;transition:width 0.22s ease;}
.sidebar.collapsed{width:0;border:none;overflow:hidden;}
/* Desktop: the menu is flat on the tiled ground, as the assistant's rail is
a white boxed sidebar here made every workspace page look like another app.
A phone keeps the white sheet, because there it slides over the page. */
@media(min-width:769px){ .sidebar{ background:transparent; border-right:none; } }
.sidebar-header{display:none;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--g200);}
.sidebar-title{font-size:13px;font-weight:700;color:var(--g700);}
/* Head pinned, list scrolls, account card pinned. */
@ -89,12 +98,14 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
/* MAIN */
/* One shell for every view. Each tab used to be a plain white page while the
assistant and the workspace launcher sat on a tiled card, so opening any menu
item changed the whole face of the app. The tiled ground and the card edge
belong to the shell, so only the contents differ. */
.main-content{flex:1;min-width:0;min-height:0;padding:14px;display:flex;flex-direction:column;
item changed the whole face of the app. The card edge belongs to the shell
and the tiled ground to .app-body, so only the contents differ. */
.main-content{flex:1;min-width:0;min-height:0;padding:14px;display:flex;flex-direction:column;}
/* The page card carries the same tiled-white fill as the assistant's chat panel
(.assistant-main.card), so entering a workspace page keeps the surface. */
.main-content > .tab-content.active{flex:1 1 auto;min-height:0;background-color:white;
background-image:linear-gradient(var(--g100) 1px, transparent 1px), linear-gradient(90deg, var(--g100) 1px, transparent 1px);
background-size:28px 28px;background-position:-1px -1px;background-color:var(--g50);}
.main-content > .tab-content.active{flex:1 1 auto;min-height:0;background:white;border:1px solid var(--g200);border-radius:14px;box-shadow:var(--shadow);padding:20px;overflow:auto;}
background-size:28px 28px;background-position:-1px -1px;border:1px solid var(--g200);border-radius:14px;box-shadow:var(--shadow);padding:20px;overflow:auto;}
/* The assistant brings its own full-bleed layout, so it takes the shell's place
rather than nesting a second card inside it. */
.main-content > #assistant-tab.active{padding:0;border:none;box-shadow:none;background:transparent;overflow:visible;}

View file

@ -423,10 +423,24 @@ test('every view sits in the same shell, so opening a menu item is not a new app
const css = read('public/css/styles.css');
// Each tab used to be a plain white page while the assistant and the workspace
// launcher sat on a tiled card, so any menu click changed the whole face.
assert.match(css, /\.main-content\{[^}]*background-image:linear-gradient/,
'the tiled ground belongs to the shell');
// The ground covers the whole page, menu included, from one origin: a second
// tile on .main-content would start 210px in and seam against the menu's.
assert.match(css, /\.app-body\{[^}]*background-image:linear-gradient/,
'the tiled ground belongs to the page');
assert.doesNotMatch(css, /\.main-content\{[^}]*background-image/, 'and only to the page');
// Flat menu on desktop, in both views; the phone sheet stays white.
assert.match(css, /@media\(min-width:769px\)\{ \.sidebar\{ background:transparent; border-right:none; \} \}/);
assert.match(css, /\.main-content > \.tab-content\.active\{[^}]*border:1px solid var\(--g200\);border-radius:14px/,
'and every tab gets the same card edge');
// ...and the same tiled-white surface as the assistant's chat panel, so a
// workspace page does not turn plain white on entry.
const tile = 'linear-gradient(var(--g100) 1px, transparent 1px), linear-gradient(90deg, var(--g100) 1px, transparent 1px)';
const card = css.slice(css.indexOf('.main-content > .tab-content.active{'), css.indexOf('}', css.indexOf('.main-content > .tab-content.active{')));
assert.ok(card.includes('background-color:white') && card.includes(tile) && card.includes('background-size:28px 28px'), 'page card is tiled white');
const panel = read('public/css/assistant.css');
// The top-level rule, not the phone override that also names .assistant-main.card.
const chat = (panel.match(/^\.assistant-main\.card \{[^}]*\}/m) || [''])[0];
assert.ok(chat.includes('background-color:white') && chat.includes(tile) && chat.includes('background-size:28px 28px'), 'the same fill as the chat panel');
// The assistant brings a full-bleed layout, so it replaces the shell card
// rather than nesting a second one inside it.
assert.match(css, /\.main-content > #assistant-tab\.active\{padding:0;border:none/);
@ -535,14 +549,15 @@ test('the menu head reads toggle, mark, search — and account tabs are not list
}
});
test('desktop: the assistant rail is the app sidebar, flush and white', () => {
test('desktop: the assistant rail is the app sidebar, flush and flat', () => {
const css = read('public/css/assistant.css');
const styles = read('public/css/styles.css');
// Inside the content area's 14px padding the rail sat on the tiled ground, its
// name card 6px lower and further in, and the page 28px taller than the window.
const desk = css.slice(css.lastIndexOf('@media (min-width:961px) {'));
assert.match(desk, /body\.assistant-workspace \.main-content \{ padding:0; \}/);
assert.match(desk, /body\.assistant-workspace \.assistant-history \{ padding:0; gap:0; background:white; border-right:1\.5px solid var\(--g200\); \}/);
assert.match(desk, /body\.assistant-workspace \.assistant-history \{ padding:0; gap:0; \}/,
'flush, and flat on the tiled ground like the workspace sidebar');
assert.match(desk, /body\.assistant-workspace \.assistant-side \{ margin-block:14px; height:auto; \}/, 'the panels carry the 14px');
// Same insets as the sidebar: .menu-head, .assistant-mode-switch, .account-card.
const rail = styles.slice(styles.lastIndexOf('@media (min-width:961px) {'));