oficina / docs Public

Spec revision 2: the menu is a disclosure, because export works without scripts

The Sheets session asked which of <details> or a JS-driven role="menubar"
we had picked, so the two apps could share one dialect. Answering it
showed the menubar version was a regression rather than a preference.

The export links are plain <a download> and work with scripts disabled;
TestShowRendersContentWithoutScript exists because this screen's whole
premise is that it works without JavaScript. A JS-only menu would have
moved the only route to a person's own document behind a control that
never opens. <details> opens with no script, and name="docs-menubar"
gives the exclusive group for free.

Declaring role="menubar" also promises the full menu pattern, and
anything short of all of it is worse for a screen reader user than the
honest disclosure it replaced. JavaScript now only adds arrow keys,
Escape and the roving tabindex.

Nested <details> in a name= group collapses its parent — rastrillo's
rst-menus trap, which Sheets hit first. Rather than work around it,
there are no submenus: Download, Text, Font, Align and the rest flatten
into sibling items. Fewer levels was the better menu anyway; the
submenus were Google Docs' shape copied rather than chosen.

Font and Size leave the Format menu entirely. They are the two controls
whose current value has to be visible without opening anything, which a
menu item cannot do and the toolbar's select already does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XrHJFx6wn4VvTzc3drkTp4
Paul Campbell pushed by paul@keymail.dev da8e01d87d1cb14a09b72152cc9fac8e2360264d parent cd5e885
1 file changed, +50 −15
  • docs/superpowers/specs/2026-09-02-word-processor-basics-design.md +50 −15
diff --git a/docs/superpowers/specs/2026-09-02-word-processor-basics-design.md b/docs/superpowers/specs/2026-09-02-word-processor-basics-design.md
index 0be0367..69ebd5e 100644
--- a/docs/superpowers/specs/2026-09-02-word-processor-basics-design.md
+++ b/docs/superpowers/specs/2026-09-02-word-processor-basics-design.md
@@ -183,9 +183,11 @@ listed because a menu that hides what the app will obviously grow is a
menu that gets redesigned; one that greys it is a menu that gets filled
in. Nothing is listed that has no plan behind it.
+Submenus are flattened rather than nested, for the reason §3.4 gives.
+
**File** — New document · Open (goes to the list) · Rename · Make a
-copy · *divider* · Download ▸ (PDF, Markdown) · Print · *divider* ·
-Page setup · *divider* · Sign out
+copy · *divider* · Download as PDF · Download as Markdown · Print ·
+*divider* · Page setup · *divider* · Sign out
**Edit** — Undo · Redo · *divider* · Cut · Copy · Paste (the three are
present and route to the browser's own clipboard behaviour, with their
@@ -197,13 +199,20 @@ and replace · *divider* · Select all
*divider* · Full screen
**Insert** — Link · Image (*disabled*, Plan 7, blocked on the sealing
-key) · Table (*disabled*, Plan 7) · *divider* · Page break (§8.4) ·
+key) · Table (*disabled*, Plan 7) · *divider* · Page break (§9.3) ·
Horizontal rule · *divider* · Special characters (*disabled*)
-**Format** — Text ▸ (Bold, Italic, Code) · Paragraph styles ▸
-(Normal, Heading 1–3) · Font ▸ · Size ▸ · Align ▸ · *divider* ·
-Bullets and numbering ▸ · Indent / Outdent · *divider* · Clear
-formatting
+**Format** — Bold · Italic · Code · *divider* · Normal text ·
+Heading 1 · Heading 2 · Heading 3 · *divider* · Align left · Align
+centre · Align right · *divider* · Bulleted list · Numbered list ·
+Indent · Outdent · *divider* · Clear formatting
+
+Font and Size are deliberately **not** in the Format menu. They are the
+two controls whose current value has to be *visible* — a person needs to
+see that they are in 12pt Sans without opening anything — and a menu
+item cannot show that the way the toolbar's select does. Duplicating
+them as eleven point sizes and three faces would also make Format the
+longest menu in the app to say what the bar beside it already says.
**Tools** — Word count · *divider* · Keyboard shortcuts · *divider* ·
Spelling (*disabled* — the browser's own spellcheck is on; this item
@@ -238,14 +247,40 @@ it has to be *principled*, or the two drift:
The app's rule is WCAG 2.2 AA with no exemptions, so this is a
requirement list, not a wish list.
-- `role="menubar"` with `role="menuitem"` / `menuitemcheckbox` /
- `menuitemradio` children; submenus `role="menu"` with
- `aria-haspopup="true"` and `aria-expanded`.
-- **Roving tabindex**, reusing `toolbar-roving.mjs` rather than a second
- implementation: the whole bar is one tab stop; Left/Right move
- between menus; Down or Enter opens; Up/Down move within; Escape
- closes and returns focus to the menu title; Tab from an open menu
- closes it and leaves the bar.
+**The menu is `<details>`/`<summary>`, and stays a disclosure.** This
+reverses what this section said in its first draft, which was
+`role="menubar"` with `menuitem` children and a JS-driven roving
+tabindex. The Sheets session — building the same chrome tonight — asked
+which of the two we had picked, and checking the answer showed the
+menubar version was a regression:
+
+- **The export links work today with scripts disabled.**
+ `<a href="/d/{sqid}/export.pdf" download>` needs no JavaScript, and
+ the whole read view is built on the guarantee that this screen works
+ without it (`TestShowRendersContentWithoutScript`). Moving export
+ into a JS-only menu breaks that silently — the menu simply would not
+ open, and the only route to a person's own document would be gone.
+ `<details>` opens with no script at all.
+- **An honest disclosure beats a half-built menu.** `<summary>` is a
+ real disclosure control that browsers and assistive technology
+ already handle. Declaring `role="menubar"` promises the full menu
+ pattern — typeahead, submenu semantics, the lot — and anything short
+ of all of it is worse for a screen reader user than the disclosure it
+ replaced.
+- **`name="docs-menubar"` gives the exclusive group for free**: opening
+ one menu closes the others, with no JavaScript and no state to keep.
+- **No nested `<details>`, anywhere.** rastrillo's `rst-menus` has a
+ known trap where a nested `<details>` in a `name=` group collapses
+ its parent. The fix is not to work around it but to have no submenus:
+ §3.2's "Download ▸", "Text ▸", "Font ▸" and the rest flatten into
+ sibling items. Fewer levels is the better menu regardless; the
+ submenus were Google Docs' shape copied rather than chosen.
+
+JavaScript then *adds*, and never enables: Left/Right between summaries,
+Up/Down within an open menu, Escape closes and returns focus to its
+summary, Tab leaves the bar, and a **roving tabindex** across the
+summaries — reusing `toolbar-roving.mjs` rather than a second
+implementation — so the bar is one tab stop.
- Every item shows its shortcut, and the shortcut works whether or not
the menu was ever opened.
- 24×24 minimum targets. The existing `[docs-tool]` is 28px and clears