🤖 Sheets is the second adopter of estilo's shared chrome. The spec is estilo spec/shared-chrome @ 49c7b01, §12.1 row 3; the brief is polish-audit/R2-BRIEF.md. Docs main 9f11ffc was the worked example. Where Sheets didn't fit the Docs-shaped API, I fixed estilo rather than working around it here: oficina/estilo feat/r2-sheets-corrections, discussion #11. That discussion lists every correction and the reason for it.
The spec names this branch polish/r2-shared-chrome. I used feat/r2-shared-chrome because that is the name the task gave.
Merge order: estilo first
go.mod pins estilo at the unmerged corrections branch head, b7988ff (v0.0.0-20260923235632-b7988ff2ac24).- Order: estilo #11 squash-merges. This branch then re-pins to estilo main (
GOPRIVATE=amadan.net go get amadan.net/oficina/estilo.git@main && go mod tidy), pushes, and gets green CI. Only then does Sheets merge. amadan.net/oficina/home/connect is bumped to Home main 01166fc. That brings in the in-app Sign out chaining to {Issuer}/signout (a 307), the signed-in /signin shortcut, and Credentials.Directory.- Deploy order is Home's as recorded on home #74:
oficina-id before oficina, then the apps. A new Sheets must not reach an id.* host that doesn't yet admit consumer origins.
Spec citations
The spec cites Sheets bf59232; origin/main here was 300c4d1. I re-checked each citation on 300c4d1. The http.Error sites the spec lists are at the lines it gives (handlers_sheets.go:95,138,264,276, handlers_table.go:165,187,210, handlers_view.go:53, theme.go:82, render.go:169,178), and so are sheets.css:32-36, app.go:155-159,187, sheet.html:45-58,113-121 and page_test.go:56.
Found already done on main: the vendor context menu the spec lists as "goes (B7)" was removed in c55252e (contextMenu: () => false).
What changed
The bar (estilo chrome.Bar, built once per request in chrome.go) replaces the rastrillo topbar markup that layout.html wrote out.
- It is 52px signed in and out. It was 45px signed out and 51px signed in (50.9 at 1440, 52.6 at 390), because Import and Create were taller than the bar.
- The layout is
estilo-shell > estilo-bar > <main>. The grid page uses estilo-shell="fill", the estilo correction that replaces [rst-shell-topbar]:has([data-sheet-surface]) { height: 100dvh }. That also removes the 15px dead scrollbar gutter beside the grid. <meta charset> comes first. The stylesheets load in the suite's order: tokens → theme → brand → estilo base → menu → chrome → sheets.css.- The in-bar rename moved onto estilo's
ContextEdit slot, still a real form posting to /sheets/{id}/rename. Escape restores the title and leaves the field. A reader sees the title read-only. - On the list, Import and Create are the bar's actions. Create is a POST both as the primary button and as a row in the narrow Menu sheet.
- The grid wrapper gets
isolation: isolate (§5.3). The drop overlay goes to z-index 50, above the bar.
The account menu holds, in order:
- "Signed in as" and the address; the trigger shows initials.
- Connect your Oficina account, only with Home configured, and hidden for Home sessions (it used to show for them).
- Appearance: Light, Dark and System, with the current choice checked (
aria-pressed on every option). - Help → Keyboard shortcuts…, a native
<dialog> opened by the invoker command, so it works with scripting off. - The one Sign out.
Appearance moved from /mode, the sheets_mode cookie and "auto" to estilo's /appearance and the estilo_appearance cookie. It is still inside the app-wide CSRF check and outside requireSession. An old sheets_mode cookie is now ignored, so everyone starts on System once.
The switcher shows All apps only (Paul's §13 answer 2), and only when Home is configured with HOME_DIRECTORY_ORIGIN. Standalone has none.
The menu bar is rebuilt on estilo-menubar and estilo-menu:
- Square titles with a hover background, 9px panels, and a shortcut column.
- One
estilo-menus exclusivity group shared with the bar, as Sheets chose with rastrillo's rst-menus. - No submenus. Format drops Number, Fill colour and Text colour, which the toolbar beside it already carries (the spec left trim-or-flyout to this adoption). Alignment becomes three plain rows. Insert's Function becomes rows under a heading.
- Help leaves the menu bar for the account menu.
- Items that need script stay hidden until commands.js runs. With scripting off, Edit's separators are now hidden too.
- Labels: "Undo my last edit" is now "Undo".
- Platform shortcut labels come from
menu.Shortcut, server-side (⌘Z / Ctrl+Z). They are used in the menus, the toolbar tips ("Undo (⌘Z)"), the shortcuts dialog, and clipboard.js's "Press ⌘C instead" status line. Pages send Vary: User-Agent.
Errors (§3).
- The router has
NotFound, and a MethodNotAllowed that answers a GET of a POST-only path with the 404 page. Any other method gets a plain 405 with Allow set. recoverToErrorPage sits inside the session group, and opts.ErrorPage is set in main.go.a.notFound splits by Accept: text/html gets "This sheet isn’t available"; the grid's /api fetches and the native apps get the plain "Not found." they always had.- The HTML
http.Error sites the spec lists are converted, and TestEveryPlainTextErrorIsClassified accounts for the rest. - 500 pages carry a reference. The log line holds the reference and the route pattern, not the path: a
/l/ path is a capability.
Callouts (§4). rst-callout="warn" becomes rst-tone="warning" (the new link and the contrast-floor notice). The share, comments and history notices get positive, negative (with role="alert"), warning or info tones. The duplicate "Link created. Copy it now" notice is gone; the one warning callout is kept. Sign-in errors are negative, and an unparseable address goes in the field's own slot.
Titles and names (§10).
- Titles now read:
Sheets, Commission Q2 · Sheets, Table view · … · Sheets, Version history · …, Comments · …, Share · …, Import a spreadsheet · Sheets, Sign in · Sheets, Check your email · Sheets, Signed out · Sheets, Page not found · Sheets. - Sub-page h1s are the page's name ("Version history", not "History — X"); the sheet's title is the bar's context.
- One name per destination: "All sheets" for the list (the job page said "Back to your sheets"), and "Back to sheet" for the grid (it was "Back to the sheet" and "Open the grid").
- "Oficina Sheets" is gone everywhere. The email subject is "Sign in to Sheets".
Sign-in and sign-out (§9).
- The sign-in pages are estilo's sign-in, sent and signed-out templates, with the §9.2 copy.
- Sign out lands on
/signed-out in both modes. With Home configured it goes 307 → id.* → /signed-out. Standalone, rastrillo's redirect to /signin is rewritten to /signed-out. - Already signed in: standalone
GET /signin 303s to a safe return_to or the list, never to a sign-in page. With Home, connect's /signin does it. /signin/local never skips, because a Home session goes there on purpose to link an account.
Sheets' / (Home #74, decision 2): with Home configured, a signed-out visitor is sent 303 → /signin; signed in, / goes to /sheets. Standalone keeps the landing page, now titled "Sheets" with the bar's Sign in. Tests cover signed-out, Home-signed-in and locally-signed-in visitors with Home, and standalone.
Other items.
- The Share page lists lose the browser's 40px indent and get
--rst-cols: minmax(0,1fr) auto, so Remove and Turn off sit beside the row instead of under it. - The table-view cell link is a square highlight that fills the cell (it was a 6px pill). Borderless toolbar buttons are square (§11, Paul's answer 4).
- Automatic numbers show at most 15 significant digits: 92.05, not 92.05000000000001.
format.generalNumber was the one place it happened, for both the grid and the table. Only the display rounds; values stay IEEE doubles. - A picker's tooltip no longer covers its own open panel.
Expected casualties, rewritten rather than deleted:
shellmenu_test.go is now TestTheAccountIsReachableOnNarrowScreens.menugroup_test.go gains TestTheMenusShareOneGroup.csstokens_test.go gains TestEveryEstiloPropertyWeUseExists.page_test.go's TestIndexIsNotACatchAll now asserts the HTML 404 page for a browser and plain text otherwise.theme_test.go moves onto /appearance.signin_test.go and home_navigation_test.go are updated; the latter now asserts the 307 chain.dropdownanchor_test.go still guards the toolbar pickers, which stay rastrillo dropdowns.tools/drive/*.mjs drive the estilo selectors, /appearance and estilo_appearance.
Left out on purpose, and disagreements with the spec
- There is no Open source item. Sheets has no credits page, so Help holds only the shortcuts (Docs made the same call).
- Standalone
return_to (§9.4, local mode) is not built. rastrillo's auth.RequireSession drops the path, and a standalone link lands on /sheets. With Home configured (production), homeRequire and connect already carry return_to (TestHomeConnectionPreservesSheetsAndCurrentAdmission). This is a follow-up if dev mode needs it. - The toolbar's pickers (number format, fill, ink) stay rastrillo dropdowns. They are already 9px and anchor-positioned, and §7.1 lists them under
estilo-menu. Moving them is follow-up work. - Two extra error rows: 400 "That didn’t work" and 413 "That’s too long". §3.2 has neither.
- The sent page says "that address". rastrillo's
Begin redirects to ?sent=1 with no address, so the page can't echo it (as in Docs). The magic-link email body is rastrillo's, so §9.2's email body copy isn't applied; only the subject changed. - Middleware refusals stay plain text: the
homeRequire non-GET 403 and 503, rejectDisabledHome, and the homeLocalComplete refusals. Each is classified with its reason (R3). - With Home configured, the account-link pages use connect's templates with Sheets' copy passed through
connect.Page's translate func ("your Oficina account", verb "Connect"). connect.English itself still says "Home"; that's Home's to change.
Verification
Local gate: make test passes with the pinned rastrillo CLI v0.23.0 on PATH (vet, gofmt, build, rastrillo migration check, go test -count=1 ./..., and -race on store and live).
New tests, each seen red first. polish-audit/scripts/sheets-r2-mutations.py applies one mutation per test and requires that test to fail. All 28 went red; the log is polish-audit/sheets-r2-logs/mutations.log. The new tests:
TestHTMLErrorsRenderTheErrorPage ({unknown route, GET of a POST-only path, missing sheet, bad id, missing link, 413, panic} × {in, out}, plus plain answers for fetches and the native apps, and the 405)TestErrorPageBackLinkIsSameOriginOnly, TestEveryPlainTextErrorIsClassifiedTestPageTitlesFollowTheSuitePattern, TestEverySignedInPageHasOneSignOutInTheSameCorner, TestTheSignedOutCornerTestTheSwitcherIsAllAppsOnly, TestTheBarRenamesTheSheet, TestTheBarsRenameFormRenames, TestTheGridFillsTheViewportTestEstiloChromeAssetsAreLinkedInOrder, TestShortcutLabelsFollowThePlatform, TestUnbuiltMenuItemsAreHidden, TestTemplatesUseOnlyCalloutTonesTestConnectYourOficinaAccountIsHiddenForHomeSessions, TestTheRootStartsSignInWhenHomeIsConfigured, TestSigninWhenAlreadySignedIn, TestSignOutLandsOnTheSignedOutPageTestGeneralShowsAtMostFifteenSignificantDigits, TestAppearance…/TestPostAppearance…- the three guards named above
One test was green at first under its mutation: that mutation didn't compile, and the harness recorded it as not red. I fixed the mutation and re-proved the test red. TestTheShareScreenIgnoresATokenInItsQuery, an existing test, went red during the adoption: the Appearance form's return_to reflected the share page's query, token included. Share now returns to its bare path.
Browser: polish-audit/r2-chrome-check.mjs with the new polish-audit/r2-sheets-check.config.mjs. It passes 989/989:
- The bar is exactly 52px with no overflow on every page, signed in and out, at 1440/840/800/390/320. That includes sign-in, sent, the error pages, signed-out, the public link page, the grid, the table, history, comments and share.
- Every bar control is reachable at its centre with no overlaps, including with the title focused. The floating Rename is reached at 390 and 320.
- Also checked: the title pattern, no default-blue or visited-purple links, exactly one Sign out, the account last,
[estilo-menu] at 9px, menubar titles and triggers at 0px, the switcher absent as configured, the account panel on top over the grid (elementFromPoint), list-row title colour equal to --rst-text, and unknown routes returning 404 text/html with the bar. - With the switcher present, 989/989 as well. Standalone has no directory, so this ran on a temporary check-only build that read
R2_CHECK_DIRECTORY into the switcher. That patch was never committed. - CLS: sign-in → first page 0.0000; list → sheet → list 0.0000; error → home 0.0000. The bar is 0/52 on every page of every hop. The 45 → 51/52px jump is gone.
- The same config against the pre-change build fails at once: 19 × "has [estilo-bar]", then no account menu (
check-before.log).
Scripts off (Chromium): Edit shows Undo alone, Format is absent, and "Keyboard shortcuts…" opens its dialog through the invoker command.
*`tools/drive/.mjs`**, run against this build:
- drive, chrome, menus, editing, batch-checks, tableformat and drop all pass.
- menu-actions, autofit, contextmenu and comments pass in Chromium and WebKit. menu-actions covers 31 actions plus Escape and outside-click.
- clipboard.mjs fails identically on the origin/main build. It is pre-existing and local to headless Chromium on this Mac (
drive-clipboard-before.log), not a regression. - canary-batch.mjs needs the deployed HTTPS canary and wasn't run.
polish-audit/sheets-audit.mjs is now R2-aware (MARKUP=r2 PREFIX=…) and was run before and after (shots/r2-sheets-{before,after}-audit-NN-*.png). Grid first-load CLS is 0.0006, the same as the audit measured; it comes from the toolbar un-hiding.
Screenshots: polish-audit/shots/r2-sheets-{before,after}-<view>-{1440,390}.png, from polish-audit/scripts/sheets-r2-views.mjs. They cover index, sign-in, both errors, sent, both 404s both ways, the public link, the list, the grid, table, history, comments, share, import and after sign-out. Also:
- every menubar menu open with hover
- the account menu at 1440 and 390
- Menu at 390 (list and grid)
- Format at 390, and the title focused at 390
- the shortcuts dialog, dark mode, the table-cell hover, and the scripts-off dialog
Audit findings the spec didn't cover (still open, R3)
- Share page: the Share and Create a link buttons sit at label height; the link-sharing help runs into the next label; the owner row offers Remove on yourself.
- Lowercase role names ("viewer", "owner") in the share selects and rows, and lowercase table-view labels ("nothing", "value").
- Table view: the sort, filter and width controls are cramped; editing one cell stacks six unstyled selects in a tall row.
- History: the uppercase "YOU" badge and full-size Put back buttons. Comments: 0-based Row and Column inputs instead of an A1 reference.
- Import: the native, unstyled file input. The inline
style="overflow-x:auto" wrappers remain. The link page loses cell formatting. An empty sheet's thumbnail is blank. - The sheet list has no row hover (measured transparent). The phone toolbar still wraps. The number-format trigger "123" has no current-format tooltip.
- Non-grid pages keep rastrillo's reserved 15px scrollbar gutter; only the grid drops it.
Review: not yet done. This needs the whole-branch review by the highest-capability model, and estilo #11's review and merge, before it merges. I've stopped here and not merged.