refactor(webui/backups): backup center sub-tabs use canonical tabs-wrapper

The embedded backup center's fragment kept its standalone-page skeleton
(.container + .sidebar restyled by a pile of overview.css overrides).
.container's fixed viewport height inflated the Backups tab pane to
~100vh, stretching the pane surface far past the content and under the
footer buttons, and the restyled strip never matched the app-detail
Config sub-tabs.

Rebuild backup-content.html on the canonical sub-tab idiom instead —
.tabs-wrapper > .tabs-list (emoji tab-buttons) + .tabs-content card,
with a .backup-actions footer below the card mirroring .config-actions.
The bespoke overview.css restyle block, the nebula special-cases, the
embed's id-stripping and BackupPage's dead page-header updater all fall
away; the export menu now opens upward from the footer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-07-04 21:08:03 +01:00
parent 9830382a4a
commit aef0c15726
6 changed files with 85 additions and 280 deletions

View File

@ -221,114 +221,10 @@
.updater-detail-empty { color: var(--text-secondary); margin: 0; } .updater-detail-empty { color: var(--text-secondary); margin: 0; }
/* ---- Backups tab: embedded backup center -------------------------------- */ /* ---- Backups tab: embedded backup center -------------------------------- */
/* The Backups tab mounts the real BackupPage, which supplies its own header, and /* The Backups tab mounts the real BackupPage. Its fragment now uses the
its left sidebar is restyled into a horizontal nested tab strip so the whole canonical .tabs-wrapper (.tabs-list strip + .tabs-content card) with a
thing reads as tabs-within-tabs. */ .backup-actions footer all styled globally (base.css / backup.css), so
#overview-view #ov-pane-backups .backup-layout { display: block; } no embed-specific restyling is needed here. */
#overview-view #ov-pane-backups .backup-layout > .sidebar {
width: auto;
max-width: none;
height: auto;
min-height: 0;
margin: 0; /* no gap — the strip joins the content card below */
padding: 0;
background: transparent;
border: none;
}
/* Match the per-app Config .tabs-list/.tab-button segmented look (full-width
bar, evenly-flexed items, 2px accent underline on the active one). */
#overview-view #ov-pane-backups .backup-layout > .sidebar #backup-sidebar-list,
#overview-view #ov-pane-backups .backup-layout > .sidebar .sidebar-section {
display: flex;
width: 100%;
background: var(--hover-bg);
border-bottom: 1px solid var(--border-color);
overflow-x: auto;
}
#overview-view #ov-pane-backups .backup-layout > .sidebar .category {
display: flex;
flex: 1 1 0;
min-width: 0;
align-items: center;
justify-content: center;
gap: 6px;
margin: 0;
padding: 12px 14px;
white-space: nowrap;
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
background: transparent;
cursor: pointer;
/* Type matches .main-tab-button so the nested strip reads like the outer
overview tab strip, not like the backup page's vertical sidebar. */
font-size: 12px;
font-weight: 500;
color: var(--text-secondary, #ccc);
}
#overview-view #ov-pane-backups .backup-layout > .sidebar .category:hover {
color: var(--accent);
background: rgba(var(--accent-rgb), 0.1);
}
#overview-view #ov-pane-backups .backup-layout > .sidebar .category.active {
color: var(--accent);
border-bottom-color: var(--accent);
background: rgba(var(--accent-rgb), 0.1);
}
#overview-view #ov-pane-backups .backup-layout > .sidebar .category .category-icon {
width: 16px;
height: 16px;
flex: 0 0 auto;
}
/* Round only the outer top corners to follow the strip's chrome (the global
.tab-button rule does this for app/Migrate sub-tabs; .category needs its own). */
#overview-view #ov-pane-backups .backup-layout > .sidebar .category:first-child { border-top-left-radius: 12px; }
#overview-view #ov-pane-backups .backup-layout > .sidebar .category:last-child { border-top-right-radius: 12px; }
/* .main is just the layout column now the visible card surface lives on the
body below (so it stops above the footer buttons), exactly like the app
Config tab where .tabs-content is the card and .config-actions sits outside. */
#overview-view #ov-pane-backups .backup-layout > .main {
width: 100%;
min-width: 0;
background: transparent;
}
/* The body is the connected .tabs-content card: card surface + rounded bottom
corners, joining the tab strip above so the strip + body read as one unit. */
#overview-view #ov-pane-backups .backup-page-body {
background: var(--card-bg);
border-radius: 0 0 12px 12px;
}
/* Embedded, the nested strip already names the section BackupPage's big
page header (icon + h1 + subtitle) is redundant chrome. Hide the title
block and flip the header below the body via flex order, so its action
buttons (Refresh + the per-section primary) become a bottom-LEFT footer
row the same place the app-detail tabs put theirs (.config-actions). */
#overview-view #ov-pane-backups .backup-page-section {
display: flex;
flex-direction: column;
}
#overview-view #ov-pane-backups .backup-page-section > .page-header {
order: 2;
border-bottom: none;
border-top: none;
background: transparent;
margin-bottom: 0;
/* Flush-left under the card, matching .config-actions (left padding 0). */
padding: 16px 22px 8px 0;
}
#overview-view #ov-pane-backups .backup-page-section > .page-header .page-header-icon-slot,
#overview-view #ov-pane-backups .backup-page-section > .page-header .page-header-title {
display: none;
}
#overview-view #ov-pane-backups .backup-page-body { order: 1; }
/* The footer row sits low on the page, so the export dropdown opens upward
(and leftward) to stay on-screen rather than clipping past the viewport. */
#overview-view #ov-pane-backups .backup-page-section > .page-header .backup-export-menu {
top: auto;
bottom: calc(100% + 6px);
right: auto;
left: 0;
}
/* ---- Migrate tab: segmented sub-tabs (per-app Config-tab design) -------- */ /* ---- Migrate tab: segmented sub-tabs (per-app Config-tab design) -------- */
/* .tabs-wrapper/.tabs-list/.tabs-content/.tab-button come from base.css; the /* .tabs-wrapper/.tabs-list/.tabs-content/.tab-button come from base.css; the

View File

@ -625,11 +625,7 @@ class OverviewManager {
this._backupLoading = true; this._backupLoading = true;
try { try {
await this._ensureBackupAssets(); await this._ensureBackupAssets();
let html = await fetch('/components/backup/core/html/backup-content.html', { cache: 'no-store' }).then((r) => r.text()); const html = await fetch('/components/backup/core/html/backup-content.html', { cache: 'no-store' }).then((r) => r.text());
// Strip ids that would collide with the apps layout (it also has #sidebar
// and #mobile-overlay). BackupPage selects its own nodes by class, so this
// is safe; it just keeps the document free of duplicate ids.
html = html.replace('id="sidebar"', '').replace('<div class="mobile-overlay" id="mobile-overlay"></div>', '');
// Lead with the shared in-content header (BackupPage supplies only its own // Lead with the shared in-content header (BackupPage supplies only its own
// per-section headers inside the layout), so Backups matches every other // per-section headers inside the layout), so Backups matches every other
// fleet tab. It persists across refreshes since revisits don't reset innerHTML. // fleet tab. It persists across refreshes since revisits don't reset innerHTML.

View File

@ -1,37 +1,22 @@
/* Backup Page — restic-engine UI */ /* Backup Page restic-engine UI.
Mounted inside the fleet Overview's Backups tab as a canonical
.backup-layout { .tabs-wrapper (strip + .tabs-content card) with a .backup-actions footer
display: flex; below the card, mirroring the app-detail Config tab's layout. */
min-height: calc(100vh - var(--topbar-height, 60px));
}
.backup-layout .main {
flex: 1;
min-width: 0;
overflow-y: auto;
}
.backup-page { .backup-page {
color: var(--text-primary); color: var(--text-primary);
width: 100%; width: 100%;
padding-bottom: 48px;
} }
/* The whole backup page is one .config-section card containing both the /* Actions footer under the tab card (Refresh + per-section primary),
.page-header and the body. Remove the card's inner padding so the bottom-left like the app-detail tabs' .config-actions row. */
.page-header sits flush at the top and its border-bottom acts as a .backup-actions {
full-width divider; the body gets its own padding. */ display: flex;
.backup-page-section { gap: 12px;
padding: 0; align-items: center;
overflow: hidden; flex-wrap: wrap;
} padding: 20px 20px 20px 0;
background: transparent;
.backup-page-section > .page-header {
margin-bottom: 0;
}
.backup-page-body {
padding: 22px;
} }
/* Configuration tab embeds /config's renderConfig, which emits its own /* Configuration tab embeds /config's renderConfig, which emits its own
@ -103,6 +88,9 @@
.backup-tabpanel { .backup-tabpanel {
display: none; display: none;
/* Content inset matching the canonical .tab-panel (5px 24px), so panels
sit at the same depth inside .tabs-content as the Config tab's. */
padding: 5px 24px;
} }
.backup-tabpanel.active { .backup-tabpanel.active {
@ -940,15 +928,21 @@
color: var(--text-primary); color: var(--text-primary);
} }
/* Export dropdown in the backup page header (Configuration tab). */ /* Export dropdown in the backup actions footer (Configuration tab). */
#backup-page-header .page-header-actions { #backup-page-header .page-header-actions {
position: relative; position: relative;
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
} }
/* The footer sits low on the page, so the menu opens upward (and leftward)
to stay on-screen rather than clipping past the viewport. */
.backup-export-menu { .backup-export-menu {
position: absolute; position: absolute;
top: calc(100% + 6px); bottom: calc(100% + 6px);
right: 0; left: 0;
z-index: 50; z-index: 50;
min-width: 210px; min-width: 210px;
padding: 6px; padding: 6px;

View File

@ -1,74 +1,26 @@
<div class="container backup-layout"> <div class="backup-layout">
<div class="mobile-overlay" id="mobile-overlay"></div> <!-- Canonical sub-tab idiom (.tabs-wrapper > .tabs-list + .tabs-content),
identical to the per-app Config tab and the Migrate tab, so the backup
<div class="sidebar" id="sidebar"> center reads as tabs-within-tabs with no bespoke chrome. -->
<div class="sidebar-section" id="backup-sidebar-list"> <div class="tabs-wrapper">
<div class="category active" data-backup-tab="dashboard"> <div class="tabs-list">
<svg class="category-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <button type="button" class="tab-button active" data-backup-tab="dashboard">
<rect x="3" y="3" width="7" height="9"></rect> <span class="tab-emoji">📊</span><span class="tab-name">Dashboard</span>
<rect x="14" y="3" width="7" height="5"></rect> </button>
<rect x="14" y="12" width="7" height="9"></rect> <button type="button" class="tab-button" data-backup-tab="backups">
<rect x="3" y="16" width="7" height="5"></rect> <span class="tab-emoji">💾</span><span class="tab-name">Backups</span>
</svg> </button>
Dashboard <button type="button" class="tab-button" data-backup-tab="locations">
</div> <span class="tab-emoji">📍</span><span class="tab-name">Locations</span>
<div class="category" data-backup-tab="backups"> </button>
<svg class="category-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <button type="button" class="tab-button" data-backup-tab="configuration">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> <span class="tab-emoji">⚙️</span><span class="tab-name">Configuration</span>
<polyline points="17 8 12 3 7 8"></polyline> </button>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
Backups
</div>
<div class="category" data-backup-tab="locations">
<svg class="category-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
<circle cx="12" cy="10" r="3"></circle>
</svg>
Locations
</div>
<div class="category" data-backup-tab="configuration">
<svg class="category-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
Configuration
</div>
</div> </div>
</div>
<div class="main"> <div class="tabs-content">
<div class="backup-page" id="backup-page"> <div class="backup-page" id="backup-page">
<div class="config-section backup-page-section"> <span class="backup-engine-badge" id="backup-engine-badge" hidden>restic</span>
<div class="page-header" id="backup-page-header">
<div class="page-header-icon-slot" id="backup-page-header-icon"></div>
<div class="page-header-title">
<h1 id="backup-section-title">Dashboard</h1>
<p id="backup-section-subtitle"></p>
</div>
<div class="page-header-actions">
<button class="backup-refresh-btn" id="backup-refresh-btn" title="Refresh">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"></polyline>
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
</svg>
Refresh
</button>
<button class="backup-primary-btn" id="backup-primary-action"></button>
<div class="backup-export-menu" id="backup-export-menu" role="menu" hidden>
<button type="button" class="backup-export-menu-item" data-action="export-passwords" role="menuitem">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Repository Passwords
</button>
</div>
</div>
</div>
<div class="backup-page-body">
<span class="backup-engine-badge" id="backup-engine-badge" hidden>restic</span>
<section class="backup-tabpanel active" id="backup-panel-dashboard"> <section class="backup-tabpanel active" id="backup-panel-dashboard">
<div class="backup-summary-row" id="backup-summary-row"></div> <div class="backup-summary-row" id="backup-summary-row"></div>
@ -116,7 +68,32 @@
<section class="backup-tabpanel" id="backup-panel-configuration"> <section class="backup-tabpanel" id="backup-panel-configuration">
<div id="backup-configuration-body"></div> <div id="backup-configuration-body"></div>
</section> </section>
</div> </div>
</div>
</div>
<!-- Actions footer: sits below the tab card at bottom-left, exactly where
the app-detail tabs put .config-actions. The per-section primary button
label is driven by updatePrimaryAction(). -->
<div class="backup-actions" id="backup-page-header">
<div class="page-header-actions">
<button class="backup-refresh-btn" id="backup-refresh-btn" title="Refresh">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"></polyline>
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
</svg>
Refresh
</button>
<button class="backup-primary-btn" id="backup-primary-action"></button>
<div class="backup-export-menu" id="backup-export-menu" role="menu" hidden>
<button type="button" class="backup-export-menu-item" data-action="export-passwords" role="menuitem">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Repository Passwords
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -29,7 +29,6 @@ class BackupPage {
await this.refreshAll(); await this.refreshAll();
await window.Dismissible?.load(); await window.Dismissible?.load();
this.render(); this.render();
this.updatePageHeader();
this.updatePrimaryAction(); this.updatePrimaryAction();
} }
@ -53,11 +52,11 @@ class BackupPage {
return null; return null;
} }
/* Toggle the sidebar .active class + panel visibility without going /* Toggle the tab strip's .active class + panel visibility without going
through switchTab's URL-update path (used on initial render and through switchTab's URL-update path (used on initial render and
browser back/forward). */ browser back/forward). */
applyActiveTabUi(tab) { applyActiveTabUi(tab) {
document.querySelectorAll('.backup-layout .sidebar .category[data-backup-tab]').forEach(b => { document.querySelectorAll('.backup-layout .tabs-list .tab-button[data-backup-tab]').forEach(b => {
b.classList.toggle('active', b.dataset.backupTab === tab); b.classList.toggle('active', b.dataset.backupTab === tab);
}); });
document.querySelectorAll('.backup-tabpanel').forEach(p => { document.querySelectorAll('.backup-tabpanel').forEach(p => {
@ -104,7 +103,7 @@ class BackupPage {
this.toggleExportMenu(false); this.toggleExportMenu(false);
} }
const tabBtn = e.target.closest('.backup-layout .sidebar .category[data-backup-tab]'); const tabBtn = e.target.closest('.backup-layout .tabs-list .tab-button[data-backup-tab]');
if (tabBtn) { if (tabBtn) {
this.switchTab(tabBtn.dataset.backupTab); this.switchTab(tabBtn.dataset.backupTab);
return; return;
@ -339,7 +338,6 @@ class BackupPage {
if (!tab || tab === this.currentTab) return; if (!tab || tab === this.currentTab) return;
this.currentTab = tab; this.currentTab = tab;
this.applyActiveTabUi(tab); this.applyActiveTabUi(tab);
this.updatePageHeader();
this.updatePrimaryAction(); this.updatePrimaryAction();
if (!opts.fromPopstate) this.pushTabToUrl(tab); if (!opts.fromPopstate) this.pushTabToUrl(tab);
} }
@ -357,58 +355,6 @@ class BackupPage {
} }
} }
updatePageHeader() {
const titleEl = document.getElementById('backup-section-title');
const subEl = document.getElementById('backup-section-subtitle');
const iconEl = document.getElementById('backup-page-header-icon');
if (titleEl) titleEl.textContent = this.titleFor(this.currentTab);
if (subEl) subEl.textContent = this.subtitleFor(this.currentTab);
if (iconEl) iconEl.innerHTML = this.iconFor(this.currentTab);
}
titleFor(tab) {
return {
dashboard: 'Dashboard',
backups: 'Backups',
locations: 'Locations',
configuration: 'Configuration'
}[tab] || 'Backups';
}
subtitleFor(tab) {
return {
dashboard: "Check what's protected — and when it last ran.",
backups: 'Every backup across every enabled location.',
locations: 'Where backups are stored. Add, edit, or remove destinations.',
configuration: 'Schedule, retention, and engine settings.'
}[tab] || '';
}
iconFor(tab) {
const icons = {
dashboard:
'<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' +
'<rect x="3" y="3" width="7" height="9"></rect>' +
'<rect x="14" y="3" width="7" height="5"></rect>' +
'<rect x="14" y="12" width="7" height="9"></rect>' +
'<rect x="3" y="16" width="7" height="5"></rect></svg>',
backups:
'<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' +
'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>' +
'<polyline points="17 8 12 3 7 8"></polyline>' +
'<line x1="12" y1="3" x2="12" y2="15"></line></svg>',
locations:
'<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' +
'<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>' +
'<circle cx="12" cy="10" r="3"></circle></svg>',
configuration:
'<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' +
'<circle cx="12" cy="12" r="3"></circle>' +
'<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>'
};
return icons[tab] || icons.backups;
}
updatePrimaryAction() { updatePrimaryAction() {
const btn = document.getElementById('backup-primary-action'); const btn = document.getElementById('backup-primary-action');
if (!btn) return; if (!btn) return;

View File

@ -105,19 +105,15 @@
/* App-page tab strips: anchor every tab bar to the same surface as /* App-page tab strips: anchor every tab bar to the same surface as
the sidebar so the chrome reads as one coherent navy surface. the sidebar so the chrome reads as one coherent navy surface.
Using var(--sidebar-bg) directly so any future sidebar tweak Using var(--sidebar-bg) directly so any future sidebar tweak
carries through. Covers all three tab places on the apps page: carries through. Covers both tab idioms on the apps page:
1. .tab-navigation main app tabs (Config / Services / ) and 1. .tab-navigation main app tabs (Config / Services / ) and
the Backup-location sub-tabs (Local / Remote 1 / Remote 2). the Backup-location sub-tabs (Local / Remote 1 / Remote 2).
2. .tabs-list inside .tabs-wrapper config sub-tabs that the 2. .tabs-list inside .tabs-wrapper config sub-tabs from the
app-config form renders inside the Config tab. app-config form, plus the fleet Overview's Migrate and Backups
3. The fleet Overview's Backups tab, where the embedded BackupPage sub-tabs (all share the canonical .tabs-wrapper markup). */
sidebar is restyled into a nested horizontal strip + joined
content card (overview.css) same surface, same anchor. */
[data-theme="nebula"] .tab-navigation, [data-theme="nebula"] .tab-navigation,
[data-theme="nebula"] .tabs-wrapper .tabs-list, [data-theme="nebula"] .tabs-wrapper .tabs-list,
[data-theme="nebula"] .tabs-content, [data-theme="nebula"] .tabs-content {
[data-theme="nebula"] #overview-view #ov-pane-backups .backup-layout > .sidebar #backup-sidebar-list,
[data-theme="nebula"] #overview-view #ov-pane-backups .backup-layout > .main {
background: var(--sidebar-bg); background: var(--sidebar-bg);
} }