Skip to content

Fix: floating operation pill stays theme-aware in Light mode (#213) - #215

Open
DeLo1585 wants to merge 6 commits into
StudioNirin:mainfrom
DeLo1585:fix/light-theme-pill-213
Open

DeLo1585 wants to merge 6 commits into
StudioNirin:mainfrom
DeLo1585:fix/light-theme-pill-213

Conversation

@DeLo1585

@DeLo1585 DeLo1585 commented Sep 23, 2026 •

Copy link
Copy Markdown

Fixes #213
Source review / writing / testing done together with Claude Sonnet 5 (Anthropic).

What changed

The floating operation-status pill (#global-operation-banner .di-pill) previously had a hard-coded black background and white text/decoration colors throughout, both in its compact and expanded (Tier 2/3, detail-log) states. The existing Light theme override only softened the drop shadow, intentionally keeping the pill black (see the removed comment "Pill stays dark in light mode").

This PR makes the pill genuinely theme-aware for Light mode instead, while leaving Dark mode pixel-identical to before.

Approach

Introduced three CSS custom properties, scoped to #global-operation-banner (the pill's outer, always-present container):

#global-operation-banner {
    --di-on: 255,255,255;   /* RGB triplet used as rgba(var(--di-on), X) */
    --di-bg: #000;
    --di-track-bg: #1a1a1a;
}

Dark theme keeps these at their original literal values, so nothing changes there. [data-theme="light"] #global-operation-banner overrides them to a light surface (var(--plex-bg-card)) and dark-on-light text, matching the rest of the app's existing Light theme palette:

[data-theme="light"] #global-operation-banner {
    --di-on: 0,0,0;
    --di-bg: var(--plex-bg-card);
    --di-track-bg: rgba(0,0,0,0.08);
}

All previously hard-coded rgba(255,255,255,X) / #fff / #1a1a1a occurrences inside the pill (label, muted metric, progress bar/track, divider, expand-hint, detail log rows, queue/countdown text, etc. — both compact and expanded states) now reference these tokens instead, in both custom.css and the few inline style="..." occurrences in global_operation_banner.html that CSS classes alone couldn't reach.

Left untouched on purpose:

  • All --accent operation-state colors (.di-pill--caching, --evicting, --error, etc.) — caching/evicting/error color coding is identical in both themes.
  • .di-error-badge keeps fixed white text, since its background is always red regardless of theme.

Testing

Manually tested on a running v3.2.0 container (Docker/Unraid) by swapping in the patched files via docker cp and comparing both themes before/after:

  • Light theme: pill now renders as a white card with dark text, consistent with the rest of the Light theme (idle, caching-in-progress with expanded log detail panel, and completed states all checked).
  • Dark theme: confirmed pixel-identical to the current behavior (black pill, white text)

Screenshots:

grafik
grafik
grafik
grafik

@StudioNirin

Copy link
Copy Markdown
Owner

@Brandon-Haney

I did some quick tests and looked through it and it seems to work. Does this interfere with anything you might be working on?

If not, ill merge it. I always use dark mode so I didnt even realise this was an issue haha

@DeLo1585

Copy link
Copy Markdown
Author

Thanks for the quick reply. I haven't worked on any other issues or FRs in here so far.

This PR actually only involves those two files.

@Brandon-Haney

Copy link
Copy Markdown
Collaborator

@StudioNirin hold off on the merge for a moment, it does interfere, though not in the way the question was meant.

@DeLo1585 the theme change itself looks good and the approach is the right one, I'll come back to that. The problem is the branch. It's based on current main, but the two files were committed as their v3.2.0 versions with the token change applied on top, so the diff carries everything that's landed in those files since the release as deletions. That's 276 lines removed against 86 added. Merging it as is would take out all of the Recently Added styles, the share-mismatch warning, the extras badge and bytes-first progress line from #210, and the group id keying that stops two groups for the same show from colliding in the banner. Easy to miss when you've been testing by copying the files into a running container, since that container is on 3.2.0 too.

The fix is small. Check out main, reapply only the token block and the rgba(255,255,255,X) to rgba(var(--di-on),X) swaps, and leave the rest of the two files alone. The diff should then be additions and one-line replacements with no removed blocks. If it's easier, I can push a rebased version of your change to a branch and you can pull it into the PR.

On the design side, some background. The pill being dark in light mode was a deliberate call back in February, not a leftover. Light theme came later than the rest of the UI, we tried a light pill at the time and it didn't look right, so we kept it dark with a stronger border and shadow. I'm not against a light pill, but the pill's status colours are hard coded literals that were tuned for a black surface, and they don't hold up on white. In your screenshots the Plex orange idle accent clashes with the card, and the muted text is going to be hard to read, since a lot of the swaps land at 0.15 to 0.25 black on white. The light blue countdown timer is a fixed colour too.

If you want to keep going with the light surface, I'd suggest:

  • Under [data-theme="light"] #global-operation-banner, override the --accent values per state with the semantic tokens from plex-theme.css (--plex-success, --plex-warning, --plex-error, --plex-info) since those already have light theme values, rather than reusing the literals.
  • Drop or heavily reduce --accent-glow in light mode, the halo reads as a smudge on white.
  • Raise the muted alphas for light. Anything at 0.15 to 0.3 white on black needs to be roughly 0.45 to 0.6 black on white to be legible.
  • Give the countdown timer and any other fixed colours a light override too.

If that ends up being a lot of tuning, the other option the issue itself raised is fine with me: keep the pill as a deliberate dark element in light mode and give it a stronger border so it reads as intentional. Either way, thanks for digging into this, light mode has had less attention than it deserves.

@StudioNirin

Copy link
Copy Markdown
Owner

Ahh I see, good catch @Brandon-Haney I didnt think to look for that

@DeLo1585

DeLo1585 commented Sep 25, 2026 •

Copy link
Copy Markdown
Author

Yeah, I used the v3.2.0 source zip, that explains it, wasn't on current main.

I redo the changes against the current branch (with Claude's Sonnet help) and then update this PR and let you both know once it's ready

first adjustments results were:

  • Diff is now 45 removed / 120 added in custom.css
  • a clean 13/13 in the template,
  • all one-line replacements,
  • no removed blocks (Recently Added, the share-mismatch warning, extras badge, bytes-first progress, and the group-id keying are all untouched and still present).

@DeLo1585

DeLo1585 commented Sep 25, 2026 •

Copy link
Copy Markdown
Author

(Worked with Claude Sonnet 5 on this patch — hoping nothing got scrambled or out of sync this time around!)

I've rebased against current main and reapplied just the token block + rgba swaps on top, nothing else touched.

Diff is now 45 removed / 120 added in custom.css and a clean 13/13 in the template, all one-line replacements, no removed blocks (Recently Added, the share-mismatch warning, extras badge, bytes-first progress, and the group-id keying are all untouched and still present).

On the design points, I went with tuning it rather than falling back to a dark pill, using your suggestions directly:

  • Each .di-pill--* state now overrides --accent to the semantic tokens (--plex-success/-warning/-error/-info) in Light theme instead of the literals. Idle keeps the Plex orange as-is, since that's already used elsewhere in Light theme.
  • To keep the icon background circle and spinner ring consistent with those updated state colors in Light theme, --accent-dim is now derived dynamically via color-mix(in srgb, var(--accent) 15%, transparent) per state, matching the pattern used elsewhere in the app so the dim tint stays in sync with whichever semantic token is active.
  • --accent-glow is set to transparent in Light theme rather than tuned down, per your "drop it" option, the pulse halo didn't read well on white at any alpha I tried.
  • Replaced the flat rgba(var(--on), X) approach for actual text with discrete tiers (--di-t15 through --di-t70, named by their dark-mode alpha) that get boosted independently in Light theme, e.g. what was 0.25 white-on-black is 0.55 black-on-white now, not a symmetric mirror. That was the fix for the "Next run" text being hard to read that you flagged.
  • The three fixed light-blue spots (queue badge, active-extras tag, countdown timer) now go through a --di-info-text token tied to --plex-info.

Pushed both files to the same branch. Let me know if the contrast tiers need further adjustment, I picked the boosted values based on your 0.45–0.6 guidance but didn't have a way to measure actual contrast ratios here.

@DeLo1585

DeLo1585 commented Sep 25, 2026 •

Copy link
Copy Markdown
Author

Small correction to my last comment, the --accent-dim description doesn't quite match what's actually in the pushed diff.

I originally tried deriving it dynamically as color-mix(in srgb, var(--accent) 15%, transparent), but went with explicit per-state overrides instead (color-mix(in srgb, var(--plex-success) 18%, transparent) etc., one line per .di-pill--* variant, same pattern as the --accent overrides above them), so it's tied directly to the semantic tokens rather than re-deriived from --accent at render time. Diff stats are 45 removed / 134 added in custom.css (not 120, that was before this part), template is still a clean 13/13.

Functionally the result is the same either way, tint stays in sync with whichever state/token is active, this is just the version that's actually in the branch now. Sorry for the mismatch in the writeup.

so @StudioNirin @Brandon-Haney its ready for review the updates 2 files

@Brandon-Haney

Brandon-Haney commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Went through the rebased branch and it's clean now. Merge base is current main, every removed line is one of the literal colour swaps, the template is 13 one-line replacements, and all the code I was worried about is untouched. Dark theme resolves to the same values as before. Nice work turning that around quickly.

I put the pill states side by side in both themes (dark left, light right) and measured the light side. All the text tiers you boosted come out above 4.5:1, most well above, so the tiering approach did its job. Click the image for full size, or open dark and light separately.

Pill states rendered with this branch, dark theme on the left and light theme on the right

Two spots are still short:

  • The idle countdown value ("in 3h 12m") is the Plex orange at 70% on the white card, which comes out around 1.8:1. That's the one that reads washed out. I'd give the value itself a text tier in light mode, --di-t60 or so, and leave the orange on the clock icon where it works.
  • The "Start Now" button in the maintenance countdown has an inline background: var(--plex-info); color: #000. In light mode --plex-info is a dark blue, so that's black on dark blue at under 4:1. That one predates your change, but since you already route the fixed blues through --di-info-text, a matching token for the button text (black in dark, white in light) would clear it.

Happy to approve once those two are in. @StudioNirin from my side this is good to merge after that.

Brandon-Haney added a commit to Brandon-Haney/PlexCache-R that referenced this pull request Sep 25, 2026
@DeLo1585

Copy link
Copy Markdown
Author

@Brandon-Haney Thank you for your Honest expertise and the comparison screenshots. This overview is truly fantastic, and helps me a lot. I will implement this change within the next 12 hours 🙏👌💪

@StudioNirin

Copy link
Copy Markdown
Owner

@Brandon-Haney Thank you for your Honest expertise and the comparison screenshots. This overview is truly fantastic, and helps me a lot. I will implement this change within the next 12 hours 🙏👌💪

Looks good, buzz me when youve made that change and ill merge. 🫡

@DeLo1585

DeLo1585 commented Sep 26, 2026 •

Copy link
Copy Markdown
Author

Both fixed.

  • The countdown value now runs through a --di-idle-value token, color-mix(...var(--plex-orange) 70%...) stays as-is in dark, switches to var(--di-t60) in light. Clock icon still just var(--plex-orange), untouched.

  • For the button, added --di-btn-text-on-accent (#000 dark / #fff light) and swapped it in on "Start Now".

@Brandon-Haney

While Sonnet5 was at it and went looking for anything else with the same shape of bug and found one more:

"Resume" in the Queue Paused state had the identical setup

  • background: var(--plex-warning); color: #000, and --plex-warning goes darker in light mode too (#ff9800 → #c77c02), same contrast drop. Same token fixes both now.

Testing on my own container in a bit, but happy for you to take another pass whenever, in case something else is still off that I haven't caught.

Don't have your nice side-by-side comparison setup though, so I'm mostly eyeballing it :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Floating status pill (global operation banner) stays hard-coded black in Light theme

3 participants