Skip to content

Instantly share code, notes, and snippets.

@BrendonKoz
Last active July 5, 2025 11:03
Show Gist options
  • Save BrendonKoz/b1df234fe3ee388b402cd8e98f7eedbd to your computer and use it in GitHub Desktop.
Save BrendonKoz/b1df234fe3ee388b402cd8e98f7eedbd to your computer and use it in GitHub Desktop.
Automatic Dark Mode for Leaflet.js
// Leaflet JS - note the *className* attribute
// [...]

L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
    className: 'map-tiles'
}).addTo(map);

// [...]
/* Only the necessary CSS shown */

:root {
    --map-tiles-filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);
}

@media (prefers-color-scheme: dark) {
    .map-tiles {
        filter:var(--map-tiles-filter, none);
	}
}

/** The non-dark mode map will be untouched since it's not styled. **/
/**
    Source: https://github.com/pkrasicki/issviewer
    Discovered via: https://github.com/openstreetmap/openstreetmap-website/issues/2332
**/
@patrickdalla
Copy link

Hi, I was using a similar approach discovered by other means. But my project manager found it too CPU intensive.
Does any one know if there are tile server urls with already applied dark theme? It seems google has.

@BrendonKoz
Copy link
Author

I've found a couple, but both require some sort of paid subscription.

  1. https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png
  2. https://tile.jawg.io/dark/{z}/{x}/{y}.png

I didn't look into the subscription(s) as I'm not personally using them (nor will I spend time to research them), but it may lead you to some option. It is possible to generate your own tile maps and serve them yourself, though I suspect that unless you know you'll only be supporting a small geographic area (and intend to keep up with geographic updates/changes) that could be problematic too.

Depending on the device(s) that will be viewing the map, CPU should, under normal conditions, be minimal since modern web browsers take advantage of GPU rendering of CSS when available. This method is definitely a tradeoff of aesthetics and resources, however.

Here's a video of one person having CPU resource issues while using CSS' filter() properties. Always good to keep these scenarios in mind.

@carloskim123
Copy link

works for me too thanks

@fitsum
Copy link

fitsum commented Aug 21, 2024

hits the spot, can worth with this. thank you πŸ€™πŸΎ

@bloodykheeng
Copy link

bloodykheeng commented Apr 27, 2025

map failed to work with tailwind if i switch the theme map doesnt switch layer unless i change page and come back
className dark:map-tiles is failing

this is failing

        <TileLayer
                                    className="dark:map-tiles"
                                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                                    attribution='&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                                />

anyone with a solution on how i can archieve dark mode light mode switching with tailwind im using nextjs 15

<MapContainer
                                center={[1.3733, 32.2903]}
                                zoom={7}
                                scrollWheelZoom={true}
                                attributionControl={false}
                                whenReady={handleMapCreated}
                                fullscreenControl={true}
                                // whenCreated={handleMapCreated}
                                style={{ height: "80vh", width: "100%", zIndex: "0" }}
                                ref={mapRef}
                            >
                                {/* <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution='&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' /> */}


                                <TileLayer
                                    className="dark:map-tiles"
                                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                                    attribution='&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                                />

                                <LayersControl>
                                    <BaseLayer checked name="OpenStreetMap">
                                        <TileLayer className="dark:map-tiles" url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' />
                                    </BaseLayer>

                                    <BaseLayer name="Terrain View">
                                        <TileLayer url="https://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}" maxZoom={20} subdomains={["mt1", "mt2", "mt3"]} />
                                    </BaseLayer>

                                    <BaseLayer name="Satellite View">
                                        <TileLayer url="https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}" maxZoom={20} subdomains={["mt1", "mt2", "mt3"]} />
                                    </BaseLayer>
                                    <BaseLayer name="Hybrid View">
                                        <TileLayer url="https://{s}.google.com/vt/lyrs=h&x={x}&y={y}&z={z}" maxZoom={20} subdomains={["mt1", "mt2", "mt3"]} />
                                    </BaseLayer>
                                </LayersControl>

                                {filteredDistrictsGeoJson && <GeoJSON data={filteredDistrictsGeoJson} onEachFeature={onEachDistrict} style={geoJsonDistrictStyles} />}
                                {filteredRegionsGeoJson && <GeoJSON data={filteredRegionsGeoJson} onEachFeature={onEachDistrict} style={geoJsonDistrictStyles} />}

                                <MarkerClusterGroup chunkedLoading>
                                    {getProjectsWithReportsQuery.data?.data?.data.map((project: any, index: number) => {
                                        // Parse latitude and longitude values
                                        const latitude = parseFloat(project?.latitude);
                                        const longitude = parseFloat(project?.longitude);

                                        // Check if latitude and longitude are valid numbers before creating the Marker
                                        if (isNaN(latitude) || isNaN(longitude)) {
                                            // console.warn(`project map Skipping marker at index ${index} due to invalid coordinates: (${project.latitude}, ${project.longitude})`);
                                            return null; // Skip this marker
                                        }

                                        return (
                                            <Marker key={index} position={[latitude, longitude]}>
                                                <Popup>{createMarkerPopup(project)}</Popup>
                                            </Marker>
                                        );
                                    })}
                                </MarkerClusterGroup>

                                <MapPrint />
                            </MapContainer>
                        </div>
                    </>

here is the css ive put in my globals.css

/* make the react leaflet map dark moded */
/* got the solution here https://gist.github.com/BrendonKoz/b1df234fe3ee388b402cd8e98f7eedbd */
:root {
  --map-tiles-filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg)
    saturate(0.3) brightness(0.7);
}

@media (prefers-color-scheme: dark) {
  .map-tiles {
    filter: var(--map-tiles-filter, none);
  }
}

@BrendonKoz
Copy link
Author

@bloodykheeng I don't use any nextjs, so I'm unfamiliar with its component templating. Unless you're manipulating things dynamically in a far different manner, the class name of the element should remain the same whether it's in light mode or dark mode - the CSS handles the switching; can't you just assign it a class name without the "dark" prefix?

@acharyarupak391
Copy link

that'll do the job thanks πŸ˜„πŸ‘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment