Repository navigation
Adopt the DMC house style, and a light theme - #141
Merged
Merged
Conversation
First of three parts on #139. The app was its own island: teal accent on a teal-dark ground, Avenir Next throughout, 125 hex literals against 90 token uses, and a tricolour stripe in colours that were not the brand's. Tokens now come from DutchMeshCore-Branding/STYLE-GUIDE.md under their canonical names — --text-muted, --text-faint, --surface, --border — with the brand constants (--red/--white/--blue/--green/--yellow) and semantic state (--success/--warning/--danger) on top. No bridge aliases: an alias would keep the divergence alive, which is the thing this issue is about. Every chrome literal moved into :root; the only hex left outside it is one in a comment. Type: Aldrich for headings and the brand, IBM Plex Sans for body, IBM Plex Mono for data, served as woff2 from assets/fonts/ (75KB total) and packaged into the image. Headings are uppercase with the house drop shadow. Ground: the red/blue glow over a dark fade, plus the grid and scanline overlays the guide layers on it, all behind a z-index:1 content layer. Panels take the glass fill instead of a flat white wash. Geometry: one --radius (8px) for controls and --radius-lg (16px) for large surfaces, replacing eight ad-hoc values between 8 and 24px. The brand stripe becomes what the guide calls for: a full-width band in normal flow above the navbar, rather than a 4px vertical bar beside it — the same idea in the wrong orientation, in near-flag hexes instead of the brand ones. Refs #139 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Second of three on #139. The app had no light theme at all — no data-theme, no prefers-color-scheme, nothing. Light is an override on the same token names, so no rule below the :root blocks changed. Dark stays the default: no attribute on <html> means dark. A pre-paint script in <head> applies the saved choice before the stylesheet renders, so a light user never gets a flash of dark, and color-scheme is set per root block so native selects and scrollbars follow. The picker is dark | light | system, persisted under the canonical meshcore-theme key so the choice carries across the DMC subdomains, following system as it changes. It reuses the topbar's existing <details> dropdown so the bar has one pattern, with the guide's menuitemradio + checkmark semantics. Getting there meant tokenising 64 more rgba() literals by role — fields, menus, scrims, hover fills, borders — since a hardcoded rgba(5,15,22,.9) field stays dark whatever the theme says. Their dark values also moved off the old teal tints onto the neutral near-black ground. Two literals stay: the text-shadow outline on map labels, which has to read against a basemap that is light in both themes. Refs #139 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Last of three on #139. Thirty colour literals were spread through the MapLibre layer definitions — a magenta for coverage, lime for observers, a blue-green- yellow-orange-red rainbow for heat, teal for links — none of them from the palette, several of them fighting each other for the same meaning. They now come from one MAP object with three roles, so six layers do not compete over four hues: green what is known observers, proven links, support nodes blue what is assumed coverage ranges, the selected candidate region warm where the target is heat ramp, estimate markers The heat ramp runs --yellow to --red with three interpolations between them, so hotter reads as one direction instead of a rainbow that collided with the other two roles. Unselected candidates and the clue hull drop to neutral. These stay literals rather than reading CSS tokens, for two reasons: MapLibre paint takes colour strings, and the basemap is the light OpenStreetMap raster in BOTH themes — a colour that flipped with the theme would be unreadable half the time. They are the brand constants, which are theme-identical by definition. Refs #139 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The z-index:1 content layer added with the background overlays put .topbar and .shell on the same level. At equal z-index the later element in the document wins, so the map strip's buttons — Ranges, Links, Heat, Relief — cut straight through the What's new panel and the theme menu hanging down from the topbar. The panels' own z-index:20 could not help: it only ranks them inside the topbar's stacking context, not against the shell's. The topbar goes to z-index:2. Verified by hit-testing the centre of each open panel: both now return themselves as the topmost element. Refs #139 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"One or two sentences" was still too loose: two long sentences render as five or six lines in a 250px panel, which is the wall of text the cap was meant to prevent. All eight of today's entries come down to ~120 characters, and AGENTS.md now says lines rather than sentences. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cooperativeGestures was on everywhere, so every zoom needed cmd/ctrl. It is there for a real reason (#117): on the stacked layout the map is full-width and sits in the page's scroll path, so without it a swipe over the map pans the map and you can never scroll past it. None of that holds from 861px up, where the map is a sticky column of its own and the page scrolls beside it. So the gesture mode follows the layout instead of being global, and syncMapGestures() keeps it in step when a window crosses the breakpoint. Verified by fresh load at both sizes: 375px keeps cooperative gestures on, 1200px has them off with scrollZoom enabled. The mid-session crossing is verified by calling the sync directly — neither resize nor matchMedia change fires under the test harness's viewport emulation, which a probe listener confirmed, so both are wired and only the initial state was observable end-to-end. Closes #140 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 triangulator was its own island: teal accent on a teal-dark ground, Avenir Next throughout, no light theme, and a tricolour stripe in colours that were not the brand's. DutchMeshCore-Branding is the source of truth for the DMC identity and the app had never been held against it.
Closes #139, #140.
1 · Tokens, type, background (
65bd110)Canonical token names from
STYLE-GUIDE.md—--text-muted,--text-faint,--surface,--border— with the brand constants and semantic state on top. No bridge aliases: an alias keeps the divergence alive, which is the thing the issue is about.assets/fonts/(75KB) and packaged into the image.--radius(8px) and--radius-lg(16px), replacing eight ad-hoc values between 8 and 24px.Every chrome literal moved into
:root. The only hex left outside it is in a comment.2 · Light theme (
8168d19)There was none — no
data-theme, noprefers-color-scheme, nothing. Light is an override on the same token names, so no rule below:rootchanged. Pre-paint script in<head>so a light user never sees a flash of dark;color-schemeper root block so native selects and scrollbars follow.dark | light | system, under the canonical
meshcore-themekey so the choice carries across the DMC subdomains, following system as it changes.This meant tokenising 64 more
rgba()literals by role: a hardcodedrgba(5,15,22,.9)field stays dark whatever the theme says.3 · Map colours (
aec4615)Thirty literals across the MapLibre layers — magenta coverage, lime observers, a blue-green-yellow-orange-red heat rainbow, teal links — several of them fighting for the same meaning. Now one
MAPobject with three roles:The heat ramp runs
--yellowto--redwith three interpolations, so hotter reads as one direction instead of a rainbow colliding with the other two roles.These stay literals rather than reading CSS tokens for two reasons: MapLibre paint takes colour strings, and the basemap is the light OSM raster in both themes, so a theme-flipping colour would be unreadable half the time. They are brand constants, which are theme-identical by definition.
4 · Three fixes found on the way
6637a24— thez-index: 1content layer put.topbarand.shellon the same level, and at equal z-index the later element wins, so the map strip's buttons cut through the What's new and theme panels. Their ownz-index: 20could not help: it only ranks them inside the topbar's context.0841e60(Scroll-to-zoom needs a modifier key on desktop, where nothing requires it #140) —cooperativeGestureswas global, so every zoom needed cmd. It exists for the stacked layout (On a phone the map traps the scroll, results appear off-screen and the top bar takes three rows #117), where the map sits in the page's scroll path; from 861px up the map is its own sticky column and there is nothing to be trapped in.c5cb545— changelog entries capped at one or two lines, not sentences; two long sentences already render as six lines in that panel.Result
var(--token)uses:rootin CSSVerified
Full run at 1200×780 and 375×812 in both themes: discovery, lock, estimate, the map layers painting green/blue/warm as designed, both dropdowns hit-tested as topmost, fields and menus following the theme, fresh loads confirming the gesture mode at each size.
Not verified end-to-end: a window crossing the 861px breakpoint mid-session. Neither
resizenormatchMedia changefires under the test harness's viewport emulation — a probe listener confirmed zero events — so that path was verified by calling the sync directly.I hit the upstream feeds' rate limits twice while testing, which
AGENTS.mdwarns against.🤖 Generated with Claude Code