The modern layout reaches the rest of the app. It styled the dashboard and the overlays and stopped there, so health, config and the inbox stayed entirely classic underneath it — a rounded, blurred context menu attached to rows that had never heard of it. Those three views, the quickstart card and the header are now covered, and the layer has tests for the first time.
Modern is still early beta; this widens what it covers rather than declaring it finished.
Also here: the config view stops ignoring your theme, and about two thirds of enhanced-features.css turns out to have been styling markup that no longer exists.
New
- new Health, config, inbox and the quickstart card follow the modern layout — previously the layer covered the dashboard grid, its presets and the overlays, and the rest of the app was untouched. Health had four modern rules, all on the row overflow menu; config had none at all, which made the screen where you choose the layout the one screen that never changed when you chose it. Now covered: the health view's tiles, filters, feed rows, row actions, monitor stat cards and empty states; config's section nav, status tiles, panels, buttons, fields, choice groups, sub-tabs and CRUD rows; the inbox's tiles, toolbar, feed rows, selection bar and empty states; and the quickstart card, which takes the overlay treatment since it floats over the dashboard rather than sitting in the page. The inbox mirrors the health view's section rather than inventing a second dialect — the two feeds are siblings and share a card grammar (
health-view.css,config-view.css,dashboard-inbox.css,dashboard-quickstart.css). - new The modern design tokens have one definition —
--layout-radius-*,--layout-shadow-*,--layout-surfaceand friends were duplicated verbatim at the top of bothlayout-modern.cssandoverlays-modern.css, andhealth-view.cssread them through hand-written fallbacks because nothing guaranteed they were defined by the time it loaded. They now live inlayout-modern-tokens.css, loaded ahead of every view stylesheet, so a view that ships modern rules can read them directly. No visual change: the values are identical and the block stays scoped tobody[data-layout-version="modern"], so classic inherits nothing from it. - new The modern layer has tests — the view-level files got theirs as each was covered, and
layout-modern.cssandoverlays-modern.css, which carry the bulk of the layer, are now covered too: page tabs, header links, bookmark row radii, the two custom properties the eight presets share, the search overlay and modals, plus a guard that a missing layout attribute renders exactly as classic. Every preset is checked for sideways overflow and overlapping rows. The assertions compare computed styles between the two layouts on the same element, since both render identical markup and only the cascade differs — and they disable transitions first, because abox-shadowstill interpolating out ofnonecomputes as fully transparent and would pass a "has an inset" check while nothing was drawn (layout-modern-*.spec.js).
Fixes
- fix The inbox tab is grouped with the views, not with the page numbers — it sits inside the page-tab strip in the DOM but opens a view, the same kind of destination as health and config. Modern gave that strip a single container pill, which bundled the inbox in with the page tabs and said the opposite of what it is: the header read as "pages · 1 · inbox" in one capsule, with health and config floating outside it. The strip now drops its own surface when a view tab is present and each group gets one instead — the run of page tabs shares a pill however many there are, and the inbox becomes a standalone pill beside the icons it belongs with. Classic never showed this, because its strip has no surface for anything to look grouped in (
layout-modern.css). - fix The modern header controls line up — the row read as three different things side by side.
.header-actionsnever setalign-items, so children of different heights — the tab strip is a padded container, the rest are single controls — aligned to the top and the strip sat 4px higher than its neighbours. The strip used its own radius and an opaque background rather than the pill shape every other header control uses. And classic pulls the health and config icons back by1remto close up a gap that is too wide between bare glyphs, which is harmless there but made the filled pills overlap by 8px in modern. All three are fixed; the regression test asserts geometry rather than styling, since the per-element tests passed throughout (layout-modern.css). - fix The config view follows your theme —
config-view.csswas written against--card-bg,--border-color,--accent-soft,--hover-bg,--text-muted,--button-bgand--accent-color. None of them was ever defined, in CSS or at runtime, so all ninety-odd uses fell through to a hardcoded grey or#4a90d9blue and the view rendered identically whatever theme was active — a green theme still got grey nav highlights and a blue tile stripe. The names are now aliased onto the variables the themes actually publish, scoped to the config layout, rather than rewriting ninety call sites; each use keeps its fallback, so a value missing here still lands on the old literal instead of on nothing. Someone had already hit this and patched.help-signature-linkby hand with a comment naming the cause (config-view.css). - fix Escape closes the config confirm dialog instead of the config view — pressing it on a delete confirmation closed config and left the dialog stranded on the dashboard underneath.
isModalOpen()enumerates the overlays that count as layered over a view, but this dialog is injected straight into the document rather than reusing#app-modal, so it was never in the list; config's Escape handler saw nothing above the view, claimed the key withstopImmediatePropagation()and closed itself — which also stopped the dialog's own handler from ever running (dashboard-ui-helpers.js). - fix Severity and unread markers follow the rounded corner — classic draws them as a 3px left border, which a rounded row clips into a wedge. In modern the health view's severity bar and the inbox's unread bar are redrawn as an inset shadow that follows the corner.
box-shadowdoes not cascade per layer, so every hover and selected state restates the bar — without that, hovering an unread row erased the one marker saying it was unread. The config tile's accent stripe had the same problem and is inset the same way (health-view.css,dashboard-inbox.css,config-view.css). - fix Two thirds of
enhanced-features.csswas styling markup that no longer exists — 607 lines to 157. The quick-add modal moved to the unified:newbookmark modal in an earlier refactor, and the settings sliders, confirmation dialogs, keyboard hints and drag hints went with their features; none ofquick-add-*,control-slider,slider-input,confirm-dialog-*,keyboard-hint,drag-hintorform-groupappears in any template or script. What stays is what still renders: the empty states, the lazy-load icon placeholder, and the barekbdrule — which needed care, becausedashboard-enhancements.cssdeclareskbdtoo and this file loads after it, so this copy is the one that actually wins. Deleting it would have silently restyled every keyboard hint in the app. - fix Three tests that had drifted from the app — the absolute-icon health test pointed at a host that resolves to nothing, so it raced the row's own "icon failed to load" fallback and failed roughly two runs in three. Three pages-and-tags tests reached for
DashboardConfigthroughconfig.constructor, which has held the lazy loader rather than the class since the config module started loading on demand. And the layout-preset test expected the grid to update while config was still open, whichrenderDashboarddeliberately declines to do so that re-rendering cannot strip the config view's own layout class. The two font-size tests read their baseline before the class they had just set had landed, which under parallel load made a timing failure look like a behaviour one.