Skip to content

Commit

Permalink
Dark mode (#135)
Browse files Browse the repository at this point in the history
Fixes #125 

Add support for Dark mode.

By default the *system* mode is used, which auto-selects the light or
dark mode based on system preferences. The user can select light or dark
mode explicitly in the settings.

Uses Bootstrap's dark mode support.
https://getbootstrap.com/docs/5.3/customize/color-modes/#enable-dark-mode

Requires patching some icons / colors in the MapLibre GL JS UI.

Hardcoded colors have been put into the `colors` map and are now
theme-dependenent.

The style generation outputs a JSON style file per theme, and the UI
selects the style based on the user's preferences.

The resulting contrast between text and the background is a bit lower
than using light mode.

The colors can be tweaked more in separate PRs if needed.
  • Loading branch information
hiddewie authored Dec 8, 2024
1 parent bd78cbe commit 34da93e
Show file tree
Hide file tree
Showing 6 changed files with 624 additions and 338 deletions.
9 changes: 3 additions & 6 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The import process will filter the file before importing it. The filtered file w

Start the tile server:
```shell
docker compose up --build martin
docker compose up --build --watch martin
```

Prepare and start the API:
Expand All @@ -25,15 +25,12 @@ docker compose up api

Start the web server:
```shell
docker compose up --build martin-proxy
docker compose up --build --watch martin-proxy
```

The OpenRailwayMap is now available on http://localhost:8000.

To ease development, Docker Compose can automatically rebuild containers when dependent files change. Enable *watch mode* with:
```shell
docker compose watch
```
Docker Compose will automatically rebuild and restart the `martin` and `martin-proxy` containers if relevant files are modified.

## Deployment

Expand Down
110 changes: 83 additions & 27 deletions proxy/css/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ body {

height: 400px;
width: 300px;
background: #fff;
background-color: rgba(var(--bs-body-bg-rgb), .9);
border-radius: 4px;
box-shadow: 0 0 0 2px rgba(0, 0, 0, .1);
overflow-y: auto;
Expand All @@ -47,40 +47,96 @@ body {
height: 1400px;
}

.maplibregl-ctrl-edit .maplibregl-ctrl-icon {
background-size: 24px;
background-image: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z' fill='%23000000'/%3E%3C/svg%3E");
/** Patch Maplibre GL to respect the page theme */
#map-container .maplibregl-ctrl-group,
.maplibregl-ctrl-attrib.maplibregl-compact,
.maplibregl-ctrl-attrib a,
.maplibregl-ctrl-scale {
background: var(--bs-body-bg);
color: var(--bs-body-color);
}
.maplibregl-ctrl-scale {
background: rgba(var(--bs-body-bg-rgb), 0.8);
color: var(--bs-body-color);
}
[data-bs-theme="dark"] .maplibregl-ctrl-scale {
border-top-color: var(--bs-border-color);
border-right-color: var(--bs-light);
border-left-color: var(--bs-light);
border-bottom-color: var(--bs-light);
}
[data-bs-theme="dark"] .maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button {
background-color: var(--bs-light);
}
.maplibregl-ctrl-configuration .maplibregl-ctrl-icon {
background-size: 24px;
background-image: url("data:image/svg+xml,%3Csvg width='800' height='800' viewBox='0 0 200 200' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M36.5 75C41 89.5 54 100 70 100s29-10.5 33.5-25H170a10 10 0 0 0 0-20h-66.5C99 40.5 86 30 70 30S41 40.5 36.5 55H25a10 10 0 0 0 0 20ZM70 50a15 15 0 1 1-15 15 14.73 14.73 0 0 1 15-15Zm105 75h-11.5c-4.5-14.5-17.5-25-33.5-25s-29 10.5-33.5 25H30a10 10 0 0 0 0 20h66.5c4.5 14.5 17.5 25 33.5 25s29-10.5 33.5-25H175a10 10 0 0 0 0-20Zm-45 25a15 15 0 1 1 15-15 14.73 14.73 0 0 1-15 15Z'/%3E%3C/svg%3E");
}
.maplibregl-ctrl-group button.maplibregl-ctrl-search {

.maplibregl-ctrl-group button.maplibregl-ctrl-search,
.maplibregl-ctrl-group button.maplibregl-ctrl-legend {
padding: 4px;
width: auto;
font-size: 1rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-search .maplibregl-ctrl-icon {
background-size: contain;
background-image: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.9536 14.9458L21 21M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z' stroke='%23000000' stroke-width='2.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-position-x: left;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-search .maplibregl-ctrl-icon span {
margin-left: 1.5rem;
font-size: 16px;

.maplibregl-ctrl-group button.maplibregl-ctrl-search .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-legend .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-compass .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-edit .maplibregl-ctrl-icon,
.maplibregl-ctrl-group button.maplibregl-ctrl-configuration .maplibregl-ctrl-icon {
background-image: none;
background-color: var(--bs-body-color);
mask-size: contain;
mask-position: center;
display: inline-block;
vertical-align: top;
}

.maplibregl-ctrl-group button.maplibregl-ctrl-legend {
padding: 4px;
width: auto;
.maplibregl-ctrl-group button.maplibregl-ctrl-search .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.9536 14.9458L21 21M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z' stroke='%23000000' stroke-width='2.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
width: 1.5rem;
height: 1.4rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-legend .maplibregl-ctrl-icon {
background-size: contain;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg fill='%23000000' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52 52' enable-background='new 0 0 52 52' xml:space='preserve'%3E%3Cg%3E%3Cpath d='M26.7,42.8c0.8,0,1.5,0.7,1.5,1.5v3.2c0,0.8-0.7,1.5-1.5,1.5h-3.2c-0.8,0-1.5-0.7-1.5-1.5v-3.2 c0-0.8,0.7-1.5,1.5-1.5H26.7z'/%3E%3Cpath d='M28.2,35.1c0-2.1,1.3-4,3.1-4.8h0.1c5.2-2.1,8.8-7.2,8.8-13.2c0-7.8-6.4-14.2-14.2-14.2 c-7.2,0-13.2,5.3-14.2,12.2v0.1c-0.1,0.9,0.6,1.6,1.5,1.6h3.2c0.8,0,1.4-0.5,1.5-1.1v-0.2c0.7-3.7,4-6.5,7.9-6.5 c4.5,0,8.1,3.6,8.1,8.1c0,2.1-0.8,4-2.1,5.5l-0.1,0.1c-0.9,1-2.1,1.6-3.3,2c-4,1.4-6.7,5.2-6.7,9.4v1.5c0,0.8,0.6,1.4,1.4,1.4h3.2 c0.8,0,1.6-0.6,1.6-1.5L28.2,35.1z'/%3E%3C/g%3E%3C/svg%3E");
background-position-x: left;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-legend .maplibregl-ctrl-icon span {
margin-left: 1.5rem;
font-size: 16px;
mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg fill='%23000000' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52 52' enable-background='new 0 0 52 52' xml:space='preserve'%3E%3Cg%3E%3Cpath d='M26.7,42.8c0.8,0,1.5,0.7,1.5,1.5v3.2c0,0.8-0.7,1.5-1.5,1.5h-3.2c-0.8,0-1.5-0.7-1.5-1.5v-3.2 c0-0.8,0.7-1.5,1.5-1.5H26.7z'/%3E%3Cpath d='M28.2,35.1c0-2.1,1.3-4,3.1-4.8h0.1c5.2-2.1,8.8-7.2,8.8-13.2c0-7.8-6.4-14.2-14.2-14.2 c-7.2,0-13.2,5.3-14.2,12.2v0.1c-0.1,0.9,0.6,1.6,1.5,1.6h3.2c0.8,0,1.4-0.5,1.5-1.1v-0.2c0.7-3.7,4-6.5,7.9-6.5 c4.5,0,8.1,3.6,8.1,8.1c0,2.1-0.8,4-2.1,5.5l-0.1,0.1c-0.9,1-2.1,1.6-3.3,2c-4,1.4-6.7,5.2-6.7,9.4v1.5c0,0.8,0.6,1.4,1.4,1.4h3.2 c0.8,0,1.6-0.6,1.6-1.5L28.2,35.1z'/%3E%3C/g%3E%3C/svg%3E");
width: 1.5rem;
height: 1.4rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E");
width: 1.8rem;
height: 1.8rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E");
width: 1.8rem;
height: 1.8rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-compass .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E");
width: 1.8rem;
height: 1.8rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E");
width: 1.8rem;
height: 1.8rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E");
width: 1.8rem;
height: 1.8rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-edit .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z' fill='%23000000'/%3E%3C/svg%3E");
width: 1.5rem;
height: 1.4rem;
}
.maplibregl-ctrl-group button.maplibregl-ctrl-configuration .maplibregl-ctrl-icon {
mask-image: url("data:image/svg+xml,%3Csvg width='800' height='800' viewBox='0 0 200 200' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M36.5 75C41 89.5 54 100 70 100s29-10.5 33.5-25H170a10 10 0 0 0 0-20h-66.5C99 40.5 86 30 70 30S41 40.5 36.5 55H25a10 10 0 0 0 0 20ZM70 50a15 15 0 1 1-15 15 14.73 14.73 0 0 1 15-15Zm105 75h-11.5c-4.5-14.5-17.5-25-33.5-25s-29 10.5-33.5 25H30a10 10 0 0 0 0 20h66.5c4.5 14.5 17.5 25 33.5 25s29-10.5 33.5-25H175a10 10 0 0 0 0-20Zm-45 25a15 15 0 1 1 15-15 14.73 14.73 0 0 1-15 15Z'/%3E%3C/svg%3E");
width: 1.5rem;
height: 1.4rem;
}

.maplibregl-ctrl.maplibregl-ctrl-attrib.maplibregl-compact {
Expand Down
28 changes: 24 additions & 4 deletions proxy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<div id="search-backdrop" class="modal backdrop" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header bg-light">
<div class="modal-header">
<h5 class="modal-title">Search</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="hideSearch()"></button>
</div>
Expand Down Expand Up @@ -94,7 +94,7 @@ <h5 class="modal-title">Search</h5>
<div id="configuration-backdrop" class="modal backdrop" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
<div class="modal-content">
<div class="modal-header bg-light">
<div class="modal-header">
<h5 class="modal-title">Map configuration</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="hideConfiguration()"></button>
</div>
Expand All @@ -116,13 +116,13 @@ <h5 class="modal-title">Map configuration</h5>
</div>
<div class="mb-3">
<label for="backgroundUrl" class="form-label" >Background map tile URL</label>
<div>
<div class="mb-1">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="backgroundType" id="backgroundTypeRaster" value="raster" checked onchange="updateConfiguration('backgroundType', 'raster'); updateBackgroundMapStyle();">
<label class="form-check-label" for="backgroundTypeRaster">Raster</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="backgroundType" id="backgroundTypeVector" value="raster" checked onchange="updateConfiguration('backgroundType', 'vector'); updateBackgroundMapStyle();">
<input class="form-check-input" type="radio" name="backgroundType" id="backgroundTypeVector" value="raster" onchange="updateConfiguration('backgroundType', 'vector'); updateBackgroundMapStyle();">
<label class="form-check-label" for="backgroundTypeVector">Vector</label>
</div>
</div>
Expand All @@ -133,6 +133,26 @@ <h5 class="modal-title">Map configuration</h5>
Vector tiles: A URL specifying a vector style. See <a href="https://wiki.openstreetmap.org/wiki/Vector_tiles#Providers" target="_blank">the wiki</a> for a list of vector tile providers, for example <code>https://americanamap.org/style.json</code>.
</small>
</div>
<div class="mb-3">
<label class="form-label">Theme</label>
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="theme" id="themeSystem" value="system" checked onchange="onThemeChange('system');">
<label class="form-check-label" for="themeSystem">System</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="theme" id="themeLight" value="light" onchange="onThemeChange('light');">
<label class="form-check-label" for="themeLight">Light</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="theme" id="themeDark" value="dark" onchange="onThemeChange('dark');">
<label class="form-check-label" for="themeDark">Dark</label>
</div>
</div>
<small class="form-text">
Control the theme of OpenRailwayMap. The default is to follow the system and browser preferences. A light or dark theme can also be configured.
</small>
</div>
</form>
</div>
</div>
Expand Down
Loading

0 comments on commit 34da93e

Please sign in to comment.