diff --git a/changelog.d/1466-panel-chrome-consistency.md b/changelog.d/1466-panel-chrome-consistency.md new file mode 100644 index 000000000..c7f1f28a1 --- /dev/null +++ b/changelog.d/1466-panel-chrome-consistency.md @@ -0,0 +1,5 @@ +- Give the side panels one look. Every panel title now uses the command dock's + label type in a brighter colour; a panel's edge and title light up on hover, + as the dock's trays do; and in the default theme each collapse button sits + 19px from its panel's edge (plus the scrollbar's width in an open, scrolling + panel where the system always shows scrollbars) (asturksever, #1466). diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index c84d878eb..f9c1decd5 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -4819,7 +4819,7 @@ silently demoting every later lookup for the session. - If a civilian or military aircraft is already tracked when `CONTACTS` becomes operational, that source-owned track is adopted as the Context subject before nearest-contact autofocus. Context rechecks the tracker after its dependencies settle, so a newer selection wins, while an explicit clear during activation prevents fallback from silently selecting a replacement. Cockpit entry remains unavailable until that Context transaction has settled, so its camera takeover cannot clear Cesium tracking before adoption. Adoption does not recreate tracking or transfer camera ownership; it initializes the normal 250 km ring, history, proximity results, and Cockpit Previous/Next state for the original aircraft. - `SEARCH NEARBY SITES` retains the bounded OSM results and makes one user-initiated, view-biased Google Maps Places text search for “military installation.” Google results are source-stamped, deduplicated against OSM by rounded location/name, and remain mapped context rather than operational claims. If Places is unavailable or the API is not enabled for the supplied key, OSM context remains available. A view across the antimeridian is searched around its own centre, with a radius from its real width. - The Context header holds only its title and collapse control. It no longer carries a Radio shortcut or a compact Radio player: Radio is the `RADIO` section inside the panel, with its own disclosure and the single `ENABLE` power control. Cockpit keeps its own compact Radio utility. -- Context is set for reading in every theme: mode, standby and action labels are 10 px, and the panel draws no glow halo. Outside Cyber, the two modes form one segmented control, the standby guidance is plain left-aligned text rather than a dashed placeholder, and the expanded panel's section titles use `--text-secondary`. Cyber keeps its separately framed mode buttons, its red panel titles, and in layout-focus a framed, centred standby card over its radar art. +- Context is set for reading in every theme: mode, standby and action labels are 10 px, and the panel draws no glow halo. Outside Cyber, the two modes form one segmented control, the standby guidance is plain left-aligned text rather than a dashed placeholder, and the panel's section titles share the side panels' dock-label colour. Cyber keeps its separately framed mode buttons, its red panel titles, and in layout-focus a framed, centred standby card over its radar art. - The expanded desktop header omits the redundant `ON` label; the active mode button carries state. Expanded Contact results also omit the duplicate `GLOBAL CONTEXT` / `CONTEXT ONLY` status row and begin with the selected subject and its 250 km scope. Global Context does not fabricate a selected-entity model preview: the provisional hand-authored aircraft wireframe was removed because it was not geometry extracted from the selected entity's actual asset. - Dependency ownership is reversible: disabling Global Context releases only dependencies it enabled, while user-enabled layers remain on. This also removes the Military-layer suppression handoff when Global Context owned Military, allowing an already-enabled civilian Flights layer to resume its normal mixed rendering. If OpenSky is unavailable and has no last-good cache, Flights requests a capped 250 nm adsb.lol point snapshot around the current view anchor and labels that provenance explicitly; it never relabels military-feed rows as civilian data. If both inputs fail, Flights remains `UNKNOWN`. - Space Missions is replay-isolated: Rocket Launches and Satellites are the only Data Layers permitted while the mode is active. Direct UI and voice entry capture the same pre-entry snapshot; internal dependency and restoration enables do not create a user-owned Context session. Entry waits for incompatible layers to shut down, direct incompatible enables are blocked before lifecycle work, and the entry gate remains active through the complete Rocket Launches enable. A newer same-target ON request takes ownership of the pending entry without releasing its isolation snapshot, including when it arrives while the prior request is awaiting the adoption guard. A caller abort, resource cancellation, newer OFF, or layer teardown waits for exact manager settlement and restores that snapshot without resurrecting Rocket Launches. If an abort lands after only part of a restore settles, Context completes the same exact target without the stale caller signal and then replays newer explicit layer intent. Dedicated Voice Context cancellation reports a stable cancelled result plus the current Context state; generic layer visibility additionally exposes manager phase, reason, successor, and lifecycle details. Once an exact voice visibility intent commits, a newer voice turn cannot relabel it as cancelled while Context settlement completes; pre-commit aborts remain cancellable and final lifecycle mismatches remain failures. Clear Selected Layers reserves its complete captured OFF set before sequential teardown; a newer absolute request of any origin supersedes only its layer reservation and remains authoritative. A rejected layer teardown retains the truthful enabled state, rolls already-stopped siblings back to the captured pre-entry set, and aborts replay; rapid exit/re-entry serializes the full Satellite enabled-state and parameter restore before a new snapshot is taken. Contacts remains additive and restores user-enabled layers normally. diff --git a/src/contextTabKeyboard.test.mjs b/src/contextTabKeyboard.test.mjs index ca44d4c25..528341dd4 100644 --- a/src/contextTabKeyboard.test.mjs +++ b/src/contextTabKeyboard.test.mjs @@ -172,10 +172,10 @@ test('Context reads as one segmented control with legible type and no halo', () assert.ok(px(standby, 'font') >= 10, 'standby guidance must stay at least 10px'); assert.doesNotMatch(standby, /dashed/); assert.ok(px(rule('.global-context-actions .panel-layer-toggle'), 'font-size') >= 10); - // Default theme only, expanded only: Cyber keeps its red titles and the - // collapsed launcher matches its sibling launchers. - assert.match( + // The shared rail title colour (brighter than --text-secondary) keeps the + // open Context title legible; no Context-only rule dims it again. + assert.equal( rule(":root:not([data-ui-theme='cyber']) #global-context-panel:not(.collapsed) .panel-title"), - /color:\s*var\(--text-secondary\)/, + '', ); }); diff --git a/src/ui/railPanelChrome.test.mjs b/src/ui/railPanelChrome.test.mjs new file mode 100644 index 000000000..3b79bbcc5 --- /dev/null +++ b/src/ui/railPanelChrome.test.mjs @@ -0,0 +1,250 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { readStylesheet } from '../testSupport/readStylesheet.mjs'; + +const css = readStylesheet(new URL('../../style.css', import.meta.url)); +const clean = (selector) => + selector + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(/\s+/g, ' ') + .trim(); +const rules = [...css.matchAll(/([^{}]+)\{([^{}]*)\}/g)].map( + ([, selector, body]) => ({ + selector: clean(selector), + body: body.replace(/\/\*[\s\S]*?\*\//g, ''), + }), +); +const railRules = rules.filter(({ selector }) => + selector.includes('#left-panel-stack, #right-context-rail'), +); +const hoverBlock = + css.match( + /@media \(hover: hover\) and \(pointer: fine\) \{((?:[^{}]*\{[^{}]*\})*)\s*\}/g, + ) || []; + +test('a side panel lights up on hover only where a pointer can hover', () => { + const lit = railRules.filter(({ selector }) => selector.includes(':hover')); + assert.ok(lit.length > 0, 'the rail hover rule is missing'); + for (const { selector } of lit) { + assert.ok( + hoverBlock.some((block) => clean(block).includes(selector)), + `${selector} must sit inside @media (hover: hover) and (pointer: fine)`, + ); + } +}); + +test('hover matches the dock tray hover, not its pinned look', () => { + const [hover] = railRules.filter(({ selector }) => + selector.endsWith(':hover'), + ); + assert.ok(hover, 'the rail surface hover rule is missing'); + assert.match(hover.body, /border-color:\s*var\(--glass-border-hover\)/); + assert.doesNotMatch(hover.body, /box-shadow/); + assert.match(hover.body, /--rail-label-hover:\s*var\(--accent\)/); + // A low-specificity list: no ID or state selector inside it lifts the rule. + assert.match(hover.selector, /:where\(/); +}); + +test('hover recolours only the hovered panel header, never nested sections', () => { + for (const { selector } of railRules) { + assert.ok( + !/\.radio-panel-inner/.test(selector), + `Radio sits inside Context and must not light up as its own panel: ${selector}`, + ); + assert.doesNotMatch( + selector, + /:hover\s+:is\(/, + 'a descendant colour list would reach labels in the panel body', + ); + } + const colour = railRules.find( + ({ body }) => /--rail-label-hover/.test(body) && /color:/.test(body), + ); + assert.ok(colour, 'the rail title colour rule is missing'); + assert.match(colour.selector, /\.panel-header > \.panel-title/); + const radio = rules.find(({ selector }) => selector === '#radio-panel'); + assert.match(radio?.body || '', /--rail-label-hover:\s*initial/); +}); + +test('no later rule dims the open Context title below its siblings', () => { + const dimmed = rules.filter( + ({ selector, body }) => + /#global-context-panel:not\(\.collapsed\) \.panel-title$/.test( + selector, + ) && /color:/.test(body), + ); + assert.deepEqual(dimmed, []); +}); + +test('collapsed launchers on both rails keep titles inside the card', () => { + const ellipsis = railRules.find( + ({ selector, body }) => + selector.endsWith( + ':is(.panel-header > .panel-title, .pp-header-label)', + ) && /text-overflow:\s*ellipsis/.test(body), + ); + assert.ok( + ellipsis, + 'rail titles must end in an ellipsis rather than overflow', + ); + assert.match(ellipsis.body, /min-width:\s*0/); + + const translated = rules.find( + ({ selector }) => + selector.startsWith("html:not([lang='en'])") && + selector.includes('#left-panel-stack, #right-context-rail') && + selector.includes('.panel-collapsible.collapsed'), + ); + assert.ok(translated, 'translated collapsed titles must fit on both rails'); + assert.match(translated.body, /font-size:\s*8px/); + // Chinese and Japanese titles fit at full size; 8px would put them under + // the 9px CJK floor in cjk.css. + assert.match(translated.selector, /:not\(:lang\(zh\)\):not\(:lang\(ja\)\)/); + // Only the rail's own launchers: Radio, nested in Context, keeps its type. + assert.match( + translated.selector, + /#right-context-rail\) > \.panel-collapsible\.collapsed > \[class\$='-panel-inner'\] > \.panel-header > \.panel-title$/, + ); +}); + +test('a collapsed launcher drops its divider so English titles fit whole', () => { + const launcher = (tail) => + railRules.filter(({ selector }) => + selector + .split(/,(?![^(]*\))/) + .some((part) => + part + .trim() + .endsWith( + `.collapsed > [class$='-panel-inner'] > .panel-header${tail}`, + ), + ), + ); + const [header] = launcher(''); + const [divider] = launcher(' > .panel-divider'); + const [toggle] = launcher(' > .panel-collapse-btn'); + assert.match(header?.body || '', /gap:\s*8px/, 'the launcher header gap'); + assert.match(divider?.body || '', /display:\s*none/, 'the launcher divider'); + assert.match( + toggle?.body || '', + /margin-left:\s*auto/, + 'the launcher toggle', + ); + const button = rules.find( + ({ selector }) => selector === '.panel-collapse-btn', + ); + assert.match( + button?.body || '', + /flex-shrink:\s*0/, + 'a long title must not squeeze the toggle', + ); +}); + +test('the default theme geometry change leaves cyber gutters alone', () => { + for (const surface of ['.data-panel-inner', '.global-context-panel-inner']) { + const base = rules.find( + ({ selector, body }) => selector === surface && /padding:/.test(body), + ); + assert.match(base?.body || '', /padding:\s*14px 16px 12px/, surface); + } + const display = rules.find( + ({ selector }) => + selector === '#pp-toggles > .pp-header-row .pp-collapse-btn', + ); + assert.match(display?.body || '', /font-size:\s*12px/); +}); + +test('rail titles and dock labels share one set of label tokens', () => { + // command-dock-trays.css sets the dock's label type; earlier dock rules + // for the same selector are overridden by it. + const dock = rules.find( + ({ selector, body }) => + selector === + '#command-dock .panel-title, #command-dock .location-toolbar-label' && + /--dock-label-/.test(body), + ); + const rail = railRules.find(({ body }) => + /--dock-label-font-size/.test(body), + ); + assert.match(dock?.body || '', /font-size:\s*var\(--dock-label-font-size\)/); + assert.match( + dock?.body || '', + /letter-spacing:\s*var\(--dock-label-letter-spacing\)/, + ); + // The dock's rem type, capped at its 16px-root size: a collapsed launcher + // is a fixed 176px, so a larger browser font must not cut titles short. + assert.match( + rail?.body || '', + /font-size:\s*min\(var\(--dock-label-font-size\),\s*9\.28px\)/, + ); + assert.match( + rail?.body || '', + /letter-spacing:\s*min\(var\(--dock-label-letter-spacing\),\s*2\.08px\)/, + ); +}); + +test('default theme overrides never reach the cyber theme', () => { + const notCyber = ":root:not([data-ui-theme='cyber'])"; + const padding = rules.find( + ({ selector, body }) => + selector.endsWith( + ':is(.data-panel-inner, .global-context-panel-inner)', + ) && /padding-left:\s*14px/.test(body), + ); + assert.ok( + padding?.selector.startsWith(notCyber), + 'the 14px gutters must skip cyber', + ); + const display = rules.find( + ({ selector, body }) => + selector.endsWith('#pp-toggles > .pp-header-row .pp-collapse-btn') && + /font-size:\s*13px/.test(body), + ); + assert.ok( + display?.selector.startsWith(notCyber), + "Display's 22px toggle must skip cyber", + ); + // Cyber draws its title marker at left: -8px; a clip on the title would cut it. + for (const { selector, body } of rules) { + if (!/\.panel-title/.test(selector) || !/overflow:\s*hidden/.test(body)) + continue; + if (!/#left-panel-stack|#right-context-rail/.test(selector)) continue; + assert.ok( + selector.startsWith(notCyber), + `${selector} clips the cyber title marker`, + ); + } +}); + +test('launcher compaction and header colour stay on the rail panels themselves', () => { + // The divider, gap and toggle rules; the translated type rule is checked + // above. + const compact = railRules.filter( + ({ selector }) => + /\.collapsed > \[class\$='-panel-inner'\] > \.panel-header/.test( + selector, + ) && !selector.startsWith('html:'), + ); + assert.equal(compact.length, 3); + for (const { selector } of compact) { + // Each launcher is a direct child of its rail: Radio, nested inside + // Context, is not a launcher. The header must not itself be required to + // be a rail child. + assert.match( + selector, + /#right-context-rail\) > \.panel-collapsible\.collapsed > \[class\$='-panel-inner'\] > \.panel-header/, + ); + assert.match( + selector, + /#right-context-rail\) > #pp-toggles\.collapsed > \.pp-header-row/, + ); + } + const colour = railRules.find(({ body }) => + /--rail-label-hover, /.test(body), + ); + assert.match( + colour.selector, + /:is\(\.panel-header, \.pp-header-row\) > \.panel-collapse-btn/, + ); + assert.doesNotMatch(colour.selector, /, \.panel-collapse-btn\)/); +}); diff --git a/src/ui/styles/command-dock-trays.css b/src/ui/styles/command-dock-trays.css index e3607a8ce..ecc6bc432 100644 --- a/src/ui/styles/command-dock-trays.css +++ b/src/ui/styles/command-dock-trays.css @@ -17,10 +17,10 @@ display: inline-flex; align-items: center; gap: 0.42rem; - color: rgba(206, 232, 239, 0.72); - font-size: 0.58rem; - font-weight: 500; - letter-spacing: 0.13rem; + color: var(--dock-label-color); + font-size: var(--dock-label-font-size); + font-weight: var(--dock-label-font-weight); + letter-spacing: var(--dock-label-letter-spacing); } #command-dock .dock-label-icon { diff --git a/src/ui/styles/controls.css b/src/ui/styles/controls.css index 59f275109..b50e9c2a1 100644 --- a/src/ui/styles/controls.css +++ b/src/ui/styles/controls.css @@ -81,16 +81,104 @@ white-space: nowrap; } -/* A longer translated title in a collapsed rail launcher tightens its - tracking rather than pushing the disclosure button out of the card. */ -html:not([lang='en']) - #right-context-rail - .panel-collapsible.collapsed - .panel-title { +/* Side panel titles read like the command dock's labels: one type for every + panel, collapsed or open, and bright enough to read over the globe. The + dock's type is in rem, but a collapsed launcher is a fixed 176px, so the + rail caps it at its 16px-root size: a larger browser font must not cut + Recent Imagery short. */ +:is(#left-panel-stack, #right-context-rail) + :is(.panel-header > .panel-title, .pp-header-label) { + font-family: var(--font-mono); + font-size: min(var(--dock-label-font-size), 9.28px); + font-weight: var(--dock-label-font-weight); + letter-spacing: min(var(--dock-label-letter-spacing), 2.08px); + text-transform: uppercase; +} + +/* A hovered panel sets --rail-label-hover on itself (see below); its own + header picks it up. Radio sits inside Context and keeps its own colour. */ +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + :is( + .panel-header > .panel-title, + .pp-header-label, + :is(.panel-header, .pp-header-row) > .panel-collapse-btn + ) { + color: var(--rail-label-hover, var(--dock-label-color)); +} + +#radio-panel { + --rail-label-hover: initial; +} + +/* A title never pushes its toggle out of the card: one that does not fit + ends in an ellipsis. Cyber draws a marker left of the title, which a clip + would cut, and keeps its own header layout. */ +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + :is(.panel-header > .panel-title, .pp-header-label) { min-width: 0; overflow: hidden; text-overflow: ellipsis; - letter-spacing: 1.5px; +} + +/* A collapsed launcher is 176px wide and its divider has no room to draw, + so the header drops it and packs closer. English titles such as Recent + Imagery then fit whole. Only the rail's own launchers: Radio, nested in + Context, keeps its divider. */ +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > .panel-collapsible.collapsed + > [class$='-panel-inner'] + > .panel-header, +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > #pp-toggles.collapsed + > .pp-header-row { + gap: 8px; +} + +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > .panel-collapsible.collapsed + > [class$='-panel-inner'] + > .panel-header + > .panel-divider, +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > #pp-toggles.collapsed + > .pp-header-row + > .panel-divider { + display: none; +} + +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > .panel-collapsible.collapsed + > [class$='-panel-inner'] + > .panel-header + > .panel-collapse-btn, +:root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + > #pp-toggles.collapsed + > .pp-header-row + > .panel-collapse-btn { + margin-left: auto; +} + +/* Translated titles run longer (Aktuelle Bilddaten), so a collapsed rail + launcher sets them in compact type rather than pushing the disclosure + button out of the card or cutting the word short. Chinese and Japanese + titles fit at full size and stay at or above their 9px floor (cjk.css); + Radio, nested in Context, is not a launcher. */ +html:not([lang='en']):not(:lang(zh)):not(:lang(ja)) + :is(#left-panel-stack, #right-context-rail) + > .panel-collapsible.collapsed + > [class$='-panel-inner'] + > .panel-header + > .panel-title { + font-size: 8px; + letter-spacing: 1px; } .panel-divider { @@ -109,6 +197,8 @@ html:not([lang='en']) font-family: var(--font-mono); font-size: 13px; line-height: 1; + /* A long title or a header action must not squeeze the toggle. */ + flex-shrink: 0; cursor: pointer; transition: background-color var(--transition-fast), @@ -116,17 +206,50 @@ html:not([lang='en']) color var(--transition-fast); } -/* A long translated title must not squeeze the toggle into a sliver. */ -html:not([lang='en']) .panel-collapse-btn { - flex-shrink: 0; -} - .panel-collapse-btn:hover { border-color: var(--glass-border-hover); color: var(--text-primary); background: rgba(255, 255, 255, 0.07); } +/* A side panel answers the pointer as the command dock's trays do: a + brighter edge and an accent title and toggle. Touch screens have no hover, + so a tap must not leave a panel lit. */ +@media (hover: hover) and (pointer: fine) { + :root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + :where( + .data-panel-inner, + .scene-panel-inner, + .cctv-panel-inner, + .weather-panel-inner, + .recent-imagery-panel-inner, + .street-level-panel-inner, + .global-context-panel-inner, + #pp-toggles:not(.collapsed), + #pp-toggles.collapsed > .pp-header-row + ) { + transition: border-color var(--transition-fast); + } + + :root:not([data-ui-theme='cyber']) + :is(#left-panel-stack, #right-context-rail) + :where( + .data-panel-inner, + .scene-panel-inner, + .cctv-panel-inner, + .weather-panel-inner, + .recent-imagery-panel-inner, + .street-level-panel-inner, + .global-context-panel-inner, + #pp-toggles:not(.collapsed), + #pp-toggles.collapsed > .pp-header-row + ):hover { + --rail-label-hover: var(--accent); + border-color: var(--glass-border-hover); + } +} + /* ── Button Grid ──────────────────────────────── */ .button-grid { display: flex; @@ -431,12 +554,21 @@ html:not([lang='en']) .panel-collapse-btn { } #pp-toggles .pp-header-label { - font-family: var(--font-mono); - font-size: 8px; - letter-spacing: 1.4px; color: var(--text-dim); } +/* Display's title and toggle sit where the other panels' do: 14px of panel + padding plus a 4px header inset. The cyber theme sets its own insets. */ +:root:not([data-ui-theme='cyber']) #pp-toggles.collapsed > .pp-header-row { + padding: 8px 18px; +} + +:root:not([data-ui-theme='cyber']) + #pp-toggles:not(.collapsed) + > .pp-header-row { + padding: 0 10px; +} + #pp-toggles > .pp-header-row .pp-collapse-btn { width: 20px; height: 20px; @@ -445,6 +577,16 @@ html:not([lang='en']) .panel-collapse-btn { padding: 0; } +/* In the default theme Display's toggle matches the other panels' 22px. */ +:root:not([data-ui-theme='cyber']) + #pp-toggles + > .pp-header-row + .pp-collapse-btn { + width: 22px; + height: 22px; + font-size: 13px; +} + #pp-toggles.collapsed { gap: 0; } diff --git a/src/ui/styles/foundation.css b/src/ui/styles/foundation.css index 2c2006094..185b256af 100644 --- a/src/ui/styles/foundation.css +++ b/src/ui/styles/foundation.css @@ -21,6 +21,11 @@ --panel-radius: 16px; --btn-radius: 10px; --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + /* Label type shared by the command dock and the side panel titles. */ + --dock-label-color: rgba(206, 232, 239, 0.72); + --dock-label-font-size: 0.58rem; + --dock-label-font-weight: 500; + --dock-label-letter-spacing: 0.13rem; --transition-smooth: 300ms cubic-bezier(0.4, 0, 0.2, 1); --left-stack-x: 52px; --right-rail-x: 52px; diff --git a/src/ui/styles/layers.css b/src/ui/styles/layers.css index aeb58c450..0f8ed5522 100644 --- a/src/ui/styles/layers.css +++ b/src/ui/styles/layers.css @@ -249,7 +249,7 @@ box-sizing: border-box; justify-content: center; overflow: visible; - padding: 8px 10px 8px 14px; + padding: 8px 14px; } #right-context-rail #global-context-panel.collapsed .panel-header { margin: 0; @@ -989,13 +989,6 @@ #global-context-panel .panel-glow { display: none; } -/* Expanded, its section titles (Context, Radio) stay legible over bright - imagery. Cyber keeps its own red titles; collapsed launchers match siblings. */ -:root:not([data-ui-theme='cyber']) - #global-context-panel:not(.collapsed) - .panel-title { - color: var(--text-secondary); -} .global-context-panel-inner { position: relative; display: flex; @@ -1011,6 +1004,15 @@ backdrop-filter: blur(24px) saturate(1.4); -webkit-backdrop-filter: blur(24px) saturate(1.4); } + +/* Data Layers and Context share the 14px side padding of the other default + theme panels, so every title and toggle sits 19px from its edge. Cyber + keeps its own gutters. */ +:root:not([data-ui-theme='cyber']) + :is(.data-panel-inner, .global-context-panel-inner) { + padding-right: 14px; + padding-left: 14px; +} .global-context-actions { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); diff --git a/src/ui/styles/recent-imagery.css b/src/ui/styles/recent-imagery.css index e13e17b95..70ee7a9c2 100644 --- a/src/ui/styles/recent-imagery.css +++ b/src/ui/styles/recent-imagery.css @@ -56,10 +56,6 @@ #recent-imagery-split-line[hidden] { display: none !important; } -#recent-imagery-panel.collapsed .panel-title { - font-size: 8px; - letter-spacing: 1.8px; -} #recent-imagery-panel.collapsed .panel-header { margin-bottom: 2px; } diff --git a/src/ui/styles/scenes.css b/src/ui/styles/scenes.css index 29250a636..da17a9c80 100644 --- a/src/ui/styles/scenes.css +++ b/src/ui/styles/scenes.css @@ -378,14 +378,6 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn { width: 100%; } -#data-panel.collapsed .panel-title, -#cctv-panel.collapsed .panel-title, -#radio-panel.collapsed .panel-title, -#scene-panel.collapsed .panel-title { - font-size: 8px; - letter-spacing: 1.8px; -} - #control-panel.collapsed .panel-inner, #data-panel.collapsed .data-panel-inner, #cctv-panel.collapsed .cctv-panel-inner, diff --git a/src/ui/styles/weather.css b/src/ui/styles/weather.css index f7cd999d3..b2c052e0e 100644 --- a/src/ui/styles/weather.css +++ b/src/ui/styles/weather.css @@ -50,10 +50,6 @@ .weather-readout[hidden] { display: none !important; } -#weather-panel.collapsed .panel-title { - font-size: 8px; - letter-spacing: 1.8px; -} #weather-panel.collapsed .panel-header { margin-bottom: 2px; }