Merge pull request #288 from ipnet-mesh/feat/more-ui-tweaks

feat(web): panel accent redesign, mobile nav two-column, light-mode polish
This commit is contained in:
JingleManSweep
2026-07-02 23:26:26 +01:00
committed by GitHub
4 changed files with 59 additions and 38 deletions
+40 -20
View File
@@ -30,6 +30,10 @@
--color-neutral: oklch(0.3 0.01 250); /* subtle dark grey */
--color-radio: oklch(0.75 0.15 210); /* cyan — radio tile icons */
/* Panel tint defaults — overridden per theme below */
--panel-tint-strength: 8%;
--panel-tint-bg: var(--color-base-100);
/* Map marker colors — theme-independent, chosen for contrast on map tiles */
--color-marker-infra: #3b82f6;
--color-marker-infra-border: #1e40af;
@@ -49,6 +53,10 @@
--color-members: oklch(0.55 0.18 25);
--color-neutral: oklch(0.85 0.01 250);
--color-radio: oklch(0.55 0.15 210);
/* Panel fill — neutral grey; colored left border carries section identity */
--panel-tint-strength: 0%;
--panel-tint-bg: oklch(97% 0 0);
}
/* ==========================================================================
@@ -100,31 +108,19 @@
}
/* ==========================================================================
Panel Glow
Radial color glow from bottom-right corner.
Set --panel-color on the element for a section-tinted glow.
Panel Tint & Accent
Subtle flat color tint plus a colored left accent border for section
identity. Set --panel-color on the element for the section color.
========================================================================== */
.panel-glow {
background-image:
radial-gradient(
ellipse at 80% 80%,
color-mix(in oklch, var(--panel-color, transparent) 15%, transparent),
transparent 70%
);
}
.panel-glow.panel-glow-tl {
background-image:
radial-gradient(
ellipse at 20% 20%,
color-mix(in oklch, var(--panel-color, transparent) 15%, transparent),
transparent 70%
);
.panel-accent {
background-color: color-mix(in oklch, var(--panel-color, transparent) var(--panel-tint-strength), var(--panel-tint-bg));
border-left: 5px solid color-mix(in oklch, var(--panel-color, transparent) 55%, transparent);
}
.panel-solid {
background-color: color-mix(in oklch, var(--panel-color, transparent) 10%, var(--color-base-100));
background-color: color-mix(in oklch, var(--panel-color, transparent) var(--panel-tint-strength), var(--panel-tint-bg));
border-left: 5px solid color-mix(in oklch, var(--panel-color, transparent) 55%, transparent);
}
.radio-tile-icon {
@@ -427,6 +423,17 @@
padding-bottom: 0.625rem;
font-size: 0.9375rem;
}
#mobile-nav {
display: block;
column-count: 2;
column-gap: 0.25rem;
width: min(90vw, 20rem);
}
#mobile-nav > li {
break-inside: avoid;
}
}
/* ==========================================================================
@@ -459,6 +466,19 @@ footer.footer {
gap: 0.5rem;
}
/* Light mode: soften the network announcement (alert-warning) banner */
[data-theme="light"] #flash-banner {
--alert-color: oklch(0.95 0.06 70);
--alert-border-color: oklch(0.82 0.12 70);
color: oklch(0.33 0.07 46);
}
/* Light mode: boost stat label contrast on tinted panels */
[data-theme="light"] .stat-title,
[data-theme="light"] .stat-desc {
color: color-mix(in oklab, var(--color-base-content) 80%, transparent);
}
.flash-banner-content {
display: inline;
}
@@ -802,10 +802,10 @@ export function renderFilterCard({ fields, basePath, navigate, submitLabel, clea
*/
export function renderStatCard({ icon, color, title, value, description }) {
return html`
<div class="stat bg-base-200 rounded-box shadow-sm panel-glow" style="--panel-color: ${color}">
<div class="stat bg-base-200 rounded-box shadow-sm panel-accent" style="--panel-color: ${color}">
<div class="stat-figure" style="color: ${color}">${icon}</div>
<div class="stat-title">${title}</div>
<div class="stat-value" style="color: ${color}">${value}</div>
<div class="stat-value">${value}</div>
${description ? html`<div class="stat-desc">${description}</div>` : nothing}
</div>`;
}
@@ -88,7 +88,7 @@ function renderChannelMessages(channelMessages, channelLabels) {
</div>`;
});
return html`<div class="card bg-base-100 shadow-xl panel-glow" style="--panel-color: var(--color-neutral)">
return html`<div class="card bg-base-100 shadow-xl panel-accent" style="--panel-color: var(--color-messages)">
<div class="card-body">
<h2 class="card-title">
${iconChannel('h-6 w-6')}
@@ -114,13 +114,13 @@ function renderChartCards({ showNodes, showAdverts, showMessages }) {
return html`
<div class="grid grid-cols-1 ${gridCols(visibleCount)} gap-6 mb-8">
${showNodes ? html`
<div class="card bg-base-100 shadow-xl panel-glow" style="--panel-color: var(--color-neutral)">
<div class="card bg-base-100 shadow-xl panel-accent" style="--panel-color: var(--color-nodes)">
<div class="card-body">
<h2 class="card-title text-base">
${iconNodes('h-5 w-5')}
${t('common.total_entity', { entity: t('entities.nodes') })}
</h2>
<p class="text-xs opacity-70">${t('time.over_time_last_7_days')}</p>
<p class="text-xs opacity-80">${t('time.over_time_last_7_days')}</p>
<div class="h-32">
<canvas id="nodeChart"></canvas>
</div>
@@ -128,13 +128,13 @@ function renderChartCards({ showNodes, showAdverts, showMessages }) {
</div>` : nothing}
${showAdverts ? html`
<div class="card bg-base-100 shadow-xl panel-glow" style="--panel-color: var(--color-neutral)">
<div class="card bg-base-100 shadow-xl panel-accent" style="--panel-color: var(--color-adverts)">
<div class="card-body">
<h2 class="card-title text-base">
${iconAdvertisements('h-5 w-5')}
${t('entities.advertisements')}
</h2>
<p class="text-xs opacity-70">${t('time.per_day_last_7_days')}</p>
<p class="text-xs opacity-80">${t('time.per_day_last_7_days')}</p>
<div class="h-32">
<canvas id="advertChart"></canvas>
</div>
@@ -142,13 +142,13 @@ function renderChartCards({ showNodes, showAdverts, showMessages }) {
</div>` : nothing}
${showMessages ? html`
<div class="card bg-base-100 shadow-xl panel-glow" style="--panel-color: var(--color-neutral)">
<div class="card bg-base-100 shadow-xl panel-accent" style="--panel-color: var(--color-messages)">
<div class="card-body">
<h2 class="card-title text-base">
${iconMessages('h-5 w-5')}
${t('entities.messages')}
</h2>
<p class="text-xs opacity-70">${t('time.per_day_last_7_days')}</p>
<p class="text-xs opacity-80">${t('time.per_day_last_7_days')}</p>
<div class="h-32">
<canvas id="messageChart"></canvas>
</div>
@@ -226,7 +226,7 @@ ${renderChartCards({ showNodes, showAdverts, showMessages })}` : nothing}
${bottomCount > 0 ? html`
<div class="grid grid-cols-1 ${bottomGrid} gap-6">
${showAdverts ? html`
<div class="card bg-base-100 shadow-xl panel-glow" style="--panel-color: var(--color-neutral)">
<div class="card bg-base-100 shadow-xl panel-accent" style="--panel-color: var(--color-adverts)">
<div class="card-body">
<h2 class="card-title">
${iconAdvertisements('h-6 w-6')}
@@ -58,23 +58,24 @@ function renderHeroSection({ networkName, logoUrl, logoInvertLight, networkCity,
: nothing;
const welcomeText = networkWelcomeText
? html`<p class="py-4 max-w-[90%] sm:max-w-[70%]">${networkWelcomeText}</p>`
: html`<p class="py-4 max-w-[90%] sm:max-w-[70%]">
? html`<p class="py-6 max-w-[90%] sm:max-w-[70%]">${networkWelcomeText}</p>`
: html`<p class="py-6 max-w-[90%] sm:max-w-[70%]">
${t('home.welcome_default', { network_name: networkName })}
</p>`;
return html`
<div class="flex flex-col items-center text-center">
<div class="flex flex-col sm:flex-row items-center gap-4 sm:gap-8 mb-4">
<div class="flex flex-col items-center text-center flex-1">
<div class="flex flex-col sm:flex-row items-center gap-4 sm:gap-8">
<img src="${logoUrl}" alt="${networkName}" class="theme-logo ${logoInvertLight ? 'theme-logo--invert-light' : ''} h-24 w-24 sm:h-36 sm:w-36" />
<div class="flex flex-col justify-center">
<h1 class="hero-title text-3xl sm:text-5xl lg:text-6xl font-black tracking-tight">${networkName}</h1>
${cityCountry}
</div>
</div>
${welcomeText}
<div class="flex-1"></div>
<div class="flex flex-wrap justify-center gap-2 sm:gap-3 mt-auto">
<div class="flex-1 flex items-center justify-center w-full">
${welcomeText}
</div>
<div class="flex flex-wrap justify-center gap-2 sm:gap-3">
${features.dashboard !== false ? renderNavCard({
href: '/dashboard',
icon: iconDashboard('w-full h-full'),
@@ -243,7 +244,7 @@ export async function render(container, params, router) {
litRender(html`
<div class="${showStats ? 'grid grid-cols-1 lg:grid-cols-3 gap-6' : ''} bg-base-100 rounded-box shadow-xl p-6">
<div class="${showStats ? 'lg:col-span-2' : ''}">
<div class="flex flex-col ${showStats ? 'lg:col-span-2' : ''}">
${heroSection}
</div>
${showStats ? statsPanel : nothing}