Skip to content

feat(cloud): show the app as a rounded card from 1024 px with a 56 px rail on every tablet and desktop width - #1261

Merged
ValentinKolb merged 6 commits into
mainfrom
feat/shell-v2-card
Oct 16, 2026
Merged

ValentinKolb merged 6 commits into
mainfrom
feat/shell-v2-card

Conversation

@ValentinKolb

Copy link
Copy Markdown
Collaborator

Outcome

Signed-in pages show the app as one rounded card on the app canvas from 1024 px, not only from 1536 px. The card holds the app's navigation, main area and detail, and the rail sits beside it on the canvas. The card's spacing grows from 4 px at 1024 px to 8 px at 1440 px and its radius from 10 px to 14 px, in whole pixels (iPad landscape 1194 px: 6 px and 12 px, as in the dense tablet mockup). From 1440 px it is the card of today. From 768 to 1023 px the app stays flush, and phones are unchanged.

The rail is 56 px wide at every width from 768 px, with 44 px controls and 20 px icons. The avatar is 32 px in a 44 px target.

In the installed iPad app, the card and the rail stay below the status bar. The card keeps its spacing at the bottom, and the workspace surfaces, or the page's scroll end, stay above the home indicator. The flush range still fills the insets (#1067).

Part of #1255 (parity rows R1, R2, C1, C2, C3; C4 is covered by tests with emulated insets). The checklist issue stays open for the other slices.

Parity rows (light, mockup next to build)

Screenshots (v3): https://artifacts.qdt-dev-1.k2b.dev/cloud/shell-v2-card/v/3/ (01 mockup, 02 Mail build, 03 Mail main before, 04 page without a workspace (Profile) build, 05 the same page before; widths 1024, 1194x834, 834x1194, 694x834, 390x844, 1440x900, 1920x1080)

Row Width Mockup Build
R1 rail 56 px, 44 px controls 1194x834, 834x1194, 1440, 1920 .rail 56, .rb 44 56 / 44, icons 20 px
R2 phone shell below 768 px 390x844, 694x834 694: rail (see open question) header, no rail (unchanged)
C1 card from 1024 px 1024, 1194x834, 1440, 1920 1194: 6 px right/bottom, radius 12 4/10, 6/12, 8/14, 8/14
C2 flush 768-1023 px 834x1194 flush, radius 0 flush, radius 0
C3 page without a workspace 1024 to 1920, 834 - same card rule as today from 1536 px: head and tabs on the app background, the page keeps its own content surface (see open question)

Open questions for the maintainer (also in #1255):

  • R2: the dense mockup shows the 56 px rail in Split View at 694 px, while the approved phone direction keeps the tab bar below 48 rem. This PR keeps 694 px as a phone, as the task asks.
  • C3: a page without a workspace (Profile, Admin, Capabilities) follows today's card mode from 1536 px: its head and tabs sit on the app background and the page keeps its own content surface. From 1024 to 1535 px this replaces the opaque flush surface, so on a tinted app background the page head and tabs now sit on the tint (see 04 vs 05 at 1194). The alternative is one opaque card around such pages at every card width; that would put the page's own content card inside another card, so this PR does not do it without a decision.

The build images are the development stack (main) with this branch's full compiled stylesheet served in place of global.css, compiled from the root styles.css like the real build, so every app utility is present. The two markup details (20 px rail icons, 32 px avatar) are applied to the page in the picture. The browser tests use the real markup.

Changed happy paths

  • 1024 to 1535 px, signed in: the app was flush to the window edges; it is now a card with 4 to 8 px spacing and rounded corners. Everything inside the workspace keeps its geometry.
  • Pages without a workspace (profile, Capabilities, Weather) at 1024 to 1535 px: content now sits on the canvas with the card spacing instead of an opaque flush surface, as from 1536 px before.
  • Rail at every width from 768 px: 48 -> 56 px wide, controls 32 -> 44 px, icons 16 -> 20 px, avatar 24 -> 32 px. Same controls, same order, same tooltips and badges (the badge sits on the icon's corner).
  • Fractional widths under browser zoom (for example 1023.5 px) now get exactly one shell mode; before, widths between 767 and 768 px and between 1535 and 1536 px matched no mode.
  • Unchanged: phones, anonymous pages (card with 8 px from 768 px), focus mode and pop-out windows, flat workspaces keep the card radius (fix(ui): keep the rounded app card on wide screens for flat workspaces too #1135), h-dvh, the shell's islands (perf(cloud): render the app shell with at most four islands #1033).

Root cause

Mostly not a defect: the shell switched the card on at 1536 px (packages/cloud/src/styles/global.css, flush block 768-1535 px), and the rail was sized for a mouse.

Bug fixed along the way (review finding): the shell modes were written as max-width: 1023px / min-width: 1024px (and 767/768 for phones), so a fractional width matched neither and the shell fell back to its base rules (rail 8 px to the right, base spacing). Each shell query now states only min-width and its negation not all and (min-width: …). Error class "Breakpoint gap", recorded in Lessons learned.

How we prevent regressions

  • packages/cloud/src/ssr/compact-shell.browser.test.ts (Chromium and WebKit) pins the card at 1024, 1194x834, 1440 and 1920 (spacing and radius per width), flush at 834x1194 and 1023, the phone at 390 and 694, a page without a workspace, the 56 px rail with 44 px controls and 20 px icons, notices, and identical unrounded boxes served, after the controller attaches and after a reload, with no horizontal overflow. 34 of its and app-canvas's assertions fail on main's stylesheet.
  • packages/cloud/src/styles/app-canvas.browser.test.ts emulates the installed app's safe areas for the card (1194x834, 1440x900) and the flush range (834x1194).
  • packages/cloud/src/styles/shell-modes.test.ts pins the shell media queries to min-width and its negation, so a max-width query cannot bring the breakpoint gap back. Playwright snaps viewports and iframes to whole pixels in Chromium and WebKit (checked), so no browser test can reach the gap.
  • The card values are whole pixels: fractional spacing made the workspace controller move a column by 0.375 px on hydration and shortened the home-indicator inset (border widths snap to whole pixels). The tests compare unrounded boxes, so this cannot come back silently.

Docs

  • Fibel frontend/layout-and-navigation.md: the shell table, the app bar's 44 px targets, the notice strip range, and the installed app from 1024 px.
  • frontend/application-shells.md, build/define-app.md, UI context layout/workspace.md, and the flat JSDoc in AppWorkspace.tsx: the card from 1024 px.
  • contributing/lessons-learned.md: new error class "Breakpoint gap" and its lesson.
  • cloud-dev skill: unchanged. It names no shell breakpoints; this is layout detail owned by Fibel.

Verification

  • After rebasing on main (incl. feat(ui): workspace columns that collapse by width, with a fixed back step and resizable widths #1249 workspace levels): bun run test --filter packages/cloud (all 3 suites pass, integration skipped: no server code changed); Chromium compact-shell, app-canvas, app-badges, page-trail, layout-viewport 109/109; WebKit (Playwright 1.63 server) the same five files 109/109 before the rebase, and compact-shell, app-canvas, ui detail-float, ui workspace-levels 133 pass / 0 fail after it.
  • Earlier rounds: core account-pages, me/app, OutgoingMail, notebooks book-links (Chromium), core account-pages (WebKit).
  • bun run --cwd packages/cloud typecheck, packages/ui typecheck, docs check (141 pages), git diff --check; Codex (gpt-6.1-sol) review against main: no actionable findings.
  • Visual check on the development stack at 1024, 1194x834, 834x1194, 694x834, 390x844, 1440x900, 1920x1080 (light).
  • Not verified: a real iPad and the installed app's real safe areas (emulated in tests); browsers without CSS round() (Chrome < 125) would lose the card spacing and radius.

Operations

No configuration change. Operators of signed-in laptops and iPads in landscape see the card layout after the update.

…6 px rail

The approved shell v2 and the dense tablet concept show the app as one
rounded card on the canvas from laptop and iPad landscape widths, not only
from 1536 px. The card's spacing and radius grow from 4 to 8 px and from
10 to 14 px up to 1440 px, so the wide card stays as it is. From 768 to
1023 px the app stays flush. The rail is 56 px wide at every width from
768 px with 44 px targets, as touch needs them. In the installed app the
card stays below the status bar and keeps its content above the home
indicator.
…led-app insets

The shell tests now cover the iPad sizes of the dense tablet mockup and
the 1023/1024 px boundary. The card's spacing and radius are whole pixels,
so hydration and the controller cannot move a column by a fraction of a
pixel and the home-indicator inset stays exact. The docs describe the card
from 1024 px.
Tooltip triggers set their own font size, which beats a utility on the
control, so search, help and the app grid kept 15 px icons. The icon
carries its 20 px size, and the rail test checks every control's icon.
The flush range ended at max-width 1023px and the card started at
min-width 1024px, so a fractional width between them, such as a zoomed
window, matched neither and fell back to the base rail and canvas rules.
The phone range had the same gap at 767/768 px. Each shell query now
states only min-width and negates it for the range below, so neighbouring
modes are exact opposites. A test pins the queries so a max-width query
cannot bring the gap back.
The flush and card modes left fractional widths in neither mode. The
lesson names the error class and its guard, so other style sheets that
pair max-width with min-width queries can be checked against it.
@ValentinKolb
ValentinKolb added this pull request to the merge queue Oct 16, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 16, 2026
# Conflicts:
#	docs-site/docs/en/contributing/lessons-learned.md
@ValentinKolb
ValentinKolb added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 52e392e Oct 16, 2026
23 checks passed
@ValentinKolb
ValentinKolb deleted the feat/shell-v2-card branch October 16, 2026 17:44
@github-actions github-actions Bot mentioned this pull request Oct 16, 2026
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