Dark theme for the admin portal #7

Closed
opened 2026-10-12 10:40:33 +02:00 by pepa · 1 comment
Owner

Add a dark theme to the admin portal. By default the theme follows the operating system setting; a switch in the top right corner of the admin navbar lets the user force light or dark.

Current state

  • The UI is the Sneat free template in assets/vendor/ (Bootstrap 5 before 5.3, so there is no data-bs-theme support). It ships a light theme only.
  • core.css contains about 960 hard-coded colour literals and theme-default.css about 180. Only a few values go through CSS variables (--bs-body-bg, --bs-body-color). There are just two .dark-style rules in core.css (form validation), so the template has a naming convention for dark mode but no implementation.
  • src/components/header.rs already sets class="light-style layout-menu-fixed ..." on <html> — the hook the template expects (light-style / dark-style).
  • Header is rendered outside <Router> and applies to every page. The public booking page and the admin portal are one single-page app, and the admin "Create booking" dialog embeds the public form (<Public/>).
  • Charts on /admin/bookings are rendered on the server by charts-rs with THEME_ANT (light background) and sent as an image (chart() in src/backend/reservation.rs). charts-rs also has THEME_DARK.
  • src/pages/all_reservations.rs uses navbar-light bg-light for the year/month bar.
  • leptos-use (already a dependency, with the ssr and actix features enabled) provides use_color_mode / use_preferred_dark and cookie storage.
  • style/main.scss is compiled to /pkg/rezervator.css, but that file is not linked anywhere in header.rs, so it has no effect. Custom CSS that is actually loaded lives in assets/ (assets/css/demo.css, assets/vendor/css/control.css, assets/data/banner.css).

Proposed solution

  1. Dark stylesheet — a new app-owned file (e.g. assets/css/dark.css), linked in header.rs after theme-default.css. It overrides only the components the admin portal uses: body, cards, navbar, side menu, footer, tables, forms, modals, dropdowns, alerts, outline buttons, links, borders. Vendor files stay untouched.
  2. Three modes: auto / light / dark.
    • auto (default) follows prefers-color-scheme in CSS, so it works on the first visit without JavaScript and without a flash.
    • light / dark are set with a class on <html> (light-style / dark-style).
    • Write the dark colours once as CSS custom properties and apply them both under html.dark-style and under @media (prefers-color-scheme: dark) for the auto mode, to avoid duplicating every rule.
  3. Persistence in a cookie, not localStorage: the server must render the same <html> class as the client, otherwise hydration differs and the page flashes. use_color_mode with cookie storage is a candidate.
  4. Switch in the navbar of src/components/admin_portal.rs, next to the settings and user icons (Boxicons bx-sun / bx-moon), cycling auto → light → dark. Labels go through trl() with Czech translations.
  5. Charts — pass the active theme to month_chart / year_chart and use THEME_DARK in dark mode. In auto mode the server does not know the system preference, so the client has to send the resolved value.
  6. Replace navbar-light bg-light in all_reservations.rs with classes that follow the theme.

Open questions

  • Scope: admin portal only (/admin*), or also the login page and the public booking page? The public page has a customer-defined banner and text, so it is proposed to keep it light for now. Because Header is global, limiting the theme to /admin* needs the class to depend on the current route.
  • Should the embedded public form inside the admin "Create booking" dialog be dark? It will be, unless it is explicitly excluded.
  • Simple light/dark toggle, or the three-state auto/light/dark switch proposed above?

Out of scope

  • Dark theme for e-mails.
  • Editing or replacing the vendor template.

Done when

  • With no stored preference the admin portal follows the system setting, including on the first server-rendered load (no flash of the wrong theme).
  • The switch changes the theme immediately and the choice survives a reload and a new session.
  • All admin pages and dialogs are readable in both themes: overview, settings, mail settings, bookings (including charts), customers, appearance.
  • Full reload of every admin page shows no hydration error in the browser console in either theme.
  • Both cargo check commands and cargo test --features ssr pass.
Add a dark theme to the admin portal. By default the theme follows the operating system setting; a switch in the top right corner of the admin navbar lets the user force light or dark. ## Current state - The UI is the Sneat free template in `assets/vendor/` (Bootstrap 5 before 5.3, so there is no `data-bs-theme` support). It ships a light theme only. - `core.css` contains about 960 hard-coded colour literals and `theme-default.css` about 180. Only a few values go through CSS variables (`--bs-body-bg`, `--bs-body-color`). There are just two `.dark-style` rules in `core.css` (form validation), so the template has a naming convention for dark mode but no implementation. - `src/components/header.rs` already sets `class="light-style layout-menu-fixed ..."` on `<html>` — the hook the template expects (`light-style` / `dark-style`). - `Header` is rendered outside `<Router>` and applies to every page. The public booking page and the admin portal are one single-page app, and the admin "Create booking" dialog embeds the public form (`<Public/>`). - Charts on `/admin/bookings` are rendered on the server by `charts-rs` with `THEME_ANT` (light background) and sent as an image (`chart()` in `src/backend/reservation.rs`). `charts-rs` also has `THEME_DARK`. - `src/pages/all_reservations.rs` uses `navbar-light bg-light` for the year/month bar. - `leptos-use` (already a dependency, with the `ssr` and `actix` features enabled) provides `use_color_mode` / `use_preferred_dark` and cookie storage. - `style/main.scss` is compiled to `/pkg/rezervator.css`, but that file is not linked anywhere in `header.rs`, so it has no effect. Custom CSS that is actually loaded lives in `assets/` (`assets/css/demo.css`, `assets/vendor/css/control.css`, `assets/data/banner.css`). ## Proposed solution 1. **Dark stylesheet** — a new app-owned file (e.g. `assets/css/dark.css`), linked in `header.rs` after `theme-default.css`. It overrides only the components the admin portal uses: body, cards, navbar, side menu, footer, tables, forms, modals, dropdowns, alerts, outline buttons, links, borders. Vendor files stay untouched. 2. **Three modes: auto / light / dark.** - `auto` (default) follows `prefers-color-scheme` in CSS, so it works on the first visit without JavaScript and without a flash. - `light` / `dark` are set with a class on `<html>` (`light-style` / `dark-style`). - Write the dark colours once as CSS custom properties and apply them both under `html.dark-style` and under `@media (prefers-color-scheme: dark)` for the auto mode, to avoid duplicating every rule. 3. **Persistence in a cookie**, not `localStorage`: the server must render the same `<html>` class as the client, otherwise hydration differs and the page flashes. `use_color_mode` with cookie storage is a candidate. 4. **Switch** in the navbar of `src/components/admin_portal.rs`, next to the settings and user icons (Boxicons `bx-sun` / `bx-moon`), cycling auto → light → dark. Labels go through `trl()` with Czech translations. 5. **Charts** — pass the active theme to `month_chart` / `year_chart` and use `THEME_DARK` in dark mode. In auto mode the server does not know the system preference, so the client has to send the resolved value. 6. Replace `navbar-light bg-light` in `all_reservations.rs` with classes that follow the theme. ## Open questions - **Scope**: admin portal only (`/admin*`), or also the login page and the public booking page? The public page has a customer-defined banner and text, so it is proposed to keep it light for now. Because `Header` is global, limiting the theme to `/admin*` needs the class to depend on the current route. - Should the embedded public form inside the admin "Create booking" dialog be dark? It will be, unless it is explicitly excluded. - Simple light/dark toggle, or the three-state auto/light/dark switch proposed above? ## Out of scope - Dark theme for e-mails. - Editing or replacing the vendor template. ## Done when - With no stored preference the admin portal follows the system setting, including on the first server-rendered load (no flash of the wrong theme). - The switch changes the theme immediately and the choice survives a reload and a new session. - All admin pages and dialogs are readable in both themes: overview, settings, mail settings, bookings (including charts), customers, appearance. - Full reload of every admin page shows no hydration error in the browser console in either theme. - Both `cargo check` commands and `cargo test --features ssr` pass.
Author
Owner

Decisions on the open questions:

  1. Scope: admin portal only. The login page and the public booking page stay as they are.
  2. The public form embedded in the admin "Create booking" dialog may be dark.
  3. Three-state switch: auto / light / dark.
Decisions on the open questions: 1. **Scope**: admin portal only. The login page and the public booking page stay as they are. 2. The public form embedded in the admin "Create booking" dialog may be dark. 3. Three-state switch: auto / light / dark.
pepa closed this issue 2026-10-12 11:07:30 +02:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ITsolved/rezervovator#7