fix(ui): resolve light-theme low-contrast semantic text app-wide (#1909)

The app was built dark-first, so hundreds of hardcoded Tailwind semantic
text/icon utilities at light shades (text-amber-400, text-blue-300, ...) had
no dark: variant. With darkMode:'class' they applied in light theme too,
producing washed-out text on pale tints and white cards — including the three
reported spots (AMS Drying banner, Archives no-3MF warning, debug-logging
banner). Give each a theme-aware pair: a darker readable shade in light theme
with the original pinned to dark:, so dark theme is unchanged. ~100 files.

The bambu-* CSS-variable palette (self-correcting) and the dark-only SpoolBuddy
kiosk are left untouched. Plain text-white is already theme-aware via the
existing index.css .text-white override, so it needed no changes.
This commit is contained in:
maziggy
2026-07-07 09:54:10 +02:00
parent 4af9da26f9
commit 1d344a8536
108 changed files with 834 additions and 833 deletions
@@ -194,9 +194,9 @@ export function FilamentMapping({
<Circle className="w-4 h-4" fill={statusColor} stroke="none" />
<span>{t('printModal.filamentMapping')}</span>
{hasTypeMismatch ? (
<span className="text-xs text-orange-400">(Type not found)</span>
<span className="text-xs text-orange-700 dark:text-orange-400">(Type not found)</span>
) : hasColorMismatch ? (
<span className="text-xs text-yellow-400">(Color mismatch)</span>
<span className="text-xs text-yellow-700 dark:text-yellow-400">(Color mismatch)</span>
) : (
<span className="text-xs text-bambu-green">(Ready)</span>
)}
@@ -264,8 +264,8 @@ export function FilamentMapping({
item.status === 'match'
? 'border-bambu-green/50 text-bambu-green'
: item.status === 'type_only'
? 'border-yellow-400/50 text-yellow-400'
: 'border-orange-400/50 text-orange-400'
? 'border-yellow-500 dark:border-yellow-400/50 text-yellow-700 dark:text-yellow-400'
: 'border-orange-500 dark:border-orange-400/50 text-orange-700 dark:text-orange-400'
} ${item.isManual ? 'ring-1 ring-blue-400/50' : ''}`}
title={item.isManual ? 'Manually selected' : 'Auto-matched'}
>
@@ -315,11 +315,11 @@ export function FilamentMapping({
<Check className="w-3 h-3 text-bambu-green" />
) : item.status === 'type_only' ? (
<span title="Same type, different color">
<AlertTriangle className="w-3 h-3 text-yellow-400" />
<AlertTriangle className="w-3 h-3 text-yellow-600 dark:text-yellow-400" />
</span>
) : (
<span title="Filament type not loaded">
<AlertTriangle className="w-3 h-3 text-orange-400" />
<AlertTriangle className="w-3 h-3 text-orange-600 dark:text-orange-400" />
</span>
)}
</div>
@@ -346,7 +346,7 @@ export function FilamentMapping({
</span>
</div>
{hasTypeMismatch && (
<p className="text-xs text-orange-400 mt-2">Required filament type not found in printer.</p>
<p className="text-xs text-orange-700 dark:text-orange-400 mt-2">Required filament type not found in printer.</p>
)}
</div>
)}
@@ -118,7 +118,7 @@ export function FilamentOverride({
disabled={compatible.length === 0}
className={`flex-1 px-2 py-1 rounded border text-xs bg-bambu-dark-secondary focus:outline-none focus:ring-1 focus:ring-bambu-green ${
isOverridden
? 'border-blue-400/50 text-blue-400'
? 'border-blue-500/50 dark:border-blue-400/50 text-blue-700 dark:text-blue-400'
: 'border-bambu-gray/30 text-bambu-gray'
}`}
>
@@ -35,7 +35,7 @@ export function PlateSelector({
<Layers className="w-4 h-4 text-bambu-gray" />
<span className="text-sm text-bambu-gray">Select Plate{multiSelect ? 's' : ''} to Print</span>
{selectedPlates.size === 0 && (
<span className="text-xs text-orange-400 flex items-center gap-1">
<span className="text-xs text-orange-700 dark:text-orange-400 flex items-center gap-1">
<AlertTriangle className="w-3 h-3" />
Selection required
</span>
@@ -109,7 +109,7 @@ export function PrintOptionsPanel({
bypasses the per-filament-type derivation. */}
<div className="pt-2 mt-1 border-t border-bambu-dark-tertiary/60">
<div className="flex items-center gap-2 mb-1.5">
<Flame className="w-3.5 h-3.5 text-amber-400" />
<Flame className="w-3.5 h-3.5 text-amber-600 dark:text-amber-400" />
<span className="text-sm text-white">{t('settings.preheatTitle', 'Preheat & Heat Soak')}</span>
</div>
<p className="text-xs text-bambu-gray mb-2">
@@ -171,8 +171,8 @@ function InlineMappingEditor({
status === 'match'
? 'border-bambu-green/50 text-bambu-green'
: status === 'type_only'
? 'border-yellow-400/50 text-yellow-400'
: 'border-orange-400/50 text-orange-400'
? 'border-yellow-500 dark:border-yellow-400/50 text-yellow-700 dark:text-yellow-400'
: 'border-orange-500 dark:border-orange-400/50 text-orange-700 dark:text-orange-400'
} ${isManual ? 'ring-1 ring-blue-400/50' : ''}`}
title={isManual ? 'Manually selected' : 'Auto-matched'}
>
@@ -190,11 +190,11 @@ function InlineMappingEditor({
<Check className="w-3 h-3 text-bambu-green" />
) : status === 'type_only' ? (
<span title="Same type, different color">
<AlertTriangle className="w-3 h-3 text-yellow-400" />
<AlertTriangle className="w-3 h-3 text-yellow-600 dark:text-yellow-400" />
</span>
) : (
<span title="Filament type not loaded">
<AlertTriangle className="w-3 h-3 text-orange-400" />
<AlertTriangle className="w-3 h-3 text-orange-600 dark:text-orange-400" />
</span>
)}
</div>
@@ -330,7 +330,7 @@ export function PrinterSelector({
if (displayPrinters.length === 0) {
return (
<div className="flex items-center gap-2 text-red-400 text-sm mb-4">
<div className="flex items-center gap-2 text-red-700 dark:text-red-400 text-sm mb-4">
<AlertCircle className="w-4 h-4" />
No {showInactive ? '' : 'active '}printers available
</div>
@@ -562,7 +562,7 @@ export function PrinterSelector({
{stateLabel && (
<span className={`text-xs px-2 py-0.5 rounded-full ${
busy
? 'bg-yellow-500/20 text-yellow-400'
? 'bg-yellow-100 dark:bg-yellow-500/20 text-yellow-700 dark:text-yellow-400'
: 'bg-bambu-green/20 text-bambu-green'
}`}>
{stateLabel}
@@ -606,8 +606,8 @@ export function PrinterSelector({
mappingResult.matchStatus === 'full'
? 'text-bambu-green'
: mappingResult.matchStatus === 'partial'
? 'text-yellow-400'
: 'text-orange-400'
? 'text-yellow-700 dark:text-yellow-400'
: 'text-orange-700 dark:text-orange-400'
}`}>
({mappingResult.exactMatches}/{mappingResult.totalSlots} matched)
</span>
@@ -654,7 +654,7 @@ export function PrinterSelector({
onClick={() => setShowAllPrinters(true)}
className="text-xs text-bambu-gray hover:text-white transition-colors mt-2 flex items-center gap-1"
>
<AlertTriangle className="w-3 h-3 text-yellow-400" />
<AlertTriangle className="w-3 h-3 text-yellow-600 dark:text-yellow-400" />
{hiddenPrinterCount} other printer{hiddenPrinterCount > 1 ? 's' : ''} hidden (different model) —
<span className="underline">show all</span>
</button>
@@ -673,7 +673,7 @@ export function PrinterSelector({
{/* Warning when no printer selected (only in printer mode) */}
{assignmentMode === 'printer' && selectedCount === 0 && (
<p className="text-xs text-orange-400 mt-1 flex items-center gap-1">
<p className="text-xs text-orange-700 dark:text-orange-400 mt-1 flex items-center gap-1">
<AlertCircle className="w-3 h-3" />
Select at least one printer
</p>
@@ -681,7 +681,7 @@ export function PrinterSelector({
{/* Warning when no model selected (only in model mode) */}
{assignmentMode === 'model' && !targetModel && (
<p className="text-xs text-orange-400 mt-1 flex items-center gap-1">
<p className="text-xs text-orange-700 dark:text-orange-400 mt-1 flex items-center gap-1">
<AlertCircle className="w-3 h-3" />
Select a target printer model
</p>
@@ -219,7 +219,7 @@ export function ScheduleOptionsPanel({
</div>
</div>
{(!isDateValid || !isTimeValid) && (
<p className="mt-1 text-xs text-red-400">
<p className="mt-1 text-xs text-red-700 dark:text-red-400">
{t('printModal.invalidDateTime')}
</p>
)}
+7 -7
View File
@@ -1127,9 +1127,9 @@ export function PrintModal({
const selectedPrinter = printers?.find(p => p.id === selectedPrinters[0]);
if (selectedPrinter && selectedPrinter.model && slicedForModel !== selectedPrinter.model) {
return (
<div className="p-3 mb-2 bg-yellow-500/10 border border-yellow-500/30 rounded-lg flex items-center gap-2">
<AlertTriangle className="w-4 h-4 text-yellow-400 flex-shrink-0" />
<span className="text-sm text-yellow-400">
<div className="p-3 mb-2 bg-yellow-50 dark:bg-yellow-500/10 border border-yellow-300 dark:border-yellow-500/30 rounded-lg flex items-center gap-2">
<AlertTriangle className="w-4 h-4 text-yellow-600 dark:text-yellow-400 flex-shrink-0" />
<span className="text-sm text-yellow-700 dark:text-yellow-400">
File was sliced for {slicedForModel}, but printing on {selectedPrinter.model}
</span>
</div>
@@ -1140,9 +1140,9 @@ export function PrintModal({
{/* Warning when archive data couldn't be loaded */}
{archiveDataMissing && (
<div className="flex items-start gap-2 p-3 mb-2 bg-orange-500/10 border border-orange-500/30 rounded-lg text-sm">
<AlertCircle className="w-4 h-4 text-orange-400 mt-0.5 flex-shrink-0" />
<p className="text-orange-400">
<div className="flex items-start gap-2 p-3 mb-2 bg-orange-50 dark:bg-orange-500/10 border border-orange-300 dark:border-orange-500/30 rounded-lg text-sm">
<AlertCircle className="w-4 h-4 text-orange-600 dark:text-orange-400 mt-0.5 flex-shrink-0" />
<p className="text-orange-700 dark:text-orange-400">
Archive data unavailable. The source file may have been deleted. Filament mapping is disabled.
</p>
</div>
@@ -1212,7 +1212,7 @@ export function PrintModal({
{/* Error message */}
{updateQueueMutation.isError && (
<div className="mb-4 p-3 bg-red-500/20 border border-red-500/50 rounded-lg text-sm text-red-400">
<div className="mb-4 p-3 bg-red-100 dark:bg-red-500/20 border border-red-500/50 rounded-lg text-sm text-red-700 dark:text-red-400">
{(updateQueueMutation.error as Error)?.message || 'Failed to complete operation'}
</div>
)}