fix(theme): per-skin legibility polish from the sweep review

Address the high-severity, systemic legibility issues found in the per-skin
screenshot review (verified across all 9 skins):

- Status pills (history): "Confirmed" text was 55%-alpha (dim); make it full
  opacity, and give status/shield pills a visible tinted fill (a48) + 1px border
  (a90) so they stop vanishing on dark-red / near-white / gradient skins.
- Console light theming: light skins now use a near-white terminal surface, and
  the log text/JsonBrace colors switch to the dark defaults in light themes (the
  schema colors are dark-terminal-authored — honor them only in dark themes) so
  text isn't pale-on-white.
- Dark-skin muted text: bump --on-surface-medium 0.85 / --on-surface-disabled
  0.58 on dragonx/dark/color-pop-dark/obsidian (obsidian addresses were ~1.1:1).
- Disabled Send button: the disabled fill was hardcoded white (invisible on
  light skins) — use a dark overlay in light themes.
- Light-skin inputs: stronger frame fill + border color and a 1px frame border
  for light themes so fields (and buttons) have defined boundaries on white/
  pastel surfaces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 23:33:48 -05:00
parent dfca9bbd92
commit 1c5bc0d4d6
8 changed files with 53 additions and 32 deletions

View File

@@ -19,8 +19,8 @@ images = { background_image = "backgrounds/texture/pop-dark_bg.png", logo = "log
--on-secondary = "#FFFFFF"
--on-background = "#E8E6F0"
--on-surface = "#E8E6F0"
--on-surface-medium = "rgba(232,230,240,0.72)"
--on-surface-disabled = "rgba(232,230,240,0.40)"
--on-surface-medium = "rgba(232,230,240,0.85)"
--on-surface-disabled = "rgba(232,230,240,0.58)"
--error = "#FF5C72"
--on-error = "#000000"
--success = "#3DE8A0"
@@ -61,7 +61,7 @@ images = { background_image = "backgrounds/texture/pop-dark_bg.png", logo = "log
--sidebar-badge = "rgba(232,230,240,1.0)"
--sidebar-divider = "rgba(200,190,240,0.05)"
--chart-line = "rgba(124,108,255,0.12)"
--window-control = "rgba(232,230,240,0.72)"
--window-control = "rgba(232,230,240,0.85)"
--window-control-hover = "rgba(124,108,255,0.12)"
--window-close-hover = "rgba(255,92,114,0.75)"
--spinner-track = "rgba(200,190,240,0.08)"

View File

@@ -19,8 +19,8 @@ images = { background_image = "backgrounds/texture/dark_bg.png", logo = "logos/l
--on-secondary = "#000000"
--on-background = "#D0D0D4"
--on-surface = "#D0D0D4"
--on-surface-medium = "rgba(208,208,212,0.75)"
--on-surface-disabled = "rgba(208,208,212,0.45)"
--on-surface-medium = "rgba(208,208,212,0.85)"
--on-surface-disabled = "rgba(208,208,212,0.58)"
--error = "#B07080"
--on-error = "#000000"
--success = "#7AAE7C"
@@ -61,7 +61,7 @@ images = { background_image = "backgrounds/texture/dark_bg.png", logo = "logos/l
--sidebar-badge = "rgba(208,208,212,1.0)"
--sidebar-divider = "rgba(220,220,225,0.05)"
--chart-line = "rgba(220,220,225,0.08)"
--window-control = "rgba(208,208,212,0.75)"
--window-control = "rgba(208,208,212,0.85)"
--window-control-hover = "rgba(220,220,225,0.10)"
--window-close-hover = "rgba(200,50,60,0.70)"
--spinner-track = "rgba(220,220,225,0.08)"

View File

@@ -19,8 +19,8 @@ images = { background_image = "backgrounds/texture/obsidian_bg.png", logo = "log
--on-secondary = "#000000"
--on-background = "#E8E0F0"
--on-surface = "#E8E0F0"
--on-surface-medium = "rgba(232,224,240,0.75)"
--on-surface-disabled = "rgba(232,224,240,0.45)"
--on-surface-medium = "rgba(232,224,240,0.85)"
--on-surface-disabled = "rgba(232,224,240,0.58)"
--error = "#CF6679"
--on-error = "#000000"
--success = "#81C784"

View File

@@ -37,8 +37,8 @@ images = { background_image = "backgrounds/texture/drgx_bg.png", logo = "logos/l
--on-secondary = "#000000"
--on-background = "#F0E0D8"
--on-surface = "#F0E0D8"
--on-surface-medium = "rgba(240,224,216,0.7)"
--on-surface-disabled = "rgba(240,224,216,0.44)"
--on-surface-medium = "rgba(240,224,216,0.85)"
--on-surface-disabled = "rgba(240,224,216,0.58)"
--error = "#FF5252"
--on-error = "#000000"
--success = "#81C784"

View File

@@ -372,7 +372,7 @@ void ApplyColorThemeToImGui(const ColorTheme& theme)
colors[ImGuiCol_Border] = ImVec4(1.0f, 1.0f, 1.0f, 0.15f);
colors[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0.08f);
} else {
colors[ImGuiCol_Border] = ImVec4(0, 0, 0, 0.12f);
colors[ImGuiCol_Border] = ImVec4(0, 0, 0, 0.22f);
colors[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0.04f);
}
@@ -383,10 +383,11 @@ void ApplyColorThemeToImGui(const ColorTheme& theme)
colors[ImGuiCol_FrameBgActive] = ImVec4(1.0f, 1.0f, 1.0f, 0.15f);
} else {
// Light themes: the old 4/7/10% black fills were nearly invisible on a white surface (input
// fields blended in). Darker fills so inputs/dropdowns read as defined boxes.
colors[ImGuiCol_FrameBg] = ImVec4(0, 0, 0, 0.09f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0, 0, 0, 0.14f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0, 0, 0, 0.18f);
// fields blended in). Darker fills + a 1px frame border (below) so inputs/dropdowns read as
// defined boxes even on the pastel/gradient light skins (iridescent, color-pop-light).
colors[ImGuiCol_FrameBg] = ImVec4(0, 0, 0, 0.11f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0, 0, 0, 0.16f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0, 0, 0, 0.20f);
}
// Title bar
@@ -508,7 +509,10 @@ void ApplyColorThemeToImGui(const ColorTheme& theme)
style.WindowBorderSize = bwElem.extraFloats.count("window") ? bwElem.extraFloats.at("window") : 1.0f;
style.ChildBorderSize = bwElem.extraFloats.count("child") ? bwElem.extraFloats.at("child") : 1.0f;
style.PopupBorderSize = bwElem.extraFloats.count("popup") ? bwElem.extraFloats.at("popup") : 1.0f;
style.FrameBorderSize = bwElem.extraFloats.count("frame") ? bwElem.extraFloats.at("frame") : 0.0f;
// Light themes get a 1px frame border so inputs have a defined boundary on white/pastel surfaces;
// dark themes stay borderless (their translucent glass FrameBg is enough). Schema can override.
style.FrameBorderSize = bwElem.extraFloats.count("frame") ? bwElem.extraFloats.at("frame")
: (s_isDarkTheme ? 0.0f : 1.0f);
style.TabBorderSize = 0.0f;
style.AntiAliasedLines = true;

View File

@@ -111,12 +111,15 @@ void ConsoleTab::refreshColors()
ImU32 defInf = dark ? IM_COL32(191, 209, 229, 255) : IM_COL32(21, 101, 192, 255);
ImU32 defRpc = dark ? IM_COL32(120, 180, 255, 210) : IM_COL32(25, 118, 210, 220);
COLOR_COMMAND = !cmd.color.empty() ? S.resolveColor(cmd.color, defCmd) : defCmd;
COLOR_RESULT = !res.color.empty() ? S.resolveColor(res.color, defRes) : defRes;
COLOR_ERROR = !err.color.empty() ? S.resolveColor(err.color, defErr) : defErr;
COLOR_DAEMON = !dmn.color.empty() ? S.resolveColor(dmn.color, defDmn) : defDmn;
COLOR_INFO = !inf.color.empty() ? S.resolveColor(inf.color, defInf) : defInf;
COLOR_RPC = !rpc.color.empty() ? S.resolveColor(rpc.color, defRpc) : defRpc;
// The schema console colors are authored for the dark terminal (light-on-dark); honor them only
// in dark themes. Light themes always use the dark-text defaults so log text stays legible on the
// now near-white console surface (otherwise the schema's light-blue text sat pale-on-white ~1.2:1).
COLOR_COMMAND = (dark && !cmd.color.empty()) ? S.resolveColor(cmd.color, defCmd) : defCmd;
COLOR_RESULT = (dark && !res.color.empty()) ? S.resolveColor(res.color, defRes) : defRes;
COLOR_ERROR = (dark && !err.color.empty()) ? S.resolveColor(err.color, defErr) : defErr;
COLOR_DAEMON = (dark && !dmn.color.empty()) ? S.resolveColor(dmn.color, defDmn) : defDmn;
COLOR_INFO = (dark && !inf.color.empty()) ? S.resolveColor(inf.color, defInf) : defInf;
COLOR_RPC = (dark && !rpc.color.empty()) ? S.resolveColor(rpc.color, defRpc) : defRpc;
} else {
// No schema — use hardcoded defaults per theme
COLOR_COMMAND = dark ? IM_COL32(191, 209, 229, 255) : IM_COL32(21, 101, 192, 255);
@@ -145,7 +148,7 @@ ImU32 ConsoleTab::channelTextColor(ConsoleChannel channel) const
case ConsoleChannel::JsonKey: return WithAlpha(Secondary(), 255);
case ConsoleChannel::JsonString: return WithAlpha(Success(), 255);
case ConsoleChannel::JsonNumber: return WithAlpha(Warning(), 255);
case ConsoleChannel::JsonBrace: return IM_COL32(200, 200, 200, 150);
case ConsoleChannel::JsonBrace: return IsLightTheme() ? IM_COL32(90, 90, 90, 180) : IM_COL32(200, 200, 200, 150);
case ConsoleChannel::None:
default: return COLOR_RESULT;
}
@@ -266,7 +269,10 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
// Terminal-dark overlay: dim the glass so console text reads like a real terminal.
{
int darkA = static_cast<int>(schema::UI().drawElement("tabs.console", "bg-darken-alpha").sizeOr(110.0f));
dlOut->AddRectFilled(outPanelMin, outPanelMax, IM_COL32(0, 0, 0, darkA), outGlass.rounding);
// Light skins get a near-white terminal surface (not a black darken) so the dark, theme-aware
// log text stays legible instead of sitting dark-on-dark-grey.
ImU32 termOverlay = IsLightTheme() ? IM_COL32(255, 255, 255, 205) : IM_COL32(0, 0, 0, darkA);
dlOut->AddRectFilled(outPanelMin, outPanelMax, termOverlay, outGlass.rounding);
}
int consoleParentVtx = dlOut->VtxBuffer.Size;

View File

@@ -904,8 +904,12 @@ static void RenderActionButtons(App* app, float width, float vScale,
// ---- Send button ----
if (!can_send) {
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)S.drawElement("tabs.send", "disabled-btn-bg-alpha").size)));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)S.drawElement("tabs.send", "disabled-btn-bg-alpha").size)));
// The disabled fill must contrast the surface: a white overlay reads on dark skins but vanished
// on the light/pastel skins (white-on-white). Use a dark overlay for light themes.
int dAlpha = (int)S.drawElement("tabs.send", "disabled-btn-bg-alpha").size;
ImU32 disFill = IsLightTheme() ? IM_COL32(0, 0, 0, dAlpha) : IM_COL32(255, 255, 255, dAlpha);
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(disFill));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(disFill));
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()));
}

View File

@@ -748,7 +748,7 @@ void RenderTransactionsTab(App* app)
} else if (tx.confirmations >= 100 && (tx.display_type == "generate" || tx.display_type == "mined")) {
statusStr = TR("mature"); statusCol = greenCol;
} else {
statusStr = TR("confirmed"); statusCol = WithAlpha(Success(), 140);
statusStr = TR("confirmed"); statusCol = Success();
}
// Position status badge in the middle-right area
ImVec2 sSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, statusStr);
@@ -762,23 +762,30 @@ void RenderTransactionsTab(App* app)
float minStatusX = cx + innerW * 0.25f; // don't overlap address
if (statusX < minStatusX) statusX = minStatusX;
float statusTextX = statusX + (stackW - sSz.x) * 0.5f;
// Pills use a visible tinted fill + a 1px border so each state separates from the
// row background on every skin (faint alpha-30 fills used to vanish on dark-red /
// near-white / gradient skins). Fill 48, border 90 of the state color's RGB.
const float pillRound = schema::UI().drawElement("tabs.transactions", "status-pill-rounding").size;
if (shieldedDisplay) {
float shieldX = statusX + (stackW - shieldSz.x) * 0.5f;
ImU32 shieldCol = Primary();
ImU32 shieldBg = (shieldCol & 0x00FFFFFFu) | (static_cast<ImU32>(30) << 24);
ImVec2 shieldPillMin(shieldX - Layout::spacingSm(), cy - 1.0f);
ImVec2 shieldPillMax(shieldX + shieldSz.x + Layout::spacingSm(),
shieldPillMin.y + capFont->LegacySize + Layout::spacingXs());
dl->AddRectFilled(shieldPillMin, shieldPillMax, shieldBg,
schema::UI().drawElement("tabs.transactions", "status-pill-rounding").size);
dl->AddRectFilled(shieldPillMin, shieldPillMax,
(shieldCol & 0x00FFFFFFu) | (static_cast<ImU32>(48) << 24), pillRound);
dl->AddRect(shieldPillMin, shieldPillMax,
(shieldCol & 0x00FFFFFFu) | (static_cast<ImU32>(90) << 24), pillRound, 0, 1.0f);
dl->AddText(capFont, capFont->LegacySize,
ImVec2(shieldX, cy), shieldCol, shieldedStr);
}
// Background pill
ImU32 pillBg = (statusCol & 0x00FFFFFFu) | (static_cast<ImU32>(30) << 24);
ImVec2 pillMin(statusTextX - Layout::spacingSm(), cy + body2->LegacySize + 1);
ImVec2 pillMax(statusTextX + sSz.x + Layout::spacingSm(), pillMin.y + capFont->LegacySize + Layout::spacingXs());
dl->AddRectFilled(pillMin, pillMax, pillBg, schema::UI().drawElement("tabs.transactions", "status-pill-rounding").size);
dl->AddRectFilled(pillMin, pillMax,
(statusCol & 0x00FFFFFFu) | (static_cast<ImU32>(48) << 24), pillRound);
dl->AddRect(pillMin, pillMax,
(statusCol & 0x00FFFFFFu) | (static_cast<ImU32>(90) << 24), pillRound, 0, 1.0f);
dl->AddText(capFont, capFont->LegacySize,
ImVec2(statusTextX, cy + body2->LegacySize + Layout::spacingXs()), statusCol, statusStr);
}