Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/geolibre-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"@dvt3d/maplibre-three-plugin": "^1.7.1",
"@electric-sql/pglite": "^0.5.4",
"@electric-sql/pglite-postgis": "^0.2.4",
"@fontsource-variable/ibm-plex-sans": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"@geolibre/core": "*",
"@geolibre/embed": "*",
"@geolibre/map": "*",
Expand Down
23 changes: 23 additions & 0 deletions apps/geolibre-desktop/src/index.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
@import "@geolibre/ui/globals.css";

/* Supplies the custom-color/border-radius theme that @geolibre/ui's globals.css
relies on via @apply (background, border, etc.). globals.css is intentionally
config-agnostic; the consuming app provides the Tailwind config here. */
@config "../tailwind.config.js";

/* App typeface: IBM Plex Sans for UI, IBM Plex Mono for the numeric/coordinate
readouts (StatusBar, code, feature IDs). One matched superfamily, so the mono
readouts and the surrounding UI share a voice. The @font-face rules are
loaded from main.tsx — see the note there for why they cannot be @imported
here. Self-hosted rather than CDN-loaded: the desktop build must render
offline, and the Tauri CSP is `default-src 'self'`.

This block drives both the `font-sans`/`font-mono` utilities and Tailwind's
preflight default, so no `font-family` on <body> is needed.

Plex covers latin, latin-ext, cyrillic, greek and vietnamese only. The
locales it does NOT cover (ar, fa, hi, ja, ka, ko, th, zh) rely on per-glyph
fallback to the system stack below — which is why that stack must stay a
full, ordered list rather than a bare `sans-serif`. */
@theme {
--font-sans:
"IBM Plex Sans Variable", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
"Noto Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
--font-mono:
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

html,
body,
#root {
Expand Down
10 changes: 10 additions & 0 deletions apps/geolibre-desktop/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import "./lib/symbol-dispose-polyfill";
import React from "react";
import ReactDOM from "react-dom/client";
/* App typeface — see the --font-sans/--font-mono note in index.css.
These must be imported from JS, not via `@import` in index.css: Tailwind v4
resolves CSS @imports itself and inlines them before Vite sees them, so the
relative `url(./files/*.woff2)` in fontsource's CSS is never rewritten into
an asset reference and no font file is emitted into dist/. The result builds
clean and 404s at runtime, silently falling back to system fonts. Importing
from JS routes the CSS through Vite's asset pipeline instead. */
import "@fontsource-variable/ibm-plex-sans/wght.css";
import "@fontsource/ibm-plex-mono/400.css";
import "@fontsource/ibm-plex-mono/700.css";
import "@geoman-io/maplibre-geoman-free/dist/maplibre-geoman.css";
import "@maplibre/maplibre-gl-directions/dist/style.css";
import "maplibre-gl-3d-tiles/style.css";
Expand Down
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/src/components/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 flex max-h-[calc(100dvh-1rem)] w-[calc(100vw-1rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] flex-col overflow-hidden border bg-background shadow-lg duration-200 supports-[not_(max-height:1dvh)]:max-h-[calc(100vh-1rem)] sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 flex max-h-[calc(100dvh-1rem)] w-[calc(100vw-1rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] flex-col overflow-hidden border bg-popover text-popover-foreground shadow-lg duration-200 supports-[not_(max-height:1dvh)]:max-h-[calc(100vh-1rem)] sm:rounded-lg",
className,
)}
{...props}
Expand Down
53 changes: 39 additions & 14 deletions packages/ui/src/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,67 @@
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
/* Neutrals sit at ~16% saturation rather than the stock 40%, so the chrome
reads as grey next to the map instead of tinting blue. --accent is one
step darker than --muted so hover fills register on white surfaces. */
--secondary: 220 16% 96%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted: 220 16% 96%;
/* Darkened from 46.9% L so secondary text meets WCAG AA (4.5:1) even on the
muted/secondary backgrounds it is paired with (e.g. kbd badges). */
--muted-foreground: 215.4 16.3% 40%;
--accent: 210 40% 96.1%;
--accent: 220 16% 94%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
/* --border is decorative (dividers, panel edges); --input is a control
boundary, so it is deliberately darker. See the note in .dark below. */
--border: 220 15% 88%;
--input: 220 13% 76%;
Comment on lines +27 to +30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use a tested control-boundary token for interactive borders.

Line 29 and Line 70 define --border as a decorative token, but Lines 802-823 in packages/plugins/src/plugins/maplibre-dggal.ts use it for actual button borders. The PR also reports that --input remains below 3:1. If these borders are the only visible boundary, MapLibre controls and inputs have insufficient non-text contrast.

Keep --border for decorative edges. Use a dedicated control-boundary token that reaches at least 3:1 in both themes. Update the affected consumers and add contrast checks for normal, hover, and focus states.

Also applies to: 70-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/globals.css` around lines 27 - 30, Keep --border as the
decorative token and introduce a dedicated control-boundary token with at least
3:1 contrast in both themes; update the MapLibre controls in maplibre-dggal.ts
and other affected interactive-border consumers to use it instead of --border or
--input. Add contrast checks covering normal, hover, and focus states, while
preserving existing decorative-edge usage.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on the factual part, and I'm leaving this thread open rather than resolving it, because it's a scope call for a human.

Confirmed: maplibre-dggal.ts does use --border as a real control boundary, not just decoration — :808 (button) and :834 (select), both 1px solid hsl(var(--border)). That's worth recording for the follow-up.

But this PR improves those ratios, it doesn't regress them. The 3:1 gap is pre-existing:

token / surface before after 1.4.11 target
--border on light bg 1.23:1 1.34:1 3:1
--border on dark card 1.37:1 1.62:1 3:1
--input on light bg 1.23:1 1.84:1 3:1
--input on dark card 1.37:1 2.58:1 3:1

So every affected boundary is measurably closer to the target than on main, and none is worse.

Why not close it here: reaching a true 3:1 in light mode needs roughly L 58% — a visibly heavy field outline that changes the look of every input, select and plugin control app-wide. That's a design decision, not a token tweak, and it's the reason the PR description calls it out as explicit follow-up rather than folding it in silently. Your own label agrees (🏗️ Heavy lift).

Leaving open for @giswqs to decide whether to take it now or as a separate PR.

--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}

/* Dark mode is built as an explicit elevation ladder. Previously --background,
--card and --popover were all 222.2 84% 4.9%, and --border, --input,
--muted, --secondary and --accent were all 217.2 32.6% 17.5% — five tokens,
one colour — so panels, dialogs and menus were indistinguishable from the
app canvas and only a 1px border separated them. Each step below is a
distinct surface:

background 9% app canvas / base chrome
card 12% toolbar, docked panels
popover 15% menus, tooltips, dialogs
muted 20% inset fills (status bar, wells)
accent 22% hover fill — must read above popover, since menu items
sit on it

Saturation also drops from 84% to ~22%: the old value was a strongly
blue-tinted near-black that clashed with satellite imagery in the map view.
Every foreground/surface pair here clears WCAG AA 4.5:1 (lowest is
muted-foreground on accent at 4.82:1). */
.dark {
--background: 222.2 84% 4.9%;
--background: 222 24% 9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card: 222 22% 12%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover: 222 21% 15%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary: 220 18% 20%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted: 220 18% 20%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent: 220 18% 22%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
--border: 220 14% 26%;
--input: 220 12% 38%;
/* Matches --primary, as every non-default accent scheme below already does,
so the focus ring stays visible against the lifted surfaces. */
--ring: 217.2 91.2% 59.8%;
}

/* Accent color schemes. Each scheme overrides only the accent-bearing tokens
Expand Down
Loading