Skip to content

Adopt the NICOS look and feel for the dashboard shell - #1245

Merged
SimonHeybrock merged 8 commits into
mainfrom
nicos-theme
Aug 20, 2026
Merged

Adopt the NICOS look and feel for the dashboard shell#1245
SimonHeybrock merged 8 commits into
mainfrom
nicos-theme

Conversation

@SimonHeybrock

@SimonHeybrock SimonHeybrock commented Aug 20, 2026

Copy link
Copy Markdown
Member
ESSlivedata dashboard with NICOS-inspired theme

The dashboard runs on the same screens as the NICOS client, so an operator's eye moves between the two all day. This makes the two look like they belong together: NICOS's dark teal chrome, and the main tab strip moved from the top edge to a left rail, where NICOS puts its own panel selector. Colours are sampled from a NICOS screenshot.

This is now the default. --theme classic keeps the previous look — same top tabs, same lighter blue header — for anyone who wants it back.

A theme covers the shell only: header colour, tab placement, tab strip palette, and the frame around tab content. Widget colours, plot colormaps and the Panel/Material design are deliberately out of reach — those are shared with the plots and carry status semantics that must not shift with the surrounding chrome.

The sidebar now starts collapsed

It holds announcements and the version label, neither of which needs to be on screen while watching plots, and with the tab rail down the left it is a second left-hand column. --no-collapsed-sidebar opens it. --collapsed-sidebar keeps working, so nothing that passes it today breaks.

Also in here: a pre-existing margin inconsistency

Independent of the theme, the Workflows and System Status tabs framed their own content on top of the frame the main area already provides, so they sat 20px from the tab edge where Manage Plots and the plot grids sit at 10px. Workflows set the margin once on its tab-level column; System Status spelled the same 10px out on every section header, table and list. Both removed — the container that decides where a tab begins is the one that should decide how far its content is inset from there. Visible with either theme.

Notes for review

Three things are load-bearing and non-obvious, and each carries a comment at its site:

  • Panel's Material design styles tabs through :host(.bk-<side>) .bk-header .bk-tab with background: transparent. Rules that do not match that selector are silently outranked and the rail renders with invisible tabs.
  • The padding around tab content cannot hang off .bk-panel: Bokeh 3.9's TabsView appends each child view's own element to the shadow root and toggles visibility on it, so no such wrapper exists (a later Bokeh reintroduces one). Hence the selection by exclusion, and box-sizing: border-box so the padding shrinks the child rather than pushing the plot grid off the right edge.
  • --collapsed-sidebar became a BooleanOptionalAction rather than being renamed to something like --expanded-sidebar: as a store_false under a positive name, argparse's generated help prints the dest's default and reads as exactly the opposite of the truth.

Test plan

Automated: dashboard suite and the Playwright browser suite pass on the new defaults. The tab-placement test is parametrized over both themes — passing only the default would not distinguish a widget that ignored the argument.

Manual, for the tester:

  • Both themes render correctly at a few window sizes (--theme nicos, --theme classic)
  • Side by side with a real NICOS client, the chrome reads as one family
  • Long tab lists scroll sensibly in the left rail
  • The sidebar still opens and works via the hamburger, and --no-collapsed-sidebar starts it open

The dashboard runs side by side with the NICOS client on the same screens,
so an operator's eye moves between the two all day. `--theme nicos` is an
experiment in making that move cheaper: NICOS's dark teal chrome, and the
main tab strip moved from the top edge to a left rail, where NICOS puts its
own panel selector. The default theme is unchanged.

A theme covers the shell only. Widget colors, plot colormaps and the
Panel/Material design stay put -- those are shared with the plots and carry
status semantics that must not shift with the surrounding chrome.

`get_header_background()` goes with it: an override hook with one
implementation and no overrider, now that the color is a theme field.
Three fixes to the left rail, all from the tab strip not owning the space
around it:

- Material's `.main-content` padding framed the rail in white, reading as a
  floating panel rather than as window chrome. The theme takes that padding
  and re-applies it inside the tab strip, around the content only.
- Which left tab contents with no margin of their own glued to the rail, so
  the strip now pads the panel it shows. Bokeh 3.9 gives that panel no
  wrapper element to select, hence the selection by exclusion.
- Inactive tabs stop 1px short of the content area, leaving the strip's teal
  showing as a hairline; the selected tab runs into it. That is NICOS's cue
  for which tab owns the panel.
NICOS puts no padding above its first tab: measured on the reference
screenshot, the rail's first tab and the content area both start on the row
where the banner ends. Ours sat 8px lower.
Material sizes the main area `calc(100vh - 84px)`: the top app bar's 64px
plus a 20px gap at the bottom of the window. The gap is white, so the rail
stopped short of the window edge and read as a panel rather than as chrome.
The content's own bottom padding, already re-applied inside the tab strip,
does that job instead.
The padding moved inside the tab strip was Material's own asymmetric
`10px 20px 20px 10px`. A plot grid fills its tab edge to edge, which makes
the heavier right and bottom edges plain to see; other tabs have it too, they
just do not reach far enough to show it.
The Workflows and System Status tabs framed their own content on top of the
frame the main area already provides, so they sat 20px from the tab edge
where Manage Plots and the plot grids sit at 10px. Workflows set the margin
once on its tab-level column; System Status spelled the same 10px out on
every section header, table and list.

Neither is the content's business: the container that decides where a tab
begins is the one that should decide how far its content is inset from there.
Not theme-specific -- the same inconsistency is visible with the default
theme, where the frame comes from Material's `.main-content` padding.
The dashboard sits next to the NICOS client all day, so matching its chrome
is the better default. `--theme classic` keeps the previous look.

Renamed the old theme from "default" to "classic": a theme called "default"
that is not the default is a name that lies. One constant, `DEFAULT_THEME`,
now says which theme that is, so the app, the CLI and the tab widget cannot
drift apart. The tab-placement test gains the classic theme as a case --
parametrized only over the default, it would pass just as well against a
widget that ignored the argument.
The drawer holds announcements and the version label, neither of which needs
to be on screen while watching plots, and with the tab rail down the left it
is a second left-hand column. `--no-collapsed-sidebar` opens it.

`--collapsed-sidebar` becomes a BooleanOptionalAction rather than the flag
being renamed: as a `store_false` under a positive name, argparse's help
prints the dest's default and reads as exactly the opposite of the truth.
Every existing invocation of it stays valid; the driving kit no longer passes
it, since the default is now what automation wanted anyway.
@SimonHeybrock
SimonHeybrock merged commit 6820c93 into main Aug 20, 2026
16 checks passed
@SimonHeybrock
SimonHeybrock deleted the nicos-theme branch August 20, 2026 13:04
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