Skip to content

Dashboard mobile version: header takes too much space #248

Description

@BorisTyshkevich
Image

Problem

The Dashboard header consumes too much vertical space on narrow screens.

The current sticky top area contains two wrapping rows:

  1. the Dashboard header;
  2. the Dashboard toolbar.

On mobile, the header can wrap the following items across several lines:

  • back link and SQL Browser text;
  • Dashboard title;
  • favorite count;
  • skipped-tile note;
  • hostname;
  • updated timestamp;
  • theme button;
  • Refresh button and text.

The toolbar also remains visible with:

  • the four-way layout switcher;
  • all Dashboard filters;
  • wrapping filter fields.

This leaves too little room for Dashboard content.

The issue is visible at typical phone widths where the sticky header and toolbar occupy a large portion of the viewport before the first panel begins.


Goal

At the application’s mobile breakpoint, render a compact Dashboard header and a single horizontally scrolling filter row.

The mobile Dashboard should show:

[back icon] [truncated Dashboard title]      [theme icon] [refresh icon]
[filter] [filter] [filter] [filter] ...

The Dashboard content must begin immediately after those rows.


Mobile breakpoint

Use the application’s canonical mobile breakpoint:

max-width: 768px

Keep this aligned with the existing mobile workbench breakpoint.

Do not introduce another Dashboard-only mobile breakpoint for the same behavior.

Existing narrower Dashboard rules may remain only where they serve a distinct purpose; otherwise merge them into the 768 px rules.


Mobile header

At max-width: 768px, the Dashboard header must remain one line.

Visible controls

Keep:

  • back navigation as an icon-only button/link;
  • Dashboard title;
  • theme toggle;
  • Refresh button as an icon-only button.

Required visual order:

back
title
flexible spacer
theme
refresh

Hidden content

Hide on mobile:

  • SQL Browser text next to the back icon;
  • favorite-count chip;
  • hostname/source chip;
  • updated timestamp;
  • Refresh text;
  • layout selector and its Layout label.

The skipped-tile note may remain hidden with the other header metadata. Hidden/empty tiles remain represented by their normal Dashboard behavior; mobile does not need a persistent count in the header.

Dashboard title

The title must consume the remaining horizontal space without forcing a wrap:

.dash-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

Long Library names must truncate with an ellipsis.

Header sizing

Recommended mobile style:

@media (max-width: 768px) {
  .dash-header {
    flex-wrap: nowrap;
    gap: 8px;
    padding: 8px 10px;
  }
}

The exact pixel values may follow the existing compact mobile vocabulary, but the header must remain one line at representative widths down to 360 px.


Back navigation

On desktop, keep the current icon plus SQL Browser label.

On mobile:

  • hide only the visible text;
  • keep the link target unchanged;
  • retain an accessible name such as:
aria-label="Back to SQL Browser"
  • retain the existing tooltip/title.

Do not replace the back link with browser-history navigation.


Hostname

Do not display the ClickHouse hostname/source chip on mobile.

Desktop behavior remains unchanged.

The mobile rule must hide the entire source chip, including:

  • status dot;
  • hostname text;
  • source tooltip trigger.

The connection remains implicit in the Dashboard context and does not justify mobile header space.


Refresh button

On mobile, render Refresh as an icon-only button.

Requirements:

  • hide the visible Refresh text;
  • keep the refresh icon;
  • preserve the existing click behavior;
  • preserve disabled state during refresh;
  • preserve the existing tooltip;
  • add or retain an accessible name:
aria-label="Refresh dashboard"

The icon-only button should use the same compact dimensions as the theme button.

Desktop retains icon plus text.

Do not change refresh scheduling, cancellation, concurrency, or query behavior.


Favorite count and updated timestamp

Hide both on mobile:

N favorites
Updated HH:MM

Desktop behavior remains unchanged.

These are secondary status details and must not force the header to wrap.

Do not delete their runtime updates. They remain available when the viewport widens.


Layout selector

The Dashboard layout switcher is not needed on mobile.

Hide:

  • Layout label;
  • Full width;
  • Report;
  • 2 columns;
  • 3 columns.

Mobile visual layout

The Dashboard must visually use one normal full-width column at mobile widths, regardless of the saved desktop preference.

This includes overriding Report-mode presentation.

Required behavior:

@media (max-width: 768px) {
  .dash-layout-wrap {
    display: none;
  }

  .dash-grid,
  .dash-grid.is-wide,
  .dash-grid.is-report {
    grid-template-columns: 1fr;
    max-width: none;
    width: 100%;
    margin: 0;
  }

  .dash-grid.is-report .dash-tile {
    min-height: 300px;
  }
}

The exact normal tile minimum height should match the ordinary Dashboard tile rule.

Persisted preference

Do not modify:

state.dashLayout
state.dashCols
asb:dashLayout
asb:dashCols

Mobile is only a responsive visual override.

When the viewport widens again, the saved desktop layout must return automatically.

Changing orientation must not write new preferences.

Changing layout on desktop must still persist exactly as before.


Filter row

Dashboard filters must render in one non-wrapping horizontal row on mobile.

Current wrapping behavior causes the toolbar to grow vertically as more filters are added.

Use the same interaction pattern as the workbench variable strip:

@media (max-width: 768px) {
  .dash-toolbar {
    flex-wrap: nowrap;
    padding: 6px 10px;
  }

  .dash-filter-host {
    width: 100%;
    min-width: 0;
    overflow: hidden;
  }

  .dash-filters {
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }

  .dash-filters::-webkit-scrollbar {
    display: none;
  }

  .dash-filters > .var-field {
    flex-shrink: 0;
  }
}

Requirements:

  • fields remain at their normal readable width;
  • fields do not shrink to fit;
  • fields never wrap to another row;
  • users can swipe horizontally;
  • the next partially visible field may serve as the scroll cue;
  • vertical Dashboard scrolling must remain natural;
  • horizontal swiping over the filter strip must not move the complete page sideways.

Filter controls and popovers

All existing filter control kinds must continue to work in the horizontal strip:

  • plain text and recent-value controls;
  • relative-time controls;
  • Enum controls;
  • curated Filter-role option controls;
  • optional activation controls;
  • validation and type-conflict styling.

Dropdowns and footers already use fixed positioning to escape scrolling containers.

Verify that:

  • combobox lists are not clipped by the filter strip;
  • dropdown positioning remains anchored to the input;
  • the footer aligns with the dropdown;
  • selecting an item does not unexpectedly scroll the Dashboard;
  • keyboard interaction remains available;
  • invalid/conflict tooltips remain available.

Do not add another mobile-specific filter renderer.


Empty toolbar

When the Dashboard has no filters, hiding the mobile layout selector must not leave an empty sticky toolbar row.

The renderer already knows whether filter controls exist.

Add an explicit class or attribute:

const toolbar = h('div', {
  class: 'dash-toolbar' + (controls.length ? ' has-filters' : ''),
}, layoutWrap, filterHost);

Then:

@media (max-width: 768px) {
  .dash-toolbar:not(.has-filters) {
    display: none;
  }
}

Equivalent naming is acceptable.

Requirements:

  • no empty padding;
  • no empty border;
  • no blank sticky row;
  • desktop layout selector remains visible even when there are no filters.

Sticky behavior

Keep the compact mobile header and filter row sticky.

The top area may contain:

  • one header row;
  • one filter row when filters exist.

It must never contain:

  • a wrapped header;
  • a layout row;
  • several wrapped filter rows;
  • an empty toolbar.

The first Dashboard panel must remain reachable without excessive initial scrolling.

Do not make the complete Dashboard header non-sticky in this issue.


Desktop behavior

At widths above 768 px, preserve the current Dashboard behavior:

  • full back-link text;
  • Dashboard title;
  • favorite count;
  • skipped count;
  • hostname chip;
  • updated timestamp;
  • theme toggle;
  • Refresh icon and label;
  • layout switcher;
  • wrapping Dashboard filter bar;
  • persisted layout application.

This issue is a responsive presentation change only.


Suggested class hooks

Add explicit hooks rather than relying on fragile :nth-child selectors.

Suggested classes:

.dash-back-label
.dash-refresh-label
.dash-header-meta
.dash-toolbar.has-filters

Existing classes may be reused where already precise:

.dash-fav
.dash-src
.dash-updated
.dash-layout-wrap

Example markup changes:

h('a', {
  class: 'dash-back',
  href: app.basePath || '/sql',
  title: 'Back to SQL Browser',
  'aria-label': 'Back to SQL Browser',
}, Icon.arrow(), h('span', { class: 'dash-back-label' }, 'SQL Browser'));

const refreshBtn = h('button', {
  class: 'dash-btn dash-refresh',
  title: 'Re-run all tiles',
  'aria-label': 'Refresh dashboard',
}, Icon.refresh(), h('span', { class: 'dash-refresh-label' }, 'Refresh'));

Files

Expected modifications:

src/ui/dashboard.js
src/styles.css

Expected tests:

tests/unit/dashboard.test.js
tests/e2e/dashboard*.spec.js

Add or update a focused Dashboard fixture if required.

No schema change.

No saved-query migration.

No new runtime dependency.


Tests

Header at phone width

At a representative viewport such as:

390 x 844

verify:

  • .dash-header remains one row;
  • back icon is visible;
  • SQL Browser text is hidden;
  • Dashboard title is visible and truncated when needed;
  • favorite-count chip is hidden;
  • hostname chip is hidden;
  • updated timestamp is hidden;
  • theme button is visible;
  • Refresh icon is visible;
  • Refresh text is hidden;
  • Refresh has an accessible name;
  • no header item overflows the viewport.

Very narrow phone

At a width near:

360 px

verify:

  • title shrinks before action buttons;
  • theme and Refresh remain reachable;
  • header does not wrap;
  • no horizontal page overflow.

Layout override

Set each persisted desktop layout:

Full width
Report
2 columns
3 columns

At mobile width, verify:

  • layout selector is hidden;
  • Dashboard grid has one column;
  • Report maximum width is removed;
  • Report tall-tile minimum is removed;
  • tiles fill the available content width;
  • persisted preference values are unchanged.

Widen the viewport and verify the saved desktop layout returns.

Filter row

With several filter controls:

  • the toolbar remains one row;
  • filters do not wrap;
  • every .var-field retains its normal width;
  • the filter strip has horizontal overflow;
  • horizontal swipe/scroll reaches the final filter;
  • Dashboard content remains vertically scrollable;
  • no horizontal page overflow is introduced.

Filter variants

Cover:

  • plain text;
  • relative time;
  • Enum;
  • curated Filter-role control;
  • optional filter;
  • type-conflict state;
  • invalid state.

Verify dropdowns remain visible outside the scroll strip.

No filters

With no Dashboard parameters:

  • layout selector is hidden on mobile;
  • toolbar is absent;
  • no empty sticky row remains;
  • grid begins immediately below the compact header.

On desktop, the layout selector remains visible.

Refresh state

During Refresh:

  • icon-only button becomes disabled;
  • loading state is still discernible;
  • no text appears and changes header width;
  • completion restores the enabled state;
  • queries run exactly once under existing scheduling.

Landscape

At a mobile-landscape or narrow-tablet width near the breakpoint:

  • behavior changes consistently at 768 px;
  • no intermediate wrapped state appears;
  • widening beyond the breakpoint restores desktop controls.

Accessibility

Verify:

  • back link accessible name;
  • Refresh button accessible name;
  • theme button accessible name;
  • hidden labels are not announced twice;
  • source/favorite/updated hidden elements are not focusable;
  • filter fields preserve their labels and keyboard behavior.

Non-goals

Do not include:

  • changes to Dashboard query execution;
  • changes to Refresh scheduling or concurrency;
  • changes to persisted layout schema;
  • deletion of desktop layout options;
  • mobile-specific saved layout preferences;
  • collapsing filters into a modal or drawer;
  • a new filter renderer;
  • hiding the Dashboard title;
  • making the Dashboard header non-sticky;
  • changes to panel content rendering;
  • changes to KPI mobile card behavior;
  • changes to workbench mobile navigation.

Acceptance criteria

  • Mobile Dashboard header remains one line at widths down to 360 px.
  • Back navigation is icon-only on mobile.
  • Back navigation retains an accessible name.
  • Dashboard title truncates instead of wrapping.
  • Favorite-count chip is hidden on mobile.
  • Hostname/source chip is hidden on mobile.
  • Updated timestamp is hidden on mobile.
  • Theme button remains visible.
  • Refresh is icon-only on mobile.
  • Refresh retains tooltip, accessible name, and disabled state.
  • Layout label and switcher are hidden on mobile.
  • Mobile grid is always one normal full-width column.
  • Report-mode width and tall-tile presentation do not apply on mobile.
  • Saved desktop layout preferences are not changed by mobile rendering.
  • Saved desktop layout returns when the viewport widens.
  • Dashboard filters render in one non-wrapping row.
  • Filter fields do not shrink.
  • Filter row scrolls horizontally by touch and pointer.
  • Filter comboboxes and dropdown footers are not clipped.
  • A Dashboard without filters has no empty mobile toolbar row.
  • Header and optional filter row remain sticky.
  • The page has no horizontal viewport overflow.
  • Desktop Dashboard behavior remains unchanged.
  • Unit tests cover structural classes and conditional toolbar state.
  • Browser tests cover mobile, narrow, and resize behavior.
  • npm test passes.
  • npm run build succeeds.
  • No new runtime dependency is added.

Definition of done

On mobile, the Dashboard uses one compact sticky header row containing only back navigation, the truncated Dashboard title, theme, and Refresh icons.

The layout selector, favorite count, hostname, updated timestamp, and Refresh label are hidden. Dashboard panels use one full-width column without changing the saved desktop preference. Filters occupy one horizontally scrollable row, and Dashboards without filters have no empty toolbar.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions