Problem
The typography contract test is intended to catch UI elements that render text without any matching CSS rule. Today it only scans source files for literal hyperscript attributes shaped like:
using:
openMenu() consumers supply their component-specific classes through different properties:
openMenu({
menuClass: 'qtab-close-confirm',
rows: [
{ kind: 'item', extraClass: 'qtab-close-confirm-go', ... },
{ kind: 'item', extraClass: 'qtab-close-confirm-cancel', ... },
],
});
openMenu() later appends these values to the real DOM class attribute, but the contract scanner never sees them because menuClass and extraClass do not match its regex.
Impact
This is a regression-detection gap, not a currently broken UI.
Known menu classes such as the query-tab close confirmation and Dashboard-tree delete confirmation already have CSS. Also, openMenu() always applies generic .file-menu, .fm-item, and .fm-section styling, so a missing consumer-specific rule would not produce a completely unstyled browser-default dialog.
However, the test would fail to catch missing presentation that communicates the menu's specific purpose, for example:
- confirmation-menu width or layout;
- destructive-action colour and emphasis;
- muted Cancel styling;
- component-specific spacing or state treatment.
A future menu could therefore remain functional but ship visually inconsistent or misleading while the typography contract still passes.
Suggested fix
Add a focused assertion for literal classes supplied through openMenu options.
Scan src/ui/**/*.ts for literal values assigned to menuClass and extraClass, split whitespace-separated class groups, and assert that each class appears in src/styles.css.
A simple extraction pattern is sufficient for the current API:
/\b(?:menuClass|extraClass):\s*'([^']+)'/g
Keeping this as a separate openMenu extension classes have CSS rules test is clearer than silently broadening the existing generic class: scanner. It should scan all UI source files rather than extending the existing curated file list, so new menu consumers are covered automatically.
Adding src/ui/menu.ts itself to the current list is not enough: that file only contains template literals which interpolate class values supplied by callers.
Acceptance criteria
- Literal
menuClass values in UI source files are checked against the stylesheet.
- Literal
extraClass values in UI source files are checked against the stylesheet.
- Multiple classes in one option value are handled.
- A fixture or focused test proves that removing one such CSS rule fails the contract.
- Existing generic
.file-menu / .fm-item classes remain covered by the current scanner.
Further unseen classes (added by #537)
The Dashboard tile head's ⋯ menu introduced five more class groups the scanner
cannot see, all supplied through openMenu / openConfirmMenu options rather
than an h() class: literal:
dash-tile-actions (menuClass — the menu's own width, and .fm-reason
wrapping)
dash-tile-menu-danger (extraClass on the destructive row)
dash-tile-confirm, dash-tile-confirm-go, dash-tile-confirm-cancel
(the removal confirmation, via ui/confirm-menu.ts)
They are styled deliberately in src/styles.css, with a comment saying the gate
will not remind anyone — which is the gap this issue closes. Note the class names
now arrive at openMenu through openConfirmMenu's own parameters for three
consumers (dash-tree-confirm*, qtab-close-confirm*, dash-tile-confirm*), so
a scanner looking only at direct openMenu({ menuClass: '…' }) call sites would
miss those; the literals live at the openConfirmMenu call sites instead.
Problem
The typography contract test is intended to catch UI elements that render text without any matching CSS rule. Today it only scans source files for literal hyperscript attributes shaped like:
using:
/class:\s*'([^']+)'/gopenMenu()consumers supply their component-specific classes through different properties:openMenu()later appends these values to the real DOMclassattribute, but the contract scanner never sees them becausemenuClassandextraClassdo not match its regex.Impact
This is a regression-detection gap, not a currently broken UI.
Known menu classes such as the query-tab close confirmation and Dashboard-tree delete confirmation already have CSS. Also,
openMenu()always applies generic.file-menu,.fm-item, and.fm-sectionstyling, so a missing consumer-specific rule would not produce a completely unstyled browser-default dialog.However, the test would fail to catch missing presentation that communicates the menu's specific purpose, for example:
A future menu could therefore remain functional but ship visually inconsistent or misleading while the typography contract still passes.
Suggested fix
Add a focused assertion for literal classes supplied through
openMenuoptions.Scan
src/ui/**/*.tsfor literal values assigned tomenuClassandextraClass, split whitespace-separated class groups, and assert that each class appears insrc/styles.css.A simple extraction pattern is sufficient for the current API:
/\b(?:menuClass|extraClass):\s*'([^']+)'/gKeeping this as a separate
openMenu extension classes have CSS rulestest is clearer than silently broadening the existing genericclass:scanner. It should scan all UI source files rather than extending the existing curated file list, so new menu consumers are covered automatically.Adding
src/ui/menu.tsitself to the current list is not enough: that file only contains template literals which interpolate class values supplied by callers.Acceptance criteria
menuClassvalues in UI source files are checked against the stylesheet.extraClassvalues in UI source files are checked against the stylesheet..file-menu/.fm-itemclasses remain covered by the current scanner.Further unseen classes (added by #537)
The Dashboard tile head's
⋯menu introduced five more class groups the scannercannot see, all supplied through
openMenu/openConfirmMenuoptions ratherthan an
h()class:literal:dash-tile-actions(menuClass— the menu's own width, and.fm-reasonwrapping)
dash-tile-menu-danger(extraClasson the destructive row)dash-tile-confirm,dash-tile-confirm-go,dash-tile-confirm-cancel(the removal confirmation, via
ui/confirm-menu.ts)They are styled deliberately in
src/styles.css, with a comment saying the gatewill not remind anyone — which is the gap this issue closes. Note the class names
now arrive at
openMenuthroughopenConfirmMenu's own parameters for threeconsumers (
dash-tree-confirm*,qtab-close-confirm*,dash-tile-confirm*), soa scanner looking only at direct
openMenu({ menuClass: '…' })call sites wouldmiss those; the literals live at the
openConfirmMenucall sites instead.