agents-inc/skills/src/skills/web-maps-mapbox/SKILL.md
web-maps-mapbox
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
- Source repository stars
- 23
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-09
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as th…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/agents-inc/skills --skill "src/skills/web-maps-mapbox"Inspect the Agent Skill "web-maps-mapbox" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/web-maps-mapbox/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
Rendering interactive vector tile maps with custom stylingDisplaying point/line/polygon data on a map with data-driven stylingBuilding map-based UIs with markers, popups, and custom controls - 02
Philosophy
Mapbox GL JS renders vector tiles on the GPU using WebGL 2, enabling smooth 60fps map interactions with large datasets. The core mental model is sources + layers + expressions:
Sources hold the data (GeoJSON, vector tiles, raster tiles, images)Layers define how to visualize sources (fill, line, circle, symbol, fill-extrusion, heatmap, raster)Expressions make layers data-driven (color by property, size by zoom, filter by attribute) - 03
Core Patterns
Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.
Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard styleSee examples/core.md Pattern 1 for cleanup patterns and bad examples. - 04
Pattern 1: Map Initialization
Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.
Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard styleSee examples/core.md Pattern 1 for cleanup patterns and bad examples. - 05
Pattern 2: Markers, Popups, and Controls
Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.
Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.Why good: Popup bound to marker (opens on click automatically), controls positioned explicitly, named color constantSee examples/core.md Pattern 2 for custom marker elements and programmatic popup examples.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 92/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 23 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- agents-inc/skills
- Skill path
- src/skills/web-maps-mapbox/SKILL.md
- Commit
- 81d43a51211aca12c85dcc16085fa99014ec548e
- License
- MIT
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
Mapbox GL JS Patterns
Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with
new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as the default base with slots (bottom,middle,top) for layer placement. Enable clustering on GeoJSON sources for large point datasets. UsesetTerrain+setFogfor 3D terrain. Types are included in themapbox-glpackage (no@types/mapbox-glneeded).
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST add sources before layers that reference them -- adding a layer without its source throws a runtime error)
(You MUST listen for load or style.load before calling addSource/addLayer -- the style is not ready on construction)
(You MUST clean up map instances with map.remove() on unmount -- leaks GPU memory and event listeners)
(You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)
(You MUST use expressions for data-driven styling instead of iterating features and setting styles individually)
</critical_requirements>
Auto-detection: Mapbox, mapbox-gl, mapboxgl, Map, Marker, Popup, NavigationControl, GeolocateControl, addSource, addLayer, GeoJSON source, vector source, expressions, flyTo, easeTo, fitBounds, setTerrain, setFog, fill-extrusion, clustering, slot, Standard style, mapbox-gl-geocoder, mapbox-gl-directions, mapbox-gl-draw
When to use:
- Rendering interactive vector tile maps with custom styling
- Displaying point/line/polygon data on a map with data-driven styling
- Building map-based UIs with markers, popups, and custom controls
- Visualizing large datasets with clustering, heatmaps, or 3D extrusions
- Adding geocoding search, routing directions, or drawing tools
- Creating 3D terrain visualizations with elevation data
When NOT to use:
- Static map images without interactivity (use Mapbox Static Images API)
- Simple embedded maps without custom data (a basic iframe embed suffices)
- Applications requiring offline-only maps without a Mapbox access token
Key patterns covered:
- Map initialization with Standard style and access token
- Markers, popups, and built-in controls
- Source/layer model (GeoJSON, vector, raster-dem)
- Expression-based data-driven styling
- Clustering with automatic expansion on click
- 3D terrain, fog, and fill-extrusion buildings
- Camera animation (flyTo, easeTo, fitBounds)
- Event handling (click, mouseenter, mouseleave on layers)
- v3 slot system and Standard style configuration
Detailed Resources:
- examples/core.md - Map setup, markers, popups, controls, events, camera animation
- examples/layers.md - Sources, layers, expressions, clustering, data-driven styling
- examples/interaction.md - 3D terrain, fog, fill-extrusion, drawing, geocoding, directions
- reference.md - Decision frameworks, layer types, expression operators, anti-patterns
Philosophy
Mapbox GL JS renders vector tiles on the GPU using WebGL 2, enabling smooth 60fps map interactions with large datasets. The core mental model is sources + layers + expressions:
- Sources hold the data (GeoJSON, vector tiles, raster tiles, images)
- Layers define how to visualize sources (fill, line, circle, symbol, fill-extrusion, heatmap, raster)
- Expressions make layers data-driven (color by property, size by zoom, filter by attribute)
This separation means one source can power multiple layers (e.g., same GeoJSON rendered as both a fill layer and a line layer for borders), and layers can be styled entirely through expressions without touching the data.
v3 Standard style: The default style is mapbox://styles/mapbox/standard, which includes 3D buildings, terrain-aware rendering, and a slot system (bottom, middle, top) for inserting custom layers at predetermined positions in the visual stack. Use setConfigProperty to customize the Standard style's appearance without replacing it.
TypeScript: Types are bundled with mapbox-gl since v3 -- do not install @types/mapbox-gl.
Core Patterns
Pattern 1: Map Initialization
Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
const DEFAULT_CENTER: [number, number] = [-74.006, 40.7128]; // [lng, lat]
const DEFAULT_ZOOM = 12;
mapboxgl.accessToken = process.env.MAPBOX_ACCESS_TOKEN!;
const map = new mapboxgl.Map({
container: "map", // HTML element ID or element reference
style: "mapbox://styles/mapbox/standard",
center: DEFAULT_CENTER,
zoom: DEFAULT_ZOOM,
});
map.on("load", () => {
// Safe to add sources and layers here
});
Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard style
See examples/core.md Pattern 1 for cleanup patterns and bad examples.
Pattern 2: Markers, Popups, and Controls
Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.
const MARKER_COLOR = "#e74c3c";
const popup = new mapboxgl.Popup({ offset: 25, maxWidth: "300px" }).setHTML(
"<h3>Location</h3><p>Description</p>",
);
new mapboxgl.Marker({ color: MARKER_COLOR })
.setLngLat([-74.006, 40.7128])
.setPopup(popup)
.addTo(map);
map.addControl(new mapboxgl.NavigationControl(), "top-right");
map.addControl(
new mapboxgl.GeolocateControl({ trackUserLocation: true }),
"top-right",
);
map.addControl(new mapboxgl.ScaleControl({ unit: "metric" }), "bottom-left");
Why good: Popup bound to marker (opens on click automatically), controls positioned explicitly, named color constant
See examples/core.md Pattern 2 for custom marker elements and programmatic popup examples.
Pattern 3: Source and Layer Model
Add a GeoJSON source, then one or more layers that reference it. Sources and layers are independent -- one source can feed multiple layers.
map.on("load", () => {
map.addSource("parks", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
/* ... */
],
},
properties: { name: "Central Park", area: 3.41 },
},
],
},
});
map.addLayer({
id: "parks-fill",
type: "fill",
source: "parks",
slot: "middle", // v3 Standard style slot
paint: {
"fill-color": "#2ecc71",
"fill-opacity": 0.5,
},
});
map.addLayer({
id: "parks-outline",
type: "line",
source: "parks",
slot: "middle",
paint: {
"line-color": "#27ae60",
"line-width": 2,
},
});
});
Why good: Source defined once, two layers visualize it differently, slot: "middle" places layers correctly in Standard style
See examples/layers.md Pattern 1-2 for all source types and layer configuration.
Pattern 4: Data-Driven Styling with Expressions
Expressions are JSON arrays that style features based on their properties or zoom level.
map.addLayer({
id: "population-circles",
type: "circle",
source: "cities",
paint: {
// Size by population
"circle-radius": [
"interpolate",
["linear"],
["get", "population"],
10000,
5,
100000,
15,
1000000,
30,
],
// Color by category
"circle-color": [
"match",
["get", "type"],
"capital",
"#e74c3c",
"major",
"#3498db",
"#95a5a6", // fallback
],
},
});
Why good: Expressions handle all styling on the GPU -- no JavaScript loops over features, scales with any dataset size
See examples/layers.md Pattern 3-4 for expression operators and filter expressions.
Pattern 5: Clustering
Enable clustering on a GeoJSON source for large point datasets. Use three layers: cluster circles, count labels, unclustered points.
const CLUSTER_RADIUS = 50;
const CLUSTER_MAX_ZOOM = 14;
map.addSource("earthquakes", {
type: "geojson",
data: "/data/earthquakes.geojson",
cluster: true,
clusterMaxZoom: CLUSTER_MAX_ZOOM,
clusterRadius: CLUSTER_RADIUS,
});
Why good: Clustering is handled entirely by the source -- no external library needed, automatic point_count property on clusters
See examples/layers.md Pattern 5 for complete cluster layers and click-to-expand interaction.
Pattern 6: Camera Animation
flyTo for dramatic transitions, easeTo for smooth pans, fitBounds for fitting data in view.
const FLY_ZOOM = 15;
const FLY_SPEED = 1.2;
const BOUNDS_PADDING_PX = 50;
map.flyTo({
center: [-122.4194, 37.7749],
zoom: FLY_ZOOM,
speed: FLY_SPEED,
essential: true, // not affected by prefers-reduced-motion
});
map.fitBounds(
[
[-122.5, 37.7],
[-122.3, 37.8],
], // [sw, ne]
{ padding: BOUNDS_PADDING_PX },
);
Why good: essential: true ensures critical navigation animations still play even with reduced-motion preferences, padding keeps data away from edges
See examples/core.md Pattern 4 for easeTo, moveend listener, and bearing/pitch animation.
Pattern 7: Layer Event Handling
Listen for events on specific layers for interactive features (click popups, hover effects).
map.on("click", "parks-fill", (e) => {
const feature = e.features?.[0];
if (!feature) return;
const coordinates = e.lngLat;
const name = feature.properties?.name ?? "Unknown";
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(`<strong>${name}</strong>`)
.addTo(map);
});
// Cursor feedback on hover
map.on("mouseenter", "parks-fill", () => {
map.getCanvas().style.cursor = "pointer";
});
map.on("mouseleave", "parks-fill", () => {
map.getCanvas().style.cursor = "";
});
Why good: Events scoped to a specific layer (not the whole map), cursor change signals interactivity
See examples/core.md Pattern 5 for feature-state hover highlighting.
Pattern 8: 3D Terrain and Fog
Add elevation with a raster-dem source and atmospheric effects with fog.
const TERRAIN_EXAGGERATION = 1.5;
const TERRAIN_MAX_ZOOM = 14;
const TERRAIN_TILE_SIZE = 512;
map.on("style.load", () => {
map.addSource("mapbox-dem", {
type: "raster-dem",
url: "mapbox://mapbox.mapbox-terrain-dem-v1",
tileSize: TERRAIN_TILE_SIZE,
maxzoom: TERRAIN_MAX_ZOOM,
});
map.setTerrain({ source: "mapbox-dem", exaggeration: TERRAIN_EXAGGERATION });
map.setFog({
range: [-1, 2],
"horizon-blend": 0.3,
color: "white",
"high-color": "#add8e6",
"space-color": "#d8f2ff",
"star-intensity": 0.0,
});
});
Why good: Named exaggeration constant, terrain source separate from visual layers, fog adds atmospheric depth
See examples/interaction.md Pattern 1-2 for fog presets and fill-extrusion 3D buildings.
Pattern 9: v3 Standard Style Configuration
Customize the Standard style's built-in appearance without replacing it.
// At initialization
const map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/standard",
config: {
basemap: {
lightPreset: "dusk",
showPointOfInterestLabels: false,
},
},
});
// At runtime
map.setConfigProperty("basemap", "lightPreset", "night");
map.setConfigProperty("basemap", "showPlaceLabels", true);
Why good: Configuration API modifies the Standard style's built-in features without needing to understand its internal layer structure
<decision_framework>
Decision Framework
Choosing a Layer Type
What geometry are you displaying?
|
+-> Points?
| +-> Few (<100) with custom HTML? -> Markers (DOM-based)
| +-> Many or data-driven styling? -> circle layer or symbol layer
| +-> Heatmap visualization? -> heatmap layer
|
+-> Lines/routes?
| +-> line layer (width, color, dash patterns)
|
+-> Polygons?
| +-> Flat colored areas? -> fill layer
| +-> 3D extruded shapes? -> fill-extrusion layer
|
+-> Raster imagery?
+-> raster layer (satellite, custom tiles)
Choosing a Source Type
Where is your data?
|
+-> Local/API GeoJSON? -> type: "geojson"
| +-> Dynamic updates? -> Use map.getSource(id).setData(newData)
| +-> Large point dataset? -> Enable cluster: true
|
+-> Mapbox tileset or third-party vector tiles? -> type: "vector"
|
+-> Elevation data? -> type: "raster-dem"
|
+-> Image overlay? -> type: "image" (with coordinates bounds)
Markers vs Circle Layers
How many points?
|
+-> < 100 with custom HTML/interaction? -> Markers (DOM elements)
+-> 100-10,000? -> circle layer (GPU-rendered)
+-> 10,000+? -> circle layer with clustering enabled on source
Styling Approach
Is the style static (same for all features)?
|
+-> YES -> Use literal paint values: "circle-color": "#e74c3c"
+-> NO -> Does it depend on a data property?
+-> Discrete categories? -> "match" expression
+-> Continuous range? -> "interpolate" expression
+-> Conditional logic? -> "case" expression
+-> Zoom-dependent? -> "interpolate" with ["zoom"]
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Calling
addSource/addLayerbefore theloadevent -- style is not ready, throws error - Adding a layer that references a source that doesn't exist -- must add source first
- Not calling
map.remove()on component unmount -- leaks GPU memory, WebGL contexts, and event listeners - Using
Popup.setHTML()with unsanitized user input -- XSS vulnerability. UsesetText()orsetDOMContent()for user data - Iterating features to set individual styles instead of using expressions -- defeats GPU rendering, O(n) JavaScript vs O(1) GPU expressions
Medium Priority Issues:
- Using Markers for large datasets (100+ points) -- DOM elements are expensive, use circle/symbol layers instead
- Not scoping layer events to a specific layer --
map.on("click", handler)fires for any click,map.on("click", "layer-id", handler)targets one layer - Missing cursor feedback on interactive layers -- users don't know features are clickable without
mouseenter/mouseleavecursor changes - Hardcoding coordinates, zoom levels, or style values -- use named constants
- Using
@types/mapbox-glpackage -- types are included inmapbox-glsince v3
Gotchas & Edge Cases:
- Coordinates are
[longitude, latitude]-- reversed from the common[lat, lng]order used by some libraries queryRenderedFeaturesonly returns features currently visible in the viewport -- for all features usequerySourceFeatures- GeoJSON source
setData()replaces the entire dataset -- for partial updates usefeatureStateviamap.setFeatureState() style.loadfires every time the style changes (includingsetStyle),loadfires only once -- usestyle.loadfor operations that must survive style switches- Expression property access returns
nullfor missing properties -- always provide fallback values inmatch/case/coalesce - Popup
setHTMLdoes not sanitize HTML -- any user-provided content must be sanitized before passing flyTowithessential: trueoverridesprefers-reduced-motion-- use only for critical navigation, not decorative animations- Clustered sources automatically add
point_countandcluster_idproperties -- do not create these manually getClusterExpansionZoomis async (callback-based) -- handle errors and check if map still exists before callingeaseTomap.getSource()returnsundefinedif the source doesn't exist -- always guard the return value- Layer
slotproperty only works with the Standard style -- classic styles usebeforeIdparameter inaddLayer fill-extrusionlayers requirefill-extrusion-heightproperty -- without it extrusions are flat (0 height)
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST add sources before layers that reference them -- adding a layer without its source throws a runtime error)
(You MUST listen for load or style.load before calling addSource/addLayer -- the style is not ready on construction)
(You MUST clean up map instances with map.remove() on unmount -- leaks GPU memory and event listeners)
(You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)
(You MUST use expressions for data-driven styling instead of iterating features and setting styles individually)
Failure to follow these rules will cause runtime errors, memory leaks, and XSS vulnerabilities.
</critical_reminders>
Frequently asked questions
What to verify before installation and use
What does the web-maps-mapbox source document cover?
Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as th…
How do I install web-maps-mapbox?
The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/web-maps-mapbox". Inspect the command and pinned source before running it.