Make panel borders survive dark mode (M-THEME THEME.5) - #38
Open
NCCU-Schultz-Lab wants to merge 2 commits into
Open
Make panel borders survive dark mode (M-THEME THEME.5)#38NCCU-Schultz-Lab wants to merge 2 commits into
NCCU-Schultz-Lab wants to merge 2 commits into
Conversation
The badge rendered as "package or version not found" even after quantui 0.5.1
published successfully. Not a packaging problem — two independent stale
caches, both holding a genuine 404 from before the package existed:
- shields.io caches badge results for 3h (max-age=10800) behind Cloudflare.
The badge URL was first requested when the README was rendered during the
pre-flight metadata work, well before anything was published.
- GitHub never lets a browser hit shields.io directly; it proxies README
images through camo.githubusercontent.com, which cached its own copy of
that stale SVG and doesn't track the origin's cache headers.
Appending ?v=2 makes camo treat it as a new image and fetch fresh. Bump the
number again if this ever recurs.
Verified before changing anything: pypi.org/pypi/quantui/json returns 200 with
version 0.5.1, and img.shields.io/pypi/v/quantui.json already reported
"v0.5.1" — the origin was correct the whole time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User report: "Fix dark mode contrasts and add borders (current approach does
not work)."
Diagnosed by measurement rather than assumption, and the measurement moved the
target. Dark mode is not a palette - it is a whole-page CSS
`filter: invert(1) hue-rotate(180deg)`, so every colour is authored once for
light mode and dark mode is its mathematical inversion. Computing WCAG ratios
through that actual filter chain (invert, then the W3C hue-rotate matrix):
- Text was never broken. Body text on a panel is 7.24:1 in light and
*improves* to 8.91:1 inverted.
- Structural separation was. Border-vs-panel measured 1.14:1 in dark and
panel-vs-page 1.03:1 - panels and their edges were invisible against the
dark page. That is why the same request asked for contrast AND borders in
one breath: they were one defect.
The fix follows from a property of the filter: a light grey border inverts to
a near-black border on a near-black panel, and a dark border has the mirror
problem in light mode. Only MID-TONES survive inversion. #7d8ea3 is the
lightest value clearing WCAG 1.4.11's 3:1 bar for non-text UI components in
both modes (3.20:1 light / 4.30:1 dark, from 1.18 / 1.14). The viewer frame,
which stands alone rather than in a card stack, uses a stronger #64748b.
New quantui/theme.py holds the tokens plus the measurements and the reasoning,
so the next person does not have to re-derive why a border is that specific
grey. 19 in-app border sites migrated to it.
Deliberately NOT done, to keep this reviewable and honest about scope:
- Not a full palette migration. ~390 hex literals exist across 17 files,
but most are semantic accents (error red, success green, link blue) whose
hue survives hue-rotate(180) and which already look right in both modes.
Migrating them wholesale would be a large, visually-unverifiable change
for no user-visible gain.
- analytics.py untouched. It writes a STANDALONE dashboard HTML opened
directly in a browser, where the app's invert filter never applies - its
light borders are correct as they are. Changing them "for consistency"
would have been wrong.
- Plot grid/bond colours untouched; those are THEME.2/.3 and some are
already theme-aware.
Trade-off worth knowing: under a global invert there is exactly one source
value per colour, so light and dark cannot be tuned independently. Light-mode
borders are therefore now more visible than before (3.20:1, previously
1.18:1). That reads as normal design weight and is the price of dark mode
working at all. Decoupling them requires removing the invert filter, which is
THEME.6's problem - the tokens are the seam where that change will land.
14 new tests re-derive the filter maths and assert both tokens clear 3:1 in
both modes, so a future "soften those borders" tweak fails loudly instead of
silently restoring the bug.
Suite 1985 passed / 17 skipped. pre-commit clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The measurement changed the target
Dark mode is not a palette — it's a whole-page CSS filter:
Every colour is authored once for light mode; dark mode is its mathematical
inversion. Computing WCAG ratios through that actual chain (invert, then the
W3C hue-rotate matrix — not an HSL rotation, which would predict different
numbers than the browser produces):
So "contrast" and "borders" were one defect, not two: structural separation,
not legibility. That's why the request asked for both in one breath.
The fix: the mid-tone rule
A property of the filter drives everything: a light grey border inverts to a
near-black border on a near-black panel; a dark border has the mirror problem
in light mode. Only mid-tones survive inversion.
#7d8ea3is the lightest value clearing WCAG 1.4.11's 3:1 bar for non-textUI components in both modes:
3:1 is the correct bar here — borders are UI components, not text. Using 4.5:1
would force a heavier border than the light-mode design wants.
New
quantui/theme.pyholds the tokens plus the measurements and reasoning,so nobody has to re-derive why a border is that specific grey. 19 in-app sites
migrated.
Deliberately not done
are semantic accents (error red, success green) whose hue survives
hue-rotate(180)and already look right in both modes. Wholesale migration =large, visually-unverifiable change for no user-visible gain.
analytics.pyuntouched — it writes a standalone dashboard HTML openeddirectly in a browser, where the invert filter never applies. Its light borders
are correct; changing them "for consistency" would have been a bug.
theme-aware.
Trade-off worth reviewing
Under a global invert there is one source value per colour, so light and dark
can't be tuned independently. Light-mode borders are consequently more visible
than before (3.20:1, was 1.18:1). That reads as normal design weight and is the
price of dark mode working at all — but it is a visible change to light mode,
so worth a look.
Decoupling them means removing the invert filter, which is THEME.6's problem. The
tokens are the seam where that lands.
Testing
14 new tests that re-derive the filter maths (including guards on the measuring
instrument itself — white-on-black must be 21:1) and assert both tokens clear
3:1 in both modes. One test asserts the old values would fail, documenting
why the tokens exist. A future "let's soften those borders" tweak now fails
loudly instead of silently restoring the bug.
Suite 1985 passed / 17 skipped.
pre-commit run --all-filesclean.not something I can verify. Worth checking both themes, and specifically that
light mode's heavier borders read as intentional.
🤖 Generated with Claude Code