# Using ELEMNT map styles

Every ELEMNT style is a standard **MapLibre GL JSON style (spec v8)** on an **OSM PMTiles** basemap.
Pick a style in the gallery, copy its style URL (the "Copy URL" button), and drop it in.

- Style URL looks like `https://carto.elemnt.earth/<style>/style.json` (plus `style.dark.json` for dark).
- The basemap is a single PMTiles file, so you must **register the PMTiles protocol** before creating the map.
- Keep the **OpenStreetMap** (ODbL) and **Protomaps** (BSD) attribution visible.
- **No API key and no tile server** are required.

Replace `STYLE_URL` below with the URL you copied.

---

## Plain HTML (CDN, single file)

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css" rel="stylesheet">
  <style>html,body,#map{height:100%;margin:0}</style>
</head>
<body>
  <div id="map"></div>
  <script src="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js"></script>
  <script src="https://unpkg.com/pmtiles@4/dist/pmtiles.js"></script>
  <script>
    maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
    new maplibregl.Map({ container: "map", style: "STYLE_URL", center: [10, 30], zoom: 3 })
      .addControl(new maplibregl.NavigationControl());
  </script>
</body>
</html>
```

## Node / bundler (Vite, webpack, etc.)

```bash
npm i maplibre-gl pmtiles
```
```js
import maplibregl from "maplibre-gl";
import { Protocol } from "pmtiles";
import "maplibre-gl/dist/maplibre-gl.css";

maplibregl.addProtocol("pmtiles", new Protocol().tile);

new maplibregl.Map({ container: "map", style: "STYLE_URL", center: [10, 30], zoom: 3 })
  .addControl(new maplibregl.NavigationControl());
```

## React (react-map-gl, MapLibre mode)

```bash
npm i react-map-gl maplibre-gl pmtiles
```
```jsx
import maplibregl from "maplibre-gl";
import { Protocol } from "pmtiles";
import Map, { NavigationControl } from "react-map-gl/maplibre";
import "maplibre-gl/dist/maplibre-gl.css";

maplibregl.addProtocol("pmtiles", new Protocol().tile);

export default function App() {
  return (
    <Map mapLib={maplibregl} mapStyle="STYLE_URL"
         initialViewState={{ longitude: 10, latitude: 30, zoom: 3 }}
         style={{ width: "100%", height: "100vh" }}>
      <NavigationControl position="top-right" />
    </Map>
  );
}
```

## Leaflet (maplibre-gl-leaflet)

```bash
npm i leaflet maplibre-gl pmtiles @maplibre/maplibre-gl-leaflet
```
```js
import L from "leaflet";
import maplibregl from "maplibre-gl";
import { Protocol } from "pmtiles";
import "@maplibre/maplibre-gl-leaflet";
import "leaflet/dist/leaflet.css";

maplibregl.addProtocol("pmtiles", new Protocol().tile);

const map = L.map("map").setView([30, 10], 3);
L.maplibreGL({ style: "STYLE_URL" }).addTo(map);
```

---

## Light and dark toggle

Each style ships `style.json` (light) and `style.dark.json` (dark). Swap the URL to switch:

```js
function setMode(mode){
  map.setStyle(mode === "dark"
    ? "https://carto.elemnt.earth/<style>/style.dark.json"
    : "https://carto.elemnt.earth/<style>/style.json");
}
```

## Troubleshooting: blank map, but tiles still load

**Symptom:** the map area renders empty (just the style's background colour, or nothing), yet the Network tab shows `style.json`, glyphs, sprites and the `.pmtiles` range requests all succeeding (HTTP 200/206). No JavaScript errors.

**Cause:** the map container collapses to 0 height. MapLibre adds the class `maplibregl-map` to the element you pass as `container`, and its stylesheet declares:

```css
.maplibregl-map { position: relative; overflow: hidden; }
```

If you size that element with utility classes that also set `position` (for example Tailwind's `absolute inset-0`), both rules have the same specificity, so whichever stylesheet loads last wins. When `maplibre-gl.css` is injected after your framework or utility CSS (common with Vite, Turbopack or webpack, especially in dev), `.maplibregl-map { position: relative }` overrides your `position: absolute`. Once the element is `relative`, `inset: 0` no longer stretches it, so it collapses to 0 height. The canvas then has no size to draw into, so the map is blank. Tiles still load because the map engine runs fine, it just has nowhere to paint.

**Fix:** give the container a size MapLibre's CSS cannot override. Any one of:

1. Inline styles on the container (inline always beats stylesheet rules):

```jsx
<div ref={mapRef} style={{ position: "absolute", inset: 0 }} />
```

2. An explicit height that does not depend on positioning (the parent needs a definite height):

```css
#map { width: 100%; height: 100%; }  /* or: height: 600px; */
```

3. Import `maplibre-gl/dist/maplibre-gl.css` before your utility or framework CSS, so your own classes win the cascade.

**Quick check (DevTools):** inspect the `.maplibregl-map` element. If its computed `position` is `relative` despite an `absolute` utility class, and its height is `0`, this is the cause.

This is not specific to ELEMNT styles. It is a CSS load-order conflict between `maplibre-gl.css` and utility CSS (such as Tailwind), and can affect any project that uses utility classes to lay out the map container.

## Notes
- The styles use a `pmtiles://` source, so they need **MapLibre GL JS** plus the `pmtiles` library (`addProtocol`).
- Standard **Mapbox GL JS** does not read `pmtiles://` without an extra plugin, so MapLibre (or a MapLibre based wrapper) is the recommended path.
- You may self-host a style: download `style.json`, change the `glyphs`, `sprite` and source `url` to your own copies, and serve it anywhere.
