Recolor the default map styles with a softer palette

This commit is contained in:
codecalm committed 2026-10-04 17:27:09 +02:00
1 parent 9fae876516
commit 993416bfd8
6 files changed
+121 -4

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"@tabler/preview": minor
"@tabler/docs": patch
---
Updated the map demos with softer map colors: a `tablerMapPalette()` script recolors the `positron` and `dark` styles.
+18 -1
View File
@@ -33,10 +33,13 @@ classnames:
---
import Example from '@components/Example.astro';
import Map from '@shared/components/demo/Map.astro';
import MapPalette from '@shared/components/demo/MapPalette.astro';
import Icon from '@ui/Icon.astro';
import { Code } from 'astro:components';
import { site } from '@shared/lib/site.ts';
<MapPalette />
## Overview
The map is a small wrapper around [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/), an open-source map library. Tabler gives you the container markup and a short init script. MapLibre draws the tiles, handles zoom, and renders the markers.
@@ -107,7 +110,7 @@ Set `style` to the URL of any MapLibre style. OpenFreeMap has five of them:
| Style | Result |
| --- | --- |
| `https://tiles.openfreemap.org/styles/positron` | Light gray map, used by the Tabler demos. Markers and data stand out on it. |
| `https://tiles.openfreemap.org/styles/positron` | Light gray map, used by the Tabler demos with [softer colors](#map-colors). Markers and data stand out on it. |
| `https://tiles.openfreemap.org/styles/dark` | Dark gray map, used by the demos in dark mode. |
| `https://tiles.openfreemap.org/styles/liberty` | Street map in color. |
| `https://tiles.openfreemap.org/styles/bright` | Street map with stronger colors. |
@@ -117,6 +120,20 @@ Set `style` to the URL of any MapLibre style. OpenFreeMap has five of them:
<Map mapId="light" />
</Example>
### Map colors
The Tabler demos make the `positron` and `dark` styles softer: near-white land, pale water and muted labels in light mode, and blue-gray water and roads in dark mode. They do it by changing the paint values of the style layers after the style loads:
```js
map.on('style.load', function () {
map.setPaintProperty('background', 'background-color', '#fafaf8')
map.setPaintProperty('water', 'fill-color', '#d4dadc')
map.setPaintProperty('boundary_2', 'line-color', '#e3cdd0')
})
```
The full list of colors for both modes is in the `tablerMapPalette()` function in the source of the [maps preview page](https://preview.tabler.io/maps.html). Copy it to get the same look. The layer names belong to the OpenFreeMap styles, so the function works only with them.
Styles from other providers work the same way, as long as they follow the MapLibre style format. Check the terms of each provider, as many of them need an API key or a paid plan.
### Markers
+7 -1
View File
@@ -1,10 +1,12 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import CaptureScript from '@shared/components/CaptureScript.astro'
import MapPalette from '@components/demo/MapPalette.astro'
---
<DefaultLayout title="Full Size Map" pageHeader={false} pageMenu="plugins.map-fullsize" pageLibs={['maplibre-gl']} navbarPosition="vertical" wrapperFull>
<h1 class="visually-hidden">Full Size Map</h1>
<MapPalette />
<div class="map flex-fill" id="map-fullsize" role="img" aria-label="Map of Berlin"></div>
<CaptureScript>
<!-- BEGIN MAP SCRIPT -->
@@ -12,7 +14,8 @@ import CaptureScript from '@shared/components/CaptureScript.astro'
window.tabler_map ??= {}
function initMap() {
const themeStyle = () => `https://tiles.openfreemap.org/styles/${document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'dark' : 'positron'}`
const theme = () => (document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'dark' : 'light')
const themeStyle = () => `https://tiles.openfreemap.org/styles/${theme() === 'dark' ? 'dark' : 'positron'}`
const map = new maplibregl.Map({
container: 'map-fullsize',
@@ -23,6 +26,9 @@ import CaptureScript from '@shared/components/CaptureScript.astro'
map.addControl(new maplibregl.NavigationControl())
// Softer map colors, set again each time the style loads
map.on('style.load', () => tablerMapPalette(map, theme()))
// Change the map style when the page theme changes
new MutationObserver(() => map.setStyle(themeStyle())).observe(document.documentElement, { attributeFilter: ['data-bs-theme'] })
+2
View File
@@ -1,6 +1,7 @@
---
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Map from '@components/demo/Map.astro'
import MapPalette from '@components/demo/MapPalette.astro'
import Card from '@ui/Card.astro'
import CardBody from '@ui/CardBody.astro'
import maps from '@data/maps.json'
@@ -78,6 +79,7 @@ const places = [
<DefaultLayout title="Maps" pageMenu="plugins.maps" pageLibs={['maplibre-gl']} description="Interactive MapLibre maps — plain, styled, with custom markers and popups.">
<DocsLink slot="page-header-actions" path="/ui/components/map" />
<MapPalette />
<CaptureScript>
<script is:inline>
// MapLibre draws on a canvas, so a layer needs a plain rgb() color instead of a CSS variable
+13 -2
View File
@@ -1,6 +1,7 @@
---
// MapLibre GL map on OpenFreeMap tiles: no account and no access token.
// The map follows the page theme: `style` in light mode, `darkStyle` in dark.
// With <MapPalette /> on the page, the default styles get softer colors.
// Markers written in HTML go in the default slot: each child has `data-lng`
// and `data-lat`, holds the marker element, and optionally a `<template>` with
// the popup content. Renders nothing unless maps[mapId] exists or `center` is set.
@@ -45,6 +46,8 @@ const mapContainerId = `map-${mapId}`;
const styleUrl = (name: string) => `https://tiles.openfreemap.org/styles/${name}`;
const style = styleUrl(data?.style ?? 'positron');
const darkStyle = styleUrl(data?.darkStyle ?? 'dark');
// The softer palette (MapPalette.astro) is made for these two styles only
const palette = { light: !data?.style || data.style === 'positron', dark: !data?.darkStyle || data.darkStyle === 'dark' };
const controls = data?.controls ?? false;
const zoom = data?.zoom ?? 13;
const center = data?.center ? [data.center[1], data.center[0]] : [13.4049, 52.518827];
@@ -74,11 +77,12 @@ const ratioClass = data?.ratio ?? ratio ?? '16x9';
data && (
<CaptureScript>
<!-- BEGIN MAP -->
<script is:inline define:vars={{ mapContainerId, style, darkStyle, controls, globe, zoom, center, markers }}>
<script is:inline define:vars={{ mapContainerId, style, darkStyle, palette, controls, globe, zoom, center, markers }}>
window.tabler_map ??= {};
function initMap() {
const themeStyle = () => (document.documentElement.getAttribute('data-bs-theme') === 'dark' ? darkStyle : style);
const theme = () => (document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'dark' : 'light');
const themeStyle = () => (theme() === 'dark' ? darkStyle : style);
const map = new maplibregl.Map({
container: mapContainerId,
@@ -87,6 +91,13 @@ const ratioClass = data?.ratio ?? ratio ?? '16x9';
center,
});
// Softer map colors, when the page has the palette script
map.on('style.load', () => {
if (window.tablerMapPalette && palette[theme()]) {
window.tablerMapPalette(map, theme());
}
});
if (controls) {
map.addControl(new maplibregl.NavigationControl());
}
+75
View File
@@ -0,0 +1,75 @@
---
// Softer colors for the OpenFreeMap `positron` and `dark` map styles: near-white
// land, pale water and muted labels. Defines `window.tablerMapPalette(map, mode)`;
// put it on the page once, before the maps. `Map.astro` calls it on each style load.
import CaptureScript from '../CaptureScript.astro';
---
<CaptureScript>
<!-- BEGIN MAP PALETTE -->
<script is:inline>
// Paint values by layer id: the first rule that matches a layer is used
window.tablerMapPalette = (map, mode) => {
const palettes = {
light: [
[/^background$/, { 'background-color': '#fafaf8' }],
[/^(park|landcover_wood)$/, { 'fill-color': 'rgba(234, 241, 233, 0.6)' }],
[/^water$/, { 'fill-color': '#d4dadc' }],
[/^waterway$/, { 'line-color': '#d1dbdf' }],
[/^landcover_(ice_shelf|glacier)$/, { 'fill-color': '#fafaf8' }],
[/^landuse_residential$/, { 'fill-color': 'rgba(237, 237, 237, 0.5)' }],
[/^building$/, { 'fill-color': '#ededed', 'fill-outline-color': '#dfdfdf' }],
[/^road_area_pier$/, { 'fill-color': '#fafaf8' }],
[/^road_pier$/, { 'line-color': '#fafaf8' }],
[/^highway_path$/, { 'line-color': '#ededed' }],
[/^highway_minor$/, { 'line-color': '#e6e6e6' }],
[/_casing$/, { 'line-color': '#dddddd' }],
[/^tunnel_.*_inner$/, { 'line-color': '#f3f3f3' }],
[/^highway_.*_inner$/, { 'line-color': '#ffffff' }],
[/_subtle$/, { 'line-color': '#e6e6e6' }],
[/^railway.*dashline$/, { 'line-color': '#fafaf8' }],
[/^railway/, { 'line-color': '#e3e3e3' }],
[/^boundary_/, { 'line-color': '#e3cdd0' }],
[/^waterway_line_label$/, { 'text-color': '#7a96a0' }],
[/^water_name/, { 'text-color': '#abb6be' }],
[/^highway-name/, { 'text-color': '#9aa5ad', 'text-halo-color': 'rgba(255, 255, 255, 0.8)' }],
[/^(airport|label_country|label_state)/, { 'text-color': '#8a99a4', 'text-halo-color': 'rgba(255, 255, 255, 0.8)' }],
[/^label_/, { 'text-color': '#697b89', 'text-halo-color': 'rgba(255, 255, 255, 0.8)' }],
],
dark: [
[/^background$/, { 'background-color': '#0e0e0e' }],
[/^water$/, { 'fill-color': '#2c353c' }],
[/^waterway$/, { 'line-color': 'rgb(63, 90, 109)' }],
[/^landcover_(ice_shelf|glacier)$/, { 'fill-color': '#0e0e0e' }],
[/^landuse_residential$/, { 'fill-color': 'rgba(0, 0, 0, 0.5)' }],
[/^(landcover_wood|landuse_park)$/, { 'fill-color': '#121412' }],
[/^building$/, { 'fill-color': '#1a1a1a' }],
[/^road_area_pier$/, { 'fill-color': '#0e0e0e' }],
[/^road_pier$/, { 'line-color': '#0e0e0e' }],
[/^highway_path$/, { 'line-color': '#232323' }],
[/^highway_minor$/, { 'line-color': 'rgba(65, 71, 88, 0.7)' }],
[/_casing$/, { 'line-color': '#232323' }],
[/^highway_major_inner$/, { 'line-color': 'rgb(65, 71, 88)' }],
[/^highway_motorway_inner$/, { 'line-color': 'rgb(83, 86, 102)' }],
[/_subtle$/, { 'line-color': 'rgba(65, 71, 88, 0.6)' }],
[/^railway.*dashline$/, { 'line-color': '#0e0e0e' }],
[/^railway/, { 'line-color': '#262626' }],
[/^boundary_/, { 'line-color': 'rgb(96, 96, 96)' }],
[/^water_name$/, { 'text-color': 'rgb(109, 123, 129)', 'text-halo-color': 'rgba(0, 0, 0, 0.7)' }],
[/^highway_name/, { 'text-color': 'rgb(120, 126, 137)', 'text-halo-color': 'rgba(0, 0, 0, 0.7)' }],
[/^place_(country|state)/, { 'text-color': 'rgb(118, 126, 137)' }],
[/^place_/, { 'text-color': 'rgb(204, 208, 228)' }],
],
};
map.getStyle().layers.forEach((layer) => {
const rule = palettes[mode].find(([pattern]) => pattern.test(layer.id));
if (rule) {
Object.entries(rule[1]).forEach(([property, value]) => map.setPaintProperty(layer.id, property, value));
}
});
};
</script>
<!-- END MAP PALETTE -->
</CaptureScript>