feat: pure black dark mode with blue story links - #31
Merged
Conversation
Dark mode rendered every link white, because UIkit's .uk-light sets
`a { color: #fff }`. That erased the distinction between a story title
and ordinary text. It also sets `.uk-text-primary { color: #fff
!important }`, which silently removed the blue high-score highlight, so
a 328-point story looked identical to a 55-point one.
Story links and the score highlight are now #58a6ff. Measured against
the new background it is 8.3:1, clearing WCAG AAA; UIkit's own #1e87f0
only reaches 5.8:1 and fails AA outright on the previous #222.
Background is pure black so OLED pixels switch off rather than draw dark
grey. That made the #DDD thread borders glaring -- 15:1 against the page,
louder than the text beside them -- so they drop to #2a2a2a, with the
hover border reusing the link blue.
The dark palette is now gated behind .hn-dark, which the script adds.
Previously the dark background came from the media query alone while the
text colour came from .uk-light via JS, so with JS disabled the page
rendered #666 text on a #222 background: a contrast ratio of 1.9:1.
It now falls back to the light palette, which is at least readable.
The script moves to <head> and runs synchronously so the background is
set before first paint instead of flashing white, and it listens for
media-query changes rather than re-checking twice a second for the life
of the tab.
Verified with Chrome over the DevTools protocol, emulating each colour
scheme and reading computed styles: dark gives rgb(0,0,0) background
with rgb(88,166,255) links and score, borders rgb(42,42,42); with script
execution disabled the html element carries no class and the background
stays white.
The story detail page title stayed white. Its link sits in an <h3>, so neither the .story nor the table selector reached it -- and that page is exactly the one the change was asked for. Confirmed in the browser at rgb(255,255,255); the heading now carries .story and reads rgb(88,166,255) like the listing. Validators were built only from database rows, so a template change alone kept serving 304 to anyone holding an older ETag. A busy listing recovers on the next worker write, but a settled thread never changes, so it could serve the old stylesheet indefinitely. A digest of the templates now feeds the key: touching a view changes TEMPLATE_VERSION and every ETag with it. Corrected a wrong comment: a:visited is a class-level component more specific than the rule above it, so it wins on specificity, not source order. Also softened the claim that white links are indistinguishable from body text -- uk-light body text is translucent white, so the difference is small rather than absent. Loosened the dark-mode test, which asserted exact CSS and JS source and would have failed on reformatting while passing on broken rendering. It now checks only that the dark background cannot apply without the scripted class, and says so.
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.
Story titles rendered white in dark mode. Two separate UIkit rules caused it:
The second one is the quieter bug: a 328-point story looked identical to a 55-point one, because the blue
uk-text-primaryhighlight was overwritten.Colour choice
Picked by measurement rather than eye. Against the new pure-black background:
#1e87f0#58a6ffPure black for OLED
Background moves from
rgb(34,34,34)to#000so OLED pixels switch off. That exposed the#DDDthread borders at 15:1 against the page — louder than the text beside them — so they drop to#2a2a2a, with hover reusing the link blue.A no-JS bug this fixes
Dark mode previously took the background from the media query but the text colour from
.uk-light, which only JS applies. With JS off that rendered#666on#222— 1.9:1, unreadable. The palette is now gated behind.hn-dark, so it falls back to the light theme instead.The script also moves to
<head>so the background is set before first paint rather than flashing white, and listens for media-query changes instead of re-checking twice a second for the life of the tab.Verification
Driven through Chrome over the DevTools protocol, emulating each scheme and reading computed styles:
rgb(0,0,0)rgb(255,255,255)rgb(255,255,255)rgb(88,166,255)rgb(13,13,13)rgb(88,166,255)rgb(30,135,240)rgb(42,42,42)<html>classhn-darkReview-driven fixes
The detail page was still white. The title link there sits in an
<h3>, so neither selector reached it — and that's the page the request was about. Measured atrgb(255,255,255), nowrgb(88,166,255).Deploys couldn't retire cached CSS. ETags came only from database rows, so a template change kept returning 304 to clients holding an old validator. A busy listing recovers on the next worker write; a settled thread never changes and could serve the old stylesheet indefinitely. Templates now feed a
TEMPLATE_VERSIONdigest — verified that touching a view changes every ETag.A comment of mine was wrong about
a:visitedneeding to follow another rule for source order; it's more specific and wins regardless.Tests: 119 runs, 320 assertions.