mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-07-27 12:03:19 +02:00
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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user