← Files MapboxARCHIVED FILE
skills/mapbox-style-patterns/AGENTS.md
6.67 KB · Oct 2, 2026 · 00:26 UTC
# Mapbox Style Patterns
Quick reference for common style patterns, layer configurations, and data-driven styling.
## Layer Types Quick Reference
| Layer Type | Use For | Key Properties |
| ------------------ | --------------------------- | ------------------------------------ |
| **fill** | Polygons (countries, parks) | `fill-color`, `fill-opacity` |
| **line** | Roads, boundaries | `line-color`, `line-width` |
| **symbol** | Labels, icons | `text-field`, `icon-image` |
| **circle** | Points (markers, heatmap) | `circle-radius`, `circle-color` |
| **heatmap** | Density visualization | `heatmap-intensity`, `heatmap-color` |
| **fill-extrusion** | 3D buildings | `fill-extrusion-height` |
| **raster** | Satellite, aerial imagery | `raster-opacity` |
## Data-Driven Styling Patterns
### Based on Property Value
```javascript
// ✅ Color by category
'fill-color': [
'match',
['get', 'type'],
'park', '#90EE90',
'water', '#87CEEB',
'urban', '#D3D3D3',
'#CCCCCC' // default
]
// ✅ Size by numeric value
'circle-radius': [
'interpolate', ['linear'],
['get', 'population'],
0, 5,
1000000, 20
]
```
### Based on Zoom Level
```javascript
// ✅ Show/hide by zoom
'visibility': [
'step',
['zoom'],
'none', // Hidden below zoom 10
10, 'visible'
]
// ✅ Size by zoom
'text-size': [
'interpolate', ['linear'],
['zoom'],
8, 10, // Small at zoom 8
16, 18 // Large at zoom 16
]
```
## Common Patterns
### 1. Clustering
```javascript
map.addSource('points', {
type: 'geojson',
data: geojson,
cluster: true,
clusterRadius: 50,
clusterMaxZoom: 14
});
// Cluster circles
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
}
});
```
### 2. Feature State (Hover/Selection)
```javascript
// ✅ Hover effect without modifying data
map.on('mousemove', 'layer', (e) => {
if (hoveredId) {
map.setFeatureState(
{ source: 'source', id: hoveredId },
{ hover: false }
);
}
hoveredId = e.features[0].id;
map.setFeatureState(
{ source: 'source', id: hoveredId },
{ hover: true }
);
});
// Style based on state
'fill-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#0080ff', // Hover color
'#3bb2d0' // Default color
]
```
For binding the event with the newer `addInteraction` API instead of `map.on()`, and for `appearances` (a declarative alternative to the `case`/`feature-state` expression above, symbol layers only), see [references/interactions.md](references/interactions.md).
### 3. Filters
```javascript
// ✅ Filter by property
map.setFilter('layer', ['==', ['get', 'type'], 'restaurant']);
// ✅ Filter by multiple conditions
map.setFilter('layer', ['all', ['==', ['get', 'type'], 'restaurant'], ['>', ['get', 'rating'], 4]]);
// ✅ Filter by zoom
map.setFilter('layer', ['all', ['>=', ['zoom'], 10], ['<', ['zoom'], 14]]);
```
### 4. Expressions
```javascript
// ✅ Conditional styling
'circle-color': [
'case',
['<', ['get', 'value'], 10], '#00ff00', // Green if < 10
['<', ['get', 'value'], 20], '#ffff00', // Yellow if < 20
'#ff0000' // Red otherwise
]
// ✅ Math operations
'circle-radius': [
'*',
['sqrt', ['get', 'population']],
0.01
]
// ✅ String concatenation
'text-field': ['concat', 'Population: ', ['get', 'pop']]
```
## Performance Patterns
### Vector Tiles vs GeoJSON
**Use vector tiles when:**
- Large datasets (>5MB)
- Need different zoom levels
- Want server-side updates
**Use GeoJSON when:**
- Small datasets (<5MB)
- Frequent client-side updates
- Simple implementation needed
### Layer Optimization
```javascript
// ✅ Set minzoom/maxzoom
map.addLayer({
id: 'layer',
minzoom: 10, // Only show zoom 10+
maxzoom: 16 // Hide above zoom 16
});
// ✅ Use feature state instead of removing/re-adding
// Bad: map.removeLayer() / map.addLayer()
// Good: map.setFeatureState()
// ✅ Combine similar layers
// Bad: Separate layer for each category
// Good: One layer with data-driven styling
```
## Style Management
### Dynamic Style Updates
```javascript
// ✅ Update paint property
map.setPaintProperty('layer', 'fill-color', '#ff0000');
// ✅ Update layout property
map.setLayoutProperty('layer', 'visibility', 'none');
// ✅ Update source data
map.getSource('source').setData(newGeojson);
// ✅ Batch updates (better performance)
map.once('idle', () => {
// Multiple style changes here
});
```
### Before Layers
```javascript
// ✅ Insert layer at specific position
map.addLayer(
{
id: 'new-layer',
type: 'fill',
source: 'source'
},
'existing-layer-id'
); // Insert before this layer
```
## Common Use Cases
### Choropleth Map
```javascript
map.addLayer({
id: 'choropleth',
type: 'fill',
source: 'counties',
paint: {
'fill-color': ['interpolate', ['linear'], ['get', 'density'], 0, '#f7fbff', 100, '#08519c'],
'fill-opacity': 0.7
}
});
```
### Route Visualization
```javascript
map.addLayer({
id: 'route',
type: 'line',
source: 'route',
paint: {
'line-color': '#0080ff',
'line-width': 5,
'line-opacity': 0.8
}
});
```
### 3D Buildings
```javascript
map.addLayer({
id: 'buildings',
type: 'fill-extrusion',
source: 'composite',
'source-layer': 'building',
paint: {
'fill-extrusion-color': '#aaa',
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'min_height'],
'fill-extrusion-opacity': 0.8
}
});
```
## Quick Reference: Expression Types
| Type | Function | Example |
| ------------ | ------------------------ | ------------------ |
| **Decision** | match, case | Color by category |
| **Ramp** | interpolate, step | Size by value |
| **Math** | +, -, \*, /, % | Calculate values |
| **String** | concat, upcase, downcase | Format labels |
| **Lookup** | get, has, at | Access properties |
| **Zoom** | zoom | Zoom-based styling |
## Debugging Tips
```javascript
// ✅ Check if layer exists
if (map.getLayer('layer-id')) {
map.removeLayer('layer-id');
}
// ✅ Check if source exists
if (map.getSource('source-id')) {
map.removeSource('source-id');
}
// ✅ List all layers
console.log(map.getStyle().layers);
// ✅ Get layer paint properties
console.log(map.getPaintProperty('layer', 'fill-color'));
```
SHA-256: 6df7859c37811a92b2a7ef55d78f96e6f00e74d44ec4f66845ae9aefc1ca4b8a