fix(explore/ui): readable detail labels, real validator metrics, stat contrast (v1.1.9)

Three UI-correctness fixes surfaced by visual inspection of explore.lux.network:

- DetailedInfo (block/tx/address detail pages): every field label + value
  rendered white-on-white because the grid inherited the @luxfi/ui Tamagui
  `t_dark` theme color while the page is in light mode. The two most-used
  drill-downs were unreadable. Anchor the container to
  --color-text-primary and labels to --color-text-secondary so text follows
  the app color mode instead of the stray Tamagui theme.

- StatsWidget (home metric cards): the value had no explicit color and
  inherited the same Tamagui white -> near-invisible "Total blocks /
  transactions / addresses". Anchor to --color-text-primary.

- Validators dashboard + Network Overview: dropped the "Connected" (0/5) and
  "Avg Uptime" (0.0%) widgets/columns. platform.getCurrentValidators reports
  connected=null and uptime=0 for every validator on the public RPC (the API
  node does not track peer uptime), so those numbers were fabricated-looking
  zeros. Only chain-verifiable metrics (count, stake, delegation fee,
  delegators) remain -- real data, no fake numbers.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-07-17 01:53:24 -07:00
co-authored by Hanzo Dev
parent 31deee7551
commit 8901706f59
5 changed files with 27 additions and 47 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@luxfi/explore",
"version": "1.1.8",
"version": "1.1.9",
"private": false,
"homepage": "https://github.com/luxfi/explore#readme",
"engines": {
+7 -3
View File
@@ -1,7 +1,7 @@
import { Skeleton } from '@luxfi/ui/skeleton';
import React from 'react';
import { cn } from 'lib/utils/cn';
import { Skeleton } from '@luxfi/ui/skeleton';
import { Hint } from 'toolkit/components/Hint/Hint';
import * as ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
@@ -16,7 +16,11 @@ interface ContainerProps {
export const Container = ({ children, className }: ContainerProps) => {
return (
<div className={ cn('grid gap-x-8 gap-y-0 lg:gap-y-3 grid-cols-1 lg:grid-cols-[max-content_minmax(728px,auto)] text-sm lg:text-base', className) }>
<div className={ cn(
'grid gap-x-8 gap-y-0 lg:gap-y-3 grid-cols-1 lg:grid-cols-[max-content_minmax(728px,auto)]',
'text-sm lg:text-base text-[var(--color-text-primary)]',
className,
) }>
{ children }
</div>
);
@@ -41,7 +45,7 @@ export const ItemLabel = ({ hint, children, isLoading, id, hasScroll, className
return (
<div
id={ id }
className={ cn('min-h-[30px] lg:min-h-[32px] [&:not(:first-child)]:mt-3 lg:[&:not(:first-child)]:mt-0', className) }
className={ cn('min-h-[30px] lg:min-h-[32px] text-[var(--color-text-secondary)] [&:not(:first-child)]:mt-3 lg:[&:not(:first-child)]:mt-0', className) }
>
<div className="flex gap-x-1 lg:gap-x-2 items-start w-full">
{ hint && <Hint label={ hint } isLoading={ isLoading } className="my-[5px] lg:my-[6px]"/> }
+3 -3
View File
@@ -1,13 +1,13 @@
import { Skeleton } from '@luxfi/ui/skeleton';
import React from 'react';
import type { Route } from 'nextjs-routes';
import { route } from 'nextjs-routes';
import { cn } from 'lib/utils/cn';
import { Link } from 'toolkit/next/link';
import { Skeleton } from '@luxfi/ui/skeleton';
import { Hint } from 'toolkit/components/Hint/Hint';
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
import { Link } from 'toolkit/next/link';
import IconSvg, { type IconName } from 'ui/shared/IconSvg';
export type Props = {
@@ -83,7 +83,7 @@ const StatsWidget = ({
<Skeleton
loading={ isLoading }
className={ cn(
'flex items-baseline font-medium text-lg',
'flex items-baseline font-medium text-lg text-[var(--color-text-primary)]',
isFallback && !isLoading && 'opacity-[var(--opacity-control-disabled)]',
) }
>
+8 -16
View File
@@ -1,11 +1,11 @@
// Network-level stats: validators, chains, stake, uptime.
// Displayed above the standard Explorer chain stats on the stats page.
import { Skeleton } from '@luxfi/ui/skeleton';
import React from 'react';
import config from 'configs/app';
import { useBlockchains, useCurrentValidators } from 'lib/api/pchain';
import { Skeleton } from '@luxfi/ui/skeleton';
// ── Constants ──
@@ -13,8 +13,6 @@ const PRIMARY_NETWORK_ID = '11111111111111111111111111111111LpoYY';
const PRIMARY_CHAIN_COUNT = 14;
const LUX_DECIMALS = 6;
const STAT_BG = { _light: 'gray.50', _dark: 'whiteAlpha.50' };
// ── Helpers ──
function formatStake(nanoLux: bigint): string {
@@ -35,8 +33,7 @@ interface StatCardProps {
const StatCard = ({ label, value, isLoading }: StatCardProps) => (
<div
>
<span className="text-[var(--color-text-secondary)]">
{ label }
@@ -71,7 +68,7 @@ const NetworkStats = () => {
Network Overview
</span>
<div
>
<StatCard
label="Total Chains"
@@ -83,21 +80,16 @@ const NetworkStats = () => {
value={ hasValidatorData ? String(stats.validatorCount) : '\u2014' }
isLoading={ isLoading }
/>
<StatCard
label="Connected"
value={ hasValidatorData ? `${ stats.connectedCount }/${ stats.validatorCount }` : '\u2014' }
isLoading={ isLoading }
/>
<StatCard
label="Total Stake"
value={ hasValidatorData ? `${ formatStake(stats.totalStake) } ${ config.chain.currency.symbol || 'LUX' }` : '\u2014' }
isLoading={ isLoading }
/>
<StatCard
label="Avg Uptime"
value={ hasValidatorData ? `${ stats.averageUptime.toFixed(1) }%` : '\u2014' }
isLoading={ isLoading }
/>
{ /*
Connected / Avg Uptime intentionally omitted: platform.getCurrentValidators
reports connected=null and uptime=0 on the public RPC, so any value would be
fabricated. Only chain-verifiable metrics are shown.
*/ }
</div>
</div>
);
+8 -24
View File
@@ -4,7 +4,7 @@ import React from 'react';
import config from 'configs/app';
import type { PChainValidator, ValidatorStats } from 'lib/api/pchain';
import { formatStake, formatUptime, truncateNodeId } from './utils';
import { formatStake, truncateNodeId } from './utils';
const CURRENCY = config.chain.currency.symbol || 'LUX';
@@ -117,12 +117,6 @@ const ActiveValidatorsTable = ({ validators, isLoading }: ActiveValidatorsTableP
<div className="font-semibold flex-1 text-right">
Delegation Fee
</div>
<div className="font-semibold flex-1 text-center">
Connected
</div>
<div className="font-semibold flex-1 text-right">
Uptime
</div>
</div>
{ /* Rows */ }
@@ -148,12 +142,6 @@ const ActiveValidatorsTable = ({ validators, isLoading }: ActiveValidatorsTableP
<div className="flex-1 text-right">
{ v.delegationFee }%
</div>
<div className="flex-1 flex justify-center">
<div/>
</div>
<div className="flex-1 text-right">
{ formatUptime(v.uptime) }
</div>
</div>
)) }
</div>
@@ -174,30 +162,26 @@ const ValidatorsDashboard = ({ validators, stats, isLoading }: ValidatorsDashboa
return (
<div className="flex flex-col gap-6 text-[var(--color-text-primary)]">
{ /* Stat cards */ }
{ /*
Connected / Avg Uptime intentionally omitted: platform.getCurrentValidators
reports connected=null and uptime=0 for every validator on the public RPC
(the API node does not track peer uptime), so any value here would be
fabricated. Only chain-sourced, verifiable metrics are shown.
*/ }
<div
className="grid gap-4"
style={{ gridTemplateColumns: 'repeat(4, 1fr)' }}
style={{ gridTemplateColumns: 'repeat(2, 1fr)' }}
>
<StatCard
label="Validators"
value={ stats.validatorCount.toLocaleString() }
isLoading={ isLoading }
/>
<StatCard
label="Connected"
value={ `${ stats.connectedCount }/${ stats.validatorCount }` }
isLoading={ isLoading }
/>
<StatCard
label="Delegators"
value={ stats.delegatorCount.toLocaleString() }
isLoading={ isLoading }
/>
<StatCard
label="Avg Uptime"
value={ `${ stats.averageUptime.toFixed(1) }%` }
isLoading={ isLoading }
/>
</div>
{ /* Stake breakdown */ }